通过消息队列MQ异步请求阿里云短信发送服务,发送验证码,并存入redis

1.创建生产者工程
application.yml配置rabbitmq

spring:
  redis:
    host: 192.168.71.100
  rabbitmq:
    host: 192.168.71.100:虚拟机ip
    virtual-host: /ysvh
    username: admin
    password: 123456
    publisher-confirm-type: CORRELATED # 确认消息是否到达交换机:SIMPLE-同步确认(阻塞) CORRELATED-异步确认
    publisher-returns: true # 确认消息是否到达队列

2.引入相关依赖

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

其他依赖

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>RELEASE</version>
            <scope>test</scope>
        </dependency>

3.配置生产者config

package com.atguigu.gmall.ums.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

@Configuration
@Slf4j
public class RabbitConfig {

    @Autowired
    private RabbitTemplate rabbitTemplate;
    //构造器初始化后就会执行
    @PostConstruct
    public void init(){
        //确认消息是否到达交换机的回调
        this.rabbitTemplate.setConfirmCallback((correlationData,ack,cause)->{
            if(ack){
                System.out.println("消息已经到达交换机");
            }else {
                System.out.println("消息未到达交换机");
            }
        });
        //确认消息是否到达队列的回调(只有消息无法到达队列才回调)
        this.rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange,  routingKey)->{
            log.error("消息未到达队列,交换机:{},路由键:{},消息内容:{}",exchange,routingKey,new String(message.getBody()));
        });
    }
    //业务交换机
    @Bean
    public TopicExchange exchange(){
        return ExchangeBuilder.topicExchange("UMS-MESSAGE-EXCHANGE").durable(true).build();
    }

    //业务队列
    @Bean
    public Queue queue(){
        return QueueBuilder.durable("UMS-SENDMESSAGE-QUEUE")
                .withArgument("x-dead-letter-exchange","UMS-DEAD-EXCHANGE")
                .withArgument("x-message-ttl", 60000)
                .withArgument("x-dead-letter-routing-key","msg.dead").build();
    }
    //业务交换机绑定业务队列
    @Bean
    public Binding binding(TopicExchange exchange, Queue queue){
        return BindingBuilder.bind(queue).to(exchange).with("message.send");
    }
    //死信交换机
    @Bean
    public TopicExchange deadExchange(){
        //return new TopicExchange("UMS-DEAD-EXCHANGE",true,false);
        return ExchangeBuilder.topicExchange("UMS-DEAD-EXCHANGE").durable(true).build();
    }
    //死信队列
    @Bean
    public Queue deadQueue(){
        //return new Queue("UMS-DEAD-EXCHANGE",true,false,false);
        return QueueBuilder.durable("UMS-DEAD-QUEUE").build();
    }
    //绑定
    @Bean
    public Binding deadBinding(TopicExchange deadExchange,Queue deadQueue){
        //return new Binding("SPRING-DEAD-QUEUE",Binding.DestinationType.QUEUE,"SPRING-DEAD-EXCHANGE","msg.dead",null);
        return BindingBuilder.bind(deadQueue).to(deadExchange).with("msg.dead");
    }
}

4.controller

@Api(tags = "用户表 管理")
@RestController
@RequestMapping("ums/user")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("send")
    public ResponseVo<Object> sendMessage(UserEntity userEntity) {
        this.userService.sendMessage(userEntity.getPhone());

        return ResponseVo.ok(null);
    }
}

5.service

public interface UserService extends IService<UserEntity> {

    void sendMessage(String phone);
}

6.service.impl
通过rabbitTemplate的convertAndSend方法发送消息phone给交换机UMS-MESSAGE-EXCHANGE

@Override
public void sendMessage(String phone){
    this.rabbitTemplate.convertAndSend("UMS-MESSAGE-EXCHANGE","message.send",phone);
}

7.创建消费者工程
application.yml配置rabbitmq

spring:
  redis:
    host: 192.168.71.100
  rabbitmq:
    host: 192.168.71.100
    virtual-host: /ysvh
    username: admin
    password: 123456
    listener:
      simple:
        acknowledge-mode: manual   #手动确认
        #none-不确认模式(原生api的自动确认)  auto-自动确认模式(正常执行即确认;执行异常会无限重试) manual手动确认
        prefetch: 1 #开启能者多劳
        #如何避免消息堆积?  搭建消费者集群(能者多劳);开启消费者的多线程
        #开启多个线程
        concurrency: 3
##阿里云短信
aliyun:
  sms:
    regionId: cn-hangzhou
    keyId: 阿里云keyid
    keySecret: XXXX
    templateCode: XXXX
    signName: 你的签名
   

8.引入相关依赖


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.atguigu</groupId>
            <artifactId>gmall-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <!--阿里云短信-->
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

9.创建消费者监听器config

package com.atguigu.gmall.message.listener;

import com.atguigu.gmall.common.utils.FormUtils;
import com.atguigu.gmall.common.utils.RandomUtils;
import com.atguigu.gmall.message.service.SmsService;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;


import java.io.IOException;
import java.util.concurrent.TimeUnit;


@Component
@Slf4j
public class MessageListener {

    @Autowired
    private SmsService smsService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    private static  final String KEY_PREFIX="message:";
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "UMS-SENDMESSAGE-QUEUE",durable = "true"),//durable:是否持久化
            //忽略声明异常:当一个交换机已经存在时,再次声明,若属性不一致,会出现声明异常
            exchange = @Exchange(value = "UMS-MESSAGE-EXCHANGE",ignoreDeclarationExceptions = "true",type = ExchangeTypes.TOPIC),
            key = {"message.send"}
            ))
    public void listener(String phone, Channel channel, Message message) throws IOException {

        try {
            //校验手机号是否合法
            if(StringUtils.isEmpty(phone) || !FormUtils.isMobile(phone)) {
                log.error("请输入正确的手机号码 ");
                //throw new GuliException(ResultCodeEnum.LOGIN_PHONE_ERROR);
            }

            //生成验证码
            String checkCode = RandomUtils.getFourBitRandom();
            //发送验证码
            smsService.send(phone, checkCode);
            //将验证码存入redis缓存
            redisTemplate.opsForValue().set(KEY_PREFIX+phone, checkCode, 5, TimeUnit.MINUTES);

            //手动确认 multiple:true:确认多条
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        } catch (Exception e) {
            e.printStackTrace();
            //是否重复投递
            if(message.getMessageProperties().getRedelivered()){
                //记录下来,放入数据库
                //如果一条消息被拒绝,并且该队列绑定了死信队列的情况下,该消息会进入死信队列
                channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
            }else {
                channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,true);
            }
        }
    }

}
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值