rocketmq本地demo搭建

rocketMQ的源码地址:RocketMQ: RocketMQ 是一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。 - Gitee.com

rocketmq的安装:

       我本地(windows10)搭建的不是按照上面的步骤,遇到了一些问题花了些时间,将代码及过程记录下来;

       本机环境:jdk1.8、maven3.8.4

       下载 rocketmq:Index of /dist/rocketmq ,我下载的是 rocketmq-all-4.9.3-bin-release.zip,解压后目录:E:\works\rocketmq-4.9.3,然后 cmd 到这个目录的bin目录下执行命令 来启动 nameserver 和 broker(我刚开始是使用的localhost而不是127.0.0.1,在启动dashbord时报错连接不到MQ)

             (1)、mqnamesrv.cmd -n 127.0.0.1:9876

             (2)、mqbroker.cmd -n 127.0.0.1:9876  

命令执行后的效果如下:

如果启动broker报错 找不到jdk的主类,参考:RocketMQ-4.9.4启动broker报错: 找不到或无法加载主类 Files\Java\jdk-1.8\lib 解决办法_rocketmq broker 启动错误:找到或无法加载主类-CSDN博客

rocketMQ的结构:
rocketMQ控制台

       如果一个topic被多个消费者订阅,在下面的consumer管理中可以看到多个 订阅组(下图二),一个消息发到了broker集群(两个broker节点,broker-a和broker-b),每个订阅组可以看到 代理者位点(队列接收到的消息数量)、消费者位点(已消费的消息)、差值(未消费的消息),

       下图二模拟的是生产者发送2000个消息到roctetMQ集群,消息均匀分布在两个broker节点、共八个队列(一个topic默认在每个broker生成四个队列,默认队列数量可以在图一的 TOPIC配置中修改),上面一个订阅者消费完全部消息,下面一个订阅者消费了1000个消息

         rocketMQ默认是没有页面的控制台的,需要安装控制台才能看到,参考左边目录 "rocketMQ控制台安装"

集群中可以看到集群信息,master表示主节点、slave表示从节点,从节点一般用于备份数据,不会在从节点上进行数据的读写,在主节点挂了后也不会由从节点顶上,roctetMQ一般使用keep-alive保持高可用(监听服务状态,在服务挂了后立马启动)

 生产者发消息到消费者使用了零拷贝技术提升速度,流程如下(DMA比CPU拷贝速度快很多):

roctetMQ的组成

NameServer

        NameServer是整个RocketMQ的“大脑”,它是RocketMQ的服务注册中心,所以RocketMQ需要先启动NameServer再启动Broker。

        Broker在启动时向所有NameServer注册(主要是服务器地址等),生产者在发送消息之前先从NameServer获取Broker服务器地址列表(消费者一样),然后根据负载均衡算法从列表中选择服务器进行消息发送。

主机(Broker)

        RocketMQ的核心,一个topic可以对应若干个broker,也就是可以将消息发到若干个主机上,用于暂存和传输消息。

生产者(Producer)

        也称为消息发布者,负责生产并发送消息至RocketMQ。

消费者(Consumer)

        也称为消息订阅者,负责从RocketMQ接收并消费消息。

消息(Message)

        生产或消费的数据,对于RocketMQ来说,消息就是字节数组。

主题(Topic)

        标识RocketMQ中一类消息的逻辑名字,消息的逻辑管理单位。无论消息生产还是消费,都需要指定Topic。主题主要用于区分消息的种类:一个生产者可以发送消息给一个或者多个Topic,消息的消费者也可以订阅一个或者多个Topic消息。

消息队列(Message Queue)

        简称Queue或Q。消息物理管理单位。一个Topic在一台broker服务器上默认有四个队列,队列数量是可以调整的。

        无论生产者还是消费者,实际的生产和消费都是针对Q级别。例如Producer发送消息的时候通过负载均衡算法(默认轮询算法)从Topic下选一条Q发送;Consumer消费的时候也会负载均衡地分配若干个Q,只拉取对应Q的消息。

分组(Group)

        生产者:标识发送同一类消息的Producer,通常发送逻辑一致。发送普通消息的时候,作为标识,并无特别用处。

        消费者:标识一类Consumer的集合名称,这类Consumer通常消费一类消息(也称为Consumer Group),且消费逻辑一致。同一个Consumer Group下的各个实例将共同消费topic的消息,起到负载均衡的作用。

标签(Tag)

        RocketMQ支持给在发送的时候给消息打tag,同一个topic的消息虽然逻辑管理是一样的。但是消费同一个topic时,如果你消费订阅的时候指定的是tagA,那么tagB的消息将不会投递。

心跳:         

        如果是NameServer 集群,各 NameServer 之间无任何数据交互,Broker 启动之后会向所有 NameServer 定期(每 30s)发送心跳包,包括:IP、Port、TopicInfo,NameServer 会定期扫描 Broker 存活列表,如果超过 120s 没有心跳则移除此 Broker 相关信息,代表下线。

        心跳的实现逻辑:新建一个配置类,在配置类的后置处理器中启动一个线程,线程中使用while死循环,每次循环向调度中心发一条数据后睡眠30秒,调度中心收到数据后更新数据及updateDate,调度中心也会启一个类似线程将updateDate是90秒之前的数据移除掉;

消息存储:

      RocketMq使用netty进行通讯,消息存储在磁盘上,在本地目录C:\Users\Administrator\store,消息存储在commitlog中,index和consumequeue是消息的索引

rocketMQ控制台安装:

        之前的控制台是 rocketmq-console 后面改为rocketmq-dashboard;

        rocketmq-dashboard的下载地址: https://github.com/apache/rocketmq-dashboard ,下载后在idea打开启动 DashboardController,浏览器输入localhost:8080   打开MQ控制台

RocketMQ的介绍可参考:rocketmq 几种队列_进阶必看的 RocketMQ ,就这篇了_Mika.w的博客-CSDN博客

不同消息类型的区别:了解下RocketMQ不同类型的消息_绅士jiejie的博客-CSDN博客_rocketmq消息类型 

rocketMQ的负载均衡:

       rocketMQ的负载均衡都是在队列层面上,生产者或消费者选择队列来发送或消费数据;

       生产者的负载均衡:生产者会缓存所有的broker和队列信息,使用客户端的负载均衡,默认使用轮询的方式向队列发送消息,除了轮询还有随机、自定义 的负载方式

       消费者的负载均衡:消费者的负载均衡有两种方式:轮询或按数量分配

            轮询:如有8个队列、3个消费者(ABC),147 三个队列给A、258给B、36给C

            数量:队列总数除以消费者数算出每个消费者消费的队列数量,123给A、456给B、78给C

       tips:客户端的负载均衡是指客户端缓存了服务端的所有broker队列信息,每次发送数据不需要从nameserver获得broker和队列信息,直接在客户端获得要发送的broker和队列

消息的分类:

       1、普通消息:分为 同步消息、异步消息、一次发送、延迟消息

        (1)、同步消息:消息发出后 会在收到接收方返回响应后才发下一个数据包;

        (2)、异步消息:消息发出后,不等接收方发回响应,接着发送下个数据包,通过回调接口接收服务器响应,并对响应结果进行处理

        (3)、一次消息:只发送消息,不等待响应且没有回调函数触发,即只发送请求不等待应答

        (4)、延迟消息:设置一个延迟时间,消息发到rocketMQ后对消费者是不可以消费的,等到延迟时间后消费者才可以消费

       2、顺序消息:是消息队列提供的一种严格按照顺序来发布和消费的消息类型

             其中 顺序消息是每个业务id使用一个队列,队列中的消息顺序执行,也可以所有数据使用一个队列顺序执行

             顺序消息的消费者端也和其他类型的消息不一样,消费者接收到消息后把消息放入到msgTreeMap(TreeMap类型,有序)中,以偏移量作为key,保证消息时有序的;从submitConsumeRequest进入到 ConsumeRequest中,消费消息时对 processQueue(存放msgTreeMap)加锁,并且消费消息是在 synchronized 中执行,

       3、事物消息

            (1). ⽣产者先发送⼀条半事务消息到MQ

            (2). MQ收到消息后返回ack确认

            (3). ⽣产者开始执⾏本地事务

            (4). 如果事务执⾏成功发送commit到MQ,失败发送rollback,

            (5). 如果MQ⻓时间未收到⽣产者的⼆次确认commit或者rollback,MQ对⽣产者发起消息回查,回查的逻辑也需要本地实现,根据悲本地事物的回查状态返回给MQ

     

五种消息类型的实现demo在下面代码中都有:

链接:https://pan.baidu.com/s/1gZtE9zgSD9KbcWbRVFFAEg?pwd=w8pa 
提取码:w8pa

消费确认(ACK):

        业务实现消费回调的时候,当且仅当此回调函数返回ConsumeConcurrentlyStatus.CONSUME_SUCCESS,RocketMQ才会认为这批消息(默认是1条)是消费完成的,中途断电、抛出异常等都不会认为成功——即都会重新投递。
        为了保证消息是肯定被至少消费成功一次,RocketMQ会把这批消息重发回Broker(topic不是原topic而是这个消费组的RETRY topic),在延迟的某个时间点(默认是10秒,业务可设置)后,再次投递到这个ConsumerGroup。而如果一直这样重复消费都持续失败到一定次数(默认16次),就会投递到DLQ死信队列。应用可以监控死信队列来做人工干预,一条消息无论重试多少次,这些重试消息的 Message ID 不会改变
        另外如果使用顺序消费的回调MessageListenerOrderly时,由于顺序消费是要前者消费成功才能继续消费,所以没有RECONSUME_LATER的这个状态,只有SUSPEND_CURRENT_QUEUE_A_MOMENT来暂停队列的其余消费,直到原消息不断重试成功为止才能继续消费

死信特性:
      不会再被消费者正常消费,有效期与正常消息相同,均为 3 天,3 天后会被自动删除。因此,请在死信消息产生后的 3 天内及时处理。

      一个死信队列对应一个 Group ID, 而不是对应单个消费者实例。如果一个 Group ID 未产生死信消息,消息队列 RocketMQ 不会为其创建相应的死信队列。一个死信队列包含了对应 Group ID 产生的所有死信消息,不论该消息属于哪个 Topic。

同步双写、异步复制

  如果一个broker组有Master和Slave,消息从Master复制到Slave上,有同步和异步两种方式。

               同步双写:是等Master和Slave均写成功后才反馈给客户端写成功状态;

               异步复制:是只要Master写成功即可反馈给客户端写成功状态;

  这两种方式各有优劣,在异步复制方式下,系统拥有较低的延迟和较高的吞吐量,但是如果Master出了故障,有些数据因为没有被写入Slave,有可能会丢失;在同步复制方式下,如果Master出故障,Slave上有全部的备份数据,容易恢复,但是同步复制会增大数据写入延迟,降低系统吞吐量。

消息的幂等:

       每个消息都有一个messageId,这个messageId也有重复出现的概率,所以建议使用业务上的唯一标识

保证消息正常消费,可参考:        RocketMQ 如何保证消息和原理_rocketmq ack_风带动了思绪的博客-CSDN博客

消息文件删除:

       rocketMQ启动的时候会加载 commitlog,ConsumeQueue 目录下的所有文件,为了避免内存与磁盘的浪费,所以需要引入一种机制来删除己过期的文件。
       RocketMQ 清除过期文件的方法是 :如果非当前写文件在一定时间间隔内没有再次被更新,则认为是过期文件,RocketMQ 不会关注这个文件上的消息是否全部被消费。默认每个文件的过期时间为 42小时(不同版本的默认值不同,这里以4.4.0为例) ,通过在 Broker 配置文件中设置 fileReservedTime 来改变过期时间,单位为小时。
        触发文件清除操作的是一个定时任务,默认每10s执行一次

RocketMQ中的高可用机制:

        RocketMQ分布式集群是通过Master和Slave的配合达到高可用性的。
        Master和Slave的区别:在Broker的配置文件中,参数 brokerId的值为0表明这个Broker是Master,大于0表明这个Broker是 Slave,同时brokerRole参数也会说明这个Broker是Master还是Slave。
        Master角色的Broker支持读和写,Slave角色的Broker仅支持读,也就是 Producer只能和Master角色的Broker连接写入消息;Consumer可以连接 Master角色的Broker,也可以连接Slave角色的Broker来读取消息。

         如果broker的Master服务挂了,不支持从Slave节点中选一个出来当成Master节点,只会将消息发到其他可用的Master节点;        

rocketMQ集群的搭建:

        集群的搭建是通过修改MQ的配置实现,brokerName一样说明是一个master组中的broker,brokerId=0表示master节点、brokerId>0 表示slave节点

        
 

rocketMQ的源码:

        rocketMQ的消费者从broker获取消息有两种方式,推模式(push)和拉模式(pull),这两种方式都是消费者发请求到broker获取消息,不同之处在于:

       推模式:Consumer定期向Broker发送请求获取消息,但Broker并不会立即返回空响应,而是会将Consumer的请求放入队列中,直到有新消息到达或者达到预设的最大等待时间(例如5秒)才返回消息。所以给消费者的感受是消息是实时推送的,默认也是使用push模式;

       拉模式:消费者从broker获得消息、时间间隔等都需要开发者写代码实现,一般很少使用。

从 PullMessageService 的run方法进入

拉取并消费消息:

消息消费成功返回 ConsumeConcurrentlyStatus.CONSUME_SUCCESS

生产者推送消息成功返回 SendStatus.SEND_OK,从demo的随便一个发送消息的代码中进入就可以看到

生产者和broker之间通过netty通信,服务启动时生成channel,发消息时选择一个channel进行写消息:

如何保证消息成功发送:

     1、生成者:同步消息、失败或超时重试
     2、 broker:
          多个master节点的集群,如果其中一个master挂了,其他的master节点可正常使用
          同步刷盘机制
     3、消费者:
         消息消费完成后再返回成功  consumer_success     
         消费重试机制,要保证幂等

    参考:https://blog.csdn.net/m0_64210833/article/details/126426814

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RocketMQ是一个分布式消息中间件,它提供可靠的消息传递和高性能的消息处理能力。下面是一个简单的RocketMQdemo示例,用于发送和接收消息: 1. 首先,你需要下载并安装RocketMQ,可以从官方网站获取安装包。 2. 启动RocketMQ服务端。进入RocketMQ的安装目录,执行以下命令启动NameServer和Broker: ``` # 启动NameServer sh mqnamesrv # 启动Broker sh mqbroker -n localhost:9876 ``` 3. 在Java项目中引入RocketMQ的依赖。在项目的pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>org.apache.rocketmq</groupId> <artifactId>rocketmq-client</artifactId> <version>4.8.0</version> </dependency> ``` 4. 发送消息的示例代码: ```java 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.common.message.Message; public class RocketMQProducer { public static void main(String[] args) throws MQClientException { // 实例化生产者 DefaultMQProducer producer = new DefaultMQProducer("producer_group"); // 设置NameServer地址 producer.setNamesrvAddr("localhost:9876"); // 启动生产者 producer.start(); try { // 创建消息实例,指定Topic、Tag和消息内容 Message msg = new Message("topic", "tag", "Hello RocketMQ".getBytes()); // 发送消息并获取发送结果 SendResult sendResult = producer.send(msg); System.out.println("发送结果:" + sendResult); } catch (Exception e) { e.printStackTrace(); } finally { // 关闭生产者 producer.shutdown(); } } } ``` 5. 接收消息的示例代码: ```java 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.common.message.MessageExt; import java.util.List; public class RocketMQConsumer { public static void main(String[] args) throws Exception { // 实例化消费者 DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer_group"); // 设置NameServer地址 consumer.setNamesrvAddr("localhost:9876"); // 订阅Topic和Tag consumer.subscribe("topic", "tag"); // 注册消息监听器 consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> { for (MessageExt msg : msgs) { System.out.println("接收到消息:" + new String(msg.getBody())); } return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; }); // 启动消费者 consumer.start(); } } ``` 以上示例代码演示了如何使用RocketMQ发送和接收消息。你可以根据自己的需求进行修改和扩展。希望对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值