Java基础知识——RocketMQ

本文介绍了RocketMQ的基础知识,包括NameServer、Broker、Producer和Consumer的角色与操作,以及MQ的优缺点。提到了启动Broker和NameServer的命令,并讨论了消息的发送、存储和消费,涉及了消息的顺序性、幂等性、可扩展性和负载均衡策略。还阐述了RocketMQ的高内聚低耦合原则,以及一致性、可用性与复杂性的权衡。
摘要由CSDN通过智能技术生成

怎么查看当前进程?怎么执行退出?怎么查看当前路径?
答案:
查看当前进程: ps
执行退出: exit
查看当前路径: pwd
目录创建用什么命令?创建文件用什么命令?复制文件用什么命令?
答案:
创建目录: mkdir
创建文件:典型的如 touch,vi 也可以创建文件,其实只要向一个不存在的文件输出,都会创建文件
复制文件: cp 7. 文件权限修改用什么命令?格式是怎么样的?
文件权限修改: chmod
格式如下
mv:移动文件
cp -r:文件
rm -rf
kill

高内聚低耦合:如果元素有高度的相关职责,除了这些职责再没有其他的工作,那么该元素就有高内聚。
可读性
复用性
可维护性和易变更性

MQ优点:解耦合,削峰,应用分发
应用解耦合:高耦合应用,如果一个子系统崩溃,系统崩溃
在这里插入图片描述
流量削峰
在这里插入图片描述
数据分发:增添系统,会直接修改发布者源码,造成麻烦

在这里插入图片描述
缺点:
一致性
可用性降低:MQ挂了系统不可用
复杂性提高:有了中介,幂等性
一致性

ActiveMQ:
RabbitMQ:
Kafka:高性能,但是对消息的重复,丢失,错误没有严格要求

启动:后台进程
nohup NameServer
nohup sh bin/mqnamesrv &

启动Broker:
nohup sh bin/mqbroker -n localhost:9876 &

producer:生产者
Consumer:消费者
Name Server:管理者
Broker(Master,Slave):储存者
topic:种类
Message Queue:消息队列
一个topic可以根据需要设置多个

在这里插入图片描述NameServer集群:无状态,节点之间无任何信息同步

Broke
Master:写操作
Slave:读操作
BrokerID 0,非0区分主从
BrokerName区分集群
多个相同BrokerName的broker叫作组,保证producer端的高可用性
每个Broker与NameServer集群中的所有节点建立长连接,定时注册 Topic 信息到所有 NameServer。
Master宕机,Slave不能切换为Master

Producer与一个NameServer建立长连接,也与Master建立长连接

public class Producer {
    public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
        DefaultMQProducer producer = new DefaultMQProducer("test_quick_producer_name");

        producer.setNamesrvAddr(Const.NAMESRV_ADDR);

        producer.start();

        for(int i=0;i<5;i++) {
            //创建消息
            Message message = new Message("test_quick_topic", // 主题
                    "TagA", // 标签
                    "keyA", // 用户自定义的key,用户定义的唯一表示
                    ("Hello Rocket" + i).getBytes() // 消息内容实体(byte[])
            );
            //发送消息
            SendResult send = producer.send(message);
            System.out.println(send);
        }

        producer.shutdown();
    }
}

Consumer:
消费者:
DefaultMQPushConsumer:由broker控制读取,设置好各种参数和传人处理消息的函数 。 系统收到消息后自动调用处理函数来处理消息,自动保存 Offset,而且加入新的 DefaultMQPushConsumer后会自动做负载均衡。

DefaultMQPullConsumer:读取功能大部分由使用者自主控制
订阅模式:
clustering负载均衡:同一个ConsumerGroup只消费订阅消息的部分内容,所有Consumer订阅的合并才是topic消息。分散消费压力
Broadcasting:每一个Consumer都能订阅并获得到topic所有消息
必须参数:GroupName,nameserverIP,topic

消息重试:
顺序消息:重试导致阻塞
无序消息:设置返回状态,集群模式
设置MessageListener
死信队列:当重试次数大于16次,就会进入死信队列。不将被消费者正常消费,需要用户进行及时处理

DefaultMQPushConsumer:
PullRequest:长轮询
当服务器收到客户端发来的请求后,服务器端不会直接进行响应,而是先将这个请求挂起,然后判断服务器端数据是否有更新。如果有更新,则进行响应,如果一直没有数据,则到达一定的时间限制(服务器端设置)才返回
Push方式缺点:1:服务器负荷大 2:客户端可能处理不过来
pull方式缺点:间隔不好定。太短忙等,太长积压

返回结果:
public class PullRequest {
private String consumerGroup;
private MessageQueue messageQueue;
private ProcessQueue processQueue;
private long nextOffset;
private boolean lockedFirst = false;
}

ProcessQueue:每一个MessageQueue(Commitlog)都有一个对象
保存在消费者端,保存所有从MessageQueue获取的消息和其他信息
拥有两个TreeMap和一个读写锁,和一些基本方法
TreeMap:key:offset,value:消息内容
msgTreeMap和msgTreeMapTemp
当从msgTreeMap获取信息后,执行的是remove方法。所以,为了保证
能够重复消费,会调用msgTreeMapTemp.put()
读写锁:用于本地顺序消费,和TreeMap的锁
基本方法:
isLockExpired:ProcessQueue锁定时间是否超过阈值(默认30000ms),如果没有超时,代表还是持有锁
isPullExpired:然后在rebalance的时候会去判断ProcessQueue已经超过一定的时间没有去拉取消息,如果是的话,则将ProcessQueue废弃
用途:流量控制。看出PushConsumer会判断获取但未处理的消息个数,消息总大小、offset跨度,任何一个超过限制就隔一段时间再拉取消息

退出:shutdown(),以便释放资源,保存offset
偏移量位置:
clustering:因为每一个consumer都不一样,所以要在broker存储
那processqueue不是有了吗?那是因为processqueue是如何发送的?肯定是根据broker上的offset发送的呀
broadcast:每一个都一样,保存在本地.
注意:clustering也会在本地储存offset
要点一:consumer在broker消费后,是由consumer主动更新broker对应的offsetStore
要点二:我们都知道从服务器会定期从主服务器拉取消费进度。但是如果主服务器宕机,我们从从服务器上读取消息,当主恢复后他的offset没变化,反而同步从的。如何解决
其实在主服务器拉取消费之前,就要根据本地缓存offset同步主服务器的offset(如果有的话如果本地缓存清空确实就没办法)

DefaultMQPullConsumer
返回的是一个set< MessageQueue>
在这里插入图片描述
在这里插入图片描述
pullMessage这个函数的PullCallBack中。PullCallBack函数里有个 switch 语句,根据从 Broker 返回的消息类型做相应的 处理

switch (pullResult.getPullStatus ()) {
    case FOUND:
       ……
      break;
    case NO_NEW_MSG
       ……
       break;
     case OFFSET_ILLEGAL :
       ……
       break;
     default:
       break;
    }

退出:可以直接退出,注意保存offset
offset:
在这里插入图片描述

问题一:
在这里插入图片描述

Producer:
同步发送:消息发送后必须等待ACK才能继续发送
异步发送:是指发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式,接收方必须回调SendCallback,否则重新发送
单向(Oneway):发送特点为发送方只负责发送消息,不等待服务器回应且没有回调函数触发

发送:根据负载均衡策略发送到不同的Message Queue中。
一个数据只在一个brokerName中,在master/slave中备份,主从型架构
指定发送:Message-QueueSelector
延时发送:setDelayTimeLevel
实现原理:Producer仍然直接发送
1:Broker对于接收到的消息首先会判断一下是不是延时消息,如果是延时消息会将消息以SCHEDULE_TOPIC_XXXX为topic替换原有的topic放入commitlog
2:定时任务服务,不断读取相应topic,以便进行还原

事务:特殊的两阶段
阶段一:
发送方发送prepare消息,MQ回复收到
阶段二:
执行本地逻辑,根据结果向MQ发送commit或者rollback。
如果发送消息丢失,一段时间后MQ对第一阶段待确认的消息发出回查请求,发送方根据本地事务再次发送co/ro

NameServer

五大HashMap:
private final HashMap<String/* topic */, List> topicQueueTable;
Key :Topic 的名称
Value :QueueData里存储着 Broker的名称、 读写queue的数量、 同步标识等。

private final HashMap<String/* brokerName */, BrokerData> brokerAddrTable;
key:BrokerName
value: 同名称的 Broker 可能存在多台机器, 一个 Master
和多个 Slave且包括了broker的IP地址和端口

private final HashMap<String/* clusterName /, Set<String/ brokerName */>> clusterAddrTable;
key:Cluster
value:由BrokerName组成的集合。

private final HashMap<String/* brokerAddr */, BrokerLiveInfo> brokerLiveTable;
Key 是 BrokerAddr,也就是对应着一台 机器,
value;Broker机器的实时状态,包括上次更新状态的时间 戳, NameServer会定期检查这个时间戳,超时没有更新就认为这个 Broker无效了,将其从 Broker列表里清除。

private final HashMap<String/* brokerAddr /, List/ Filter Server */> filterServerTable;
Filter Server是过滤服务器,是 RocketMQ 的一种服务端过滤方式,一 个 Broker 可以有 一个 或 多个 FilterServer。
这个结构的 Key 是 Broker 的地址, Value 是和这个 Broker关联的多个 Filter Server 的地址 。

在这里插入图片描述

路由注册:
注册:
broker:利用定时任务调用线程池SchedulerExecutorService
向NameServer注册BrokerController.registerBrokerAll每隔30S

相应:
ScanNotActiveBroker:由定时器触发的扫描函数,定期扫描执行。为检查Broker的更新信息并进行调用BrokerLiveTable
DefaultRequest­Processor:根据上报信息的请求做相应处理。更新或者删除
onChannelClose
onChannelException
onChannelIdle—
对broker交互:指导其创建topic/message queue
UpdateTopicSubCommand
optionB:在那个broker上创建本topic的message queue
optionC:在所有broker上创建本topic的message queue
在这里插入图片描述
在这里插入图片描述
NameserverStartup:启动:
步骤一:
得到NameserverController:
创建并填充
nameserverConfig:环境变量
NettyserverConfig:属性配置,监听端口,线程池个数等等
步骤二:
NameServerController初始化
NettyRemoteServer,注册线程池,ScanNotActiveBroker
步骤三:
Runtime.getRuntime().addShutdownHook(shutdownHook);
controller.shutdown()

在jvm中增加一个关闭的钩子,当jvm关闭的时候,会执行系统中已经设置的所有通过方法addShutdownHook添加的钩子,当系统执行完这些钩子后,jvm才会关闭。

broker

底层通信协议:
Remoting模块:netty
NettyRemotingClient,NettyRemotingServer
请求相应的格式:RemotingCommand,用户只需要关注信息,剩下的包括encode/decode由RemotingCommand完成

一致性:
吞吐量和一致性的权衡
produce:
同步双写:produce生产消息后,主从服务器都需要刷盘后才返回确认
异步复刻:produce生产消息,只需要在master写成功
同步刷盘:消息写入内存的pagecache后,立即刷盘,成功后返回ACK
异步刷盘:仅仅写入pagecache就可返回。pagecache积累到一定量后一次性刷盘
consumer
从服务器会定期从主服务器拉取信息

存储结构:
效率提升:零拷贝技术mmap,顺序写随机读
PAGE CACHE:页缓存
CommitLog+ConsumeQueue:
CommitLog:存储消息的文件,顺序存储
mappedFileQueue+mappedFile
真正存储数据的是mappedFile【msg + msg + msg + …+msg+blank】。也就是当最后的位置放不下消息的时候就填充空白
mappedFile:102410241024=1073741824进行递增,也就是说第一个文件名字为000000001073741824,第二个名字是以00000000002147483648进行命令,以次递增。体现在代码中就是以上一个文件的便宜量加上1073741824即可
每一个mappedFile大小就是1G,原因是零拷贝的文件限制大小为2G左右

pagecahce机制:页表机制

ConsumeQueue:offset文件,存储的是物理存储的地址
与process queue的区别在于,process存储的是存储的地址指的是哪个broker哪个topic
存储在内存中,快速读取
当ConsumeQueue丢失,可以通过CommitLog快速恢复

性质:

可用性
消费端的高可用性:master宕机后,consumer仍然可以从slave读取
生产端的高可用性:创建topic把多个Message queue创建在多个broker组上,这样当一个broker组不可用,其他组仍然可用。

消息的可靠性:主要从三个方面进行分析
1:从Producer分析:如何确保消息正确的发送到了Broker?
阻塞式发送
2:日志的索引,如果一条消息发送之后超时,也可以通过查询日志的API,来check是否在Broker存储成功

从Broker分析:如果确保接收到的消息不会丢失?
1:同步刷盘
2:同步复制

从Cunmser分析:如何确保拉取到的消息被成功消费?
消费者可以根据自身的策略批量Pull消息
Consumer自身维护一个持久化的offset(对应MessageQueue里面的min offset),标记已经成功消费或者已经成功发回到broker的消息下标

全局顺序消息:Topic下的Message设置为1,produce和consumer的并发也设置为1
部分顺序消息:把同一业务ID的消息发送到同一个Messagequeue,消息处理的时候不能够并发处理
区别在于若queue1,queue2都有订单,部分顺序消息只需要queue1顺序,queue2顺序,但是两者可以交叉执行
MessageQueueSelector
MessageListenerOrderly:为每个consumerqueue加锁

消息的不重复性:需要业务自己实现
方法一:幂等性【broker维护一个ID 】
方法二:维护一个消费记录,消费前查询这个消费是否被消费过
【consumer维护一个ID】

可扩展性:
NameServer:
通过HTTP服务来设置,程序会向一个HTTP地址请求来获取NameServer地址,默认URL是http://jmenv.tbsite.net:8080/rocketmq/nsaddr。
Broker:
增加:不会产生影响
减少:
情况一:当一个Topic只有一个Master Broker,停掉这个Broker后,消息的发送必定会受影响,需要再停止Broker前,停止发消息。
某个Topic下有多个Master Broker:
1.如果是同步方式发送:
在DefaultMQProducer内部有关而自动重试逻辑,其中一个Broker停了,会自动向另一个Broker发消息,不会出现丢消息的情况。
2.如果是异步方式或sendOneWay方式发送:
会丢失切换过程中的消息,因为在异步和sendOneWay方式下,发送失败不会重试。
DefaultMQProducer默认每30S到NameServer请求最新的路由消息,Producer如果获取不到已停止的Broker下的队列消息,后续就自动不再想这些队列发送消息。

消息优先级:
弱优先级,即不会让消息饿死,但是也不能保证按优先级执行
1:多topic。根据优先级划分不同topic
2:多messageQueue
强优先级:
DefaultMQPullConsumer

提高吞吐量:
一:消息过滤:
1:基于tag
在这里插入图片描述
ConsumerQueue中含有tag字段,作为消费者端的过滤依据

2:基SQL方式
构造message时利用putUserProperty设置属性用于过滤
之后在consumer的时候通过SQL过滤

3:Fliter Server过滤
在broker端通过设置函数进行过滤,过滤的数据再传给用户,占用brokerCPU资源

二:提高Consumer处理能力:
1:增加consumer实例。但是不要超过message queue数量否则接受不到消息
2:批量消费。10次update1条语句效率远低于1次update10条语句。所以批量消费
3:严重消息堆积时,丢弃不重要的消息使得consumer尽快追上producer的进度

三:Consumer负载均衡:
DefaultMqPushConsumer的负载均衡:
每一个consumer启动或者宕机后执行doRebalance操作,默认平均分配,即将Messagequeue平均分给consumer进行处理
DefaultMqPullConsumer:
负载均衡由使用者控制,系统也提供了registerMessageQueueListener

四:提供producer的发送速度:
1:同步改异步或oneway
2:多个producer并行执行。
会并发写入DirectMem中,然后异步刷盘。不存在竞争

五:broker的deadline算法 :
实现四个队列:
两个处理正常的read/write,按扇区号排序,进行正常的IO合并处理以提高吞吐量
两个处理超时的read/write,按请求创建时间排序,保证超时的请求会优先处理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值