本文将讲解如何在IDEA中导入 RocketMQ 源码,并运行 Broker 和 NameServer,编写一个消息发送与消息消费的示例。

一. 源码导入及调试

1.1 导入源码

RocketMQ 原先是阿里巴巴集团内部的消息中间件,于2016年提交至Apache基金会孵化,并最终成为Apache顶级项目。

第一步:从GitHub上clone RocketMQ 源码:RocketMQ。点击 File->Open 导入项目。

后面关于RocketMQ的源码分析若无特殊说明,将默认使用 4.6.0 版本

第二步:构建项目

在项目根目录下执行下列命令

mvn clean install -Dmaven.test.skip=true

1.2 启动 NameServer

第一步:展开 namesrv 模块,鼠标右键选中 NamesrvStartup.java,将其拖拽到Debug As,选中 Debug ‘NamesrvStartup.java.main()’,弹出下图:

第二步:单击 Environment variables 后面的按钮,弹出配置界面,配置如下环境变量:

ROCKETMQ_HOME=E:\software-engineer\java\open-source\rocketmq-home

点击 OK->Apply 保存即可。

第三步:在刚刚指定的文件夹内创建conf、logs、store文件夹。

第四步:从 distribution 部署目录中将 broker.conflogback_broker.xmllogback_namesrv.xml 等文件复制到 conf 目录中,按需修改 broker.conf

# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License. brokerClusterName = DefaultCluster
brokerName = broker-a
brokerId = 0 #nameServer地址
namesrvAddr=127.0.0.1:9876
deleteWhen = 04
fileReservedTime = 48
brokerRole = ASYNC_MASTER
flushDiskType = ASYNC_FLUSH #存储路径
storePathRootDir=E:\software-engineer\java\open-source\rocketmq-home\store
#CommitLog存储路径
storePatchCommitLog=E:\software-engineer\java\open-source\rocketmq-home\store\commitlog
#消费队列存储路径
storePathConsumeQueue=E:\software-engineer\java\open-source\rocketmq-home\store\consumequeue
#消息索引存储路径
storePathIndex=E:\software-engineer\java\open-source\rocketmq-home\store\index
#checkpoint 文件存储路径
storeCheckpoint=E:\software-engineer\java\open-source\rocketmq-home\store\checkpoint
#abort 文件存储路径
abortFile=E:\software-engineer\java\open-source\rocketmq-home\store\abort

第五步:点击 Debug 运行 NamesrvStartup.java,并输出“The Name Server boot success. serializeType=JSON”

1.3 启动 Broker

第一步:展开 broker 模块,启动 BrokerStartup.java ,会提示需要配置 ROCKETMQ_HOME 环境变量。与 NameServer启动时一样,配置环境变量即可:

第二步:配置-c参数,指定broker.conf 配置文件地址:

第三步:以 Debug 模式启动 BrokerStartup.java,查看${ROCKET_HOME}/logs/broker.log 文件,未报错则表示启动成功:

1.4 代码测试

1.4.1 生产者

第一步:修改org.apache.rocketmq.example.quickstart.Producer 示例程序,设置 RocketMQ NameServer地址:

public class Producer {
public static void main(String[] args) throws MQClientException, InterruptedException { DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name"); producer.setNamesrvAddr("127.0.0.1:9876");
producer.start(); for (int i = 0; i < 1; i++) {
try {
Message msg = new Message("TopicTest",
"TagA",
("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET)
);
SendResult sendResult = producer.send(msg);
System.out.printf("%s%n", sendResult);
} catch (Exception e) {
e.printStackTrace();
Thread.sleep(1000);
}
}
producer.shutdown();
}
}

第二步:运行该方法发送消息

1.4.2 消费者

第一步:修改 org.apache.rocketmq.example.quickstart.Consumer 示例程序,设置RocketMQ NameServer地址

public class Consumer {

    public static void main(String[] args) throws InterruptedException, MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name_4");
consumer.setNamesrvAddr("127.0.0.1:9876");
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
consumer.subscribe("TopicTest", "*");
consumer.registerMessageListener(new MessageListenerConcurrently() { @Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeConcurrentlyContext context) {
System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
System.out.printf("Consumer Started.%n");
}
}

第二步:运行该方法,消费消息

二. RocketMQ 源码结构

RocketMQ源码组织方式基于 Maven 模块组织,RocketMQ核心目录说明如下:

  • acl:权限控制模块
  • broker:broker模块(broker启动进程)
  • client:消息客户端,包含消息生产者和消费者
  • common:公共包
  • dev:开发者信息(非源码)
  • distribution:打包分发目录(非源码)
  • example:示例代码
  • filter:消息过滤相关基础类
  • logappender:日志实现相关类
  • logging:自主实现日志相关类
  • namesrv:NameServer实现相关类(NameServer启动进程)
  • openmessaging:消息开放标准,已发布
  • remoting:远程通信模块,基于Netty
  • srvutil:服务器工具类
  • store:消息存储相关实现类
  • style:checkstyle相关实现
  • test:测试相关类
  • tools:工具类,监控命令相关实现类。

三. Rocket设计理念和设计目标

3.1 设计理念

RocketMQ 设计基于主题的发布与订阅模式,其核心功能包括消息发送、消息存储和消息消费,整体设计追求简单和性能高效,主要体现在一下三个方面:

  1. 首先,NameServer设计极其简单,摒弃了业界常用的使用Zookeeper充当信息管理的“注册中心”,而是自研NameServer来实现元数据的管理(Topic路由信息等)。从实际需求出发,因为Topic路由信息无须在集群之间保持强一致,追求最终一致性,并且能容忍分钟级的不一致。正是基于此种情况,RocketMQ的NameServer集群之间互不通信,极大地降低了NameServer实现的复杂程度,对网络的要求也降低了不少,但是性能相比较Zookeeper有了极大的提升。
  2. 其次是高效的IO存储机制。RocketMQ追求消息发送的高吞吐量,RocketMQ的消息存储文件设计成文件组的概念,组内单个文件大小固定,方便引入内存映射机制,所有主题的消息存储基于顺序写,极大地提供了消息写性能,同时为了兼顾消息消费与消息查找,引入了消息消费队列文件与索引文件。
  3. 最后是容忍存在设计缺陷,适当将某些工作下放给RocketMQ使用者。消息中间件的实现者经常会遇到一个难题:如何保证消息一定能被消息消费者消费,并且保证只消费一次。RocketMQ的设计者给出的解决办法是不解决这个难题,而是退而求其次,只保证消息被消费者消费,但设计上允许消息被重复消费,这样极大地简化了消息中间件的内核,使得实现消息发送高可用变得非常简单与高效,消息重复问题由消费者在消息消费时实现幂等。

3.2 设计目标

RocketMQ作为一款消息中间件,需要解决如下问题:

  1. 架构模式 RocketMQ与大部分消息中间件一样,采用发布订阅模式,基本的参与组件主要包括:消息发送者、消息服务器(消息存储)、消息消费、路由发现。

  2. 顺序消息 所谓顺序消息,就是消息消费者按照消息达到消息存储服务器的顺序消费。RocketMQ可以严格保证消息有序。

  3. 消息过滤 消息过滤是指在消息消费时,消息消费者可以对同一主题下的消息按照规则只消费自己感兴趣的消息。RocketMQ消息过滤支持在服务端与消费端的消息过滤机制。

    1. 消息在Broker端过滤。Broker只将消息消费者感兴趣的消息发送给消息消费者。
    2. 消息在消息消费端过滤,消息过滤方式完全由消息消费者自定义,但缺点是有很多无用的消息会从Broker传输到消费端。
  4. 消息存储 消息中间件的一个核心实现是消息的存储,对消息存储一般有如下两个维度的考量:消息堆积能力和消息存储性能。RocketMQ追求消息存储的高性能,引入内存映射机制,所有主题的消息顺序存储在同一个文件中。同时为了避免消息无限在消息存储服务器中累积,引入了消息文件过期机制与文件存储空间报警机制。

  5. 消息高可用性 通常影响消息可靠性的有以下几种情况。

    1. Broker正常关机。
    2. Broker异常Crash。
    3. OS Crash。
    4. 机器断电,但是能立即恢复供电情况。
    5. 机器无法开机(可能是CPU、主板、内存等关键设备损坏)。
    6. 磁盘设备损坏。

    针对上述情况,情况14的RocketMQ在同步刷盘机制下可以确保不丢失消息,在异步刷盘模式下,会丢失少量消息。情况56属于单点故障,一旦发生,该节点上的消息全部丢失,如果开启了异步复制机制,RoketMQ能保证只丢失少量消息,RocketMQ在后续版本中将引入双写机制,以满足消息可靠性要求极高的场合。

  6. 消息到达(消费)低延迟 RocketMQ在消息不发生消息堆积时,以长轮询模式实现准实时的消息推送模式。

  7. 确保消息必须被消费一次 RocketMQ 通过消息消费确认机制(ACK)来确保消息至少被消费一次(least once),但由于ACK消息有可能丢失等其他原因,RocketMQ无法做到消息只被消费一次,有重复消费的可能。

  8. 回溯消息 回溯消息是指消息消费端已经消费成功的消息,由于业务要求需要重新消费消息。RocketMQ支持按时间回溯消息,时间维度可精确到毫秒,可以向前或向后回溯。

  9. 消息堆积 消息中间件的主要功能是异步解耦,必须具备应对前端的数据洪峰,提高后端系统的可用性,必然要求消息中间件具备一定的消息堆积能力。RocketMQ消息存储使用磁盘文件(内存映射机制),并且在物理布局上为多个大小相等的文件组成逻辑文件组,可以无限循环使用。RocketMQ消息存储文件并不是永久存储在消息服务器端,而是提供了过期机制,默认保留3天

  10. 定时消息 定时消息是指消息发送到Broker后,不能被消息消费端立即消费,要到特定的时间点或者等待特定的时间后才能被消费。如果要支持任意精度的定时消息消费,必须在消息服务端对消息进行排序,势必带来很大的性能损耗,故RocketMQ不支持任意进度的定时消息,而只支持特定延迟级别。

  11. 消息重试机制 消息重试是指消息在消费时,如果发送异常,消息中间件需要支持消息重新投递,RocketMQ支持消息重试机制。

RocketMQ阅读源码前的准备的更多相关文章

  1. 俯瞰Dubbo全局,阅读源码前必须掌握这些!!

    写在前面 在上一篇<冰河开始对Dubbo下手了!>一文中,我们重点介绍了为何要学习Dubbo,而且还是要深入理解Dubbo的原理和核心源码.既然是要写深度解析Dubbo源码的系列专题,我们 ...

  2. 学习Vue源码前的几项必要储备(一)

    从接下来的一段时间里,Mg要进行阅读源码的工作.再阅读源码前,梳理一下准备工作. 7项重要储备 Flow 基本语法 发布/订阅模式 ES6+ 语法 原型链.闭包 函数柯里化 event loop 1. ...

  3. 深入理解RocketMQ(一)---阅读源码准备

    本文主要描述使用Idea获取rocketMQ源码及源码的读取. 在演示搭建源码环境前,先简要描述一下RocketMQ的设计目标. 1.架构模式 和大多数消息中间件一样,采用的是发布订阅模式,基本组件包 ...

  4. 【转】使用 vim + ctags + cscope + taglist 阅读源码

    原文网址:http://my.oschina.net/u/554995/blog/59927 最近,准备跟学长一起往 linux kernel 的门里瞧瞧里面的世界,虽然我们知道门就在那,但我们还得找 ...

  5. Spring源码解析——如何阅读源码(转)

    最近没什么实质性的工作,正好有点时间,就想学学别人的代码.也看过一点源码,算是有了点阅读的经验,于是下定决心看下spring这种大型的项目的源码,学学它的设计思想. 手码不易,转载请注明:xingoo ...

  6. Spring源码解析——如何阅读源码

    最近没什么实质性的工作,正好有点时间,就想学学别人的代码.也看过一点源码,算是有了点阅读的经验,于是下定决心看下spring这种大型的项目的源码,学学它的设计思想. 手码不易,转载请注明:xingoo ...

  7. 使用 vim + ctags + cscope + taglist 阅读源码

    转自:http://my.oschina.net/u/554995/blog/59927 最近,准备跟学长一起往 linux kernel 的门里瞧瞧里面的世界,虽然我们知道门就在那,但我们还得找到合 ...

  8. 学会阅读源码后,我觉得自己better了

    我有一个大学同学,名叫石磊,我在之前的文章里提到过几次,我们俩合作过很多项目.只要有他在,我就特别放心,因为几乎所有难搞的问题,到他这,都能够巧妙地化解.他给我印象最深刻的一句话就是,"有啥 ...

  9. 阅读源码(III)

    往期系列: <由阅读源码想到> <由阅读源码想到 | 下篇> Medium上有一篇文章Why You Don't Deserve That Dream Developer Jo ...

  10. 阅读源码(IV)

    往期系列: <由阅读源码想到> <由阅读源码想到 | 下篇> <阅读源码(III)> Eric S.Raymond的写于2014年的<How to learn ...

随机推荐

  1. Djangorestframework 记录一个报错 -- rest_framework.authentication.ToKenAuthentication

    今天在使用 Djangorestframework 这个框架时,发生报错: ImportError: Could not import 'rest_framework.authentication.T ...

  2. 填报表中也可以添加 html 事件

    在实际的项目开发中,填报表的应用十分广泛. 多数情况下,填报表会作为整个项目的一部分配合需求灵活使用,但有时也会受大项目环境的影响,产生一些特别的要求.比如,通常报表单元格的数据类型大多是文本,有时却 ...

  3. MMDeploy部署实战系列【第六章】:将编译好的MMdeploy导入到自己的项目中 (C++)

    MMDeploy部署实战系列[第六章]:将编译好的MMdeploy导入到自己的项目中 (C++) 这个系列是一个随笔,是我走过的一些路,有些地方可能不太完善.如果有那个地方没看懂,评论区问就可以,我给 ...

  4. vue项目中嵌入软键盘(中文/英文)

    键盘效果是这样,样式可以自己调整.gittee地址:https://gitee.com/houxianzhou/VirtualKeyboard.git步骤1 安装使用jQuery npm instal ...

  5. 阿里云日志服务SLS携手观测云发布可观测性解决方案,共建可观测应用创新

    简介: 2022年云栖大会期间,阿里云同观测云共同发布可观测性联合解决方案.观测云通过集成日志服务SLS的产品能力,发布了观测云SAAS专属版. 2022年云栖大会期间,阿里云同观测云共同发布可观测性 ...

  6. PolarDB-X拆分键推荐

    简介: PolarDB-X 2.0提供了透明分布式的能力,默认进行主键的哈希拆分,让用户无感知的从单机数据库迁移到分布式数据库.拆分键的选择是学术界和工业界研究已久的问题,一个重要选型是tp优先还是a ...

  7. PolarDB-X源码解读系列:DML之Insert流程

    简介: Insert类的SQL语句的流程可初略分为:解析.校验.优化器.执行器.物理执行(GalaxyEngine执行).本文将以一条简单的Insert语句通过调试的方式进行解读. 在阅读本文之前,强 ...

  8. 如何 0 改造,让单体/微服务应用成为 Serverless Application

    简介: 随着 2013 年以 Docker 为代表的容器技术.CNCF 基金会以及 K8s 的发展等,云原生开始被广大开发者所熟知.云原生时代之前还有两个阶段:一是自建 IDC 机房,二是简单地把原有 ...

  9. 阿里云实时数仓Hologres年度发布,解读数仓新趋势

    ​简介:阿里云实时数仓Hologres年度发布,解读数仓新趋势. 1月7日,阿里云实时数仓Hologres发布最新版本,在成本.可用性.在线高可用等多方面进行了能力升级,行存吞吐提升100%,列存吞吐 ...

  10. [Gin] gin-jwt 中间件的请求流程与使用思路

    gin-jwt 中间件是对 jwt-go 的封装以适应 gin 框架.gin-jwt 对不同的请求流程有不同的 handler: 登录请求流程 是用 LoginHandler. 需要 jwt 令牌的后 ...