0


Springboot项目整合RabbitMQ+Redis实现可靠的阿里云短信异步收发功能(手把手实操详细教程)

文章目录

1、项目介绍

1.1、项目描述

以下是一个完整的 Spring Boot 项目案例,整合 RabbitMQ 实现阿里云短信异步收发,并将发送情况存入数据库,同时使用 Redis 缓存验证码;

这个项目旨在实现一个可靠的短信发送系统,结合了多种技术来确保短信的高效发送和管理。以下是对各个部分的详细描述:

1.2、项目结构

项目结构如下:

├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── tigerhhzz
│   │   │           ├── SpringbootMqAliyunAmsApplication.java
│   │   │           ├── config
│   │   │           │   └── RabbitMQConfig.java
│   │   │           ├── model
│   │   │           │   └── SmsLog.java
│   │   │           ├── service
│   │   │           │   ├── SmsConsumer.java
│   │   │           │   ├── SmsSenderService.java
│   │   │           │   ├── SmsService.java
│   │   │           │   └── VerificationCodeService.java
│   │   │           └── task
│   │   │               └── SmsResendTask.java
│   │   ├── resources
│   │   │   ├── application.yml
│   │   │   └── schema.sql
│   └── test
│       └── java
└── pom.xml

2、创建项目(idea)

2.1、依赖引入

  • 通过pom.xml文件引入了必要的依赖,包括 Spring Boot 的 AMQP 模块用于与 RabbitMQ 集成、JDBC 模块用于数据库操作、MySQL 数据库驱动、阿里云短信服务 SDK 和 Redis 模块用于缓存验证码。

pom.xml

<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.0.RELEASE</version></parent><modelVersion>4.0.0</modelVersion><groupId>com.tigerhhzz</groupId><artifactId>springboot-rabbitmq-aliyun-sms-demo</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId><version>4.0.6</version><!-- 注:如提示报错,先升级基础包版,无法解决可联系技术支持 --></dependency><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-dysmsapi</artifactId><version>2.1.0</version></dependency><dependency><groupId>com.aliyun</groupId><artifactId>tea-openapi</artifactId><version>0.2.2</version></dependency><dependency><groupId>com.aliyun</groupId><artifactId>tea-util</artifactId><version>0.2.20</version></dependency><!-- redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!-- commons-lang3 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.4</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

2.2、 配置文件

  • application.yml文件中配置了 RabbitMQ、阿里云短信服务、数据库和 Redis 的连接信息。这些配置使得项目能够连接到相应的服务和资源。
server:port:8080spring:application:name: springboot-rabbitmq-demo
    # 添加数据源配置datasource:url: jdbc:mysql://localhost:3306/springbootrabbitmq_db_msg?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghaiusername: root
    password:123456driver-class-name: com.mysql.jdbc.Driver
  #rabbitmqrabbitmq:host: xxxxxxxxxxx
    port:5672virtual-host: /
    username: guest
    password: guest
    # 开启confirms回调 P -> Exchangepublisher-confirms:true# 开启returnedMessage回调 Exchange -> Queuepublisher-confrms-type: correlation
    publisher-returns:true# 设置手动确认(ack) Queue -> Clistener:simple:acknowledge-mode: manual
      prefetch:100#redisredis:host: 8.217.205.108
    port:6379password: xxxxxxxx
    timeout:50000#阿里短信配置aliyun:sms:accessKey: xxxxxxxx
    accessKeySecret: xxxxxxxxx
    signName: 珑湾崖头
    templateCode: SMS_474285221
    domain: dysmsapi.aliyuncs.com
    regionId: cn-beijing

# 配置mybatis的xml配置文件扫描目录mybatis:mapper-locations:- classpath:mapper/*.xmlconfiguration:# 打印SQL语句,需要注射掉这个mybatis属性配置,否则启动报错log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

2.3、 数据库表

  • schema.sql文件中创建了sms_log表,用于记录短信的发送状态和时间。这个表有助于跟踪短信的发送历史,并可以用于后续的分析和故障排除。
CREATETABLE sms_log (
    id INTAUTO_INCREMENTPRIMARYKEY,
    unique_id VARCHAR(36),-- 存储 UUID
    phone_number VARCHAR(20),
    message_content VARCHAR(255),
    send_status TINYINT,-- 0 表示未发送成功,1 表示发送成功
    send_time TIMESTAMP);

2.4、 实体类

  • SmsLog类是一个简单的 Java 对象,用于表示数据库中的短信记录。它包含了短信的相关信息,如手机号码、发送状态和发送时间。
packagecom.tigerhhzz.model;importlombok.Data;importjava.util.Date;/**
 * @Author tigerhhzz
 * @Date 2024 10 05 11 37
 **/@DatapublicclassSmsLog{privateint id;privateString uniqueId;privateString phoneNumber;privateString messageContent;privateint sendStatus;privateDate sendTime;}

2.5、 配置类

  • RabbitMQConfig类配置了 RabbitMQ 的队列、交换器和绑定关系。通过定义这些元素,项目可以使用 RabbitMQ 进行异步消息传递。
packagecom.tigerhhzz.config;importorg.springframework.amqp.core.Binding;importorg.springframework.amqp.core.BindingBuilder;importorg.springframework.amqp.core.Queue;importorg.springframework.amqp.core.TopicExchange;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;/**
 * @Author tigerhhzz
 * @Date 2024 10 05 11 38
 **/@ConfigurationpublicclassRabbitMQConfig{publicstaticfinalString SMS_QUEUE ="smsQueue";publicstaticfinalString SMS_EXCHANGE ="smsExchange";publicstaticfinalString SMS_ROUTING_KEY ="smsRoutingKey";@BeanpublicQueuesmsQueue(){returnnewQueue(SMS_QUEUE);}@BeanpublicTopicExchangesmsExchange(){returnnewTopicExchange(SMS_EXCHANGE);}@BeanpublicBindingbinding(Queue smsQueue,TopicExchange smsExchange){returnBindingBuilder.bind(smsQueue).to(smsExchange).with(SMS_ROUTING_KEY);}}

2.6、 验证码服务类

  • VerificationCodeService类负责生成和验证验证码。它使用 Redis 来缓存生成的验证码,并设置了过期时间。生成验证码时,它会生成一个随机的 6 位数字验证码,并将其存储在 Redis 中,同时返回给调用者。验证验证码时,它会从 Redis 中获取存储的验证码,并与用户输入的验证码进行比较。
packagecom.tigerhhzz.service;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.stereotype.Service;importjava.util.Random;importjava.util.concurrent.TimeUnit;@ServicepublicclassVerificationCodeService{privatefinalRedisTemplate<String,String> redisTemplate;publicVerificationCodeService(RedisTemplate<String,String> redisTemplate){this.redisTemplate = redisTemplate;}publicStringgenerateVerificationCode(String key){// 生成随机验证码String verificationCode =generateRandomCode();// 将验证码存入 Redis,并设置过期时间,比如 5 分钟
        redisTemplate.opsForValue().set(key, verificationCode,5,TimeUnit.MINUTES);return verificationCode;}publicbooleanverifyVerificationCode(String key,String code){String storedCode = redisTemplate.opsForValue().get(key);return storedCode!=null&& storedCode.equals(code);}privateStringgenerateRandomCode(){// 生成 6 位随机数字验证码Random random =newRandom();int code = random.nextInt(900000)+100000;returnString.valueOf(code);}}

2.7、 短信发送服务类

  • SmsService类负责实际的短信发送操作。它使用阿里云短信服务的 SDK 来发送短信,并在发送成功或失败时更新数据库中的发送状态。此外,它还与验证码服务类交互,生成并包含验证码在短信内容中。
packagecom.tigerhhzz.service;importcom.aliyun.teaopenapi.Client;importcom.aliyun.teaopenapi.models.Config;importcom.aliyuncs.CommonRequest;importcom.aliyuncs.CommonResponse;importcom.aliyuncs.DefaultAcsClient;importcom.aliyuncs.IAcsClient;importcom.aliyuncs.http.MethodType;importorg.apache.commons.lang3.StringUtils;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.jdbc.core.JdbcTemplate;importorg.springframework.stereotype.Service;importcom.aliyuncs.profile.DefaultProfile;/**
 * @Author tigerhhzz
 * @Date 2024 10 05 11 40
 **/@ServicepublicclassSmsService{@Value("${aliyun.sms.accessKey}")privateString accessKeyId;@Value("${aliyun.sms.regionId}")privateString regionId;@Value("${aliyun.sms.domain}")privateString domain;@Value("${aliyun.sms.accessKeySecret}")privateString accessKeySecret;@Value("${aliyun.sms.signName}")privateString signName;@Value("${aliyun.sms.templateCode}")privateString templateCode;privatefinalJdbcTemplate jdbcTemplate;privatefinalVerificationCodeService verificationCodeService;publicSmsService(JdbcTemplate jdbcTemplate,VerificationCodeService verificationCodeService){this.jdbcTemplate = jdbcTemplate;this.verificationCodeService = verificationCodeService;}publicStringsendSms(String phoneNumber,String uniqueId){try{// 生成验证码String verificationCode = verificationCodeService.generateVerificationCode(phoneNumber);DefaultProfile profile =DefaultProfile.getProfile(regionId,accessKeyId, accessKeySecret);IAcsClient client =newDefaultAcsClient(profile);CommonRequest request =newCommonRequest();
            request.setMethod(MethodType.POST);
            request.setDomain(domain);
            request.setVersion("2017-05-25");
            request.setAction("SendSms");
            request.putQueryParameter("RegionId", regionId);
            request.putQueryParameter("PhoneNumbers", phoneNumber);//目标手机号
            request.putQueryParameter("SignName", signName);//签名名称
            request.putQueryParameter("TemplateCode", templateCode);//短信模板code
            request.putQueryParameter("TemplateParam","{\"code\":\""+ verificationCode +"\"}");//模板中变量替换CommonResponse response = client.getCommonResponse(request);String data = response.getData();System.out.println("发送短信后的响应结果:"+data);if(StringUtils.contains(data,"\"Message\":\"OK\"")){// 发送成功,更新数据库状态
                jdbcTemplate.update("UPDATE sms_log SET send_status = 1, send_time = NOW() WHERE unique_id =?", uniqueId);return"发送成功,验证码:"+verificationCode;}else{// 发送失败,也可以记录失败原因等
                jdbcTemplate.update("UPDATE sms_log SET send_status = 0 WHERE unique_id =?", uniqueId);return"发送失败,验证码:"+verificationCode;}}catch(Exception e){
            e.printStackTrace();// 发送异常,更新数据库状态为失败
            jdbcTemplate.update("UPDATE sms_log SET send_status = 0 WHERE unique_id =?", uniqueId);System.out.println("发送短信验证码失败~ phoneNumber = "+ phoneNumber + e);}returnnull;}}

2.8、 消费者类

  • SmsConsumer类是 RabbitMQ 的消费者,它监听特定的队列,并在接收到消息时调用短信发送服务类的方法来发送短信。
packagecom.tigerhhzz.service;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.amqp.rabbit.annotation.RabbitListener;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;importjava.util.Map;@ComponentpublicclassSmsConsumer{privatestaticfinalLogger LOGGER =LoggerFactory.getLogger(SmsConsumer.class);@AutowiredprivateSmsService smsService;//    @RabbitListener(queues = "smsQueue")@RabbitListener(queues ={"smsQueue"})publicvoidreceiveSmsTask(Map<String,String> map){String phoneNumber = map.get("phoneNumber");String uniqueId = map.get("uniqueId");// 解析消息,获取手机号码和 UUID
        smsService.sendSms(phoneNumber, uniqueId);}}

2.9、发送服务类

  • SmsSenderService类提供了一个方法,用于将短信发送任务异步发送到 RabbitMQ。在发送任务之前,它会将任务记录到数据库中,初始状态为未发送。
packagecom.tigerhhzz.service;importorg.springframework.amqp.core.AmqpTemplate;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.jdbc.core.JdbcTemplate;importorg.springframework.stereotype.Service;importjava.util.UUID;@ServicepublicclassSmsSenderService{@AutowiredprivateAmqpTemplate amqpTemplate;@AutowiredprivateJdbcTemplate jdbcTemplate;publicvoidsendSmsAsync(String phoneNumber){// 生成 UUID 作为唯一 IDString uniqueId = UUID.randomUUID().toString();// 先记录发送任务到数据库,初始状态为未发送
        jdbcTemplate.update("INSERT INTO sms_log (phone_number, message_content, send_status, unique_id) VALUES (?,?, 0,?)", phoneNumber,null, uniqueId);String message = phoneNumber +","+ uniqueId;
        amqpTemplate.convertAndSend("smsExchange","smsRoutingKey", message);}}

2.10、定时任务类

  • SmsResendTask类定义了一个定时任务,用于检查数据库中未发送成功的短信,并重新发送它们。这个定时任务每分钟执行一次,确保未发送成功的短信能够尽快得到重新发送。
packagecom.tigerhhzz.task;importcom.tigerhhzz.service.SmsSenderService;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.jdbc.core.JdbcTemplate;importorg.springframework.scheduling.annotation.Scheduled;importorg.springframework.scheduling.config.ScheduledTask;importorg.springframework.stereotype.Component;/**
 * 定时任务
 * @Author tigerhhzz
 * @Date 2024 10 05 13 15
 **/@ComponentpublicclassSmsResendTask{privatestaticfinalLogger LOGGER =LoggerFactory.getLogger(SmsResendTask.class);privatefinalSmsSenderService smsSenderService;privatefinalJdbcTemplate jdbcTemplate;publicSmsResendTask(SmsSenderService smsSenderService,JdbcTemplate jdbcTemplate){this.smsSenderService = smsSenderService;this.jdbcTemplate = jdbcTemplate;}//@Scheduled(fixedRate = 60000) // 每分钟检查一次@Scheduled(cron ="0/30 * * * * ?")publicvoidcheckAndResendSms(){
        LOGGER.info("开始执行重新发送失败的消息!");
        jdbcTemplate.query("SELECT phone_number, unique_id FROM sms_log WHERE send_status = 0",(rs, rowNum)->{String phoneNumber = rs.getString("phone_number");String uniqueId = rs.getString("unique_id");
            smsSenderService.sendSmsAsync(phoneNumber);returnnull;});}}

2.11、启动类

  • Application类是项目的启动类,它使用 Spring Boot 的自动配置功能来启动应用程序。通过@SpringBootApplication注解和@ComponentScan注解,确保项目中的所有组件都能够被正确扫描和加载。
packagecom.tigerhhzz;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.context.annotation.ComponentScan;/**
 * @Author tigerhhzz
 * @Date 2024 10 05 10 10
 **/@SpringBootApplication@ComponentScan(basePackages ="com.tigerhhzz")publicclassSpringbootMqAliyunAmsApplication{publicstaticvoidmain(String[] args){SpringApplication.run(SpringbootMqAliyunAmsApplication.class, args);System.out.println("SpringbootMqAliyunAmsApplication启动成功!!!");}}

2.12、测试控制器

packagecom.tigerhhzz.controller;importcom.tigerhhzz.service.SmsSenderService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.*;/**
 * @Author tigerhhzz
 * @Date 2024 10 05 13 35
 **/@RestController@RequestMapping("/sms")publicclassSmsController{@AutowiredprivateSmsSenderService smsSenderService;/**
     * 通过mq发送短信验证码
     * @param phoneNumber
     */@RequestMapping(value="/sendsmsbymq/{phoneNumber}",method=RequestMethod.POST)publicStringsendCode(@PathVariableString phoneNumber ){
        smsSenderService.sendSmsAsync(phoneNumber);System.out.println("手机号:"+phoneNumber+";发送短信验证码成功");return"短信发送任务已提交,将异步发送。";}}

3、效果测试

启动主启动类:

post请求:http://localhost:8080/sms/sendsmsbymq/150xxxx0598

使用postman进行测试:在这里插入图片描述

idea后台打印结果:
在这里插入图片描述
rabbitmq监控消息:
在这里插入图片描述
查看数据库:
在这里插入图片描述
结果:消息成功发送!!!

4、总结

通过以上的设计和实现,这个项目可以实现可靠的短信发送功能,并使用 Redis 缓存验证码来提高系统的安全性和用户体验。同时,通过异步发送和定时任务重发机制,可以确保短信的高可用性和可靠性。在实际应用中,可以根据具体需求进一步扩展和优化这个项目。

5、附件-整个源码仓库

https://gitee.com/spring2020/springboot-mq-aliyun-ams-application
感谢关注点赞!!!

在这里插入图片描述


人生从来没有真正的绝境。只要一个人的心中还怀着一粒信念的种子,那么总有一天,他就能走出困境,让生命重新开花结果。



本文转载自: https://blog.csdn.net/weixin_43025151/article/details/142712485
版权归原作者 hhzz 所有, 如有侵权,请联系我们删除。

“Springboot项目整合RabbitMQ+Redis实现可靠的阿里云短信异步收发功能(手把手实操详细教程)”的评论:

还没有评论