activeMQ 了解一下(三)——重发机制+项目应用

  • 项目场景:

推送通知给第三方,并得到第三方的feedback,json交互

  • 问题:

推送过程遇到网络异常?第三方异常?数据格式错误?等等问题怎么办

【这就是重试机制了,即什么时候触发+触发后做什么】

  • 希望达成的目的:

1.网络异常,代码异常,对方响应码为失败等无关业务数据的推送失败,重试发送消息,上限为3次
2.数据异常,针对消息里数据解析后对方给回失败响应,不重发,记录发送失败

  • 实现方式:

1,使用activemq,mq的好处见参见文章activeMQ了解一下(一)。且其有重发机制,刚好适合本场景(配置mq)
2,每次重发,都记录次数;视情况记录发送结果为失败/成功(数据库记录)

搭建mq见文章activeMQ了解一下(二),本文只说如何配置重发机制

【第一步,配置xml】

spring-activemq.xml如下


  

    
    
    
        
        
        
        
        
        
        
        
        
        
    
    
        
            
                tcp://192.168.28.2:61616?
                
            
        
        
    

    
    
        
        
    
    
    
    
        
        
            
        
        
    
    
        
      
    
    
    
    
        
        
        
        
    

敲重点:

  • 要配置重发机制
  • 监听器要开启session事务,见配置最后一行

【第二步,代码里触发】

public class AwardMsgQueueListener implements MessageListener{
    
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    @Autowired
    private IOrderService orderService;
    
    @Override
    @SuppressWarnings("unchecked")
    public void onMessage(Message message) {
        TextMessage tm = (TextMessage) message;
        Integer sendTimes = 0;
        String msg="",data = "",notifyUrl="",channel="";
        try {
            msg = tm.getText();
        } catch (JMSException e1) {
            logger.error("从消息队列获取消息出现异常,请检查");
            e1.printStackTrace();
            return;
        }
        logger.info("接收到的消息为:"+msg);
        try{
            Map> msgMap = JSONObject.parseObject(msg, new TypeReference>>(){});
            notifyUrl = msgMap.keySet().iterator().next();
            data = JSONObject.toJSONString(msgMap.get(notifyUrl));
//          notifyUrl = "http://127.0.0.1:10003/order/receiveMsg.json";
            Map dataMap = JSONObject.parseObject(data,new TypeReference>(){});
            channel = (String)dataMap.get("channel");
            CloseableHttpClient httpClient = HttpClients.createDefault();
            HttpPost httpPost= new HttpPost(notifyUrl);
            httpPost.setHeader("Content-type", "application/json");
            httpPost.setEntity(new StringEntity(data));
            CloseableHttpResponse response = httpClient.execute(httpPost);
            if(200 !=response.getStatusLine().getStatusCode()){
                //请求失败时,记录推送次数,状态仍为推送中,不提交事务
                logger.error("推送失败,statusCode为:"+response.getStatusLine().getStatusCode());
                this.updateOrderAfterFail(data);
                throw new RuntimeException();
            }else{
                HttpEntity responseEntity = response.getEntity();
                if(responseEntity == null || responseEntity.getContent()==null){
                    logger.error("推送商户中奖信息成功,但返回内容为空,url:"+notifyUrl);
                    this.updateOrderAfterFail(data);
                    throw new RuntimeException("推送商户中奖信息成功,但返回内容为空,url:"+notifyUrl);
                }else{
                    StringBuilder entityStringBuilder = new StringBuilder();  
                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(responseEntity.getContent(),"UTF-8"), 8 * 1024);  
                    String line = null;  
                    while ((line = bufferedReader.readLine()) != null) {  
                        entityStringBuilder.append(line);  
                    } 
                    String result = entityStringBuilder.toString();
                    Map resultMap = JSONObject.parseObject(result,new TypeReference>(){});
                    if(!resultMap.containsKey("data") || !resultMap.containsKey("security") || !resultMap.containsKey("response")){
                        logger.error("推送商户中奖信息成功,但返回内容不合规,响应数据中可能不包含“data,security,reponse”中的一个或多个");
                        this.updateOrderAfterFail(data);
                        throw new RuntimeException("推送商户中奖信息成功,但返回内容不合规,响应数据中可能不包含“data,security,reponse”中的一个或多个");
                    }
                    if("11111".equals((String)resultMap.get("response"))){
                        logger.error("推送商户中奖信息成功,但返回内容响应码为11111");
                        this.updateOrderAfterFail(data);
                        throw new RuntimeException("推送商户中奖信息成功,但返回内容响应码为11111");
                    }
                    List> orderList = (List>)resultMap.get("data");
                    for(Map order : orderList){
                        List orderResult = orderService.getByOrderNumAndChannel(order.get("orderNum"),channel);
                        String serialNumber = orderResult.get(0).getSerialNumber();
                        OrderInfoVo existOrder = orderService.queryBySerialNumber(serialNumber);
                        OrderInfo orderInfo = new OrderInfo();
                        orderInfo.setSerialNumber(serialNumber);
                        orderInfo.setSendAwardTimes(existOrder.getSendAwardTimes()+1);
                        orderInfo.setSendAwardFlag(SendAwardFlagEnum.SEND_SUCCESS.getCode());
                        if("111111".equals(order.get("code"))){
                            orderInfo.setSendAwardFlag(SendAwardFlagEnum.SEND_FAIL.getCode());
                        }
                        orderService.updateByOrderInfo(orderInfo);
                    }
                }
            }
        } catch (Exception e){
            logger.error("推送消息异常捕捉:"+e.getMessage());
            e.printStackTrace();
            this.updateOrderAfterFail(data);
            throw new RuntimeException();//抛出此异常,触发重发机制
        }
    }

敲重点:

在需要重发时,抛出RuntimeException异常,会自动触发重发机制
由于还涉及到其他可能的异常,所以整体代码try..catch..最后统一throw

不过呢,现在还存在一个问题,配置里设置了重发次数为2,即包括初次发,一共发三次。但是最后数据库显示是最后发了6次的,6次是mq默认的次数,说明那个配置没生效,暂时还未解决这个问题

另外,消息队列里的消息这里是同步发送的,即按顺序一条条的来。前一条发送失败并重发时候,后一条是等待的。也可以配置异步发送,这里暂不做研究

PS:这里用到的消费者监听器时最简单基础的,还有一种SessionAwareMessageListener,重发机制的配置会略有不同,待尝试

你可能感兴趣的:(activeMQ 了解一下(三)——重发机制+项目应用)