SA实战 ·《SpringCloud Alibaba实战》第19章-消息服务:项目整合RocketMQ

简介: SA实战 ·《SpringCloud Alibaba实战》第19章-消息服务:项目整合RocketMQ

本章总览

图片.png


RocketMQ环境准备

RocketMQ是阿里开源的消息中间件,目前是Apache下的顶级项目。正式在项目中接入RocketMQ之前,我们需要搭建RocketMQ的环境。这里呢,我把搭建RocketMQ的基础环境分为两个部分:搭建RocketMQ环境和搭建RocketMQ控制台。

「注意:冰河这里都是先下载RocketMQ的源码和RocketMQ控制台的源码,然后对源码进行编译后,再搭建的。目的也是让小伙伴们能够跟着冰河实现手动编译RocketMQ的源码,另外,编译RocketMQ源码和控制台源码需要JDK1.8+Maven。」

图片.png

(1)到链接https://github.com/apache/rocketmq/releases/tag/rocketmq-all-4.9.3下载RocketMQ 4.9.3版本的源码。下载并解压后的源码如下所示。

图片.png

(2)打开cmd命令行,进入RocketMQ的解压目录,我这里是E:\Application\RocketMQ\rocketmq-rocketmq-all-4.9.3目录,然后在cmd命令行输入如下命令开始编译打包。

mvn clean install -Dmaven.test.skip=true -Prelease-all

编译过程如下所示。

图片.png

编译打包成功后,如下图所示。

图片.png

(3)编译成功后,会在RocketMQ解压目录下的distribution目录下的target目录下生成RocketMQ的安装包,在我电脑上的目录就是:E:\Application\RocketMQ\rocketmq-rocketmq-all-4.9.3\distribution\target。如下所示。

图片.png

这样,我们就自己下载RocketMQ的源码,并打包成功了。

「注意:这里,为了方便,我还是将RocketMQ部署到我本机Windows操作系统上,小伙伴们也可以将之前的Nacos、Sentinel和这次的RocketMQ都部署在Linux操作系统上,部署方式几乎与在Windows操作系统一样,这里,冰河就不再赘述了。」

(4)将编译出的安装包,解压到电脑的某个目录下,例如我解压后的目录为:E:\Application\microservices\RocketMQ\rocketmq-4.9.3。

(5)在RocketMQ的解压目录下的conf目录下修改broker.conf文件,修改后的文件内容如下所示。

brokerClusterName = DefaultCluster
brokerName = broker-a
brokerId = 0
deleteWhen = 04
fileReservedTime = 48
brokerRole = ASYNC_MASTER
flushDiskType = ASYNC_FLUSH
# 自动创建Topic
autoCreateTopicEnable=true
# nameServ地址
namesrvAddr=127.0.0.1:9876
# 存储路径
storePathRootDir=E:/RocketMQ/data/rocketmq/dataDir
# commitLog路径
storePathCommitLog=E:/RocketMQ/data/rocketmq/dataDir/commitlog
# 消息队列存储路径
storePathConsumeQueue=E:/RocketMQ/data/rocketmq/dataDir/consumequeue
# 消息索引存储路径
storePathIndex=E:/RocketMQ/data/rocketmq/dataDir/index
# checkpoint文件路径
storeCheckpoint=E:/RocketMQ/data/rocketmq/dataDir/checkpoint
# abort文件存储路径
abortFile=E:/RocketMQ/data/rocketmq/dataDir/abort

小伙伴们可以根据自己的实际情况,自行修改上述文件中配置的目录地址。

(6)非常重要的一步,在启动RocketMQ之前,需要配置下ROCKETMQ_HOME环境变量,否则在启动RocketMQ的时候,会提示如下错误信息。

E:\Application\microservices\RocketMQ\rocketmq-4.9.3\bin>mqnamesrv.cmd
Please set the ROCKETMQ_HOME variable in your environment!

「提示:设置ROCKETMQ_HOME环境变量。」

接下来,就在系统环境变量中,设置下ROCKETMQ_HOME的环境变量,如下所示。

图片.png

(7)配置完RocketMQ的环境变量后,打开cmd命令行,进入RocketMQ的bin目录,例如,我电脑的目录是:E:\Application\microservices\RocketMQ\rocketmq-4.9.3\bin。执行mqnamesrv.cmd命令启动NameServer,如下所示。

图片.pngimage.gif

打印出如下信息,说明RocketMQ的NameServer启动成功了。

The Name Server boot success. serializeType=JSON

(8)重新打开一个cmd命令行,进入RocketMQ的bin目录,输入mqbroker.cmd -n localhost:9876命令启动RocketMQ的Broker服务,如下所示。

图片.pngimage.gif

打印出如下信息,说明RocketMQ的Broker服务启动成功了。

boot success. serializeType=JSON and name server is localhost:9876

图片.png

RocketMQ内置了大量的测试案例,并且这些测试案例可以通过RocketMQ的bin目录下的tools.cmd命令进行测试。接下来,我们就使用RocketMQ自带的tools.cmd命令测试RocketMQ的环境。

(1)启动生产者程序向RocketMQ发送消息。

重新打开cmd命令行,进入RocketMQ的bin目录,在命令行输入如下命令调用RocketMQ自带的生产者程序向RocketMQ发送消息。

set NAMESRV_ADDR=localhost:9876
tools.cmd org.apache.rocketmq.example.quickstart.Producer

可以看到,执行完上述两条命令后,生产者程序开始向RocketMQ发送消息。

图片.pngimage.gif

(2)启动消费者程序消费RocketMQ中的消息。

重新打开cmd命令行,进入RocketMQ的bin目录,在命令行输入如下命令调用RocketMQ自带的消费者程序消费RocketMQ中的消息。

set NAMESRV_ADDR=localhost:9876
tools.cmd org.apache.rocketmq.example.quickstart.Consumer

可以看到,执行完上述两条命令后,消费者程序开始消费RocketMQ中的消息。

图片.pngimage.gif

说明我们使用源码编译搭建RocketMQ环境成功了。

图片.png

这里需要注意的是:RocketMQ控制台本质上是一个SpringBoot程序,启动后默认监听的端口是8080。RocketMQ的新版控制台已经从RocketMQ的rocketmq-externals项目中分离出来了。也就是说,新版的RocketMQ控制台已经从https://github.com/apache/rocketmq-externals链接所示的项目中分离出来,新版控制台的链接地址为:https://github.com/apache/rocketmq-dashboard

(1)从链接https://github.com/apache/rocketmq-dashboard下载新版的RocketMQ控制台源码。下载后解压。

(2)进入到RocketMQ控制台源码解压目录的src/main/resources目录下,编辑application.yml文件,修改namesrvAddrs地址,去掉多余的namesrvAddrs地址。

application.yml文件中原来的配置如下所示。

rocketmq:
  config:
    # if this value is empty,use env value rocketmq.config.namesrvAddr  NAMESRV_ADDR | now, default localhost:9876
    # configure multiple namesrv addresses to manage multiple different clusters
    namesrvAddrs:
      - 127.0.0.1:9876
      - 127.0.0.2:9876

将127.0.0.2:9876删除或者注释掉,如下所示。

rocketmq:
  config:
    # if this value is empty,use env value rocketmq.config.namesrvAddr  NAMESRV_ADDR | now, default localhost:9876
    # configure multiple namesrv addresses to manage multiple different clusters
    namesrvAddrs:
      - 127.0.0.1:9876
    #  - 127.0.0.2:9876

RocketMQ控制台启动时默认监听的端口是8080,由于我们项目中订单微服务监听的端口也是8080,所以,将RocketMQ控制台监听的端口修改为10003,修改前的配置如下所示。

server:
  port: 8080

修改后的配置如下所示。

server:
  port: 10003

(3)修改完application.yml文件后,打开cmd命令行,进入RocketMQ控制台源码的根目录,输入如下Maven命令开始编译RocketMQ控制台的源码。

mvn clean install -Dmaven.test.skip=true

编译过程如下所示。

图片.png

(4)编译完成后,会在RocketMQ控制台源码的根目录下生成target目录,如下所示。

图片.png

进入target目录后,可以看到生成了rocketmq-dashboard-1.0.1-SNAPSHOT.jar文件,如下所示。

图片.png

这个jar文件就是RocketMQ控制台的运行文件。

(5)重新打开cmd命令行,进入rocketmq-dashboard-1.0.1-SNAPSHOT.jar文件所在的命令,在命令行直接输入如下命令启动RocketMQ控制台程序。

java -jar rocketmq-dashboard-1.0.1-SNAPSHOT.jar

图片.png

在浏览器中输入http://localhost:10003后,出现如下画面说明RocketMQ启动成功。

图片.pngimage.gif

界面默认是英文,我们也可以点击右上角的changeLanguage切换语言,切换成中文显示,如下所示。

图片.pngimage.gif

选择主题菜单想后如下所示。

图片.pngimage.gif

可以看到目前RocketMQ中存在一个名称为TopicTest的主题,点击TopicTest主题的状态按钮,如下所示。图片.png

image.gif

会显示TopicTest主题的消息队列信息,如下所示。

图片.pngimage.gif

可以看到,正确显示出了TopicTest主题的消息队列信息,说明RocketMQ控制台启动成功了。

编码测试RocketMQ

我们使用RocketMQ自带的生产者和消费者程序实现了消息的生成与消费,为了让小伙伴们能够更加直观的感受到消息中间件在项目中的作用,接下来,我们自己编码测试下RocketMQ。

导入RocketMQ依赖

在用户微服务shop-user的pom.xml中,添加RocketMQ相关的依赖,如下所示。

<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-spring-boot-starter</artifactId>
    <version>2.0.3</version>
</dependency>
<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-client</artifactId>
    <version>4.5.2</version>
</dependency>

编写生产者代码

在用户微服务的sec/test/java目录下新建io.binghe.shop.rocketmq.test包,在包下创建RocketMQProducer类,作为RocketMQ的生产者,代码如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description RocketMQ生产者
 */
public class RocketMQProducer {
    public static void main(String[] args) throws Exception {
        //创建消息生产者
        DefaultMQProducer producer = new DefaultMQProducer("bingheProducerGroup");
        //设置NameServer地址
        producer.setNamesrvAddr("127.0.0.1:9876");
        //启动生产者
        producer.start();
        //构建消息对象
        Message message = new Message("bingheTopic", "bingheTag", "Hello RocketMQ".getBytes());
        System.out.println("生产者发出的消息为:" + JSONObject.toJSONString(message));
        //发送消息并接收结果
        SendResult sendResult = producer.send(message);
        //打印结果信息
        System.out.println("生产者收到的发送结果信息为:" + JSONObject.toJSONString(sendResult));
        //关闭生产者
        producer.shutdown();
    }
}

生产者的代码比较简单,这里就不再赘述了。

编写消费者代码

io.binghe.shop.rocketmq.test包下新建RocketMQConsumer类,作为RocketMQ的消费者,代码如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description RocketMQ消费者
 */
public class RocketMQConsumer {
   public static void main(String[] args) throws Exception {
       try{
           //创建消息消费者
           DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("bingheConsumerGroup");
           //设置NameServer地址
           consumer.setNamesrvAddr("127.0.0.1:9876");
           //订阅bingheTopic主题
           consumer.subscribe("bingheTopic", "*");
           //设置消息监听,当收到消息时RocketMQ会回调消息监听
           consumer.registerMessageListener(new MessageListenerConcurrently() {
               @Override
               public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list,
                                                               ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                   //打印消息消费者收到的RocketMQ消息
                   System.out.println("消费者收到的消息为:" + list);
                   //返回消息消费成功的标识
                   return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
               }
           });
           //启动消费者
           consumer.start();
           System.out.println("消费者启动成功");
       }catch (Exception e){
           e.printStackTrace();
       }
   }
}

测试消息的生产与消费

(1)为了便于观察,这里我们先启动消费者程序RocketMQConsumer,启动RocketMQConsumer后会在IDEA的控制台打印如下信息。

消费者启动成功

说明消费者启动成功了。

(2)运行生产者程序RocketMQProducer,运行后RocketMQProducer程序控制台会输出如下信息。

生产者发出的消息为:{"body":"SGVsbG8gUm9ja2V0TVE=","delayTimeLevel":0,"flag":0,"properties":{"WAIT":"true","TAGS":"bingheTag"},"tags":"bingheTag","topic":"bingheTopic","waitStoreMsgOK":true}
生产者收到的发送结果信息为:{"messageQueue":{"brokerName":"DESKTOP-PSKC7T1","queueId":1,"topic":"bingheTopic"},"msgId":"C0A8006F538418B4AAC25B9EDDAC0000","offsetMsgId":"C0A8B80100002A9F0000000000036B16","queueOffset":2,"regionId":"DefaultRegion","sendStatus":"SEND_OK","traceOn":true}

说明生产者程序RocketMQProducer成功将消息发送到RocketMQ。

(3)接下来,再看下消费者程序RocketMQConsumer的控制台,如下所示。

消费者收到的消息为:[MessageExt [queueId=1, storeSize=206, queueOffset=2, sysFlag=0, bornTimestamp=1652871538093, bornHost=/192.168.184.1:52915, storeTimestamp=1652871538099, storeHost=/192.168.184.1:10911, msgId=C0A8B80100002A9F0000000000036B16, commitLogOffset=224022, bodyCRC=1774740973, reconsumeTimes=0, preparedTransactionOffset=0, toString()=Message{topic='bingheTopic', flag=0, properties={MIN_OFFSET=0, MAX_OFFSET=3, CONSUME_START_TIME=1652871538103, UNIQ_KEY=C0A8006F538418B4AAC25B9EDDAC0000, CLUSTER=DefaultCluster, WAIT=true, TAGS=bingheTag}, body=[72, 101, 108, 108, 111, 32, 82, 111, 99, 107, 101, 116, 77, 81], transactionId='null'}]]

说明生成者发送到RocketMQ的消息,被消费者成功消费到了。

项目整合RocketMQ

我们在项目中模拟一个用户成功下单后,为用户发送通知,通知用户下单成功的逻辑,具体的流程就是下单成功后将订单的信息发送到RocketMQ,然后用户微服务订阅RocketMQ的消息,接收到消息后进行打印。

用户微服务整合RocketMQ

(1)编码测试RocketMQ时,导入了RocketMQ的依赖,这里就不用再次导入了。

(2)在用户微服务shop-user的application.yml文件中添加如下RocketMQ的配置。

rocketmq:
  name-server: 127.0.0.1:9876

(3)在用户微服务shop-user中创建io.binghe.shop.user.rocketmq包,在包下创建RocketConsumeListener,实现org.apache.rocketmq.spring.core.RocketMQListener接口,具体代码如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description 监听消费
 */
@Slf4j
@Component
@RocketMQMessageListener(consumerGroup = "user-group", topic = "order-topic")
public class RocketConsumeListener implements RocketMQListener<Order> {
    @Override
    public void onMessage(Order order) {
        log.info("用户微服务收到了订单信息:{}", JSONObject.toJSONString(order));
    }
}

其中,RocketConsumeListener类上的@RocketMQMessageListener注解,表示当前类是一个RocketMQ的消费者,在@RocketMQMessageListener注解中配置了消费者组为user-group,主题为order-topic。

至此,用户微服务整合RocketMQ完毕。

订单微服务整合RocketMQ

(1)在订单微服务shop-order的pom.xml文件中添加RocketMQ的依赖,如下所示。

<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-spring-boot-starter</artifactId>
    <version>2.0.3</version>
</dependency>
<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-client</artifactId>
    <version>4.5.2</version>
</dependency>

(2)在订单微服务shop-order的application.yml文件中添加如下配置。

rocketmq:
  name-server: 127.0.0.1:9876
  producer:
    group: order-group

(3)将io.binghe.shop.order.service.impl.OrderServiceV6Impl类,复制一份成io.binghe.shop.order.service.impl.OrderServiceV7Impl类,接下来,在io.binghe.shop.order.service.impl.OrderServiceV7Impl类中操作。

io.binghe.shop.order.service.impl.OrderServiceV7Impl类上的@Service注解中的名称修改为orderServiceV7,如下所示。

@Slf4j
@Service("orderServiceV7")
public class OrderServiceV7Impl implements OrderService {
    //省略具体代码
}

(4)在io.binghe.shop.order.service.impl.OrderServiceV7Impl类中,注入RocketMQTemplate对象,如下所示。

@Autowired
private RocketMQTemplate rocketMQTemplate;

(5)在io.binghe.shop.order.service.impl.OrderServiceV7Impl#saveOrder()方法中,提交订单成功后将订单信息写入RocketMQ,如下所示。

@Override
@Transactional(rollbackFor = Exception.class)
public void saveOrder(OrderParams orderParams) {
    //省略上面所有代码
    rocketMQTemplate.convertAndSend("order-topic", order);
}

(6)在io.binghe.shop.order.controller.OrderController中,将注入的OrderService的名称修改成orderServiceV7,如下所示。

@Autowired
@Qualifier(value = "orderServiceV7")
private OrderService orderService;

「注意:订单微服务shop-order中,修改后的代码见源码工程,冰河在这里不再粘贴完整的源代码。」

测试项目整合的RocketMQ

(1)分别启动Nacos,Sentinel,ZipKin和RocketMQ。

(2)分别启动用户微服务、商品微服务、订单微服务和网关服务。

(3)在浏览器中输入localhost:10001/server-order/order/submit_order?userId=1001&productId=1001&count=1,如下所示。

图片.png

(4)查看用户微服务shop-user的控制台,发现会输出订单的信息,如下所示。

2022-05-18 20:37:26.440  INFO [server-user,,,] 18064 --- [MessageThread_1] i.b.s.u.rocketmq.RocketConsumeListener   : 用户微服务收到了订单信息:{"address":"北京","id":13176882400989185,"phone":"13212345678","totalPrice":2399.00,"userId":1001,"username":"binghe"}

说明项目中成功集成了RocketMQ。

相关实践学习
通过轻量消息队列(原MNS)主题HTTP订阅+ARMS实现自定义数据多渠道告警
本场景将自定义告警信息同时分发至多个通知渠道的需求,例如短信、电子邮件及钉钉群组等。通过采用轻量消息队列(原 MNS)的主题模型的HTTP订阅方式,并结合应用实时监控服务提供的自定义集成能力,使得您能够以简便的配置方式实现上述多渠道同步通知的功能。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
相关文章
|
9月前
|
消息中间件 Java 数据库
RocketMQ实战—9.营销系统代码初版
本文主要介绍了实现营销系统四大促销场景的代码初版:全量用户推送促销活动、全量用户发放优惠券、特定用户推送领取优惠券消息、热门商品定时推送。
RocketMQ实战—9.营销系统代码初版
|
9月前
|
消息中间件 搜索推荐 调度
RocketMQ实战—8.营销系统业务和方案介绍
本文详细介绍了电商营销系统的业务流程、技术架构及挑战解决方案。涵盖核心交易与支付后履约流程,优惠券和促销活动的发券、领券、用券、销券机制,以及会员与推送的数据库设计。技术架构基于Nacos服务注册中心、Dubbo RPC框架、RocketMQ消息中间件和XXLJob分布式调度工具,实现系统间高效通信与任务管理。针对千万级用户量下的推送和发券场景,提出异步化、分片处理与惰性发券等优化方案,解决高并发压力。同时,通过RocketMQ实现系统解耦,提升扩展性,并利用XXLJob完成爆款商品推荐的分布式调度推送。整体设计确保系统在大规模用户场景下的性能与稳定性。
RocketMQ实战—8.营销系统业务和方案介绍
|
4月前
|
应用服务中间件 Nacos nginx
黑马头条_SpringCloud项目阶段一:环境搭建(Mac版本)
本文为 Mac 用户介绍微服务项目环境搭建,含阿里云服务器用 Docker 装 Nacos 1.2.0,本地通过 brew 装 OpenJDK 8、Maven 3.6.1、Redis,Docker 部署 MySQL 5.7 并配字符集,及 Nginx 安装与反向代理设置,附命令与配置步骤。
327 4
黑马头条_SpringCloud项目阶段一:环境搭建(Mac版本)
|
4月前
|
消息中间件 Ubuntu Java
SpringBoot整合MQTT实战:基于EMQX实现双向设备通信
本教程指导在Ubuntu上部署EMQX 5.9.0并集成Spring Boot实现MQTT双向通信,涵盖服务器搭建、客户端配置及生产实践,助您快速构建企业级物联网消息系统。
2000 1
|
9月前
|
消息中间件 存储 NoSQL
RocketMQ实战—6.生产优化及运维方案
本文围绕RocketMQ集群的使用与优化,详细探讨了六个关键问题。首先,介绍了如何通过ACL配置实现RocketMQ集群的权限控制,防止不同团队间误用Topic。其次,讲解了消息轨迹功能的开启与追踪流程,帮助定位和排查问题。接着,分析了百万消息积压的处理方法,包括直接丢弃、扩容消费者或通过新Topic间接扩容等策略。此外,提出了针对RocketMQ集群崩溃的金融级高可用方案,确保消息不丢失。同时,讨论了为RocketMQ增加限流功能的重要性及实现方式,以提升系统稳定性。最后,分享了从Kafka迁移到RocketMQ的双写双读方案,确保数据一致性与平稳过渡。
|
6月前
|
物联网 Linux 开发者
快速部署自己私有MQTT-Broker-下载安装到运行不到一分钟,快速简单且易于集成到自己项目中
本文给物联网开发的朋友推荐的是GMQT,让物联网开发者快速拥有合适自己的MQTT-Broker,本文从下载程序到安装部署手把手教大家安装用上私有化MQTT服务器。
1770 5
|
9月前
|
消息中间件 NoSQL 大数据
RocketMQ实战—5.消息重复+乱序+延迟的处理
本文围绕RocketMQ的使用与优化展开,分析了优惠券重复发放的原因及解决方案。首先,通过案例说明了优惠券系统因消息重复、数据库宕机或消费失败等原因导致重复发券的问题,并提出引入幂等性机制(如业务判断法、Redis状态判断法)来保证数据唯一性。其次,探讨了死信队列在处理消费失败时的作用,以及如何通过重试和死信队列解决消息处理异常。接着,分析了订单库同步中消息乱序的原因,提出了基于顺序消息机制的代码实现方案,确保消息按序处理。此外,介绍了利用Tag和属性过滤数据提升效率的方法,以及延迟消息机制优化定时退款扫描的功能。最后,总结了RocketMQ生产实践中的经验.
RocketMQ实战—5.消息重复+乱序+延迟的处理
|
9月前
|
消息中间件 Java 测试技术
RocketMQ实战—7.生产集群部署和生产参数
本文详细介绍了RocketMQ生产集群的部署与调优过程,包括集群规划、环境搭建、参数配置和优化策略。
RocketMQ实战—7.生产集群部署和生产参数
|
9月前
|
消息中间件 NoSQL Java
RocketMQ实战—10.营销系统代码优化
本文主要介绍了如何对营销系统的四大促销场景的代码进行优化,包括:全量用户推送促销活动、全量用户发放优惠券、特定用户推送领取优惠券消息、热门商品定时推送。

热门文章

最新文章