springboot + RocketMq 练习

最近项目上在使用rocektmq,特此记录一下

一、pom依赖

        <!-- rocketmq -->
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-client</artifactId>
            <version>4.7.1</version>
        </dependency>

二、配置

#rocketmq配置
rocketmq:
  produceGroupName: unique_group_name 
  consumerGroupName: unique_group_name   
  namesrvAddr: 127.0.0.1:9876
  topic: test-service 
   
  producer:
    maxMessageSize: 4096
    sendMsgTimeout: 3000
    retryTimesWhenSendFailed: 2
  consumer:
    consumeThreadMin: 5
    consumeThreadMax: 32
    consumeMessageBatchMaxSize: 1
  

三、代码

1、读取配置

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;


@Component
public class RocketMqConfig {

    //发送同一类消息的设置为同一个group,保证唯一,默认不需要设置,rocketmq会使用ip@pid(pid代表jvm名字)作为唯一标示
    public static String produceGroupName;
    //消费者分组
    public static String consumerGroupName;
    // mq的nameserver地址
    public static String namesrvAddr;

    // 主题
    public static String topic;

    //消息最大长度 默认1024*4(4M)
    public static Integer producerMaxMessageSize;
    //发送消息超时时间,默认3000
    public static Integer producerSendMsgTimeout;
    //发送消息失败重试次数,默认2
    public static Integer producerRetryTimesWhenSendFailed;


    //消费者线程数量
    public static Integer consumeThreadMin;
    public static Integer consumeThreadMax;
    //设置一次消费消息的条数,默认为1条
    public static Integer consumeMessageBatchMaxSize;

    @Value("${rocketmq.produceGroupName}")
    public  void setProduceGroupName(String produceGroupName) {
        RocketMqConfig.produceGroupName = produceGroupName;
    }

    @Value("${rocketmq.consumerGroupName}")
    public  void setConsumerGroupName(String consumerGroupName) {
        RocketMqConfig.consumerGroupName = consumerGroupName;
    }

    @Value("${rocketmq.namesrvAddr}")
    public void setNamesrvAddr(String namesrvAddr) {
        RocketMqConfig.namesrvAddr = namesrvAddr;
    }

    @Value("${rocketmq.topic}")
    public void setTopic(String topic) {
        RocketMqConfig.topic = topic;
    }

    @Value("${rocketmq.producer.maxMessageSize}")
    public void setProducerMaxMessageSize(Integer producerMaxMessageSize) {
        RocketMqConfig.producerMaxMessageSize = producerMaxMessageSize;
    }

    @Value("${rocketmq.producer.sendMsgTimeout}")
    public void setProducerSendMsgTimeout(Integer producerSendMsgTimeout) {
        RocketMqConfig.producerSendMsgTimeout = producerSendMsgTimeout;
    }

    @Value("${rocketmq.producer.retryTimesWhenSendFailed}")
    public void setProducerRetryTimesWhenSendFailed(Integer producerRetryTimesWhenSendFailed) {
        RocketMqConfig.producerRetryTimesWhenSendFailed = producerRetryTimesWhenSendFailed;
    }

    @Value("${rocketmq.consumer.consumeThreadMin}")
    public void setConsumeThreadMin(Integer consumeThreadMin) {
        RocketMqConfig.consumeThreadMin = consumeThreadMin;
    }

    @Value("${rocketmq.consumer.consumeThreadMax}")
    public void setConsumeThreadMax(Integer consumeThreadMax) {
        RocketMqConfig.consumeThreadMax = consumeThreadMax;
    }

    @Value("${rocketmq.consumer.consumeMessageBatchMaxSize}")
    public void setConsumeMessageBatchMaxSize(Integer consumeMessageBatchMaxSize) {
        RocketMqConfig.consumeMessageBatchMaxSize = consumeMessageBatchMaxSize;
    }
}

2、生产者定义

import xxx.configuration.RocketMqConfig;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * RocketMQ 生产着
 * @Author YHL
 * @Date 2020/7/23 16:14
 * @Version 1.0
 */
public class BaseServiceMqProducer {

    private static Logger log = LoggerFactory.getLogger(BaseServiceMqProducer.class);

    private static DefaultMQProducer producer = new DefaultMQProducer(RocketMqConfig.produceGroupName);
    private static int initialState = 0;

    private BaseServiceMqProducer() {

    }

    public static DefaultMQProducer getDefaultMQProducer(){
        if(producer == null){
            producer = new DefaultMQProducer(RocketMqConfig.produceGroupName);
        }

        if(initialState == 0){
            producer.setNamesrvAddr(RocketMqConfig.namesrvAddr);
            //消息最大长度 默认1024*4(4M)
            producer.setMaxMessageSize(RocketMqConfig.producerMaxMessageSize);
            //发送消息超时时间
            producer.setSendMsgTimeout(RocketMqConfig.producerSendMsgTimeout);
            // 如果发送消息失败,设置重试次数,默认为2次
            producer.setRetryTimesWhenSendAsyncFailed(RocketMqConfig.producerRetryTimesWhenSendFailed);
            try {
                producer.start();
                log.info("rocketmq-producer 启动成功---------------------------------------");
            } catch (MQClientException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                return null;
            }

            initialState = 1;
        }

        return producer;
    }
}

3、生产者工具类

import xxx.component.BaseServiceMqProducer;
import xxx.configuration.RocketMqConfig;
import org.apache.rocketmq.client.exception.MQBrokerException;
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.client.producer.SendStatus;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;


public class RocketMqProducerUtil {

    private static Logger log = LoggerFactory.getLogger(RocketMqProducerUtil.class);

    private static String tag = "videoTag";

    /**
     * 发送正常消息
     * @param msg
     */
    public static String sendMsg(String msg){
        // 获取消息生产者
        DefaultMQProducer producer = BaseServiceMqProducer.getDefaultMQProducer();
        try {
            Message message = new Message(RocketMqConfig.topic,tag, msg.getBytes("UTF-8"));  // body
            // 日志收集,要求没有那么高,只需要单项发送即可
          SendResult sendResult = producer.send(message);
          SendStatus sendStatus = sendResult.getSendStatus();
          if (sendStatus.equals(SendStatus.SEND_OK)) {
              log.info("消息发送成功,msg:{}",msg);
              return "成功";
          } else if (sendStatus.equals(SendStatus.FLUSH_DISK_TIMEOUT)) {
              log.info("消息发送失败,消息刷盘失败,msg:{}",msg);
              return "消息发送失败,消息刷盘失败";
          } else if (sendStatus.equals(SendStatus.FLUSH_SLAVE_TIMEOUT)) {
              log.info("消息发送失败,主从服务器同步超时,msg:{}",msg);
              return "消息发送失败,主从服务器同步超时";
          } else if (sendStatus.equals(SendStatus.SLAVE_NOT_AVAILABLE)) {
              log.info("消息发送失败,Broker不可用,msg:{}",msg);
              return "消息发送失败,Broker不可用";
          } else {
              log.info("消息发送返回未知状态,msg:{}",msg);
          }
        } catch (MQClientException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (RemotingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (MQBrokerException e) {
            e.printStackTrace();
        }
//        producer.shutdown();
        return "消息发送返回未知状态";
    }
}

4、消费者定义

import xxx.configuration.RocketMqConfig;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;


public class BaseServiceMqConsumer {

    private static DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(RocketMqConfig.consumerGroupName);
    private static int initialState = 0;

    private BaseServiceMqConsumer() {

    }

    public static DefaultMQPushConsumer getDefaultMQPushConsumer(){
        if(consumer == null){
            consumer = new DefaultMQPushConsumer(RocketMqConfig.consumerGroupName);
        }

        if(initialState == 0){
            consumer.setNamesrvAddr(RocketMqConfig.namesrvAddr);
            //消费者线程数量
            consumer.setConsumeThreadMin(RocketMqConfig.consumeThreadMin);
            consumer.setConsumeThreadMax(RocketMqConfig.consumeThreadMax);
            //设置一次消费消息的条数,默认为1条
            consumer.setConsumeMessageBatchMaxSize(RocketMqConfig.consumeMessageBatchMaxSize);
            //一个新的订阅组第一次启动从队列的最前位置开始消费,后续再启动接着上次消费的进度开始消费
            consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
            initialState = 1;
        }

        return consumer;
    }
}

5、消费者工具类

import xxx.component.BaseServiceMqConsumer;
import xxx.configuration.RocketMqConfig;
import xxx.service.v1.VideoConsumerService;
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.message.MessageExt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.util.List;


@Component
public class RocketMqConsumerUtil {

    private static Logger log = LoggerFactory.getLogger(RocketMqConsumerUtil.class);

    @Autowired
    private VideoConsumerService videoConsumerService;

    private static String tag = "videoTag";

    /**
     * 接收消息
     */
    public void listener(){

        // 获取消息生产者
        DefaultMQPushConsumer consumer = BaseServiceMqConsumer.getDefaultMQPushConsumer();

        // 订阅主体
        try {
            consumer.subscribe(RocketMqConfig.topic, "*");

            consumer.registerMessageListener(new MessageListenerConcurrently() {

                /**
                 * * 默认msgs里只有一条消息,可以通过设置consumeMessageBatchMaxSize参数来批量接收消息
                 */
                public ConsumeConcurrentlyStatus consumeMessage(
                        List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                    MessageExt messageExt = msgs.get(0);
                    String msg = null;
                    try {
                        msg = new String(messageExt.getBody(),"utf-8");
                    } catch (UnsupportedEncodingException e) {
                        log.error("消息编码失败,MsgBody:{}",new String(messageExt.getBody()));
                        e.printStackTrace();
                    }
                    log.info("消费者-bornHost:{},storeHost:{}. groupName:{},topic:{}",messageExt.getBornHost(),messageExt.getStoreHost(),consumer.getConsumerGroup(),RocketMqConfig.topic);
                    log.info("消费开始-MsgBody:{}",msg);
//                    String msg = new String(messageExt.getBody());
//                    log.info("MsgBody:{}",new String(messageExt.getBody()));

                    if (messageExt.getTopic().equals(RocketMqConfig.topic)) {
                        // topic的消费逻辑
                        if (messageExt.getTags() != null && messageExt.getTags().equals(tag)) {
                            // 根据Tag消费消息,具体消费消息的业务方法
                            videoConsumerService.dealVideoMsg(msg);

                        }
                    } else if (messageExt.getTopic().equals("TopicTest2")) {
                        // 执行TopicTest2的消费逻辑
                    }

                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                }
            });

            /**
             * Consumer对象在使用之前必须要调用start初始化,初始化一次即可<br>
             */
            consumer.start();
            log.info("rocketmq-consumer 启动成功---------------------------------------");
        } catch (MQClientException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

四、调用说明

1、生产者工具类可以直接调用

 
    @RequestMapping(value = "/test", method = RequestMethod.POST)
    public void tes(@RequestParam("msg") String msg) {
        RocketMqProducerUtil.sendMsg(msg);

    }     
  

2、消费者工具类需要在项目启动时候初始化一下才可以调用,可以在主启动类上直接调用

    public static void main(String[] args) {
        SpringApplication.run(Jnwsn4residentapiApplication.class, args);
        RocketMqConsumerUtil rocketMqConsumerUtil = new RocketMqConsumerUtil();
        rocketMqConsumerUtil.listener();
    }

    

也可以自定义初始化,需要实现CommandLineRunner接口

import xxx.util.RocketMqConsumerUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class TestRunner implements CommandLineRunner {

    @Autowired
    private RocketMqConsumerUtil rocketMqConsumerUtil;

    @Override
    public void run(String... args) throws Exception {
        rocketMqConsumerUtil.listener();
    }
}

 

最后,就可以在消费者工具类中注入自己需要的业务处理Service了 ,把VideoConsumerServie注入替换掉就好

 

springboot + RocketMq 练习

上一篇:微信企业号接入JDK6和JDK7及JDK8加解密失败处理(Java)


下一篇:Java Object *父类