rocketmq入门使用&常见问题

入门使用

实际工作中使用阿里云rocketmq时,是通过底层工具类引入;工具类涉及定义通用mq消息模型,生产者,消费者,以及消息发送的方法。

0 工具类首先需要添加rocketmq java客户端依赖

<dependency>
			<groupId>com.aliyun.openservices</groupId>
			<artifactId>ons-client</artifactId>
			<version>1.8.8.8.Final</version>
		</dependency>

1 定义通用的消息

也就是发送消息时的消息对象

public class MqMessage implements Serializable {

   
    private static final long serialVersionUID = 5686153644127179622L;
    /**
     * 标签,一个业务点一个
     */
    private String tag;
    /**
     * 发送的消息体
     */
    private byte[] body;
    /**
     * key 用来阿里云后台检索消息
     */
    private String key;

    /**
     * 延迟时间,毫秒值
     */
    private long delayTime;

    private MqMessage(){}
....
public class MqMsg implements Serializable {
    private static final long serialVersionUID = -1688230884284293203L;

    /**
     * 通用参数
     */
    private String userId;    
    private Date serviceTime;
    private String uniqueId;
    
    private boolean refresh;
   

    /**
     * mq消息体body  不同业务消息体不同,可定义不同的java PO处理
     */
    private Map<String, Object> param;

    public MqMsg() {

    }

2 创建自定义的生产者

生产者不直接对外提供,发送消息的工具类使用自定义的生产者

public class MyProducer {
    private static final Logger logger = LoggerFactory.getLogger(MyProducer.class);

    private static Producer producer;

    private static Config CFG = new ConfigImpl();

    /**
     * Spring bean init-method
     */
    public static Producer getDefaultMQProducer() {
        if(producer == null){
            synchronized (MyProducer.class) {
                Properties properties = new Properties();
                properties.put(PropertyKeyConst.AccessKey, CFG.getString(ConfigConstants.ROCKETMQ_ACCESSKEY));// AccessKey 阿里云身份验证,在阿里云服务器管理控制台创建
                properties.put(PropertyKeyConst.SecretKey,  CFG.getString(ConfigConstants.ROCKETMQ_SECRETKEY));// SecretKey 阿里云身份验证,在阿里云服务器管理控制台创建
                properties.put(PropertyKeyConst.ONSAddr,CFG.getString(ConfigConstants.ROCKETMQ_ONSADDR));//mq实例访问地址
                properties.put(PropertyKeyConst.ProducerId, CFG.getString(ConfigConstants.ROCKETMQ_PRODUCERGROUP));//您在控制台创建的Producer ID
                properties.setProperty(PropertyKeyConst.SendMsgTimeoutMillis, "3000");
                producer = ONSFactory.createProducer(properties);
                producer.start();
                logger.info("DefaultMQProudcerV2 start success! {}", properties.get(PropertyKeyConst.AccessKey));
            }
        }
        return producer;
    }

    /**
     * Spring bean destroy-method
     */
    public static void destroy() {
        logger.info("rocketmq Producer destroy!");
        if(producer!=null){
            producer.shutdown();
            logger.info("rocketmq Producer destroy success!");
            producer = null;
        }

    }
}

3 工具类创建自定义的消费者

public class MyConsumer {
    private static final Logger logger = LoggerFactory.getLogger(MyConsumer.class);

    private MessageListener listener;
    private static Consumer consumer;


    public void init(InitInfo initInfo){
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.ONSAddr, initInfo.getoNSAddr());
        properties.put(PropertyKeyConst.AccessKey, initInfo.getAccessKey());
        properties.put(PropertyKeyConst.SecretKey,  initInfo.getSecretKey());
        properties.put(PropertyKeyConst.GROUP_ID, initInfo.getGroupID());
        properties.setProperty(PropertyKeyConst.ConsumeThreadNums,String.valueOf(initInfo.getConsumeThreadNums()));
        properties.setProperty(PropertyKeyConst.MaxReconsumeTimes, String.valueOf(initInfo.getMaxReconsumeTimes()));
        properties.setProperty(PropertyKeyConst.MessageModel, MessageModel.CLUSTERING.name()); //指定消费者模式 集群或广播
        

        consumer = ONSFactory.createConsumer(properties);
        String[] topicTags = initInfo.getTopictag().split(",");
        for (String topicTag : topicTags) {
            String[] s = topicTag.split(":");

            // 订阅消息

//参数1:topic – 消息主题
//参数2:subExpression – 订阅过滤表达式字符串,ONS服务器依据此表达式进行过滤。只支持或运算 eg: "tag1 || tag2 || tag3" 如果subExpression等于null或者*,则表示全部订阅
//参数3:listener – 消息回调监听器
            consumer.subscribe(s[0], s[1], listener);

        }
        consumer.start();
        logger.info("###### MQ consumer started, topiocTags - {} ######", initInfo.getTopictag());

        Thread shutdownHookOne = new Thread() {
            @Override
            public void run() {
                MyConsumer.destroy();
            }
        };
        Runtime.getRuntime().addShutdownHook(shutdownHookOne);
    }


    public static void destroy() {
        logger.info("rocketmq Consumer shutdown!");
        if(consumer!=null){
            consumer.shutdown();
            logger.info("rocketmq Consumer shutdown success!");
        }
    }

    public MessageListener getListener() {
        return listener;
    }

    public void setListener(MessageListener listener) {
        this.listener = listener;
    }
}

4 定义发消息工具类

public class MqUtil {
    /**
     * 阿里提供的生产者
     */
    private static Producer producer;

    private static Logger log = LoggerFactory.getLogger(cn.org.bjca.ywq.common.base.mq.rocketmqV2.MqUtil.class);

    private static Config cfg = new ConfigImpl();

    public MqUtil() {
        producer = MyProducer.getDefaultMQProducer();

    }

    /**
     * <p>
     * sendMsgAsync
     * </p>
     *
     * @param msg
     * @Description:异步发消息
     */
    public static void sendMsgAsync(String topic, final MqMessage msg) {
        //topic 后缀环境
        String topicEnv = cfg.getString(ConfigConstants.ROCKETMQ_TOPIC_ENV);
        if (StringUtils.isBlank(topicEnv)) {
            throw new BizException(GlobalExcpEnum.TOPIC_ENV_EMPTY);
        }
        topic = topic + topicEnv;
        Message msgs = new Message(topic, msg.getTag(), msg.getKey(), msg.getBody());
        long delayTime = msg.getDelayTime();
        if (delayTime > 0){
            long millis = System.currentTimeMillis();
            
            msgs.setStartDeliverTime(millis + delayTime);
        }
       
        try {
            if (producer == null) {
                
                producer = MyProducer.getDefaultMQProducer();
            }
            producer.sendAsync(msgs, new SendCallback() {
                @Override
                // 成功的回调函数
                public void onSuccess(SendResult sendResult) {
                    log.debug("send message success. topic:{}, msgId:{}, tag:{}, key:{}", sendResult.getTopic(),
                        sendResult.getMessageId(), msg.getTag(), msg.getKey());
                }

                @Override
                public void onException(OnExceptionContext context) {
                    log.error("send message failed. topic:{}, msgId:{}, tag:{}, key:{} " , context.getTopic(),
                        context.getMessageId(), msg.getTag(), msg.getKey());
                    log.error("mq send exception", context.getException());
                }
            });
        } catch (ONSClientException e) {
            log.error("TMS-MQ发消息异常", e);
            throw new RuntimeException("MQ发消息异常");
        }
    }

    public void destroy(){
        MyProducer.destroy();
    }
}

5 应用端启动类mq配置


@Configuration
public class MqConfig {
    private static Logger log = LoggerFactory.getLogger(MqConfig.class);

    @Autowired
    private ConfigNacosImpl config4Nacos;

    @Resource
    private Config conf;

    /**
     * 初始化自动签消费者监听
     * @return
     */
    @Bean
    public MyConsumer initAAAAConsumer() {
        String topicEnv = conf.getString(ConfigConstants.ROCKETMQ_TOPIC_ENV);
        if (StringUtils.isBlank(topicEnv)) {
            throw new BizException(GlobalExcpEnum.TOPIC_ENV_EMPTY);
        }
        MyConsumer myConsumer = new MyConsumer();
        //配置所在dataId = es_ywx
        String onsAddr = config4Nacos.getString(ConfigConstants.ROCKETMQ_ONSADDR.getName(), "");
        String accessKey = conf.getString(ConfigConstants.ROCKETMQ_ACCESSKEY);
        String secretKey = conf.getString(ConfigConstants.ROCKETMQ_SECRETKEY);
        String consumerGroupID =
            config4Nacos.getString(ConfigConstants.ROCKETMQ_CONSUMERGROUP.getName(), "") + RecipeInfoMQCons.SELF_SIGN + "_" + topicEnv;
        String topicTag = config4Nacos.getString(ConfigConstants.ROCKETMQ_TOPICTAG_SELFSIGN.getName(), "");
        int consumeThreadNums = config4Nacos.getInt(ConfigConstants.ROCKETMQ_CONSUMER_THREAD_NUMS_SELF_SIGN.getName(), 10);
        int maxReconsumeTimes = config4Nacos.getInt(ConfigConstants.ROCKETMQ_MAX_RECONSUME_TIMES.getName(), 16);
        myConsumer.setListener(AAAAListener()); //设置消息监听处理类
        myConsumer.init(new InitInfo(onsAddr, accessKey, secretKey, consumerGroupID, topicTag, consumeThreadNums,
            maxReconsumeTimes));
        log.info("selfSign消费者初始化成功:groupID:{}, topicTag:{}", consumerGroupID, topicTag);
        return myConsumer;
    }

  

    /**
     * 初始化mq生产者组件 用于业务发消息
     * @return
     */
    @Bean(destroyMethod = "destroy")
    public MqUtil initMqProducer() {
        log.info("rocketmq release!");
        return new MqUtil();
    }

   

    @Bean
    public AAAAListener aAAAListener(){
    //
        return new AAAAListener ();
    }

   
}

6 应用端发送消息(根据不同业务,消息体也不一样,但流程大概一致)

//创建自定义消息体
***Msg = new ...


// es异步写入消息  
public void sendEsAdd(String orderId, Integer signType, String userId, String clientId, boolean isTimeStamp, ***Msg **msg) {
        MqMsg mqMsg = new MqMsg();
        mqMsg.setRefresh(false);
        mqMsg.setUserId(userId);       
        mqMsg.setServiceTime(new Date());
        Map param = new HashMap();
      
        mqMsg.setParam(param);
          mqMsg.getParam().put(MqParamKeyEnum....getMapKey(), **msg);

        String jsonString = JsonUtils.toJSONString(mqMsg);
        MqMessage mes = new MqMessage(MqTagEnum.ADD.getType(), jsonString, orderId);
        String topicName = MqTopicEnum.****.getTopicName();
        try {
            MqUtil.sendMsgAsync(topicName, mes);
        } catch (Exception e) {
            log.error("发送ES写消息失败sendEsCoolAddMq recipeIds#{}#", orderId, e);
        }

        
    }

7 应用端消费消息Listener

public class AAAAListener implements MessageListener {

    private static Logger log = LoggerFactory.getLogger(AAAAListener.class);
   
     /**
    
     * 第几次重试       1,   2,   3,   4,  5,   6,  7,  8,  9,  10,  11,  12,   13,   14,   15,  16,  17
     * 与上次重试间隔   10s, 30s, 1m,  2m, 3m,  4m, 5m, 6m, 7m, 8m,  9m,  10m,  20m,  30m,  1h,  2h,  2h
     *
     */
    @Override
    public Action consume(Message message, ConsumeContext context) {
        String topic = message.getTopic();
        String tag = message.getTag();
        String key = message.getKey();
        MqMsg mqMsg = JSONObject.parseObject(message.getBody(), MqMsg.class);

        Map<String, Object> param = mqMsg.getParam();
        try {
           
            boolean isOk = false;
             // isOk = 消费逻辑处理
            if (isOk){
                return Action.CommitMessage;
            }else {
                log.error("self_mq消费失败 topic:{}, tag:{}, key:{}, 次数:{}, body:{} ", topic, tag, key, message.getReconsumeTimes(),JSONObject.toJSONString(mqMsg));
                return Action.ReconsumeLater;
            }
       
        }catch (Exception e){
            log.error("消费异常 topic:{}, tag:{}, key:{}, 次数上限:{}, body:{}", topic, tag, key, message.getReconsumeTimes(),JSONObject.toJSONString(mqMsg));          
            return Action.ReconsumeLater;
        }
            
        }

消费失败可以设置重试次数,阿里云官方文档描述的重试策略如下图所示

生产问题

某个节点的mq堆积了。。

疑问点

1 看阿里云日志,消费者日志从凌晨就没有了,listener挂了?为什么?

尚未找到原因,java进程已重启,当时的线程堆栈也无法jstack分析了...

2 为什么消息还会一直往这个消费者放,不应该将消息分发到其他节点的消费么?

这个只能说自己技术不佳,使用rocketmq不够熟练,基本概念没搞清楚,消费者模式有2种,集群消费和广播消费,默认不配置的话就是集群消费。

阿里云官方文档这样描述:

云消息队列 RocketMQ 版的消费方式支持在消费者客户端修改,您需要在订阅消息的SDK代码中设置相关参数。若未设置,则默认使用集群消费方式。

初始化消费者时,可以指定消息消费模式
properties.setProperty(PropertyKeyConst.MessageModel, MessageModel.CLUSTERING.name()); //指定消费者模式 集群或广播

补充下官网集群消费的概念

未完待续,需要学习。。。有知情的同学欢迎留言指教 感谢:)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值