短信发送+实现高并发下高可用(HTTP连接池+异步)
2023-09-06 23:01 由
xietingweia 发表于
#后端开发
依赖注入
<properties>
<spring.boot.version>2.5.5</spring.boot.version>
<lombok.version>1.18.16</lombok.version>
</properties>
<dependencies>
<!--springBoot初始化-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<!--springBootWeb-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
<version>${spring.boot.version}</version>
</dependency>
<!--lombok工具-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!-- OSS各个项目单独加依赖,根据需要进行添加-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.10.2</version>
</dependency>
<!--测试相关-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.26-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>${spring.boot.version}</version>
<scope>test</scope>
</dependency>
<!--guava-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1-jre</version>
</dependency>
</dependencies>
application.properties
server.port=8001
spring.application.name=Sms-service
#----sms 短信配置-------
sms.app-code=8b620f10437441f5889d91caa0267a06
sms.template-id=JM1000372
SmsComponent
import com.xtw.config.SmsConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
@Component
@Slf4j
public class SmsComponent {
@Autowired
private RestTemplate restTemplate;
@Autowired
private SmsConfig smsConfig;
/**
* 短信发送api接口
*/
private static final String URL_TEMPLATE = "https://jmsms.market.alicloudapi.com/sms/send?mobile=%s&templateId=%s&value=%s";
/**
* 发送验证码
*/
public void send(String to,String template,String value){
String url = String.format(URL_TEMPLATE, to, template, value);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set("Authorization","APPCODE "+smsConfig.getAppCode());
HttpEntity<Object> entity = new HttpEntity<>(httpHeaders);
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
if(responseEntity.getStatusCode().is2xxSuccessful()){
log.info("验证码发送成功");
}else {
log.error("发送短信验证码失败:{}",responseEntity.getBody());
}
}
}
SmsConfig
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@ConfigurationProperties(prefix = "sms")
@Configuration
@Data
public class SmsConfig {
/**
* 短信验证码发送 appcode
*/
private String appCode;
/**
* 短信内容模板
*/
private String templateId;
}
RestTemplateConfig(使用HTTP协议请求)
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory httpRequestFactory){
return new RestTemplate(httpRequestFactory);
}
@Bean
public ClientHttpRequestFactory httpRequestFactory(){
return new HttpComponentsClientHttpRequestFactory();
}
@Bean
public HttpClient httpClient(){
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
//设置连接池最大是500个连接
connectionManager.setMaxTotal(500);
//MaxPerRoute是对maxtotal的细分,每个主机的并发最大是300,route是指域名
connectionManager.setDefaultMaxPerRoute(300);
RequestConfig requestConfig = RequestConfig.custom()
//返回数据的超时时间
.setSocketTimeout(20000)
//连接上服务器的超时时间
.setConnectTimeout(10000)
//从连接池中获取连接的超时时间
.setConnectionRequestTimeout(1000)
.build();
return HttpClientBuilder.create().setDefaultRequestConfig(requestConfig)
.setConnectionManager(connectionManager)
.build();
}
}
测试
import com.xtw.SmsApplication;
import com.xtw.component.SmsComponent;
import com.xtw.config.SmsConfig;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes= SmsApplication.class)
@Slf4j
public class SmsTest {
@Autowired
private SmsConfig smsConfig;
@Autowired
private SmsComponent smsComponent;
@Test
public void testProperties(){
System.out.println(smsConfig.getAppCode());
}
@Test
public void testSend(){
smsComponent.send("15070159890",smsConfig.getTemplateId(),"652421");
}
}
异步配置
@Configuration
@EnableAsync
public class ThreadPoolTaskConfig {
@Bean("threadPoolTaskExecutor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程数
executor.setCorePoolSize(16);
//阻塞队列容量
executor.setQueueCapacity(3000);
//最大线程数
executor.setMaxPoolSize(300);
//非核心线程空闲时,30s后被释放
executor.setKeepAliveSeconds(30);
//如果allowCoreThreadTimeout=true,核心线程也会被释放
executor.setAllowCoreThreadTimeOut(false);
//线程名前缀
executor.setThreadNamePrefix("自定义线程池-");
//线程溢出,处理方案
//CallerRunsPolicy():交由调用方线程运行,比如 main 线程;如果添加到线程池失败,那么主线程会自己去执行该任务,不会等待线程池中的线程去执行
//AbortPolicy():该策略是线程池的默认策略,如果线程池队列满了丢掉这个任务并且抛出RejectedExecutionException异常。
//DiscardPolicy():如果线程池队列满了,会直接丢掉这个任务并且不会有任何异常
//DiscardOldestPolicy():丢弃队列中最老的任务,队列满了,会将最早进入队列的任务删掉腾出空间,再尝试加入队列
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
//初始化
executor.initialize();
return executor;
}
}
使用异步
@Async("threadPoolTaskExecutor")
public void send(String to,String template,String value) {
String url = String.format(URL_TEMPLATE, to, template, value);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set("Authorization","APPCODE "+smsConfig.getAppCode());
HttpEntity<Object> entity = new HttpEntity<>(httpHeaders);
// ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
ResponseEntity<String> responseEntity = restTemplate.exchange("http://localhost:8002/api/visit_stats/v1/str", HttpMethod.POST, entity, String.class);
if(responseEntity.getStatusCode().is2xxSuccessful()){
log.info("验证码发送成功");
}else {
log.error("发送短信验证码失败:{}",responseEntity.getBody());
}
}
热门相关:我有一座恐怖屋 已经湿了,快进去 让我随心所欲投资的姐姐 我的姐夫只找我 智娥迷人的性爱