畅购商城:微信支付

畅购商城文章系列

畅购商城:分布式文件系统FastDFS
畅购商城:商品的SPU和SKU概念
畅购商城:Lua、OpenResty、Canal实现广告缓存
畅购商城:微服务网关和JWT令牌(上)
畅购商城:微服务网关和JWT令牌(下)
畅购商城:Spring Security Oauth2 JWT(上)
畅购商城:Spring Security Oauth2 JWT(下)
畅购商城:购物车
畅购商城:订单
畅购商城:微信支付
畅购商城:秒杀(上)
畅购商城:秒杀(下)
畅购商城:分布式事务
畅购商城:集群高可用

学习目标

  • 能够说出微信支付开发的整体思路
  • 生成支付二维码
  • 查询支付状态
  • 实现支付日志的生成与订单状态的修改、删除订单
  • 支付状态回查 -> 微信服务器将支付状态返回给支付微服务
  • MQ处理支付回调状态
  • RabbitMQ延时队列实现超时订单取消回滚

1. 开发准备

1.1 微信支付模式

在这里插入图片描述
业务流程说明:

  1. 商户后台系统根据用户选购的商品生成订单,获取支付的订单号【唯一性】。
  2. 用户确认支付后调用微信支付【统一下单API】生成预支付交易(http -> 订单号);
  3. 微信支付系统收到请求后生成预支付交易单(订单号的预付款记录),并返回交易会话的二维码链接code_url(支付地址)。
  4. 商户后台系统根据返回的code_url生成二维码。
  5. 用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。
  6. 微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。
  7. 用户在微信客户端输入密码,确认支付后,微信客户端提交授权。
  8. 微信支付系统根据用户授权完成支付交易。
  9. 微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。
  10. 微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。
  11. 未收到支付通知的情况,商户后台系统调用【查询订单API】。
  12. 商户确认订单已支付后给用户发货。

在这里插入图片描述
为了实现支付的功能,选择接入微信支付。流程就是我们通过订单系统下单,然后订单系统调用支付系统去向微信支付的服务器发送请求,然后获取二维码返回给用户,然后订单系统就开始监听MQ。用户扫码支付后,支付系统将支付状态存进MQ中。订单系统检测到用户已经付钱了,就将订单设为已支付,然后存进MySQL中。可能会因为网络问题导致订单系统获取不到支付状态,所以订单系统会定时向微信支付服务器发送请求去查询订单状态。

1.2 微信支付SDK–主要作用数据封装

微信支付提供了SDK(微信支付开发工具包), 下载后打开源码,install到本地仓库。

使用微信支付SDK,在maven工程中引入依赖

<!--微信支付-->
<dependency>
    <groupId>com.github.wxpay</groupId>
    <artifactId>wxpay-sdk</artifactId>
    <version>0.0.3</version>
</dependency>

我们主要会用到微信支付SDK的以下功能:

获取随机字符串

WXPayUtil.generateNonceStr()

MAP转换为XML字符串(自动添加签名)

WXPayUtil.generateSignedXml(param, partnerkey)

XML字符串转换为MAP

WXPayUtil.xmlToMap(result)

为了方便微信支付开发,我们可以在changgou-common工程下引入依赖

<!--微信支付-->
<dependency>
    <groupId>com.github.wxpay</groupId>
    <artifactId>wxpay-sdk</artifactId>
    <version>0.0.3</version>
</dependency>

1.3 HttpClient工具类

HttpClient是Apache Jakarta Common下的子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。

HttpClient通俗的讲就是模拟了浏览器的行为,如果我们需要在后端向某一地址提交数据获取结果,就可以使用HttpClient。

作用:发送Http/Https请求
·········发送指定参数
········ 可以获取响应的结果

1.4 支付微服务搭建

(1)创建changgou-service-pay

创建支付微服务changgou-service-pay,只要实现支付相关操作。

(2)application.yml

创建application.yml,配置文件如下:

server:
  port: 18092
spring:
  application:
    name: pay
  main:
    allow-bean-definition-overriding: true
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:7001/eureka
  instance:
    prefer-ip-address: true
feign:
  hystrix:
    enabled: true
#hystrix 配置
hystrix:
  command:
    default:
      execution:
        timeout:
        #如果enabled设置为false,则请求超时交给ribbon控制
          enabled: true
        isolation:
          strategy: SEMAPHORE

#微信支付信息配置
weixin:
  appid: wx8397f8696b538317
  partner: 1473426802
  partnerkey: T6m9iK73b0kn9g5v426MKfHQH7X8rKwb
  notifyurl: http://www.itcast.cn

appid: 应用ID(微信公众账号或开放平台APP的唯一标识)

partner:商户ID(财付通平台的商户账号)

partnerkey:秘钥(财付通平台的商户密钥)

notifyurl: 支付回调地址

(3)启动类创建

在changgou-service-pay中创建com.changgou.WeixinPayApplication,代码如下:

@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
@EnableEurekaClient
public class WeixinPayApplication {

    public static void main(String[] args) {
        SpringApplication.run(WeixinPayApplication.class,args);
    }
}

2. 微信支付二维码生成

2.1 需求分析与实现思路

在支付页面上生成支付二维码,并显示订单号和金额

用户拿出手机,打开微信扫描页面上的二维码,然后在微信中完成支付

2.2 实现思路

我们通过HttpClient工具类实现对远程支付接口的调用。

接口链接:https://api.mch.weixin.qq.com/pay/unifiedorder

具体参数参见“统一下单”API, 构建参数发送给统一下单的url ,返回的信息中有支付url,根据url生成二维码,显示的订单号和金额也在返回的信息中。

2.3 代码实现

(1)业务层

新增com.changgou.service.WeixinPayService接口,代码如下:

public interface WeixinPayService {
    /*****
     * 创建二维码
     * @param out_trade_no : 客户端自定义订单编号
     * @param total_fee    : 交易金额,单位:分
     * @return
     */
    public Map createNative(String out_trade_no, String total_fee);
}

创建com.changgou.service.impl.WeixinPayServiceImpl类,并发送Post请求获取预支付信息,包含二维码扫码支付地址。代码如下:

@Service
public class WeixinPayServiceImpl implements WeixinPayService {

    @Value("${weixin.appid}")
    private String appid;

    @Value("${weixin.partner}")
    private String partner;

    @Value("${weixin.partnerkey}")
    private String partnerkey;

    @Value("${weixin.notifyurl}")
    private String notifyurl;

    /****
     * 创建二维码
     * @param out_trade_no : 客户端自定义订单编号
     * @param total_fee    : 交易金额,单位:分
     * @return
     */
    @Override
    public Map createNative(String out_trade_no, String total_fee){
        try {
            //1、封装参数
            Map param = new HashMap();
            param.put("appid", appid);                              //应用ID
            param.put("mch_id", partner);                           //商户ID号
            param.put("nonce_str", WXPayUtil.generateNonceStr());   //随机数
            param.put("body", "畅购");                            	//订单描述
            param.put("out_trade_no",out_trade_no);                 //商户订单号
            param.put("total_fee", total_fee);                      //交易金额
            param.put("spbill_create_ip", "127.0.0.1");           //终端IP
            param.put("notify_url", notifyurl);                    //回调地址
            param.put("trade_type", "NATIVE");                     //交易类型

            //2、将参数转成xml字符,并携带签名
            String paramXml = WXPayUtil.generateSignedXml(param, partnerkey);

            ///3、执行请求
            HttpClient httpClient = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
            httpClient.setHttps(true);
            httpClient.setXmlParam(paramXml);
            httpClient.post();

            //4、获取参数
            String content = httpClient.getContent();
            Map<String, String> stringMap = WXPayUtil.xmlToMap(content);
            System.out.println("stringMap:"+stringMap);

            //5、获取部分页面所需参数
            Map<String,String> dataMap = new HashMap<String,String>();
            dataMap.put("code_url",stringMap.get("code_url"));
            dataMap.put("out_trade_no",out_trade_no);
            dataMap.put("total_fee",total_fee);

            return dataMap;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

(2) 控制层

创建com.changgou.controller.WeixinPayController,主要调用WeixinPayService的方法获取创建二维码的信息,代码如下:

@RestController
@RequestMapping(value = "/weixin/pay")
@CrossOrigin
public class WeixinPayController {

    @Autowired
    private WeixinPayService weixinPayService;

    /***
     * 创建二维码
     * @return
     */
    @RequestMapping(value = "/create/native")
    public Result createNative(String outtradeno, String money){
        Map<String,String> resultMap = weixinPayService.createNative(outtradeno,money);
        return new Result(true, StatusCode.OK,"创建二维码预付订单成功!",resultMap);
    }
}

这里我们订单号通过随机数生成,金额暂时写死,后续开发我们再对接业务系统得到订单号和金额

3. 检测支付状态

3.1 需求分析

当用户支付成功后跳转到成功页面,当返回异常时跳转到错误页面。

3.2 实现思路

我们通过HttpClient工具类实现对远程支付接口的调用。

接口链接:https://api.mch.weixin.qq.com/pay/orderquery

具体参数参见“查询订单”API, 我们在controller方法中轮询调用查询订单(间隔3秒),当返回状态为success时,我们会在controller方法返回结果。前端代码收到结果后跳转到成功页面。

3.3 代码实现

(1)业务层

修改com.changgou.service.WeixinPayService,新增方法定义

/***
 * 查询订单状态
 * @param out_trade_no : 客户端自定义订单编号
 * @return
 */
public Map queryPayStatus(String out_trade_no);

在com.changgou.pay.service.impl.WeixinPayServiceImpl中增加实现方法

/***
 * 查询订单状态
 * @param out_trade_no : 客户端自定义订单编号
 * @return
 */
@Override
public Map queryPayStatus(String out_trade_no) {
    try {
        //1.封装参数
        Map param = new HashMap();
        param.put("appid",appid);                            //应用ID
        param.put("mch_id",partner);                         //商户号
        param.put("out_trade_no",out_trade_no);              //商户订单编号
        param.put("nonce_str",WXPayUtil.generateNonceStr()); //随机字符

        //2、将参数转成xml字符,并携带签名
        String paramXml = WXPayUtil.generateSignedXml(param,partnerkey);

        //3、发送请求
        HttpClient httpClient = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
        httpClient.setHttps(true);
        httpClient.setXmlParam(paramXml);
        httpClient.post();

        //4、获取返回值,并将返回值转成Map
        String content = httpClient.getContent();
        return WXPayUtil.xmlToMap(content);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

(2)控制层

在com.changgou.controller.WeixinPayController新增方法,用于查询支付状态,代码如下:

/***
 * 查询支付状态
 * @param outtradeno
 * @return
 */
@GetMapping(value = "/status/query")
public Result queryStatus(String outtradeno){
    Map<String,String> resultMap = weixinPayService.queryPayStatus(outtradeno);
    return new Result(true,StatusCode.OK,"查询状态成功!",resultMap);
}

4. 订单状态操作准备工作

4.1 需求分析

在这里插入图片描述
我们现在系统还有个问题需要解决:支付后订单状态没有改变

流程回顾:

1.用户下单之后,订单数据会存入到MySQL中,同时会将订单对应的支付日志存入到Redis,以List+Hash的方式存储。
2.用户下单后,进入支付页面,支付页面调用支付系统,从微信支付获取二维码数据,并在页面生成支付二维码。
3.用户扫码支付后,微信支付服务器会通调用前预留的回调地址,并携带支付状态信息。
4.支付系统接到支付状态信息后,将支付状态信息发送给RabbitMQ
5.订单系统监听RabbitMQ中的消息获取支付状态,并根据支付状态修改订单状态
6.为了防止网络问题导致notifyurl没有接到对应数据,定时任务定时获取Redis中队列数据去微信支付接口查询状态,并定时更新对应状态。

需要做的工作:

1.创建订单时,同时将订单信息放到Redis中,以List和Hash各存一份
2.实现回调地址接收支付状态信息
3.将订单支付状态信息发送给RabbitMQ
4.订单系统中监听支付状态信息,如果是支付成功,修改订单状态,如果是支付失败,删除订单(或者改成支付失败)
5.防止网络异常无法接收到回调地址的支付信息,定时任务从Redis List中读取数据判断是否支付,如果支付了,修改订单状态,如果未支付,将支付信息放入队列,下次再检测,如果支付失败删除订单(或者改成支付失败)。

4.2 Redis存储订单信息

每次添加订单后,会根据订单检查用户是否是否支付成功,我们不建议每次都操作数据库,每次操作数据库会增加数据库的负载,我们可以选择将用户的订单信息存入一份到Redis中,提升读取速度。

修改changgou-service-order微服务的com.changgou.order.service.impl.OrderServiceImpl类中的add方法,如果是线上支付,将用户订单数据存入到Redis中,由于每次创建二维码,需要用到订单编号 ,所以也需要将添加的订单信息返回。

/**
 * 增加Order
 * 金额校验:后台校验
 * @param order
 */
@Override
public Order add(Order order){
    //...略

    //修改库存
    skuFeign.decrCount(order.getUsername());

    //添加用户积分
    userFeign.addPoints(2);

    //线上支付,记录订单
    if(order.getPayType().equalsIgnoreCase("1")){
        //将支付记录存入到Reids namespace  key  value
        redisTemplate.boundHashOps("Order").put(order.getId(),order);
    }

    //删除购物车信息
    //redisTemplate.delete("Cart_" + order.getUsername());

    return order;
}

修改com.changgou.order.controller.OrderController的add方法,将订单对象返回,因为页面需要获取订单的金额和订单号用于创建二维码,
在这里插入图片描述

4.3 修改订单状态

订单支付成功后,需要修改订单状态并持久化到数据库,修改订单的同时,需要将Redis中的订单删除,所以修改订单状态需要将订单日志也传过来,实现代码如下:

修改com.changgou.order.service.OrderService,添加修改订单状态方法,代码如下:

/***
 * 根据订单ID修改订单状态
 * @param transactionid 交易流水号
 * @param orderId
 */
void updateStatus(String orderId,String transactionid);

修改com.changgou.order.service.impl.OrderServiceImpl,添加修改订单状态实现方法,代码如下:

/***
 * 订单修改
 * @param orderId
 * @param transactionid  微信支付的交易流水号
 */
@Override
public void updateStatus(String orderId,String transactionid) {
    //1.修改订单
    Order order = orderMapper.selectByPrimaryKey(orderId);
    order.setUpdateTime(new Date());    //时间也可以从微信接口返回过来,这里为了方便,我们就直接使用当前时间了
    order.setPayTime(order.getUpdateTime());    //不允许这么写
    order.setTransactionId(transactionid);  //交易流水号
    order.setPayStatus("1");    //已支付
    orderMapper.updateByPrimaryKeySelective(order);

    //2.删除Redis中的订单记录
    redisTemplate.boundHashOps("Order").delete(orderId);
}

4.4 删除订单

如果用户订单支付失败了,或者支付超时了,我们需要删除用户订单,删除订单的同时需要回滚库存。

修改changgou-service-order的com.changgou.order.service.OrderService,添加删除订单方法,我们只需要将订单id传入进来即可实现,代码如下:

/***
 * 删除订单操作
 * @param id
 */
void deleteOrder(String id);

修改changgou-service-order的com.changgou.order.service.impl.OrderServiceImpl,添加删除订单实现方法,代码如下:

/***
 * 订单的删除操作
 */
@Override
public void deleteOrder(String id) {
    //改状态
    Order order = (Order) redisTemplate.boundHashOps("Order").get(id);
    order.setUpdateTime(new Date());
    order.setPayStatus("2");    //支付失败
    orderMapper.updateByPrimaryKeySelective(order);

    //删除缓存
    redisTemplate.boundHashOps("Order").delete(id);
}

5. 支付信息回调

5.1 接口分析

每次实现支付之后,微信支付都会将用户支付结果返回到指定路径,而指定路径是指创建二维码的时候填写的notifyurl参数,响应的数据以及相关文档参考一下地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7&index=8

5.2 回调接收数据实现

修改changgou-service-pay微服务的com.changgou.pay.controller.WeixinPayController,添加回调方法,代码如下:

/***
 * 支付回调
 * @param request
 * @return
 */
@RequestMapping(value = "/notify/url")
public String notifyUrl(HttpServletRequest request){
    InputStream inStream;
    try {
        //读取支付回调数据
        inStream = request.getInputStream();
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = inStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }
        outSteam.close();
        inStream.close();
        // 将支付回调数据转换成xml字符串
        String result = new String(outSteam.toByteArray(), "utf-8");
        //将xml字符串转换成Map结构
        Map<String, String> map = WXPayUtil.xmlToMap(result);

        //响应数据设置
        Map respMap = new HashMap();
        respMap.put("return_code","SUCCESS");
        respMap.put("return_msg","OK");
        return WXPayUtil.mapToXml(respMap);
    } catch (Exception e) {
        e.printStackTrace();
        //记录错误日志
    }
    return null;
}

6. MQ处理支付回调状态

6.1 业务分析

在这里插入图片描述
支付系统是独立于其他系统的服务,不做相关业务逻辑操作,只做支付处理,所以回调地址接收微信服务返回的支付状态后,立即将消息发送给RabbitMQ,订单系统再监听支付状态数据,根据状态数据做出修改订单状态或者删除订单操作。

在这里插入图片描述

6.2 发送支付状态

6.2.1 集成RabbitMQ

修改支付微服务,集成RabbitMQ,添加如下依赖:

<!--加入ampq-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

如果使用程序创建队列,可以按照如下方式实现。
修改application.yml,配置支付队列和交换机信息,代码如下:

#位置支付交换机和队列
mq:
  pay:
    exchange:
      order: exchange.order
    queue:
      order: queue.order
    routing:
      key: queue.order

创建队列以及交换机并让队列和交换机绑定,修改com.changgou.WeixinPayApplication,添加如下代码:

/***
 * 创建DirectExchange交换机
 * @return
 */
@Bean
public DirectExchange basicExchange(){
    return new DirectExchange(env.getProperty("mq.pay.exchange.order"), true,false);
}

/***
 * 创建队列
 * @return
 */
@Bean(name = "queueOrder")
public Queue queueOrder(){
    return new Queue(env.getProperty("mq.pay.queue.order"), true);
}

/****
 * 队列绑定到交换机上
 * @return
 */
@Bean
public Binding basicBinding(){
    return BindingBuilder.bind(queueOrder()).to(basicExchange()).with(env.getProperty("mq.pay.routing.key"));
}

6.2.2 发送MQ消息

修改回调方法,在接到支付信息后,立即将支付信息发送给RabbitMQ,代码如下:

@Value("${mq.pay.exchange.order}")
private String exchange;
@Value("${mq.pay.queue.order}")
private String queue;
@Value("${mq.pay.routing.key}")
private String routing;

@Autowired
private WeixinPayService weixinPayService;

@Autowired
private RabbitTemplate rabbitTemplate;

/***
 * 支付回调
 * @param request
 * @return
 */
@RequestMapping(value = "/notify/url")
public String notifyUrl(HttpServletRequest request){
    InputStream inStream;
    try {
        //读取支付回调数据
        inStream = request.getInputStream();
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = inStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }
        outSteam.close();
        inStream.close();
        // 将支付回调数据转换成xml字符串
        String result = new String(outSteam.toByteArray(), "utf-8");
        //将xml字符串转换成Map结构
        Map<String, String> map = WXPayUtil.xmlToMap(result);
        //将消息发送给RabbitMQ
        rabbitTemplate.convertAndSend(exchange,routing, JSON.toJSONString(map));

        //响应数据设置
        Map respMap = new HashMap();
        respMap.put("return_code","SUCCESS");
        respMap.put("return_msg","OK");
        return WXPayUtil.mapToXml(respMap);
    } catch (Exception e) {
        e.printStackTrace();
        //记录错误日志
    }
    return null;
}

6.3 监听MQ消息处理订单

在订单微服务中,我们需要监听MQ支付状态消息,并实现订单数据操作。

6.3.1 集成RabbitMQ

在订单微服务中,先集成RabbitMQ,再监听队列消息。

在pom.xml中引入如下依赖:

<!--加入ampq-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

在application.yml中配置rabbitmq配置,代码如下:
在这里插入图片描述
在application.yml中配置队列名字,代码如下:

#位置支付交换机和队列
mq:
  pay:
    queue:
      order: queue.order

6.3.2 监听消息修改订单

在订单微服务于中创建com.changgou.order.consumer.OrderPayMessageListener,并在该类中consumeMessage方法,用于监听消息,并根据支付状态处理订单,代码如下:

@Component
@RabbitListener(queues = {"${mq.pay.queue.order}"})
public class OrderPayMessageListener {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private OrderService orderService;

    /***
     * 接收消息
     */
    @RabbitHandler
    public void consumeMessage(String msg){
        //将数据转成Map
        Map<String,String> result = JSON.parseObject(msg,Map.class);

        //return_code=SUCCESS
        String return_code = result.get("return_code");
        //业务结果
        String result_code = result.get("result_code");

        //业务结果 result_code=SUCCESS/FAIL,修改订单状态
        if(return_code.equalsIgnoreCase("success") ){
            //获取订单号
            String outtradeno = result.get("out_trade_no");
            //业务结果
            if(result_code.equalsIgnoreCase("success")){
                if(outtradeno!=null){
                    //修改订单状态  out_trade_no
                    orderService.updateStatus(outtradeno,result.get("transaction_id"));
                }
            }else{
                //订单删除
                orderService.deleteOrder(outtradeno);
            }
        }

    }
}

7. 定时处理订单状态

7.1 业务分析

在现实场景中,可能会出现这么种情况,就是用户支付后,有可能畅购服务网络不通或者服务器挂了,此时会导致回调地址无法接收到用户支付状态,这时候我们需要取微信服务器查询。所以我们之前订单信息的ID存入到了Redis队列,主要用于解决这种网络不可达造成支付状态无法回调获取的问题。

实现思路如下:

1.每次下单,都将订单存入到Reids List队列中
2.定时每5秒检查一次Redis 队列中是否有数据,如果有,则再去查询微信服务器支付状态
3.如果已支付,则修改订单状态
4.如果没有支付,是等待支付,则再将订单存入到Redis队列中,等会再次检查
5.如果是支付失败,直接删除订单信息并修改订单状态

8. 超时订单处理思路–RabbitMQ延时队列

在这里插入图片描述

当创建一个订单后,可能用户并不会去支付,或者是因为网络的原因导致我们获取不到用户支付的结果。所以可以采用定时处理的方式,具体的操作就是下单成功后就往mq的队列1中发送一条消息,设置30分钟过期,过期后将消息发送给队列2,然后我们监听队列2。这样一旦监听到了队列2的消息,则说明离下单已经过去了30分钟,这时候我们去查询一下订单状态,如果是已支付就不去管它,要是未支付的话就通知微信服务器关闭订单,然后从数据库中删除订单。

在这里插入图片描述

8.1 RabbitMQ延时队列原理

8.1.1 延时队列介绍

延时队列即放置在该队列里面的消息是不需要立即消费的,而是等待一段时间之后取出消费。

那么,为什么需要延迟消费呢?我们来看以下的场景

网上商城下订单后30分钟后没有完成支付,取消订单(如:淘宝、去哪儿网)系统创建了预约之后,需要在预约时间到达前一小时提醒被预约的双方参会系统中的业务失败之后,需要重试这些场景都非常常见,我们可以思考,比如第二个需求,系统创建了预约之后,需要在预约时间到达前一小时提醒被预约的双方参会。那么一天之中肯定是会有很多个预约的,时间也是不一定的,假设现在有1点 2点 3点 三个预约,如何让系统知道在当前时间等于0点 1点 2点给用户发送信息呢,是不是需要一个轮询,一直去查看所有的预约,比对当前的系统时间和预约提前一小时的时间是否相等呢?这样做非常浪费资源而且轮询的时间间隔不好控制。如果我们使用延时消息队列呢,我们在创建时把需要通知的预约放入消息中间件中,并且设置该消息的过期时间,等过期时间到达时再取出消费即可。

Rabbitmq实现延时队列一般而言有两种形式:
第一种方式:利用两个特性: Time To Live(TTL)、Dead Letter Exchanges(DLX)[A队列过期->转发给B队列]

第二种方式:利用rabbitmq中的插件x-delay-message

8.1.2 TTL DLX实现延时队列

(1)TTL介绍

RabbitMQ可以针对队列设置x-expires(则队列中所有的消息都有相同的过期时间)或者针对Message设置x-message-ttl(对消息进行单独设置,每条消息TTL可以不同),来控制消息的生存时间,如果超时(两者同时设置以最先到期的时间为准),则消息变为dead letter(死信)。

(2)Dead Letter Exchanges(DLX)介绍

RabbitMQ的Queue可以配置x-dead-letter-exchange和x-dead-letter-routing-key(可选)两个参数,如果队列内出现了dead letter,则按照这两个参数重新路由转发到指定的队列。x-dead-letter-exchange:出现dead letter之后将dead letter重新发送到指定exchange。

x-dead-letter-routing-key:出现dead letter之后将dead letter重新按照指定的routing-key发送。
在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值