RocketMQ发送消息和消费消息

RocketMQ发送消息和消费消息

原文:https://blog.csdn.net/qq_18603599/article/details/81172866
在这里插入图片描述
看一下pom.xml的文件内容

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.suning.mq</groupId>
    <artifactId>rocketmq</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-client</artifactId>
            <version>4.2.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.44</version>
        </dependency>
    </dependencies>
</project>

先定义一个消息保存的载体:

package producer;
 
import com.alibaba.fastjson.JSON;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;
 
import java.io.Serializable;
 
/**
* @Author 18011618
* @Date 10:41 2018/7/17
* @Function 消息生产者
*/
public class Producer {
    public static void main(String[] args) throws MQClientException {
        DefaultMQProducer producer = new DefaultMQProducer("test-group");
        producer.setNamesrvAddr("localhost:9876");
        producer.setInstanceName("rmq-instance");
        producer.start();
        try {
            for (int i=0;i<100;i++){
                User user = new User();
                user.setLoginName("abc"+i);
                user.setPwd(String.valueOf(i));
                Message message = new Message("log-topic", "user-tag",JSON.toJSONString(user).getBytes());
                System.out.println("生产者发送消息:"+JSON.toJSONString(user));
                producer.send(message);
            }
 
 
        } catch (Exception e) {
            e.printStackTrace();
        }
        producer.shutdown();
    }
 
    /**
     * 发送用户消息
     */
    static  class User implements Serializable{
        private String loginName;
        private String pwd;
 
        public String getLoginName() {
            return loginName;
        }
 
        public void setLoginName(String loginName) {
            this.loginName = loginName;
        }
 
        public String getPwd() {
            return pwd;
        }
 
        public void setPwd(String pwd) {
            this.pwd = pwd;
        }
    }

定义消息的发送者:

package com.springboot.rocketmq.one.producer;
 
import com.alibaba.fastjson.JSON;
import com.springboot.rocketmq.content.UserContent;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
import javax.annotation.PostConstruct;
 
/**
* @Author 18011618
* @Date 10:31 2018/7/18
* @Function 模拟用户消息发送
*/
@Component
public class UserProducer {
    /**
     * 生产者的组名
     */
    @Value("${suning.rocketmq.producerGroup}")
    private String producerGroup;
 
    /**
     * NameServer 地址
     */
    @Value("${suning.rocketmq.namesrvaddr}")
    private String namesrvAddr;
 
    @PostConstruct
    public void produder() {
         DefaultMQProducer producer = new DefaultMQProducer(producerGroup);
         producer.setNamesrvAddr(namesrvAddr);
        try {
            producer.start();
            for (int i = 0; i < 100; i++) {
                UserContent userContent = new UserContent(String.valueOf(i),"abc"+i);
                String jsonstr = JSON.toJSONString(userContent);
                System.out.println("发送消息:"+jsonstr);
                Message message = new Message("user-topic", "user-tag", jsonstr.getBytes(RemotingHelper.DEFAULT_CHARSET));
                SendResult result = producer.send(message);
                System.err.println("发送响应:MsgId:" + result.getMsgId() + ",发送状态:" + result.getSendStatus());
            }
 
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            producer.shutdown();
        }
    }
}

定义消息的消费者:

package consumer;
 
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 java.util.List;
 
public class Consumer {
    public static void main(String[] args) throws MQClientException {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("test-group");
 
        consumer.setNamesrvAddr("localhost:9876");
        consumer.setInstanceName("rmq-instance");
        consumer.subscribe("log-topic", "user-tag");
        
        consumer.registerMessageListener(new MessageListenerConcurrently() {
 
            public ConsumeConcurrentlyStatus consumeMessage(
                    List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                for (MessageExt msg : msgs) {
                    System.out.println("消费者消费数据:"+new String(msg.getBody()));
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        consumer.start();
     }
}

接下来先启动消费者,然后再启动生产者,看一下效果

生产者控制台发送消息:

在这里插入图片描述

消费者控制台消费消息

在这里插入图片描述

且能发现消费消息的机制 默认是乱序的

扫描二维码关注公众号,回复: 5464223 查看本文章

五 RocketMQ Web启动

上面虽然成功了发送了消息和消费了消息,可惜都是通过控制台查看的,其实rocketmq有扩展的组件,其中有一个组件就是支持web界面查看消息相关的功能,下面就简单介绍一下如何使用这个功能:访问 https://github.com/apache/rocketmq-externals/ 可以看到如下界面,选择红色标注的模块,下载到本地:

在这里插入图片描述

然后进行部署,它是一个完整的springboot项目,所以可以用IDEA打开
在这里插入图片描述

然后需要修改application.properties的相关内容

server.contextPath=
server.port=8080
#spring.application.index=true
spring.application.name=rocketmq-console
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true
logging.config=classpath:logback.xml
#if this value is empty,use env value rocketmq.config.namesrvAddr  NAMESRV_ADDR | now, you can set it in ops page.default localhost:9876
rocketmq.config.namesrvAddr=localhost:9876
#if you use rocketmq version < 3.5.8, rocketmq.config.isVIPChannel should be false.default true
rocketmq.config.isVIPChannel=
#rocketmq-console's data path:dashboard/monitor
rocketmq.config.dataPath=/tmp/rocketmq-console/data
#set it false if you don't want use dashboard.default true
rocketmq.config.enableDashBoardCollect=true

其实主要就是这个地址,然后启动App这个类,这个时候在浏览器端访问 http://localhost:8080/ 会出现如下界面

在这里插入图片描述

点击Dashboard可以查看相关消息主题的信息

在这里插入图片描述

还可以查看具体的发送消息和消费消息:点击Message

在这里插入图片描述

点击Detail 能看查看消息具体的消息

在这里插入图片描述

这个控制台的功能还是很强大的,都可以去摸索一下如何使用.

六SpringBoot和RocketMQ整合

整合方式有很多种,但是可以总结为两大类,一类是基于高度封装(使用起来非常简单,通用性高),一类是基于原始开发(没啥可通用性),在这里把这两个类别都讲解一下.

& 先讲一种,基于原始API开发,不要啥封装,只需要引入一个配置文件即可

看一下整体项目结构,主要实现的部分是红色标注的,其它类是第二种封装使用的,

在这里插入图片描述

看一下pom.xml的文件内容

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring_boot</artifactId>
        <groupId>com.suning.springboot</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
 
    <artifactId>spring_boot_rocketmq</artifactId>
    <!--<url>https://github.com/apache/rocketmq-externals/tree/master/spring-boot-starter-rocketmq</url>-->
 
    <dependencies>
       
 
        <!--简化javabean相关操作-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
 
 
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-client</artifactId>
            <version>4.1.0-incubating</version>
        </dependency>
 
    </dependencies>
 
</project>

在这里为了简化javabean的操作,引入了lombok组件.定义一个javabean用来承载消息

package com.springboot.rocketmq.content;
 
import lombok.*;
import lombok.experimental.Accessors;
 
/**
* @Author 18011618
* @Date 19:28 2018/7/17
* @Function 发送消息体
*/
@ToString
@AllArgsConstructor
@EqualsAndHashCode
@Accessors(chain = true)
@Getter
@Setter
public class UserContent {
    private String username;
    private String pwd;
 
}

关于这里的注解参数可以具体参考:lombok的常用注解含义

定义生产者:

package com.springboot.rocketmq.producer;
 
import com.alibaba.fastjson.JSON;
import com.springboot.rocketmq.content.UserContent;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
 
import javax.annotation.PostConstruct;
 
/**
* @Author 18011618
* @Date 10:31 2018/7/18
* @Function 模拟用户消息发送
*/
@Component
public class UserProducer {
    /**
     * 生产者的组名
     */
    @Value("${suning.rocketmq.producerGroup}")
    private String producerGroup;
 
    /**
     * NameServer 地址
     */
    @Value("${suning.rocketmq.namesrvaddr}")
    private String namesrvAddr;
 
    @PostConstruct
    public void produder() {
         DefaultMQProducer producer = new DefaultMQProducer(producerGroup);
         producer.setNamesrvAddr(namesrvAddr);
        try {
            producer.start();
            for (int i = 0; i < 100; i++) {
                UserContent userContent = new UserContent(String.valueOf(i),"abc"+i);
                String jsonstr = JSON.toJSONString(userContent);
                System.out.println("发送消息:"+jsonstr);
                Message message = new Message("user-topic", "user-tag", jsonstr.getBytes(RemotingHelper.DEFAULT_CHARSET));
                SendResult result = producer.send(message);
                System.err.println("发送响应:MsgId:" + result.getMsgId() + ",发送状态:" + result.getSendStatus());
            }
 
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            producer.shutdown();
        }
    }
}

这里使用了两个新注解:

@PostConstruct:它的作用是相当于servlet的Init功能,在对象的构造器执行完了,就会立马调用该方法
@Value:注入classpath下面的配置文件中的值到变量中
定义消费者:

package com.springboot.rocketmq.consumer;
 
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
import javax.annotation.PostConstruct;
 
/**
* @Author 18011618
* @Date 10:31 2018/7/18
* @Function 模拟用户消息消费
*/
@Component
public class UserConsumer {
    /**
     * 消费者的组名
     */
    @Value("${suning.rocketmq.conumerGroup}")
    private String consumerGroup;
 
    /**
     * NameServer 地址
     */
    @Value("${suning.rocketmq.namesrvaddr}")
    private String namesrvAddr;
 
    @PostConstruct
    public void consumer() {
        System.err.println("init defaultMQPushConsumer");
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(consumerGroup);
        consumer.setNamesrvAddr(namesrvAddr);
        try {
            consumer.subscribe("user-topic", "user-tag");
            consumer.registerMessageListener((MessageListenerConcurrently) (list, context) -> {
                try {
                    for (MessageExt messageExt : list) {
 
                        System.err.println("消费消息: " + new String(messageExt.getBody()));//输出消息内容
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    return ConsumeConcurrentlyStatus.RECONSUME_LATER; //稍后再试
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; //消费成功
            });
            consumer.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
 
    }
}

最后再看一下配置文件:

server.port=8080
# 生产者的组名
suning.rocketmq.producerGroup=user-group
# 消费者的组名
suning.rocketmq.conumerGroup=user-group
# NameServer地址
suning.rocketmq.namesrvaddr=localhost:9876

写一个启动类

package com.springboot.rocketmq;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
 
/**
 * @Author 18011618
 * @Description
 * @Date 11:02 2018/7/17
 * @Modify By
 */
@SpringBootApplication
public class RocketmqApplication {
    public static void main(String[] args) {
        SpringApplication.run(RocketmqApplication.class,args);
    }
}

这个时候直接启动该应用就能看到消息的发送和消费的情况:

在这里插入图片描述

其实这样就实现了和springboot的整合,其实也很简单,但是有的人会感觉代码有点啰嗦,而且没啥通用性,如果业务中有多个消息队列的应用,那么就会出现很多重复代码,针对这种情况,可以按照下面这种方式来进行简化,如果不想自己动手的话,可以去找开源的代码,看有没有人实现了类似功能,拿过来直接用,还有就是在熟悉原理之后,也可以自己进行二次封装.

& 使用第三方组件整合springboot

首先看一下整体项目结构:以红色标注

在这里插入图片描述

接下来具体分析要封装的步骤:

1 定义rocketmq相关参数的配置文件

参数可以自己根据实际业务进行选择,因为本身rocketmq中很多的参数都是有默认值的,所以可以不需要配置所有的参数,按需即可,存储位置,网上很多的例子都是配置在默认的application.yaml中(不建议这样做,因为这个是全局的,建议发在一个非全局的文件,然后动态注入进来,达到隔离)

2 定义保存配置文件的实体和对应的配置文件加载解析类

& 保存配置文件的类

package com.springboot.rocketmq.two.config;
 
import java.util.ArrayList;
import java.util.List;
 
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
 
/**
* @Author 18011618
* @Date 19:31 2018/7/18
* @Function 读取配置文件信息
*/
@PropertySource("classpath:config/rocketmq.properties")
@ConfigurationProperties(prefix = "suning.rocketmq")
@Configuration
@Setter
@Getter
@ToString
@Accessors(chain = true)
public class RocketMQProperties {
   private String namesrvAddr;
   private String producerGroupName;
   private String transactionProducerGroupName;
   private String consumerGroupName;
   private String producerInstanceName;
   private String consumerInstanceName;
   private String producerTranInstanceName;
   private int consumerBatchMaxSize;
   private boolean consumerBroadcasting;
   private boolean enableHistoryConsumer;
   private boolean enableOrderConsumer;
   private List<String> subscribe = new ArrayList<String>();
 
}

@PropertySource:指定要加载的配置文件路径,其实默认是可以不写的,如果是加载classpath下面的配置文件,因为它会自己去寻找,但是有时候不同的版本,不写的话又会出现错误,所以为了不出现错误,通常建议配置一下,这样肯定不会有错的
@ConfigurationProperties:指定读取配置文件的规则,比如前缀是什么,不存在的字段是否可以忽略等
@Configuration:相当于xml的配置标签,这样
& 加载配置文件的参数,实例化相关的bean

package com.springboot.rocketmq.two.config;
 
import javax.annotation.PostConstruct;
 
import com.springboot.rocketmq.two.bean.MessageEvent;
import lombok.extern.slf4j.Slf4j;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
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.ConsumeOrderlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.TransactionMQProducer;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
import java.util.List;
import java.util.stream.Collectors;
/**
* @Author 18011618
* @Date 19:36 2018/7/18
* @Function 通过使用指定的文件读取类 来加载配置文件到字段中
*/
@Configuration
@EnableConfigurationProperties(RocketMQProperties.class)
@Slf4j
public class RocketMQConfiguration {
 
   @Autowired
   private RocketMQProperties rocketMQProperties;
 
   //事件监听
   @Autowired
   private ApplicationEventPublisher publisher = null;
 
   private static boolean isFirstSub = true;
 
   private static long startTime = System.currentTimeMillis();
 
    /**
     * 容器初始化的时候 打印参数
     */
   @PostConstruct
   public void init() {
      System.err.println(rocketMQProperties.getNamesrvAddr());
      System.err.println(rocketMQProperties.getProducerGroupName());
      System.err.println(rocketMQProperties.getConsumerBatchMaxSize());
      System.err.println(rocketMQProperties.getConsumerGroupName());
      System.err.println(rocketMQProperties.getConsumerInstanceName());
      System.err.println(rocketMQProperties.getProducerInstanceName());
      System.err.println(rocketMQProperties.getProducerTranInstanceName());
      System.err.println(rocketMQProperties.getTransactionProducerGroupName());
      System.err.println(rocketMQProperties.isConsumerBroadcasting());
      System.err.println(rocketMQProperties.isEnableHistoryConsumer());
      System.err.println(rocketMQProperties.isEnableOrderConsumer());
      System.out.println(rocketMQProperties.getSubscribe().get(0));
   }
 
   /**
    * 创建普通消息发送者实例
    * @return
    * @throws MQClientException
    */
   @Bean
   public DefaultMQProducer defaultProducer() throws MQClientException {
      DefaultMQProducer producer = new DefaultMQProducer(
            rocketMQProperties.getProducerGroupName());
      producer.setNamesrvAddr(rocketMQProperties.getNamesrvAddr());
      producer.setInstanceName(rocketMQProperties.getProducerInstanceName());
      producer.setVipChannelEnabled(false);
      producer.setRetryTimesWhenSendAsyncFailed(10);
      producer.start();
      log.info("rocketmq producer server is starting....");
      return producer;
   }
 
   /**
    * 创建支持消息事务发送的实例
    * @return
    * @throws MQClientException
    */
   @Bean
   public TransactionMQProducer transactionProducer() throws MQClientException {
      TransactionMQProducer producer = new TransactionMQProducer(
            rocketMQProperties.getTransactionProducerGroupName());
      producer.setNamesrvAddr(rocketMQProperties.getNamesrvAddr());
      producer.setInstanceName(rocketMQProperties
            .getProducerTranInstanceName());
      producer.setRetryTimesWhenSendAsyncFailed(10);
      // 事务回查最小并发数
      producer.setCheckThreadPoolMinSize(2);
      // 事务回查最大并发数
      producer.setCheckThreadPoolMaxSize(2);
      // 队列数
      producer.setCheckRequestHoldMax(2000);
      producer.start();
      log.info("rocketmq transaction producer server is starting....");
      return producer;
   }
 
   /**
    * 创建消息消费的实例
    * @return
    * @throws MQClientException
    */
   @Bean
   public DefaultMQPushConsumer pushConsumer() throws MQClientException {
      DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(
            rocketMQProperties.getConsumerGroupName());
      consumer.setNamesrvAddr(rocketMQProperties.getNamesrvAddr());
      consumer.setInstanceName(rocketMQProperties.getConsumerInstanceName());
 
      //判断是否是广播模式
      if (rocketMQProperties.isConsumerBroadcasting()) {
         consumer.setMessageModel(MessageModel.BROADCASTING);
      }
      //设置批量消费
      consumer.setConsumeMessageBatchMaxSize(rocketMQProperties
            .getConsumerBatchMaxSize() == 0 ? 1 : rocketMQProperties
            .getConsumerBatchMaxSize());
 
      //获取topic和tag
      List<String> subscribeList = rocketMQProperties.getSubscribe();
      for (String sunscribe : subscribeList) {
         consumer.subscribe(sunscribe.split(":")[0], sunscribe.split(":")[1]);
      }
 
      // 顺序消费
      if (rocketMQProperties.isEnableOrderConsumer()) {
         consumer.registerMessageListener(new MessageListenerOrderly() {
            @Override
            public ConsumeOrderlyStatus consumeMessage(
                  List<MessageExt> msgs, ConsumeOrderlyContext context) {
               try {
                  context.setAutoCommit(true);
                  msgs = filterMessage(msgs);
                  if (msgs.size() == 0)
                     return ConsumeOrderlyStatus.SUCCESS;
                  publisher.publishEvent(new MessageEvent(msgs, consumer));
               } catch (Exception e) {
                  e.printStackTrace();
                  return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
               }
               return ConsumeOrderlyStatus.SUCCESS;
            }
         });
      }
      // 并发消费
      else {
 
         consumer.registerMessageListener(new MessageListenerConcurrently() {
 
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(
                  List<MessageExt> msgs,
                  ConsumeConcurrentlyContext context) {
               try {
                   //过滤消息
                  msgs = filterMessage(msgs);
                  if (msgs.size() == 0)
                     return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                  publisher.publishEvent(new MessageEvent(msgs, consumer));
               } catch (Exception e) {
                  e.printStackTrace();
                  return ConsumeConcurrentlyStatus.RECONSUME_LATER;
               }
 
               return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
         });
      }
      new Thread(new Runnable() {
         @Override
         public void run() {
            try {
               Thread.sleep(5000);
 
               try {
                  consumer.start();
               } catch (Exception e) {
                        e.printStackTrace();
               }
               log.info("rocketmq consumer server is starting....");
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
 
      }).start();
 
      return consumer;
   }
 
    /**
     * 消息过滤
     * @param msgs
     * @return
     */
   private List<MessageExt> filterMessage(List<MessageExt> msgs) {
      if (isFirstSub && !rocketMQProperties.isEnableHistoryConsumer()) {
         msgs = msgs.stream()
               .filter(item -> startTime - item.getBornTimestamp() < 0)
               .collect(Collectors.toList());
      }
      if (isFirstSub && msgs.size() > 0) {
         isFirstSub = false;
      }
      return msgs;
   }
 
}

这个类主要加载配置文件里面参数的值,然后初始化生成producer,事务producer,consumer等实例。

@EnableConfigurationProperties:启动自动配置文件属性的获取,通过指定的类

看一下配置文件的内容

# 指定namesrv地址
suning.rocketmq.namesrvAddr=localhost:9876
#生产者group名称
suning.rocketmq.producerGroupName=user_group
#事务生产者group名称
suning.rocketmq.transactionProducerGroupName=order_transaction
#消费者group名称
suning.rocketmq.consumerGroupName=user_consumer_group
#生产者实例名称
suning.rocketmq.producerInstanceName=user_producer_instance
#消费者实例名称
suning.rocketmq.consumerInstanceName=user_consumer_instance
#事务生产者实例名称
suning.rocketmq.producerTranInstanceName=user_producer_transacition
#一次最大消费多少数量消息
suning.rocketmq.consumerBatchMaxSize=1
#广播消费
suning.rocketmq.consumerBroadcasting=false
#消费的topic:tag
suning.rocketmq.subscribe[0]=user-topic:white
#启动的时候是否消费历史记录
suning.rocketmq.enableHistoryConsumer=false
#启动顺序消费
suning.rocketmq.enableOrderConsumer=false

3 创建消息的发送

package com.springboot.rocketmq.two.producer;
 
import com.springboot.rocketmq.two.bean.User;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.*;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
 
import com.alibaba.fastjson.JSON;
 
 
 
import java.util.List;
 
@RestController
public class ProducerController {
   @Autowired
   private DefaultMQProducer defaultProducer;
 
   @Autowired
   private TransactionMQProducer transactionProducer;
 
 
    /**
     * 发送普通消息
     */
   @GetMapping("/sendMessage")
   public void sendMsg() {
      
      for(int i=0;i<100;i++){
         User user = new User();
         user.setId(String.valueOf(i));
         user.setUsername("jhp"+i);
         String json = JSON.toJSONString(user);
         Message msg = new Message("user-topic","white",json.getBytes());
         try {
            SendResult result = defaultProducer.send(msg);
            System.out.println("消息id:"+result.getMsgId()+":"+","+"发送状态:"+result.getSendStatus());
      } catch (Exception e) {
         e.printStackTrace();
      }
      }
      
      
   }
 
    /**
     * 发送事务消息
     * @return
     */
   @GetMapping("/sendTransactionMess")
   public String sendTransactionMsg() {
       SendResult sendResult = null;
       try {
           // a,b,c三个值对应三个不同的状态
           String ms = "c";
           Message msg = new Message("user-topic","white",ms.getBytes());
           // 发送事务消息
           sendResult = transactionProducer.sendMessageInTransaction(msg, (Message msg1, Object arg) -> {
               String value = "";
               if (arg instanceof String) {
                   value = (String) arg;
               }
	               if (value == "") {
	                   throw new RuntimeException("发送消息不能为空...");
	               } else if (value =="a") {
	                   return LocalTransactionState.ROLLBACK_MESSAGE;
	               } else if (value =="b") {
	                   return LocalTransactionState.COMMIT_MESSAGE;
	               }
	               return LocalTransactionState.ROLLBACK_MESSAGE;
	           }, 4);
	           System.out.println(sendResult);
	       } catch (Exception e) {
	           e.printStackTrace();
	       }
	       return sendResult.toString();
	   }
	 
	    /**
	     * 支持顺序发送消息
	     */
	  @GetMapping("/sendMessOrder")
	   public void sendMsgOrder() {
	      for(int i=0;i<100;i++) {
	          User user = new User();
	          user.setId(String.valueOf(i));
	          user.setUsername("jhp" + i);
	          String json = JSON.toJSONString(user);
	          Message msg = new Message("user-topic", "white", json.getBytes());
	          try{
	              defaultProducer.send(msg, new MessageQueueSelector() {
	                  @Override
	                  public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
	                      int index = ((Integer) arg) % mqs.size();
	                      return mqs.get(index);
	                  }
	              },i);
	          }
	          catch (Exception e){
	              e.printStackTrace();
	          }
	      }
	   }
	}

创建消息的消费:

package com.springboot.rocketmq.two.consumer;
 
import java.util.List;
 
import com.springboot.rocketmq.two.bean.MessageEvent;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
 
/**
 * 监听消息进行消费
 */
@Component
public class ConsumerService {
   @EventListener(condition = "#event.msgs[0].topic=='user-topic' && #event.msgs[0].tags=='white'")
   public void rocketmqMsgListener(MessageEvent event) {
       try {
          List<MessageExt> msgs = event.getMsgs();
          for (MessageExt msg : msgs) {
               System.err.println("消费消息:"+new String(msg.getBody()));
           }
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
}

为了方便监听消费,增加了一个事件监听功能:

package com.springboot.rocketmq.two.bean;
 
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.context.ApplicationEvent;
 
import java.io.UnsupportedEncodingException;
import java.util.List;
/**
 * 监听对象
 * @author 18011618
 *
 */
public class MessageEvent extends ApplicationEvent {
   private static final long serialVersionUID = -4468405250074063206L;
   private DefaultMQPushConsumer consumer;
   private List<MessageExt> msgs;
 
   public MessageEvent(List<MessageExt> msgs, DefaultMQPushConsumer consumer) throws Exception {
       super(msgs);
       this.consumer = consumer;
       this.setMsgs(msgs);
   }
 
 
 
   public DefaultMQPushConsumer getConsumer() {
       return consumer;
   }
 
   public void setConsumer(DefaultMQPushConsumer consumer) {
       this.consumer = consumer;
   }
   
   public List<MessageExt> getMsgs() {
       return msgs;
   }
 
   public void setMsgs(List<MessageExt> msgs) {
       this.msgs = msgs;
   }
}

写一个启动应用类:

package com.springboot.rocketmq;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
 
/**
 * @Author 18011618
 * @Description
 * @Date 11:02 2018/7/17
 * @Modify By
 */
@SpringBootApplication
//@ComponentScan(basePackages = "com.springboot.rocketmq.one")
@ComponentScan(basePackages = "com.springboot.rocketmq.two")
public class RocketmqApplication {
    public static void main(String[] args) {
        SpringApplication.run(RocketmqApplication.class,args);
    }
}

这里要区别于第一种方式,所以只扫描第二种方式的包

然后访问浏览器端 http://localhost:8080/sendMessage 效果如下截图所示

在这里插入图片描述

至此生产者和消息这就可以进行正常的发送和消费了.

扩展:根据条件动态创建bean

场景:通常在项目中我们会有一些开关或者一些标识值,来做不同的操作,以前的话大家可能都是通过在代码中加入大量的if判断,这样虽然实现了功能,但是维护和可读性都是比较差,所以spring在3.x版本之后开始加入了@Conditonal条件注解:很简单就是开发者根据一些条件来决定是否创建bean或者动态来创建bean,这就比较简单了,而springboot为了方便开发,又对上面这个注解进行了一些列的扩展,让使用起来更加的简单:,下面就列举常用的条件注解:

@ConditionalOnClass:当前上下文存存在个类,才会创建对应的bean实例
解释:如果写入Hello.class,那么就代表在当前的类路径下面肯定又要这样的一个类存在
@ConditionalOnBean:当前上下文存在某个类的实例,才会创建对应的bean实例
解释:所谓实例,就是已经被spring实例化过了,比如一般需要在类上加一些注解@Component
@Service @Configuration @Respositry ……
@ConditionalOnMissingBean:和上面整好相反,不在对应的实例,创建当前bean的实例
@ConditionalOnMissingClass:不存在某个类,就创建当前bean的实例
@ConditionalOnProperty:根据配置文件中的属性条件,来创建当前bean的实例
解释:这个参数组合有好几种,下面一一列举
1 prefix and value
prefix:参数的前缀
value:参数前缀后面的字段名称
2 name and havingValue
name:完整的字段名称
havingValue:字段名称对应的值
3 value and matchIfMissing
value:配置文件里面参数的值 是否为true

看一下这个注解的源码:
在这里插入图片描述

@ConditionalOnExpression:根据表达式的值,来创建当前bean的实例

解释:目前该值只能是boolean类型,也就是说是true或者false,在熟悉这些知识之后,优化一下上面的那个配置代码:

package com.springboot.rocketmq.two.config;
 
import javax.annotation.PostConstruct;
 
import com.springboot.rocketmq.two.bean.MessageEvent;
import lombok.extern.slf4j.Slf4j;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
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.ConsumeOrderlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.TransactionMQProducer;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.*;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
import java.util.List;
import java.util.stream.Collectors;
/**
* @Author 18011618
* @Date 19:36 2018/7/18
* @Function 通过使用指定的文件读取类 来加载配置文件到字段中
*/
@Configuration
@EnableConfigurationProperties(RocketMQProperties.class)
@Slf4j
@ConditionalOnProperty(prefix = "suning.rocketmq",value = "namesrvAddr")
public class RocketMQConfiguration {
 
   @Autowired
   private RocketMQProperties rocketMQProperties;
 
   //事件监听
   @Autowired
   private ApplicationEventPublisher publisher = null;
 
   private static boolean isFirstSub = true;
 
   private static long startTime = System.currentTimeMillis();
 
    /**
     * 容器初始化的时候 打印参数
     */
   @PostConstruct
   public void init() {
        System.out.println("配置信息:"+rocketMQProperties);
   }
 
   /**
    * 创建普通消息发送者实例
    * @return
    * @throws MQClientException
    */
   @Bean
    @ConditionalOnClass(DefaultMQProducer.class)
    @ConditionalOnMissingBean(DefaultMQProducer.class)
    @ConditionalOnProperty(prefix = "suning.rocketmq",value = "namesrvAddr")
   public DefaultMQProducer defaultProducer() throws MQClientException {
        System.err.println("create defaultProducer....");
      DefaultMQProducer producer = new DefaultMQProducer(
            rocketMQProperties.getProducerGroupName());
      producer.setNamesrvAddr(rocketMQProperties.getNamesrvAddr());
      producer.setInstanceName(rocketMQProperties.getProducerInstanceName());
      producer.setVipChannelEnabled(false);
      producer.setRetryTimesWhenSendAsyncFailed(10);
      producer.start();
      log.info("rocketmq producer server is starting....");
      return producer;
   }
 
   /**
    * 创建支持消息事务发送的实例
    * @return
    * @throws MQClientException
    */
   @Bean
    @ConditionalOnProperty(prefix = "suning.rocketmq",value = "transactionProducerGroupName")
    @ConditionalOnClass(TransactionMQProducer.class)
    @ConditionalOnMissingBean(TransactionMQProducer.class)
   public TransactionMQProducer transactionProducer() throws MQClientException {
        System.err.println("create transactionProducer....");
      TransactionMQProducer producer = new TransactionMQProducer(
            rocketMQProperties.getTransactionProducerGroupName());
      producer.setNamesrvAddr(rocketMQProperties.getNamesrvAddr());
      producer.setInstanceName(rocketMQProperties
            .getProducerTranInstanceName());
      producer.setRetryTimesWhenSendAsyncFailed(10);
      // 事务回查最小并发数
      producer.setCheckThreadPoolMinSize(2);
      // 事务回查最大并发数
      producer.setCheckThreadPoolMaxSize(2);
      // 队列数
      producer.setCheckRequestHoldMax(2000);
      producer.start();
      log.info("rocketmq transaction producer server is starting....");
      return producer;
   }
 
   /**
    * 创建消息消费的实例
    * @return
    * @throws MQClientException
    */
   @Bean
    @ConditionalOnProperty(prefix = "suning.rocketmq",value = "consumerGroupName")
    @ConditionalOnClass(DefaultMQPushConsumer.class)
    @ConditionalOnMissingBean(DefaultMQPushConsumer.class)
   public DefaultMQPushConsumer pushConsumer() throws MQClientException {
        System.err.println("create pushConsumer....");
      DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(
            rocketMQProperties.getConsumerGroupName());
      consumer.setNamesrvAddr(rocketMQProperties.getNamesrvAddr());
      consumer.setInstanceName(rocketMQProperties.getConsumerInstanceName());
 
      //判断是否是广播模式
      if (rocketMQProperties.isConsumerBroadcasting()) {
         consumer.setMessageModel(MessageModel.BROADCASTING);
      }
      //设置批量消费
      consumer.setConsumeMessageBatchMaxSize(rocketMQProperties
            .getConsumerBatchMaxSize() == 0 ? 1 : rocketMQProperties
            .getConsumerBatchMaxSize());
 
      //获取topic和tag
      List<String> subscribeList = rocketMQProperties.getSubscribe();
      for (String sunscribe : subscribeList) {
         consumer.subscribe(sunscribe.split(":")[0], sunscribe.split(":")[1]);
      }
 
      // 顺序消费
      if (rocketMQProperties.isEnableOrderConsumer()) {
         consumer.registerMessageListener(new MessageListenerOrderly() {
            @Override
            public ConsumeOrderlyStatus consumeMessage(
                  List<MessageExt> msgs, ConsumeOrderlyContext context) {
               try {
                  context.setAutoCommit(true);
                  msgs = filterMessage(msgs);
                  if (msgs.size() == 0)
                     return ConsumeOrderlyStatus.SUCCESS;
                  publisher.publishEvent(new MessageEvent(msgs, consumer));
               } catch (Exception e) {
                  e.printStackTrace();
                  return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
               }
               return ConsumeOrderlyStatus.SUCCESS;
            }
         });
      }
      // 并发消费
      else {
 
         consumer.registerMessageListener(new MessageListenerConcurrently() {
 
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(
                  List<MessageExt> msgs,
                  ConsumeConcurrentlyContext context) {
               try {
                   //过滤消息
                  msgs = filterMessage(msgs);
                  if (msgs.size() == 0)
                     return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                  publisher.publishEvent(new MessageEvent(msgs, consumer));
               } catch (Exception e) {
                  e.printStackTrace();
                  return ConsumeConcurrentlyStatus.RECONSUME_LATER;
               }
 
               return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
         });
      }
      new Thread(new Runnable() {
         @Override
         public void run() {
            try {
               Thread.sleep(5000);
 
               try {
                  consumer.start();
               } catch (Exception e) {
                        e.printStackTrace();
               }
               log.info("rocketmq consumer server is starting....");
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
 
      }).start();
 
      return consumer;
   }
 
    /**
     * 消息过滤
     * @param msgs
     * @return
     */
   private List<MessageExt> filterMessage(List<MessageExt> msgs) {
      if (isFirstSub && !rocketMQProperties.isEnableHistoryConsumer()) {
         msgs = msgs.stream()
               .filter(item -> startTime - item.getBornTimestamp() < 0)
               .collect(Collectors.toList());
      }
      if (isFirstSub && msgs.size() > 0) {
         isFirstSub = false;
      }
      return msgs;
   }
 
}

ok 到此为止本篇文章就讲解完了,谢谢!

猜你喜欢

转载自blog.csdn.net/qq_22764659/article/details/86699344