【RocketMQ】实战-发企业微信通知

RocketMQ实战记录

背景

客户需要用企业微信通知客户。

先上代码
下面是异步生产者的代码,第一次发送时会报错,如下所示

2023-12-22 16:46:57.577  INFO 20832 --- [enderExecutor_1] c.c.m.plan.service.WeixinBasicsProducer  : 异步发送 - 发送失败
org.apache.rocketmq.client.exception.MQClientException: Send [1] times, still failed, cost [6008]ms, Topic: asyncSend, BrokersSent: [broker-a]
See http://rocketmq.apache.org/docs/faq/ for further details.
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendDefaultImpl(DefaultMQProducerImpl.java:665)
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.access$300(DefaultMQProducerImpl.java:97)
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl$4.run(DefaultMQProducerImpl.java:511)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
	at java.util.concurrent.FutureTask.run(FutureTask.java)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
Caused by: org.apache.rocketmq.remoting.exception.RemotingTooMuchRequestException: sendKernelImpl call timeout
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendKernelImpl(DefaultMQProducerImpl.java:832)
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendDefaultImpl(DefaultMQProducerImpl.java:584)
	... 8 more
package com.chinaztt.mes.plan.service;

import com.alibaba.fastjson.JSONArray;
import com.chinaztt.mes.common.util.JsonUtils;
import com.chinaztt.mes.plan.entity.WeixinLog;
import com.chinaztt.mes.technology.dto.OperationRoleDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;


/**
 *
 * 异步消息
 *
 */
@Service
@Slf4j
public class WeixinBasicsProducer {
    @Resource
    RocketMQTemplate rocketMQTemplate;
    @Resource
    WeixinLogService weixinLogService;
    @Resource
    private WeixinBasicsProducerFail weixinBasicsProducerFail;

    /**
     * 异步消息:异步消息通常用在对响应时间敏感的业务场景,即发送端不能容忍长时间地等待Broker的响应。
     */
    public void async(List<OperationRoleDTO> listMap) {
        WeixinLog weixinLog = new WeixinLog();
        weixinLog.setType("sentRktMq");
        weixinLog.setParameter(JsonUtils.toJsonArray(listMap).toString());
        weixinLogService.save(weixinLog);
        String text = "基本信息案例-异步发送" + System.currentTimeMillis();
        log.info(text);

        for (int a = 0; a < listMap.size(); a++) {
            OperationRoleDTO operationRoleDTO = listMap.get(a);
            rocketMQTemplate.asyncSend("asyncSend", listMap.get(a), new SendCallback() {

                // SendCallback接收异步返回结果的回调
                // 成功发送
                @Override
                public void onSuccess(SendResult sendResult) {

                    log.info("异步发送 - 发送成功");
                }

                // 发送失败
                @Override
                public void onException(Throwable throwable) {
                    log.info("异步发送 - 发送失败");
                    throwable.printStackTrace();
                    //异步发送者失败后,会再次发送一次
                    weixinBasicsProducerFail.sent(operationRoleDTO);
                }
            });
        }
        log.info("异步发送-已发送...");
    }
}


异步发送者失败后,会再次发送一次,调用一下代码

package com.chinaztt.mes.plan.service;

import com.chinaztt.mes.technology.dto.OperationRoleDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;


/**
 *
 * 当发送失败消息时,再次发送---异步消息
 *
 */
@Service
@Slf4j
public class WeixinBasicsProducerFail {
    @Resource
    RocketMQTemplate rocketMQTemplate;

    public void sent(OperationRoleDTO operationRoleDTO){

        rocketMQTemplate.asyncSend("asyncSendFail", operationRoleDTO, new SendCallback() {

            // SendCallback接收异步返回结果的回调
            // 成功发送
            @Override
            public void onSuccess(SendResult sendResult) {
                log.info("异步发送 - 发送成功");
            }

            // 发送失败
            @Override
            public void onException(Throwable throwable) {
                log.info("异步发送 - 发送失败");
                throwable.printStackTrace();

            }
        });
    }

}

下面开始消费者代码

package com.chinaztt.mes.plan.service;


import com.chinaztt.mes.common.util.JsonUtils;
import com.chinaztt.mes.plan.entity.WeixinLog;
import com.chinaztt.mes.technology.dto.OperationRoleDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * 异步消息
 */
@Component
@RocketMQMessageListener(selectorExpression = "", topic = "asyncSend", consumerGroup = "asyncSend")
@Slf4j
public class WeixinAsyncConsumer implements RocketMQListener<OperationRoleDTO> {
    @Resource
    WeixinLogService weixinLogService;
    @Resource
    WeixinBasicsProducerFail weixinBasicsProducerFail;

    @Override
    public void onMessage(OperationRoleDTO map) {
        Integer integer = 0;
        //上线后解开
        //integer = SendWxCpMsgUtil.sendToWxCp(map.getUsername(), map.getMessage());
        if (integer != 1) {
            try {
                TimeUnit.SECONDS.sleep(5);

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            weixinBasicsProducerFail.sent(map);
        }
        WeixinLog weixinLog = new WeixinLog();
        weixinLog.setType("sentWeixin");
        weixinLog.setParameter(JsonUtils.toJsonObject(map).toString());
        weixinLog.setResult(String.valueOf(integer));
        weixinLogService.save(weixinLog);

        log.info("异步消息-接受到消息: {}{}", map.getUsername(), map.getMessage());
    }

}

下面是异步消费者的代码

package com.chinaztt.mes.plan.service;


import com.chinaztt.mes.common.util.JsonUtils;
import com.chinaztt.mes.plan.entity.WeixinLog;
import com.chinaztt.mes.technology.dto.OperationRoleDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * 异步消息
 */
@Component
@RocketMQMessageListener(selectorExpression = "", topic = "asyncSendFail", consumerGroup = "asyncSendFail")
@Slf4j
public class WeixinAsyncConsumerFail implements RocketMQListener<OperationRoleDTO> {
    @Resource
    WeixinLogService weixinLogService;
    @Override
    public void onMessage(OperationRoleDTO map) {
        Integer integer = 0;
        //上线后解开
        //integer = SendWxCpMsgUtil.sendToWxCp(map.getUsername(), map.getMessage());

        WeixinLog weixinLog = new WeixinLog();
        weixinLog.setType("sentWeixin");
        weixinLog.setParameter(JsonUtils.toJsonObject(map).toString());
        weixinLog.setResult(String.valueOf(integer));
        weixinLogService.save(weixinLog);

        log.info("异步消息-接受到消息: {}{}", map.getUsername(), map.getMessage());
    }

}

下面是生产者的重试机制

package com.chinaztt.mes.plan.service;

import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;

/**
 * Producer,发送消息
 */
public class Producer {
    public static void main(String[] args) throws MQClientException, InterruptedException {
        DefaultMQProducer producer = new DefaultMQProducer("group_name");
        producer.setNamesrvAddr("192.168.2.222:9876;192.168.2.223:9876");
        producer.setRetryTimesWhenSendFailed(3);
        producer.start();

        for (int i = 0; i < 100; i++) {
            try {
                Message msg = new Message("TopicTest", 				// topic
                        "TagA", 									// tag
                        ("HelloWorld - RocketMQ" + i).getBytes()	// body
                );
                SendResult sendResult = producer.send(msg, 1000);
                System.out.println(sendResult);
            } catch (Exception e) {
                e.printStackTrace();
                Thread.sleep(1000);
            }
        }

        producer.shutdown();
    }
}


下面是消费者的重试代码

package com.chinaztt.mes.plan.service;


    import lombok.extern.slf4j.Slf4j;
    import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
    import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
    import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
    import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
    import org.apache.rocketmq.client.exception.MQClientException;
    import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
    import org.apache.rocketmq.common.message.MessageExt;
    import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
    import org.apache.rocketmq.remoting.common.RemotingHelper;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Service;
    import org.springframework.util.CollectionUtils;

    import javax.annotation.PostConstruct;
    import java.util.List;

/**

 * @Description:RocketMQ消息消费者
 */
@Slf4j
@Service
public class MessageConsumer implements MessageListenerConcurrently {
/*    @Value("${spring.rocketmq.namesrvAddr}")
    private String namesrvAddr;*/

    private final DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("DefaultConsumer");


    @PostConstruct
    public void start() {
        try {
            consumer.setNamesrvAddr("10.1.200.45:9876");

            //从消息队列头部开始消费
            consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);

            //设置集群消费模式
            consumer.setMessageModel(MessageModel.CLUSTERING);

            //订阅主题
            consumer.subscribe("asyncSendwew", "*");

            //注册消息监听器
            consumer.registerMessageListener(this);

            //启动消费端
            consumer.start();

            log.info("Message Consumer Start...");
            System.err.println("Message Consumer Start...");
        } catch (MQClientException e) {
            log.error("Message Consumer Start Error!!",e);
        }

    }

    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
        if (CollectionUtils.isEmpty(msgs)) {
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
        }

        MessageExt message = msgs.get(0);
        try {
            //逐条消费
            String messageBody = new String(message.getBody(), RemotingHelper.DEFAULT_CHARSET);
            System.err.println("Message Consumer: Handle New Message: messageId: " + message.getMsgId() + ",topic: " +
                    message.getTopic() + ",tags: " + message.getTags() + ",messageBody: " + messageBody);

            //模拟业务异常
            int i = 1 / 0;
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
        } catch (Exception e) {
            log.error("Consume Message Error!!", e);
            //抛出异常时,返回ConsumeConcurrentlyStatus.RECONSUME_LATER,尝试重试。当重试指定次数后返回ConsumeConcurrentlyStatus.CONSUME_SUCCESS
            int reconsumeTimes = message.getReconsumeTimes();
            System.err.println("Now Retry Times: " + reconsumeTimes);
            if (reconsumeTimes >= 18) {
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
            return ConsumeConcurrentlyStatus.RECONSUME_LATER;
        }
    }

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
u-upload 添加微信文件上传方式是指,在u-upload文件上传库中增加了支持微信文件上传的功能。通常情况下,u-upload库用于实现网站或应用中的文件上传功能,但原本的u-upload库并不支持微信文件上传。 微信文件上传是指用户通过微信平台将文件上传到网站或应用的过程。为了实现这一功能,开人员对u-upload库进行了修改和调整,使其能够与微信平台进行交互。 具体而言,为了实现u-upload的微信文件上传方式,开人员首先需要在网站或应用上放置一个微信登录的入口,以便用户可以使用微信登录,并授权访问其文件。 一旦用户使用微信登录并授权,u-upload库会调用微信接口获取用户在微信中上传的文件,并将其上传到指定的服务器上。开人员需要在服务器端进行文件接收和处理的逻辑实现。 这样,用户就可以通过微信平台将文件上传到网站或应用中,实现了u-upload的微信文件上传方式。 需要注意的是,开人员在实现u-upload的微信文件上传方式时,需要遵循微信平台的开规范和技术要求,确保用户的文件上传过程安全可靠。此外,还需要处理文件上传过程中可能出现的错误和异常,确保系统的稳定性和可用性。 总之,通过对u-upload库的修改和调整,开人员成功实现了u-upload的微信文件上传方式,使用户能够通过微信平台方便地将文件上传到网站或应用中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值