关于rocketmq:Apache-RocketMQ-50-消息进阶如何支撑复杂的业务消息场景

一致性首先来看 RocketMQ 的第一个个性-事务音讯,事务音讯是 RocketMQ 与一致性相干的个性,也是 RocketMQ 有别于其余音讯队列的最具区分度的个性。 以大规模电商零碎为例,付款胜利后会在交易系统中订单数据库将订单状态更新为已付款。而后交易系统再发送一条音讯给 RocketMQ,RocketMQ 将订单已付款的事件告诉给所有上游利用,保障后续的履约环节。 但上述流程存在一个问题,交易系统写数据库与发消息相互离开,它不是一个事务,会呈现多种异常情况,比方数据库写胜利但音讯发失败,这个订单的状态上游利用接管不到,对于电商业务来说,可能造成大量用户付款但卖家不发货的状况;而如果先发消息胜利再写数据库失败,会造成上游利用认为订单已付款,推动卖家发货,然而理论用户未付款胜利。这些异样都会对电商业务造成大量脏数据,产生灾难性业务结果。 而 RocketMQ 事务音讯的能力能够保障生产者的本地事务(如写数据库)、发消息事务的一致性,最初通过 Broker at least once 的生产语义,保障消费者的本地事务也能执行胜利,最终实现生产者、消费者对同一业务的事务状态达到最终统一。 一致性:事务音讯-原理如下图所示,事务音讯次要通过两阶段提交+事务弥补机制联合实现。 首先生产者会发送 half 音讯,也就是 prepare 音讯,broker 会把 half 存到队列中。接下来生产者执行本地事务,个别是写数据库,本地事务实现后,会往 RocketMQ 发送 commit 操作,RocketMQ 会把 commit 操作写入 OP 队列,并进行 compact,把已提交的音讯写到 ConsumeQueue 对消费者可见。反过来如果是 rollback 操作,则会跳过对应的 half 音讯。 面对异样的状况,比方生产者在发送 commit 或者 rollback 之前宕机了,RocketMQ broker 还会有弥补查看机制,定期回查 Producer 的事务状态,持续推动事务。 无论是 Prepare 音讯、还是 Commit/Rollback 音讯、或者是 compact 环节,在存储层面都是恪守 RocketMQ 以程序读写为主的设计理念,达到最优吞吐量。 一致性:事务音讯 demo接下来来看一个事务音讯的简略示例。应用事务音讯须要实现一个事务状态的查询器,这也是和一般音讯一个最大的区别。如果咱们是一个交易系统,这个事务回查器的实现可能就是依据订单 ID 去查询数据库来确定这个订单的状态到底是否是提交,比如说创立胜利、已付款、已退款等。主体的音讯生产流程也有很多不同,须要开启分布式事务,进行两阶段提交,先发一个 prepare 的音讯,而后再去执行本地事务。这里的本地事务个别就是执行数据库操作。而后如果本地事务执行胜利的话,就整体 commit,把之前的 prepare 的音讯提交掉。这样一来消费者就能够生产这条音讯的。如果本地事务出现异常的话,那么就把整个事务 rollback 掉,之前的那条 prepare 的音讯也会被勾销掉,整个过程就回滚了。事务音讯的用法变动次要体现在生产者代码,消费者应用形式和一般音讯统一,demo 外面就不展现了。 ...

September 4, 2023 · 2 min · jiezi

关于rocketmq:RocketMQ-50-架构解析如何基于云原生架构支撑多元化场景

本文将从技术角度理解 RocketMQ 的云原生架构,理解 RocketMQ 如何基于一套对立的架构撑持多元化的场景。 文章次要蕴含三局部内容。首先介绍 RocketMQ 5.0 的外围概念和架构概览;而后从集群角度登程,从宏观视角学习 RocketMQ 的管控链路、数据链路、客户端和服务端如何交互;最初介绍音讯队列最重要的模块存储系统,理解 RocketMQ 如何实现数据的存储和数据的高可用,以及如何利用云原生存储进一步晋升竞争力。 01 概览在介绍 RocketMQ 的架构之前,先从用户视角来看下 RocketMQ 的要害概念以及畛域模型。如下图,这里依照音讯的流转程序来介绍。 在 RocketMQ 中,音讯生产者个别对应业务零碎的上游利用,在某个业务动作触发后发送音讯到 Broker。Broker 是音讯零碎数据链路的外围,负责接管音讯、存储音讯、保护音讯状态、消费者状态。多个 broker 组成一个音讯服务集群,独特服务一个或多个 Topic。 生产者生产音讯并发送到 Broker,音讯是业务通信的载体,每个音讯蕴含音讯 ID、音讯 Topic、音讯体内容、音讯属性、音讯业务 key 等。每条音讯都属于某个 Topic,示意同一个业务的语义。 在阿里外部,交易音讯的 Topic 被称为 Trade,购物车音讯称为 Cart,生产者利用会将音讯发送到对应的 Topic 上。Topic 里还有 MessageQueue,用于音讯服务的负载平衡与数据存储分片,每个 Topic 蕴含一个或多个 MessageQueue,散布在不同的音讯 Broker。 生产者发送音讯,Broker 存储音讯,消费者负责生产音讯。消费者个别对应业务零碎的上游利用,同一个消费者利用集群共用一个 Consumer Group。消费者会与某个 Topic 产生订阅关系,订阅关系是 Consumer Group+Topic +过滤表达式的三元组,合乎订阅关系的音讯会被对应的消费者集群生产。 接下来就从技术实现角度进一步深刻理解 RocketMQ。 02 架构概览下图是一张 RocketMQ 5.0 的架构图,RocketMQ 5.0 的架构从上往下可分为 SDK、NameServer、Proxy 与 Store 层。 ...

August 17, 2023 · 3 min · jiezi

关于rocketmq:SpringBoot3集成RocketMq

标签:RocketMq5.Dashboard;一、简介RocketMQ因其架构简略、业务功能丰富、具备极强可扩展性等特点被广泛应用,比方金融业务、互联网、大数据、物联网等畛域的业务场景; 二、环境部署1、编译打包1、下载5.0版本源码包rocketmq-all-5.0.0-source-release.zip2、解压后进入目录,编译打包mvn -Prelease-all -DskipTests -Dspotbugs.skip=true clean install -U 2、批改配置在distribution/target/rocketmq-5.0.0/rocketmq-5.0.0/bin/runserver.sh distribution/target/rocketmq-5.0.0/rocketmq-5.0.0/bin/runbroker.sh 3、服务启动1、该目录下distribution/target/rocketmq-5.0.0/rocketmq-5.0.0/bin/2、启动NameServersh mqnamesrv输入日志The Name Server boot success. serializeType=JSON3、启动Broker+Proxysh mqbroker -n localhost:9876 --enable-proxy输入日志rocketmq-proxy startup successfully4、敞开服务sh mqshutdown namesrvSend shutdown request to mqnamesrv(18636) OKsh mqshutdown brokerSend shutdown request to mqbroker with proxy enable OK(18647)4、控制台装置1、下载master源码包rocketmq-dashboard-master2、解压后进入目录,编译打包mvn clean package -Dmaven.test.skip=true3、启动服务java -jar target/rocketmq-dashboard-1.0.1-SNAPSHOT.jar4、输入日志INFO main - Tomcat started on port(s): 8080 (http) with context path ''5、拜访服务:localhost:8080 三、工程搭建1、工程构造 2、依赖治理在rocketmq-starter组件中,实际上依赖的是rocketmq-client组件的5.0版本,因为两个新版框架间的兼容问题,须要增加相干配置解决该问题; <dependency> <groupId>org.apache.rocketmq</groupId> <artifactId>rocketmq-spring-boot-starter</artifactId> <version>${rocketmq-starter.version}</version></dependency>3、配置文件配置RocketMq服务地址,音讯生产者和消费者; rocketmq: name-server: 127.0.0.1:9876 # 生产者 producer: group: boot_group_1 # 音讯发送超时工夫 send-message-timeout: 3000 # 音讯最大长度4M max-message-size: 4096 # 音讯发送失败重试次数 retry-times-when-send-failed: 3 # 异步音讯发送失败重试次数 retry-times-when-send-async-failed: 2 # 消费者 consumer: group: boot_group_1 # 每次提取的最大音讯数 pull-batch-size: 54、配置类在配置类中次要定义两个Bean的加载,即RocketMQTemplate和DefaultMQProducer,次要是提供音讯发送的能力,即生产音讯; ...

August 17, 2023 · 2 min · jiezi

关于rocketmq:RocketMQ入门实践与踩雷

RocketMQ的装置与启动(基于Linux与JDK1.8)下载我的项目首先Java工程抉择对应的RocketMQ版本,具体参考以下两图:举例:SpirngCloudAlibabaVersion为2.2.9RELEASE,MQ则抉择4.9.4版本。举例:如果是SpringBoot我的项目,如我的SpringBoot我的项目为2.2.3版本,对应SpirngCloudAlibaba为2.2.0RELEASE,MQ则抉择4.4.0版本。确定版本后到官网下载(版本号自行批改):https://www.apache.org/dyn/closer.cgi?path=rocketmq/4.9.3/ 装置解压并批改启动配置将下载好的ZIP文件在提前创立好的文件夹上unzip命令解压缩。赋权操作:执行chmod 777 /rocketmq-all-5.1.2-bin-release/*因为默认启动配置应用内存较大,对其进行批改。 启动启动namesrv(默认9876端口)进入门路 cd bin/后盾启动 nohup sh mqnamesrv &查看日志确认是否启动胜利 tail -f ~/logs/rocketmqlogs/namesrv.log也能够通过端口查看 netstat -an|grep 9876敞开命令sh mqshutdown namesrv 启动broker(默认10911端口)进入门路 cd bin/后盾启动 nohup ./mqbroker -n localhost:9876 &查看日志确认是否启动胜利 tail -f ~/logs/rocketmqlogs/broker.log敞开命令sh mqshutdown broker 测试# 申明一个 namesrv 的地址export NAMESRV_ADDR=localhost:9876# 发送音讯./tools.sh org.apache.rocketmq.example.quickstart.Producer# 申明一个 namesrv 的地址export NAMESRV_ADDR=localhost:9876# 接管音讯./tools.sh org.apache.rocketmq.example.quickstart.ConsumerDashBoard的搭建//可视化页面的搭建 SpringBoot工程接入RocketMQ//TODO最佳实际 部署投产过程中踩的雷本机SpringBoot我的项目无奈往近程服务器的RocketMQ发送音讯;具体报错:Producer无奈发送音讯且Client日志始终打印如下音讯: RocketMq closeChannel: close the connection to remote address[] 解决方案: 1.Client为Java工程本地启动。2.确认MQ服务与Client版本匹配。3.MQ服务机的9876与10911端口平安组须要凋谢给Clinet的ip,同时MQ服务机能够curl通Client的ip本机SpringBoot我的项目无奈生产音讯;具体报错:本机我的项目Producer往近程服务器MQ发送音讯状态OK,且依据MessageId能够在dashboard查问到音讯,然而本机Client无奈生产到音讯。解决方案1: 1.进入dashboard,确认音讯对应的topic下的生产组。2.RocketMQ分为集群模式和播送模式,集群模式下,topic+tag在一个订阅组内只会被其中一个消费者终端生产,须要确认是否有其余消费者终端生产了该音讯。解决方案2: 1.近程服务器未创立该topic。2.查看话题列表:sh mqadmin topicList -n 127.0.0.1:98763.在bin门路下执行 ./mqadmin updateTopic -n localhost:9876 -b localhost:10911 -t 你的topic名称4.或应用该命令启动brokernohup sh mqbroker -n localhost:9876 autoCreateTopicEnable=true -c ../conf/broker.conf &消费者无奈注册到MQ的话题的订阅组中(//TODO);如下两图所示:1.参考文章:https://blog.csdn.net/xhmico/article/details/122938904

July 9, 2023 · 1 min · jiezi

关于rocketmq:解读-RocketMQ-50-全新的高可用设计

高可用架构演进背景在分布式系统中不可避免的会遇到网络故障,机器宕机,磁盘损坏等问题,为了向用户不中断且正确的提供服务,要求零碎有肯定的冗余与容错能力。RocketMQ 在日志,统计分析,在线交易,金融交易等丰盛的生产场景中施展着至关重要的作用,而不同环境对基础设施的老本与可靠性提出了不同的诉求。在 RocketMQ v4 版本中有两种支流高可用设计,别离是主备模式的无切换架构和基于 Raft 的多正本架构(图中左侧和右侧所示)。生产实践中咱们发现,两正本的冷备模式下备节点资源利用率低,主宕机时非凡类型音讯存在可用性问题;而 Raft 高度串行化,基于多数派的确认机制在扩大只读正本时不够灵便,无奈很好的反对两机房对等部署,异地多核心等简单场景。RocketMQ v5 版本交融了上述计划的劣势,提出 DLedger Controller 作为管控节点(两头局部所示),将选举逻辑插件化并优化了数据复制的实现。 如何实现高可用零碎正本组与数据分片在 Primary-Backup 架构的分布式系统中,一份数据将被复制成多个副原本防止数据失落。解决雷同数据的一组节点被称为正本组(ReplicaSet),正本组的粒度能够是单个文件级别的(例如 HDFS),也能够是分区级 / 队列级的(例如 Kafka),每个实在存储节点上能够包容若干个不同正本组的正本,也能够像 RocketMQ 一样粗粒度的独占节点。独占可能显著简化数据写入时确保长久化胜利的复杂度,因为每个正本组上只有主正本会响应读写申请,备机个别配置只读来提供平衡读负载,选举这件事儿等价于让正本组内一个正本持有独占的写锁。 RocketMQ 为每个存储数据的 Broker 节点配置 ClusterName,BrokerName 标识来更好的进行资源管理。多个 BrokerName 雷同的节点形成一个正本组。每个正本还领有一个从 0 开始编号,不反复也不肯定间断的 BrokerId 用来示意身份,编号为 0 的节点是这个正本组的 Leader / Primary / Master,故障时通过选举来从新对 Broker 编号标识新的身份。例如 BrokerId = {0, 1, 3},则 0 为主,其余两个为备。 一个正本组内,节点间共享数据的形式有多种,资源的共享水平由低到高来说个别有 Shared Nothing,Shared Disk,Shared Memory,Shared EveryThing。典型的 Shared Nothing 架构是 TiDB 这类纯分布式的数据库,TiDB 在每个存储节点上应用基于 RocksDB 封装的 TiKV 进行数据存储,下层通过协定交互实现事务或者 MVCC。相比于传统的分库分表策略来说,TiKV 易用性和灵便水平很高,更容易解决数据热点与伸缩时数据打散的一系列问题,但实现跨多节点的事务就须要波及到屡次网络的通信。另一端 Shared EveryThing 的案例是 AWS 的 Aurora,Aliyun 的 PolarStore,旁路 Kernal 的形式使利用齐全运行于用户态,以最大水平的存储复用来缩小资源耗费,一主多备齐全共用一份底层牢靠的存储,实现一写多读,疾速切换。 ...

July 3, 2023 · 5 min · jiezi

关于rocketmq:RocketMQ-学习社区重磅上线AI-互动一秒了解-RocketMQ-功能源码

RocketMQ 背景Apache RocketMQ 诞生至今,始终服务于 100% 阿里团体外部业务、阿里云以及开源社区数以万计的企业客户。历经十多年双十一严苛流量验证的 RocketMQ,承载了超过万亿级音讯规模的洪峰压力。2021 年 Apache RocketMQ 更是进入全新 5.0 时代。立足于企业业务集成的外围场景,RocketMQ 在高牢靠低提早方面重点优化,构建了全新的低提早存储引擎和多场景容灾解决方案;面向业务集成过程中链路逻辑的多样性,RocketMQ 提供了丰盛的业务音讯类型,这些个性的积攒使得 RocketMQ 成为金融级业务音讯的首选计划。 残缺内容请点击下方链接查看: https://developer.aliyun.com/article/1242116?utm_content=g_10... 版权申明:本文内容由阿里云实名注册用户自发奉献,版权归原作者所有,阿里云开发者社区不领有其著作权,亦不承当相应法律责任。具体规定请查看《阿里云开发者社区用户服务协定》和《阿里云开发者社区知识产权爱护指引》。如果您发现本社区中有涉嫌剽窃的内容,填写侵权投诉表单进行举报,一经查实,本社区将立即删除涉嫌侵权内容。

July 3, 2023 · 1 min · jiezi

关于rocketmq:业务高速增长如祺出行如何用腾讯云消息队列-RocketMQ-应对挑战

导语作为广汽团体旗下的智慧出行平台,如祺出行上线四年工夫,用户规模和订单量保持高速增长。在过来的2022年,如祺出行平台累计注册用户冲破1800万,同比增长64%,年度订单总量超7000万,同比增长52%。 高速增长的用户规模和订单量,对技术平台提出更高要求。 随着专慢车业务的快速增长,越来越多业务需要与业务主流程耦合,导致调用链过长,接口提早减少了数倍,整体架构无论是性能还是扩展性,都存在很大的危险,遇到节假日顶峰,随时都有解体的危险。 为了晋升架构的稳定性,保障用户体验,如祺出行于2021年启动架构降级。其中,引入音讯队列做异步化是整个分布式架构设计的外围伎俩之一。 音讯队列是一种异步通信机制,能够将音讯从发送方发送到接管方,而不须要立刻解决。这种机制能够带来以下益处: 1.  异步化解决:音讯队列能够将不同组件或服务之间的通信异步化,使得用户下单速度更快,体验更好,进步零碎的响应速度和吞吐量。 2.  冗余解决:音讯队列能够将音讯复制到多个正本中,确保即便某个节点呈现故障,音讯也不会失落。 3.  解耦合:音讯队列能够将不同组件或业务之间的通信解耦合,每个业务只须要关注本人订阅的音讯,从而将下单主流程跟其它业务流程解耦,保障了主流程的稳固和晋升了运维的可观测性。 4.  流量管制:音讯队列能够对音讯进行缓冲和限流,避免音讯发送方发送过多的申请导致接管方解决不过去,从而进步零碎的稳定性。 音讯队列选型2019年以来,如祺出行次要采纳 CMQ 作为订单主业务的音讯队列,CMQ 是一种大规模分布式音讯零碎,它具备高可用性、高吞吐量、海量存储和高并发能力等特点,能够帮忙用户在分布式系统中进行异步通信,进步应用程序的可用性和可扩展性,但因为 CMQ 协定和性能简略,不反对事务音讯,程序音讯和大规模提早音讯等局限性,研发团队决定采纳 RocketMQ 作为降级计划,满足日益简单的订单业务场景。 RocketMQ 介绍Apache RocketMQ 是一个开源的分布式消息中间件,因其架构简略、业务功能丰富、具备极强可扩展性等特点被泛滥企业开发者以及云厂商采纳,它具备高性能、高可用性、高可靠性和易于应用等劣势,尤其在互联网、大数据、企业应用、金融交易等在线业务场景成为开发者首选的音讯队列产品。 RocketMQ 能够帮忙业务实现异步通信、流量削峰、数据同步和日志解决等利用场景, 还提供了丰盛的高级个性,比方事务音讯、定时音讯、重试音讯和死信音讯等特色性能,腾讯云针对 RocketMQ 做了大量的优化加强,在齐全兼容社区版的根底上,提供了秒级定时音讯、命名空间,音讯轨迹和丰盛的监控告警指标等企业级个性,能够很好地满足如祺订单零碎等各种简单的音讯解决需要。 如祺打车业务流程介绍 在整个下单流程中,从预估到下单,再从派单到开始服务,最初到费用结算,一共要通过 20+ 流程环节,其中计费订单零碎是所有零碎的外围,从用户输出上下车地点,背地的业务零碎就开始工作,比方营销零碎查问用户折扣和优惠、地图零碎开始做门路布局、安全监控零碎做分控计算、预派单零碎提前做派单剖析等,业务零碎须要实现大量的计算工作,对数据的实时性、准确性和一致性要求十分高,并且波及大量的零碎交互,这对整个零碎的稳定性要求提出很大的挑战。 通过 RocketMQ 做异步化革新 在没有通过 RocketMQ 解决之前,各个系统之间的耦合度十分高,零碎稳定性危险高,引入 RocketMQ 实现了下单异步化革新, 大量工作异步通信解决,大大晋升了订单业务零碎的可扩展性和可靠性,其次要体现在以下几个方面: 1.  下单速度 因为整个下单的流程都曾经实现了异步化,因而从询价到接单的整个流程只有3-5个简略的同步操作,保障了整个下单的体验。(在运力短缺的前提下,大概1.2s 就能实现接单流程)。 2.  业务的扩展性 风控、平安、营销、派单有十分多简单的业务逻辑,例如发券、动静折扣等,如果没有用音讯队列做音讯共享,就须要做N*N 的近程调用,减少了零碎复杂度,当初只须要减少一个上游订阅,就能够扩大新业务逻辑。 3.  业务可靠性   因为业务的链条很长,因而须要做好各个环节的兜底解决,下单异步化后,能够做到关注点拆散,下单主流程不会因为某个节点呈现问题导致阻塞。举个例子: 在下单前,零碎会异步计算好途程的近似途程公里数,假如地图服务呈现提早或者异样,就能够间接应用,保障了零碎的牢靠。 RocketMQ 在订单零碎的利用定时音讯场景定时音讯是指音讯发送方将音讯发送到 RocketMQ 时,音讯不是立刻能够被上游订阅者生产,而是指定音讯在未来一段时间才能够被生产。 RocketMQ 开源社区版(4.x版本)没有反对秒级精度的定时音讯计划,只能指定几个固定的提早级别,腾讯云在社区版的根底上,通过基于工夫轮的调度引擎,先将定时音讯暂存在外部定时音讯队列,再通过调度引擎按指定的定时工夫,将音讯调度到一般音讯队列,上游生产刚才能够失常生产音讯,默认最长能够反对40天的定时范畴(如果购买了专业版的客户,能够通过工单申请更长的定时范畴)。 在打车业务中,有大量的定时音讯场景,比方订单实现超过肯定工夫后其状态主动流转,订单超过肯定工夫未接单主动揭示等业务场景,在未接入 RocketMQ 之前,要依赖轮询数据库来实现,对数据库压力十分大,接入 RocketMQ 后,不仅大大缓解了数据库压力,零碎的依赖关系也大量简化。 事务音讯场景事务音讯是 Apache RocketMQ 提供的一种高级音讯类型,反对在分布式场景下保障音讯生产和本地事务的最终一致性,尤其是在对数据一致性要求高的交易场景有宽泛的利用,次要外围过程如下。 ...

June 1, 2023 · 1 min · jiezi

关于rocketmq:开源之夏-2023|欢迎报名-Apache-RocketMQ-社区项目

开源之夏是由中科院软件所“开源软件供应链点亮打算”发动并长期反对的一项暑期开源流动,旨在激励在校学生积极参与开源软件的开发保护,造就和挖掘更多优良的开发者,促成优良开源软件社区的蓬勃发展,助力开源软件供应链建设。 参加学生通过近程线上合作形式,配有资深导师领导,参加到开源社区各组织我的项目开发中并播种奖金、礼品与证书。这些播种,不仅仅是将来毕业简历上浓墨重彩的一笔,更是迈向顶尖开发者的闪亮终点,能够说十分值得一试。每个我的项目难度分为根底和进阶两档,对应学生结项奖金别离为税前人民币 8000 元和税前人民币 12000 元。 Apache RocketMQ社区开源之夏2023我的项目课题汇总 作为支流的分布式消息中间件,RocketMQ 于 2012 年开源,并在 2017 年正式成为 Apache 顶级我的项目,继续迸发出旺盛的生命力。随同着云原生时代的到来以及实时计算的衰亡, 生于云、长于云的 RocketMQ 5.0 应运而生,全新降级为云原生音讯、事件、流交融解决平台,帮忙用户更容易地构建下一代事件驱动和流解决利用。 RocketMQ 5.0 专一于音讯基础架构的云原生化演进,聚焦在音讯畛域的后处理场景,反对音讯的流式解决和轻计算,帮忙用户实现音讯的就近计算和剖析,并全面拥抱 Serverless 和 EDA。本次开源之夏提供6个课题我的项目: RocketMQ Streams 状态本地存储优化,导师 nizeIceberg Sink Connector,导师 ZhouBoHBase Sink Connector的设计与实现,导师 ShannonDingApache RocketMQ Go Client 稳定性优化,导师小伟Canal Source Connector binglog 同步设计与实现,导徒弟冲RocketMQ e2e 测试优化,导师凌楚 如何参加开源之夏2023并疾速选定我的项目? 欢送通过上方我的项目海报中的联系方式,与各我的项目导师沟通并筹备我的项目申请材料。课题参加期间,学生能够在世界任何中央线上工作,相干我的项目结项须要在 9月30 日前以 PR 的模式提交到社区仓库中并实现合并。以下是开源之夏的流动流程。

May 19, 2023 · 1 min · jiezi

关于rocketmq:RocketMQ-监控告警生产环境如何快速通过监控预警发现堆积收发失败等问题

本文次要向大家介绍如何利用 RocketMQ 可观测体系中的指标监控,对生产环境中典型场景:音讯沉积、音讯收发失败等场景配置正当的监控预警,疾速发现问题,定位问题。 RocketMQ 可观测体系作为一款典型的分布式中间件产品,RocketMQ 被广泛应用于业务外围链路中,每条音讯都关联着外围业务数据的变动。业务链路有其显著的复杂性:• 生产者、消费者多对多:业务调用链路网状结构,上下游梳理艰难• 上下游解耦、异步链路:异步化调用,信息收集不残缺• 音讯是状态数据:未生产胜利、定时中等状态减少排查的复杂度• 音讯链路耦合简单的业务解决逻辑:无奈疾速定位问题边界鉴于音讯链路耦合业务零碎,简单带状态,RocketMQ 通过弱小的可观测零碎和教训撑持,及时发现问题、定位问题、解决问题有助于晋升运维效率,对于业务运行是一项重要的保障能力。 残缺内容请点击下方链接查看: https://developer.aliyun.com/article/1154054?utm_content=g_10... 版权申明:本文内容由阿里云实名注册用户自发奉献,版权归原作者所有,阿里云开发者社区不领有其著作权,亦不承当相应法律责任。具体规定请查看《阿里云开发者社区用户服务协定》和《阿里云开发者社区知识产权爱护指引》。如果您发现本社区中有涉嫌剽窃的内容,填写侵权投诉表单进行举报,一经查实,本社区将立即删除涉嫌侵权内容。

May 19, 2023 · 1 min · jiezi

关于rocketmq:RocketMQ-在小米的多场景灾备实践案例

本文作者:邓志文、王帆 01为什么要容灾? 在小米外部,咱们应用 RocketMQ 来为各种在线业务提供音讯队列服务,比方商城订单、短信告诉甚至用来收集 IoT 设施的上报数据,能够说 RocketMQ 的可用性就是这些在线服务的生命线。作为软件开发者,咱们通常心愿服务能够依照现实状态去运行:在没有Bug的前提下,零碎能够提供失常的服务能力。 但事实的运维教训通知咱们这是不可能的,硬件故障是十分常见的问题,比方内存故障、磁盘故障等,甚至是机房相干的故障(专线故障、机房拉闸等)。因而咱们须要对数据进行备份,应用多正本的形式来保障服务的高可用。Apache RocketMQ 设计上就反对多正本、多节点容灾,比方 Master-Slave 架构、DLedger 部署模式。 在小米外部,因为是面向在线业务,服务的复原速度至关重要,而基于 Raft 协定的 DLedger 模式能够实现秒级 RTO,因而咱们在 2020 年初选用了 DLedger 架构作为根本的部署模式(在 5.0 中,主从模式也能够做到主动 failover)。反对机房灾备须要减少额定的老本,上面我将用三个灾备部署的实际案例,解说小米如何在老本和可用性的取舍下来反对灾备。 02怎么去做容灾? 单机房高可用 理论在应用中,有许多业务是不须要机房级别容灾的,只有可能做到单机房高可用即可。Apache RocketMQ 自身就是分布式的音讯队列服务,能够很好的做到同机房多节点高可用,上面次要分享下小米在衡量老本、可用性的前提下,如何去做部署架构的降级优化。 咱们晓得在 Raft 协定中,个别配置三个节点,利用机器冗余 + 主动选主切换来实现高可用的指标。因而在小米引入 RocketMQ 之初,单 Broker 组均部署三个 Broker 节点。同时为了保障集群中始终存在 Master 节点,咱们个别会至多部署两个 Broker 组,一个简略的部署架构图如下: 能够说是一个很根本的部署架构,在单个机房中,通过多正本、多Broker组做到了单机房容灾。但不难发现,这样做有一个很重大的问题:资源节约。RocketMQ 的从节点只有在客户端读取较旧的数据时才会起到从读的作用,其余时候都只是单纯地作为正本运行,机器利用率只有33%,这是让人无法忍受的。 出于老本上的思考,咱们须要从新思考现有的部署架构,如何能力利用起来从节点呢?一个很简略的思路便是节点混布:在从节点也部署 Broker 过程,让其能够作为 Master 来提供服务。比拟偶合的是,社区过后也提出了 Broker Container 的概念,计划的原理是在 RocketMQ Broker 之上形象一个 Container 角色,Container 用来治理 Broker 的增删改查,以此来达到单台服务主机上运行多个 Broker 的目标,具体架构图如下所示: ...

May 16, 2023 · 1 min · jiezi

关于rocketmq:聊聊rocketmq的订阅关系

序本文次要钻研一下rocketmq的订阅关系 报错org.apache.rocketmq.client.exception.MQClientException: The consumer group[demo-group] has been created before, specify another name please.See http://rocketmq.apache.org/docs/faq/ for further details. at org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl.start(DefaultMQPushConsumerImpl.java:629) at org.apache.rocketmq.client.consumer.DefaultMQPushConsumer.start(DefaultMQPushConsumer.java:693)启动了两个consumer,别离生产topic1和topic2,然而都应用了同一个group,后果启动报错DefaultMQPushConsumerImpl#startorg/apache/rocketmq/client/impl/consumer/DefaultMQPushConsumerImpl.java boolean registerOK = mQClientFactory.registerConsumer(this.defaultMQPushConsumer.getConsumerGroup(), this); if (!registerOK) { this.serviceState = ServiceState.CREATE_JUST; this.consumeMessageService.shutdown(); throw new MQClientException("The consumer group[" + this.defaultMQPushConsumer.getConsumerGroup() + "] has been created before, specify another name please." + FAQUrl.suggestTodo(FAQUrl.GROUP_NAME_DUPLICATE_URL), null); }DefaultMQPushConsumerImpl的start办法会应用mQClientFactory.registerConsumer去注册consumer,如果返回false则抛出MQClientException异样registerConsumerorg/apache/rocketmq/client/impl/factory/MQClientInstance.java public boolean registerConsumer(final String group, final MQConsumerInner consumer) { if (null == group || null == consumer) { return false; } MQConsumerInner prev = this.consumerTable.putIfAbsent(group, consumer); if (prev != null) { log.warn("the consumer group[" + group + "] exist already."); return false; } return true; }MQClientInstance的registerConsumer应用consumerTable保护了group与consumer的关系,这里要求一个consumer group只能与一个consumer关联,如果不同consumer用了同一个group名称则会返回false订阅一致性问题@Test public void testConsume() throws MQClientException, InterruptedException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("string_consumer_test"); consumer.setNamesrvAddr("192.168.64.3:9876");// consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); consumer.subscribe("string-topic-new", "*"); consumer.registerMessageListener((MessageListenerConcurrently) (msg, context) -> { System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msg); for (MessageExt ext : msg) { System.out.printf("consumer1: %s \n",new String(ext.getBody())); } return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; }); consumer.start(); DefaultMQPushConsumer consumer2 = new DefaultMQPushConsumer("string_consumer_test"); consumer2.setNamesrvAddr("192.168.64.3:9876");// consumer2.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); consumer2.subscribe("string-topic2-new", "*"); consumer2.registerMessageListener((MessageListenerConcurrently) (msg, context) -> { System.out.printf("consumer2 %s Receive New Messages: %s %n", Thread.currentThread().getName(), msg); for (MessageExt ext : msg) { System.out.printf("consumer2: %s \n",new String(ext.getBody())); } return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; }); consumer2.start(); System.out.printf("Consumer Started.%n"); TimeUnit.HOURS.sleep(1); }像如上代码,两个consumer应用了同一个group,然而他们订阅了不同的topic,这种最初会造成consumer1及consumer2不能如预期那样失常生产音讯DefaultMQPushConsumer.start()org/apache/rocketmq/client/consumer/DefaultMQPushConsumer.java ...

May 8, 2023 · 4 min · jiezi

关于rocketmq:RocketMQ-多级存储设计与实现

随着 RocketMQ 5.1.0 的正式公布,多级存储作为 RocketMQ 一个新的独立模块达到了 Technical Preview 里程碑:容许用户将音讯从本地磁盘卸载到其余更便宜的存储介质,能够用较低的老本缩短音讯保留工夫。本文具体介绍 RocketMQ 多级存储设计与实现。 设计总览RocketMQ 多级存储旨在不影响热数据读写的前提下将数据卸载到其余存储介质中,实用于两种场景: 冷热数据拆散:RocketMQ 早先产生的音讯会缓存在 page cache 中,咱们称之为热数据;当缓存超过了内存的容量就会有热数据被换出成为冷数据。如果有少许消费者尝试生产冷数据就会从硬盘中从新加载冷数据到 page cache,这会导致读写 IO 竞争并挤压 page cache 的空间。而将冷数据的读取链路切换为多级存储就能够防止这个问题;缩短音讯保留工夫:将音讯卸载到更大更便宜的存储介质中,能够用较低的老本实现更长的音讯保留工夫。同时多级存储反对为 topic 指定不同的音讯保留工夫,能够依据业务须要灵便配置音讯 TTL。RocketMQ 多级存储比照 Kafka 和 Pulsar 的实现最大的不同是咱们应用准实时的形式上传音讯,而不是等一个 CommitLog 写满后再上传,次要基于以下几点思考: 均摊老本:RocketMQ 多级存储须要将全局 CommitLog 转换为 topic 维度并从新构建音讯索引,一次性解决整个 CommitLog 文件会带来性能毛刺;对小规格实例更敌对:小规格实例往往配置较小的内存,这意味着热数据会更快换出成为冷数据,期待 CommitLog 写满再上传自身就有冷读危险。采取准实时上传的形式既能躲避音讯上传时的冷读危险,又能尽快使得冷数据能够从多级存储读取。Quick Start多级存储在设计上心愿升高用户心智累赘:用户无需变更客户端就能实现无感切换冷热数据读写链路,通过简略的批改服务端配置即可具备多级存储的能力,只需以下两步: 批改 Broker 配置,指定应用 org.apache.rocketmq.tieredstore.TieredMessageStore 作为 messageStorePlugIn配置你想应用的贮存介质,以卸载音讯到其余硬盘为例:配置 tieredBackendServiceProvider 为 org.apache.rocketmq.tieredstore.provider.posix.PosixFileSegment,同时指定新贮存的文件门路:tieredStoreFilepath可选项:反对批改 tieredMetadataServiceProvider 切换元数据存储的实现,默认是基于 json 的文件存储更多应用阐明和配置项能够在 GitHub 上查看多级存储的 README[1] 技术架构architecture 接入层:TieredMessageStore/TieredDispatcher/TieredMessageFetcher 接入层实现 MessageStore 中的局部读写接口,并为他们减少了异步语意。TieredDispatcher 和 TieredMessageFetcher 别离实现了多级存储的上传/下载逻辑,相比于底层接口这里做了较多的性能优化:包含应用独立的线程池,防止慢 IO 阻塞拜访热数据;应用预读缓存优化性能等。 ...

April 17, 2023 · 2 min · jiezi

关于rocketmq:基于-RocketMQ-Connect-构建数据流转处理平台

01 从问题中来的 RocketMQ Connect在电商零碎、金融零碎及物流零碎,咱们常常能够看到 RocketMQ 的身影。起因不难理解,随着数字化转型范畴的扩充及过程的放慢,业务零碎的数据也在每日暴增,此时为了保证系统的稳固运行,就须要把运行压力分担进来。RocketMQ 就负责着这样的角色,它的异步音讯解决与高并发读写能力,决定了零碎底层的重构不会影响下层利用的性能。而 RocketMQ 的另一个劣势——可伸缩能力,使零碎在面临流量的不确定性时,实现对流量的缓冲解决。此外,RocketMQ 的程序设计个性使其成为一个人造的排队引擎,例如,三个利用同时对一个后盾引擎发动申请,确保不引起“撞车”事变。因而,RocketMQ 被用在异步解耦、削峰填谷以及事务音讯等场景中。 然而,数字化转型浪潮也带来了更多用户对数据价值的关注——如何让数据产生更大利用价值?RocketMQ 本身不具备数据分析能力,然而有不少用户心愿从 RocketMQ Topic 中获取数据并进行在线或离线的数据分析。然而,应用市面上的数据集成或数据同步工具,将 RocketMQ Topic 数据同步到一些剖析零碎中尽管是一种可行计划,却会引入新的组件,造成数据同步的链路较长,时延绝对较高,用户体验不佳。 举个例子,假如业务场景中应用 OceanBase 作为数据存储,同时心愿将这些数据同步到 Elasticsearch 进行全文搜寻,有两种可行的数据同步计划。 计划一:从 OceanBase 中获取数据,写入 Elasticsearch 组件并进行数据同步,在数据源较少时此计划没什么问题,一旦数据增多,开发和保护都非常复杂,此时就要用到第二种计划。 计划二:引入消息中间件对上下游进行解藕,这能解决第一种计划的问题,然而一些较为简单的问题还没有齐全解决。比方,如何将数据从源数据同步到指标零碎并保障高性能,如果保障同步工作的局部节点挂掉,数据同步仍然失常进行,节点复原仍然能够断点续传,同时随着数据管道的增多,如何治理数据管道也变得十分困难。 总的来说,数据集成过程中的挑战次要有五个。 挑战一:数据源多,市面上可能有上百个数据源,且各数据源的零碎差别较大,实现任意数据源之间的数据同步工作量较大,研发周期很长。 挑战二:高性能问题,如何高效地从源数据系统同步到目标数据系统,并保障其性能。 挑战三:高可用问题,即 Failover 能力,当一个节点挂掉是否这个节点的工作就进行了,工作重新启动是否还能够断点续传。 挑战四:弹性扩缩容能力,依据零碎流量动静减少或缩小节点数量,既能通过扩容满足高峰期业务,也能在低峰期缩减节点,节省成本。 挑战五:数据管道的治理运维,随着数据管道的增多,运维监控的数据管道也会变得越来越简单,如何高效治理监控泛滥的同步工作。 面对上述挑战 RocketMQ 如何解决? 第一,标准化数据集成 API (Open Messaging Connect API)。在 RocketMQ 生态中减少 Connect 组件,一方面对数据集成过程形象,形象规范的数据格式以及形容数据的 Schema,另一方面对同步工作进行形象,工作的创立、分片都形象成一套标准化的流程。 第二,基于规范的 API 实现 Connect Runtime。Runtime 提供了集群治理、配置管理、位点治理、负载平衡相干的能力,领有了这些能力,开发者或者用户就只须要关注数据如何获取或如何写入,从而疾速构建数据生态,如与OceanBase、MySQL、Elasticsearc 等疾速建设连贯,搭建数据集成平台。整个数据集成平台的构建也非常简单,通过 Runtime 提供的 RESTFull API 进行简略调用即可。 第三,提供欠缺的运维工具,方便管理同步工作,同时提供丰盛的 Metrics 信息,不便查看同步工作的 TPS、流量等信息。 ...

April 17, 2023 · 3 min · jiezi

关于rocketmq:rocketmqspring-实战与源码解析一网打尽

RocketMQ 是大家耳熟能详的音讯队列,开源我的项目 rocketmq-spring 能够帮忙开发者在 Spring Boot 我的项目中疾速整合 RocketMQ。这篇文章会介绍 Spring Boot 我的项目应用 rocketmq-spring SDK 实现音讯收发的操作流程,同时笔者会从开发者的角度解读 SDK 的设计逻辑。 1 SDK 简介 我的项目地址: https://github.com/apache/rocketmq-spring rocketmq-spring 的实质是一个 Spring Boot starter 。Spring Boot 基于“约定大于配置”(Convention over configuration)这一理念来疾速地开发、测试、运行和部署 Spring 利用,并能通过简略地与各种启动器(如 spring-boot-web-starter)联合,让利用间接以命令行的形式运行,不需再部署到独立容器中。Spring Boot starter 结构的启动器应用起来十分不便,开发者只须要在 pom.xml 引入 starter 的依赖定义,在配置文件中编写约定的配置即可。上面咱们看下 rocketmq-spring-boot-starter 的配置:1、引入依赖<dependency>  <groupId>org.apache.rocketmq</groupId>  <artifactId>rocketmq-spring-boot-starter</artifactId>  <version>2.2.3</version></dependency>2、约定配置 接下来,咱们别离依照生产者和消费者的程序,具体的解说音讯收发的操作过程。2 生产者首先咱们增加依赖后,进行如下三个步骤:1、配置文件中配置如下rocketmq:  name-server: 127.0.0.1:9876  producer:      group: platform-sms-server-group    # access-key: myaccesskey    # secret-key: mysecretkey  topic: sms-common-topic生产者配置非常简单,次要配置名字服务地址和生产者组。2、须要发送音讯的类中注入 RcoketMQTemplate@Autowiredprivate RocketMQTemplate rocketMQTemplate;@Value("${rocketmq.topic}")private String smsTopic;3、发送音讯,音讯体能够是自定义对象,也能够是 Message 对象rocketMQTemplate 类蕴含多钟发送音讯的办法: 同步发送 syncSend异步发送 asyncSend程序发送 syncSendOrderlyoneway发送 sendOneWay上面的代码展现如何同步发送音讯。String destination = StringUtils.isBlank(tags) ? topic : topic + ":" + tags;SendResult sendResult =         rocketMQTemplate.syncSend(            destination,             MessageBuilder.withPayload(messageContent).            setHeader(MessageConst.PROPERTY_KEYS, uniqueId).            build()          );if (sendResult != null) {    if (sendResult.getSendStatus() == SendStatus.SEND_OK) {       // send message success ,do something     }}syncSend 办法的第一个参数是发送的指标,格局是:topic + ":" + tags ,第二个参数是:spring-message 标准的 message 对象 ,而 MessageBuilder 是一个工具类,办法链式调用创立音讯对象。3 消费者1、配置文件中配置如下rocketmq:  name-server: 127.0.0.1:9876  consumer1:    group: platform-sms-worker-common-group    topic: sms-common-topic2、实现音讯监听器@Component@RocketMQMessageListener(    consumerGroup = "${rocketmq.consumer1.group}",  //生产组    topic = "${rocketmq.consumer1.topic}"       //主题)public class SmsMessageCommonConsumer implements RocketMQListener<String> {    public void onMessage(String message) {       System.out.println("一般短信:" + message);    }}消费者实现类也能够实现 RocketMQListener<MessageExt>, 在 onMessage 办法里通过 RocketMQ 原生音讯对象 MessageExt 获取更具体的音讯数据 。public void onMessage(MessageExt message) {    try {        String body = new String(message.getBody(), "UTF-8");        logger.info("一般短信:" + message);    } catch (Exception e) {        logger.error("common onMessage error:", e);    }}4 源码概览 ...

April 2, 2023 · 1 min · jiezi

关于rocketmq:RocketMQ-x-OpenTelemetry-分布式全链路追踪最佳实践

在分布式系统中,多个服务之间的交互波及到简单的网络通信和数据传输,其中每个服务可能由不同的团队或组织负责保护和开发。因而,在这样的环境下,当一个申请被收回并通过多个服务的解决后,如果呈现了问题或谬误,很难疾速定位到根因。分布式全链路追踪技术则能够帮忙咱们解决这个问题,它可能跟踪和记录申请在零碎中的传输过程,并提供具体的性能和日志信息,使得开发人员可能疾速诊断和定位问题。对于分布式系统的可靠性、性能和可维护性起到了十分重要的作用。 RocketMQ 5.0 与分布式全链路追踪Apache RocketMQ 5.0 版本作为近几年来最大的一次迭代,在整个可观测性上也进行了诸多改良。其中,反对标准化的分布式全链路追踪就是一个重要的个性。 RocketMQ 5.0 可观测 而由 Google、Microsoft、Uber 和 LightStep 联结发动的 CNCF OpenTelemetry 作为 OpenTracing 和 OpenCensus 的官网继任者,曾经成为可观测畛域的事实标准,RocketMQ 的分布式全链路追踪也围绕 OpenTelemetry 进行开展。 分布式链路追踪零碎的起源能够追溯到 2007 年 Google 公布的《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》[1]论文。这篇论文具体介绍了 Google 外部应用的链路追踪零碎 Dapper,其中应用的 span 概念被宽泛采纳,并成为起初开源链路追踪零碎中的根底概念之一。 Dapper Trace Tree 在 Dapper 中,每个申请或事务被追踪时都会创立一个 span,记录整个申请或事务处理过程中的各个组件和操作的工夫和状态信息。这些 span 能够嵌套,造成一个树形构造,用于示意整个申请或事务处理过程中各个组件之间的依赖关系和调用关系。起初,很多开源链路追踪零碎,如 Zipkin 和 OpenTracing,也采纳了相似的 span 概念来形容分布式系统中的链路追踪信息。当初,合并了 OpenTracing 和 OpenCensus 的 CNCF OpenTelemetry 天然也一样采纳了 span 概念,并在此基础上进行了进一步倒退。 OpenTelemetry 为 messaging 相干的 span 定义了一组语义约定(semantic convention)[2],旨在制订一套与特定音讯零碎无关的 specification,而 OpenTelmetry 本身的开发其实也都是由 specification 驱动进行开展。 ...

March 31, 2023 · 3 min · jiezi

关于rocketmq:RocketMQ源码broker-消息接收流程写入commitLog

从本文开始,咱们来剖析rocketMq音讯接管、散发以及投递流程。 RocketMq音讯解决整个流程如下: 音讯接管:音讯接管是指接管producer的音讯,解决类是SendMessageProcessor,将音讯写入到commigLog文件后,接管流程处理完毕;音讯散发:broker解决音讯散发的类是ReputMessageService,它会启动一个线程,一直地将commitLong分到到对应的consumerQueue,这一步操作会写两个文件:consumerQueue与indexFile,写入后,音讯散发流程解决 结束;音讯投递:音讯投递是指将音讯发往consumer的流程,consumer会发动获取音讯的申请,broker收到申请后,调用PullMessageProcessor类解决,从consumerQueue文件获取音讯,返回给consumer后,投递流程处理完毕。以上就是rocketMq解决音讯的流程了,接下来咱们就从源码来看相干流程的实现。 1. remotingServer的启动流程在正式剖析接管与投递流程前,咱们来理解下remotingServer的启动。 remotingServer是一个netty服务,他开启了一个端口用来解决producer与consumer的网络申请。remotingServer是在BrokerController#start中启动的,代码如下: public void start() throws Exception { // 启动各组件 ... if (this.remotingServer != null) { this.remotingServer.start(); } ... }持续查看remotingServer的启动流程,进入NettyRemotingServer#start办法: public void start() { ... ServerBootstrap childHandler = this.serverBootstrap.group(this.eventLoopGroupBoss, this.eventLoopGroupSelector) ... .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline() .addLast(defaultEventExecutorGroup, HANDSHAKE_HANDLER_NAME, handshakeHandler) .addLast(defaultEventExecutorGroup, encoder, new NettyDecoder(), new IdleStateHandler(0, 0, nettyServerConfig.getServerChannelMaxIdleTimeSeconds()), connectionManageHandler, // 解决业务申请的handler serverHandler ); } }); ...}这就是一个规范的netty服务启动流程了,套路与nameServer的启动是一样的。对于netty的相干内容,这里咱们仅关注pipeline上的channelHandler,在netty中,解决读写申请的操作为一个个ChannelHandler,remotingServer中解决读写申请的ChanelHandler为NettyServerHandler,代码如下: ...

March 25, 2023 · 8 min · jiezi

关于rocketmq:RocketMQ-50-vs-49X-图解架构对比

本文作者:李伟,Apache RocketMQ Committer,RocketMQ Python客户端我的项目Owner ,Apache Doris Contributor,腾讯云数据库开发工程师。 01 RocketMQ 4.9.X架构 在4.9.X中每个组件和组件之间的通信简略阐明如下: 组件和数据流阐明Namesrv无状态服务,保留Topic路由信息Topic路由=topic-queue-brokerBroker有状态服务,解决计算和存储。计算 = 生产者申请,消费者申请,治理申请,Broker零碎服务(比方索引构建服务,音讯过期服务)存储  = 音讯存储,索引存储Broker -> NamesrvBroker定期把Broker信息+以后Broker中的Topic信息上报Namesrv生产者生产音讯生产者<-> Namesrv生产者从Namesrv获取Topic路由信息, 蕴含Broker IP生产者<-> Broker生产者通过Topic路由信息,把音讯间接发送给Broker生产者定期和Broker心跳,上报以后生产者实例信息消费者生产音讯消费者<-> Namesrv消费者从Namesrv获取Topic路由信息, 蕴含Broker IP消费者 <-> Broker消费者通过Topic路由信息,从指定Broker中拉取音讯生产消费者定期和Broker心跳,上报以后消费者实例信息1、生产者通过Topic路由信息,把音讯间接发送给Broker。 2、生产者定期和Broker心跳,上报以后生产者实例信息。 消费者生产音讯消费者<-> Namesrv 1、消费者从Namesrv获取Topic路由信息, 蕴含 Broker IP消费者 <-> Broker 。 消费者通过Topic路由信息,从指定Broker中拉取音讯生产。消费者定期和Broker心跳,上报以后消费者实例信息。02 RocketMQ 5.0.0 架构 在5.0.0 中每个组件和组件之间的通信简略阐明如下: 组件和数据流阐明Namesrv无状态服务,保留Topic路由信息Topic路由=topic-queue-broker。在5.0.0时,Namesrv过程中能够嵌入Controller模块。若设置enableControllerInNamesrv=true,在Namesrv过程中嵌入启动一个Controller实例Broker有状态服务,解决计算和存储。计算 = 生产者申请,消费者申请,治理申请,Broker零碎服务(比方索引构建服务,音讯过期服务)存储  = 音讯存储,索引存储在5.0.0时, Broker反对主从切换,Broker的角色蕴含:master,slave,learner。若设置asyncLearner=true,则Broker为learner,只同步数据, 不参加选举masterBroker -> NamesrvBroker定期把Broker信息+以后Broker中的Topic信息上报NamesrvRemoting生产者生产音讯Remoting生产者<-> Namesrv生产者从Namesrv获取Topic路由信息, 蕴含Broker IPRemoting生产者<-> Broker产者通过Topic路由信息,把音讯间接发送给Broker生产者定期和Broker心跳,上报以后生产者实例信息Remoting消费者生产音讯Remoting消费者<-> Namesrv消费者从Namesrv获取Topic路由信息, 蕴含Broker IPRemoting消费者 <-> Broker消费者通过Topic路由信息,从指定Broker中拉取音讯生产消费者定期和Broker心跳,上报以后消费者实例信息5.0.0新增模块Controller(控制器)和Kafka Controller相似,在RocketMQ中负责Broker Master的选举和告诉Broker5.0.0新增模块Broker <-> ControllerBroker定期把Broker信息上报ControllerBroker同步正本状态Controller选举新的Broker Master后,告诉全副Broker5.0.0新增模块Proxy无状态服务,新客户端通过Grpc接口拜访Proxy进行收发音讯。社区已反对Remoting协定。Proxy中反对嵌入Broker。若设置proxyMode=LOCAL,则会在Proxy过程中启动一个Broker实例5.0.0新增模块Proxy <-> BrokerProxy通过Remoting协定和Broker通信,能够把Proxy当作一个Remoting的Client5.0.0新增模块新 Client新客户端,反对生产、生产、治理性能。目前反对Grpc协定5.0.0新增模块新Client <-> Proxy新客户端拜访Proxy进行收发音讯,治理1、生产者通过Topic路由信息,把音讯间接发送给Broker。 ...

February 27, 2023 · 1 min · jiezi

关于rocketmq:RocketMqBroker-启动脚本分析

引言继 [[【RocketMq】NameServ启动脚本剖析(Ver4.9.4)]] 之后又来看看Broker的脚本。总体上来看大差不差,以浏览外围的配置局部调优为主。 mqbroker#!/bin/sh if [ -z "$ROCKETMQ_HOME" ] ; then ## resolve links - $0 may be a link to maven's home PRG="$0" # need this for relative symlinks while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG="`dirname "$PRG"`/$link" fi done saveddir=`pwd` ROCKETMQ_HOME=`dirname "$PRG"`/.. # make it fully qualified ROCKETMQ_HOME=`cd "$ROCKETMQ_HOME" && pwd` cd "$saveddir" fi export ROCKETMQ_HOME sh ${ROCKETMQ_HOME}/bin/runbroker.sh org.apache.rocketmq.broker.BrokerStartup $@后面的一大段脚本的最终目标就是获取ROCKETMQ_HOME的变量。 ...

February 21, 2023 · 5 min · jiezi

关于rocketmq:RocketMqNameServ启动脚本分析Ver494

NameServ启动脚本剖析概览mqnamesrv命令mqnamesrv 脚本runserver.sh 脚本 大于等于JDK9的启动参数等于JDK8的启动参数mqnamesrv 启动命令这里间接摘录了官网文档: Start NameServer ### Start Name Server first$ nohup sh mqnamesrv &### Then verify that the Name Server starts successfully$ tail -f ~/logs/rocketmqlogs/namesrv.logThe Name Server boot success...mqnamesrv 脚本#!/bin/sh# 从环境变量当中获取RocketMq环境变量地址if [ -z "$ROCKETMQ_HOME" ] ; then ## resolve links - $0 may be a link to maven's home ## 解决链接问题 - $0 可能是maven的主页链接 # PS:$0 是脚本的命令自身 PRG="$0" # need this for relative symlinks # 须要相干链接 while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG="`dirname "$PRG"`/$link" fi done # 暂存以后的执行门路 saveddir=`pwd` ROCKETMQ_HOME=`dirname "$PRG"`/.. # make it fully qualified # 拼接获取RocketMQ绝对路径 ROCKETMQ_HOME=`cd "$ROCKETMQ_HOME" && pwd` # 跳转到以后暂存的命令执行门路 cd "$saveddir"fiexport ROCKETMQ_HOME# 要害: 执行runserver.sh脚本,携带logback的日志xml配置,以及传递JVM的启动main办法的入口类绝对路径sh ${ROCKETMQ_HOME}/bin/runserver.sh -Drmq.logback.configurationFile=$ROCKETMQ_HOME/conf/rmq.namesrv.logback.xml org.apache.rocketmq.namesrv.NamesrvStartup $@最开始的mqnamesrv.sh 脚本获取环境变量的局部看不懂其实没啥影响,大略有个印象即可,当然能够截取局部的命令到Linux运行测试一下就明确了,比方筹备环境变量等等,最初一句话比拟要害。 ...

February 20, 2023 · 6 min · jiezi

关于rocketmq:RocketMQ-集成生态再升级轻松构建云上数据管道

阿里云音讯队列 RocketMQ 版是阿里云基于 Apache RocketMQ 构建的低提早、高并发、高可用、高牢靠的分布式“音讯、事件、流”对立解决平台,面向互联网分布式应用场景提供微服务异步解耦、流式数据处理、事件驱动解决等外围能力。其自诞生以来始终为阿里团体提供稳固牢靠的音讯服务,历经多年双十一万亿级流量洪峰的验证。 随着业务需要场景日渐丰盛,在多年教训积攒后,阿里云 RocketMQ 也迎来了革命性的更新,正式公布了阿里云音讯队列 RocketMQ 版 5.0,在架构、网络、负载平衡、存储等诸多方面进行了显著优化。其定位不再局限于音讯解耦场景,将全新布局事件驱动和音讯流式解决场景。 阿里云 EventBridge 作为云上事件枢纽始终以来都放弃着对云上事件、数据的敌对生态反对。随着 RocketMQ 5.0版本的用户日渐增多,EventBridge 在近期对 RocketMQ Connector 进行了全面降级。降级之后的 RocketMQ Connector 不仅能够反对RocketMQ 5.0 版本,同时也能反对云上自建 RocketMQ 实例。除此之外,基于成熟的事件流能力,用户应用 EventBridge 也能轻松构建音讯路由能力,实现对灾备、数据同步的需要。 本文将从业务架构和 API 应用等方面解说如何应用 EventBridge 创立阿里云 RocketMQ 4.0、5.0 版本,开源自建版本以及音讯路由的相干工作。 EventBridge-RocketMQ 4.0业务架构RocketMQ 4.0 版本应用较为经典的 client-nameserver-broker 架构,整个利用次要由生产者、消费者、NameServer 和 Broker 组成。 Name Server:是一个简直无状态节点,可集群部署,在音讯队列 RocketMQ 版中提供命名服务,更新和发现 Broker 服务。Broker:音讯直达角色,负责存储音讯,转发音讯。分为 Master Broker 和 Slave Broker,一个 Master Broker 能够对应多个 Slave Broker,然而一个 Slave Broker 只能对应一个 Master Broker。Broker 启动后须要实现一次将本人注册至 Name Server 的操作;随后每隔 30s 定期向 Name Server 上报 Topic 路由信息。生产者:与 Name Server 集群中的其中一个节点(随机)建设长连贯(Keep-alive),定期从 Name Server 读取 Topic 路由信息,并向提供 Topic 服务的 Master Broker 建设长连贯,且定时向 Master Broker 发送心跳。消费者:与 Name Server 集群中的其中一个节点(随机)建设长连贯,定期从 Name Server 拉取 Topic 路由信息,并向提供 Topic 服务的 Master Broker、Slave Broker 建设长连贯,且定时向 Master Broker、Slave Broker 发送心跳。Consumer 既能够从 Master Broker 订阅音讯,也能够从 Slave Broker 订阅音讯,订阅规定由 Broker 配置决定。 ...

February 13, 2023 · 2 min · jiezi

关于rocketmq:RocketMQ源码NameServer架构设计及启动流程

本文咱们来剖析NameServer相干代码,在正式剖析源码前,咱们先来回顾下NameServer的性能: NameServer是一个非常简单的Topic路由注册核心,其角色相似Dubbo中的zookeeper,反对Broker的动静注册与发现。次要包含两个性能: Broker治理,NameServer承受Broker集群的注册信息并且保留下来作为路由信息的根本数据。而后提供心跳检测机制,查看Broker是否还存活;路由信息管理,每个NameServer将保留对于Broker集群的整个路由信息和用于客户端查问的队列信息。而后Producer和Conumser通过NameServer就能够晓得整个Broker集群的路由信息,从而进行音讯的投递和生产。1. 架构设计Broker启动的时候会向所有的NameServer注册,生产者在发送音讯时会先从NameServer中获取Broker音讯服务器的地址列表,依据负载平衡算法选取一台Broker音讯服务器发送音讯。NameServer与每台Broker之间放弃着长连贯,并且每隔10秒会查看Broker是否存活,如果检测到Broker超过120秒未发送心跳,则从路由注册表中将该Broker移除。 然而路由的变动不会马上告诉音讯生产者,这是为了升高NameServe的复杂性,所以在RocketMQ中须要音讯的发送端提供容错机制来保障音讯发送的高可用性,这在后续对于RocketMQ音讯发送的章节会介绍。 2. 启动流程源码剖析2.1 主办法:NamesrvStartup#mainNameServer位于RocketMq我的项目的namesrv模块下,主类是org.apache.rocketmq.namesrv.NamesrvStartup,代码如下: public class NamesrvStartup { ... public static void main(String[] args) { main0(args); } public static NamesrvController main0(String[] args) { try { // 创立 controller NamesrvController controller = createNamesrvController(args); // 启动 start(controller); String tip = "The Name Server boot success. serializeType=" + RemotingCommand.getSerializeTypeConfigInThisServer(); log.info(tip); System.out.printf("%s%n", tip); return controller; } catch (Throwable e) { e.printStackTrace(); System.exit(-1); } return null; } ...}复制代码能够看到,main()办法里的代码还是相当简略的,次要蕴含了两个办法: createNamesrvController(...):创立 controllerstart(...):启动nameServer接下来咱们就来剖析这两个办法了。 ...

February 1, 2023 · 5 min · jiezi

关于rocketmq:终于弄明白-RocketMQ-的存储模型

RocketMQ 优异的性能体现,必然绕不开其优良的存储模型 。这篇文章,笔者依照本人的了解 , 尝试剖析 RocketMQ 的存储模型,心愿对大家有所启发。 1 整体概览首先复习下 RocketMQ 架构。 整体架构中蕴含四种角色 : Producer :音讯公布的角色,Producer 通过 MQ 的负载平衡模块抉择相应的 Broker 集群队列进行音讯投递,投递的过程反对疾速失败并且低提早。Consumer :音讯生产的角色,反对以 push 推,pull 拉两种模式对音讯进行生产。NameServer :名字服务是一个非常简单的 Topic 路由注册核心,其角色相似 Dubbo 中的 zookeeper ,反对 Broker 的动静注册与发现。BrokerServer :Broker 次要负责音讯的存储、投递和查问以及服务高可用保障 。 本文的重点在于剖析 BrokerServer 的音讯存储模型。咱们先进入 broker 的文件存储目录 。 音讯存储和上面三个文件关系十分严密: 数据文件 commitlog音讯主体以及元数据的存储主体 ;生产文件 consumequeue音讯生产队列,引入的目标次要是进步音讯生产的性能 ;索引文件 index索引文件,提供了一种能够通过 key 或工夫区间来查问音讯。 RocketMQ 采纳的是混合型的存储构造,Broker 单个实例下所有的队列共用一个数据文件(commitlog)来存储。生产者发送音讯至 Broker 端,而后 Broker 端应用同步或者异步的形式对音讯刷盘长久化,保留至 commitlog 文件中。只有音讯被刷盘长久化至磁盘文件 commitlog 中,那么生产者发送的音讯就不会失落。Broker 端的后盾服务线程会不停地散发申请并异步构建 consumequeue(生产文件)和 indexFile(索引文件)。2 数据文件RocketMQ 的音讯数据都会写入到数据文件中, 咱们称之为 commitlog 。所有的音讯都会程序写入数据文件,当文件写满了,会写入下一个文件。 ...

December 30, 2022 · 2 min · jiezi

关于rocketmq:RocketMq商用RocketMq和开源RocketMq的兼容问题解决方案

引言在阿里云的官方网站提供了RocketMq的商用版本,然而集体在我的项目利用上发现和SpirngBoot以及Spring Cloud(Alibaba)等开源的RocketMQ依赖尽管能够失常兼容,然而仍然呈现了注解生效、启动报错,商用和开源版本的不兼容导致局部代码要反复编写的蛋疼问题。 这样的兼容问题不是简略加个SDK依赖,切换到商用配置就能够间接应用的(因为集体起初真就是这么想),为了防止前面再遇到这种奇葩的开发测试用开源RocketMq,生产环境须要应用商用集群的RocketMq的混合配置的业务场景,集体花了小半天工夫熟读阿里云的接入文档,加上各种尝试和测试,总结出一套能够疾速应用的兼容模板计划。 如果不理解阿里云商用RocketMq,能够看最初一个小节的【阿里云商用RocketMq介绍】介绍。集体的兼容计划灵感来自于官网提供的这个DEMO我的项目:springboot/java-springboot-demo。 留神本计划是基于SpringBoot2.X和 Spring cloud Alibaba 的两个我的项目环境构建我的项目根底,在SpringBoot上只做了生产者的配置,而在Spring Cloud Alibaba的Nacos上进行了生产者和消费者的残缺兼容计划。 最初留神兼容集成的版本为商用RocketMq应用4.x版本,最近新出的5.X 的版本并未进行测试,不保障失常应用。 兼容关键点在沿用SpringBoot的YML根底配置根底上实现商用和开源模式的兼容。商用RocketMq须要应用官网提供的依赖包,依赖包能够失常兼容SpringBoot等依赖。集成之后便于开发和扩大,并且易于其余开发人员了解。两种模式之间相互不会产生烦扰。SpringBoot2.X 兼容上面先介绍SpringBoot我的项目的兼容。 SpringBoot我的项目兼容开源版本首先咱们察看YAML文件,对于开源版本的RocketMq设置,单机版本能够间接配置一个ip和端口即可,如果是集群则用分号隔开多个NameServer的连贯IP地址(NameServ独立部署,外部进行主动同步 )。 #音讯队列rocketmq: # 自定义属性,作用下文将会进行解释 use-aliyun-rocketSever: false name-server: 192.168.58.128:9876 # 192.168.244.128:9876;192.168.244.129:9876; producer: group: testGroup # 本地开发不应用商业版,能够不配置 secret-key: NONE # 本地开发不应用商业版,能够不配置 access-key: NONE # 商用版本申请超时工夫,开源版本不应用此参数 timeoutMillis: NONESpringBoot中集成开源的RocketMq非常简单,只须要一个依赖就能够主动实现相干筹备: <dependency> <groupId>org.apache.rocketmq</groupId> <artifactId>rocketmq-spring-boot</artifactId> <version>2.2.2</version> </dependency>具体的应用通常为封装或者间接应用RocketMqTemplate: @Autowiredprivate RocketMQTemplate mqTemplate;public void sendRocketMqUniqueTextMessage(String topic, String tag, String queueMsg) { if (StringUtils.isNotEmpty(topic) && StringUtils.isNotEmpty(tag) && StringUtils.isNotEmpty(queueMsg)) { String queueName = topic + ":" + tag; //封装音讯,调配惟一id MessageData messageData = new MessageData(); messageData.setMsgId(IdUtil.randomUUID()); messageData.setMsgContent(queueMsg); queueMsg = JSON.toJSONString(messageData); log.info("线程:{},向队列:{},发送音讯:{}", Thread.currentThread() .getName(), queueName, queueMsg); try { mqTemplate.syncSend(queueName, queueMsg); } catch (Exception e) { log.info("向队列:{},发送音讯出现异常:{}", queueName, queueMsg); //出现异常,保留异样信息到数据库 SaMqMessageFail saMqMessageFail = new SaMqMessageFail(); // 封装失败音讯,调用生效解决Service将失败发送申请入库,或者通过其余办法重试 saMqMessageFailService.insert(saMqMessageFail); } } }开源版本的SpringBoot集成RocketMq就是如此简略。 ...

December 30, 2022 · 8 min · jiezi

关于rocketmq:RocketMqRocketMqNameServ-源码分析Ver494

引言RocketMq3.X的版本和Kafka一样是基于Zookeeper进行路由治理的,然而这意味着运维须要多部署一套Zookeeper集群,起初RocketMq抉择去ZK最终呈现了NameServ。NameServ作为RocketMq源码浏览的切入点十分不错,本文将会介绍Ver 4.9.4 版本的NameServ源码剖析。 NameServer次要有两个性能,Broker治理和路由信息管理。 整个NameServ理论代码只有几百行,因为自身呈现基本目标就是代替ZK,所以角色相似ZK。在上面的截图中,NamesrvStartup为启动类,NamesrvController为外围控制器,RouteInfoManager为路由信息表,整个NameServ基本上就是围绕这三个类做文章。 NameServe的类结构图如下: 源码剖析NameServ 启动NameServ的启动步骤次要有上面几个点: 创立NameServ控制器,解析和创立重要配置,重要外围控制器创立并注入配置。NameServ外围控制器初始化,NettyServ服务等次重要相干组件创立和初始化。启动定时工作,定期扫描过期Broker并且移除不沉闷Broker,定期打印零碎全副的KV配置。注册JVM钩子函数优雅敞开资源(Netty和线程池),启动Netty。Netty服务启动在理解代码细节之前,咱们先画一个时序图理解NameServ的启动过程: 显然NameServ的整个启动基本上是在为Nettty做了一系列周边服务,Netty是网络通信的外围框架。 拜访入口整个NameServ的入口为org.apache.rocketmq.namesrv.NamesrvStartup#main0,咱们间接定位到相干代码。 public static NamesrvController main0(String[] args) { try { // 1. 构建外围控制器 NamesrvController controller = createNamesrvController(args); // 2. 启动控制器 start(controller); String tip = "The Name Server boot success. serializeType=" + RemotingCommand.getSerializeTypeConfigInThisServer(); log.info(tip); System.out.printf("%s%n", tip); return controller; } catch (Throwable e) { e.printStackTrace(); System.exit(-1); } return null; }构建外围控制器NameServer一开始的工作是构建外围控制器,从整体上看次要做了上面几个操作: 调用Apach Commons CLI 命令行解析工具进行命令解析。依据运行时参数生成commandLine命令行对象。创立NamesrvConfig和NettyServerConfig对象,读取-c指定的配置文件门路解析配置文件。namesrvConfig 和 nettyServerConfig 对象进行初始化。Apach Commons CLI 工具能够帮忙开发者疾速构建服务器启动命令参数,并且反对输入到列表。这里咱们接着进入到 org.apache.rocketmq.namesrv.NamesrvStartup#createNamesrvController 一探到底。进入之后发现代码还不少,所以咱们拆成多个局部剖析。 ...

November 29, 2022 · 10 min · jiezi

关于rocketmq:RocketMqRocketMq-高版本JDK编译报错问题处理

引言简略记录RocketMq的JDK8以上版本的编译问题,在RocketMq的github - issue外面探讨还挺多的,这里简略记录一下踩坑。 总得来说是个小问题,然而如同竟然没啥文章介绍过。难道都是JDK8去部署RocketMq的源码的么?报错问题因为IDEA缓存的存在,很有可能看到这些内容不是爆红而是失常导入的,此时编译却会诡异般的报错。 java: 程序包sun.nio.ch不存在sun.util.locale.BaseLocale.SEP不存在Idea的报错状况 在Idea 2021.1 的版本中会呈现如下问题,这个报错第一眼看着挺懵逼的: Idea 2022.1.3 版本就好很多,报错是人话了: java: package sun.util.locale is not visible (package sun.util.locale is declared in module java.base, which does not export it)排查如果对于Java9的模块化有一丁点概念,根本这个问题都能迎刃而解,然而如果像我一样不懂的话,大略会在这个问题上卡上一段时间,并且谷歌也查不出间接关联的状况,不过查相似报错会跟你讲模块化的事件。 思考要害的灵感来自于上面这篇文章,集体依照文章的思路,先把整个我的项目从maven到我的项目整个配置应用的对立JDK版本,所以索性全换成JDK11尝试。 intellij idea - Error: java: invalid source release 1.9 - Stack Overflow 此外还参阅了文章: (1) java9迁徙注意事项_集体文章 - SegmentFault 思否 ,这篇文章中介绍了迁徙到高版本JDK之前倡议先到jdk.1.9 转为模块化之后再往更高版本升级。但从IDEA的默认下载的JDK在新版本中只有9、11 - 17 这几个大版本了,所以就没有尝试了。集体也倡议先到JDK9的模块化先弄一遍再去降级。先把整个Compiler我的项目的版本换成JDK 11,这个页面比拟重要后续还会在用到: 在RocketMq的pom.xml当中把编译版本改为11。 保险期间,利用 ctrl + shift + F的快捷键把所有有可能指定的中央全改了。 最初就是我的项目自身的版本了: ...

November 8, 2022 · 1 min · jiezi

关于rocketmq:读完-RocketMQ-源码我学会了如何优雅的创建线程

RocketMQ 是一款开源的分布式音讯零碎,基于高可用分布式集群技术,提供低延时、高牢靠的音讯公布与订阅服务。这篇文章,笔者整顿了 RocketMQ 源码中创立线程的几点技巧,心愿大家读完之后,可能有所播种。1 创立单线程首先咱们先复习下罕用的创立单线程的两种形式:实现 Runnable 接口继承 Thread 类 ▍一、实现 Runnable 接口图中,MyRunnable 类实现了 Runnable 接口的 run 办法,run 办法中定义具体的工作代码或解决逻辑,而Runnable 对象是作为线程构造函数的参数。▍二、 继承 Thread 类线程实现类间接继承 Thread ,实质上也是实现 Runnable 接口的 run 办法。2 单线程抽象类创立单线程的两种形式都很简略,但每次创立线程代码显得有点冗余,于是 RocketMQ 里实现了一个抽象类 ServiceThread 。咱们能够看到抽象类中蕴含了如下外围办法:定义线程名;启动线程;敞开线程。下图展现了 RocketMQ 泛滥的单线程实现类。实现类的编程模版相似 : 咱们仅仅须要继承抽象类,并实现 getServiceName 和 run 办法即可。启动的时候,调用 start 办法 , 敞开的时候调用 shutdown 办法。3 线程池原理线程池是一种基于池化思维治理线程的工具,线程池保护着多个线程,期待着监督管理者调配可并发执行的工作。这防止了在解决短时间工作时创立与销毁线程的代价。线程池不仅可能保障内核的充分利用,还能避免过分调度。 JDK中提供的 ThreadPoolExecutor 类,是咱们最常应用的线程池类。 参数名作用corePoolSize队列没满时,线程最大并发数maximumPoolSizes队列满后线程可能达到的最大并发数keepAliveTime闲暇线程过多久被回收的工夫限度unitkeepAliveTime 的工夫单位workQueue阻塞的队列类型threadPoolFactory扭转线程的名称、线程组、优先级、守护过程状态RejectedExecutionHandler超出 maximumPoolSizes + workQueue 时,工作会交给RejectedExecutionHandler来解决工作的调度通过执行 execute办法实现,办法的外围流程如下:如果 workerCount < corePoolSize,创立并启动一个线程来执行新提交的工作。如果 workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将工作增加到该阻塞队列中。如果 workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创立并启动一个线程来执行新提交的工作。如果 workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则依据回绝策略来解决该工作, 默认的解决形式是间接抛异样。4 线程池封装在 RocketMQ 里 ,网络申请都会携带命令编码,每种命令映射对应的处理器,而处理器又会注册对应的线程池。当服务端 Broker 接管到发送音讯命令时,都会有独自的线程池 sendMessageExecutor 来解决这种命令申请。基于 ThreadPoolExecutor 做了一个简略的封装 ,BrokerFixedThreadPoolExecutor 构造函数蕴含六个外围参数:外围线程数和最大线程数雷同 ,数量是:cpu核数和4比拟后的最小值;闲暇线程的回收的工夫限度,默认1分钟;发送音讯队列,有界队列,默认10000;线程工厂 ThreadFactoryImpl ,定义了线程名前缀:SendMessageThread_ 。RocketMQ 实现了一个简略的线程工厂:ThreadFactoryImpl,线程工厂能够定义线程名称,以及是否是守护线程 。开源我的项目 Cobar ,Xmemcached,Metamorphosis 中都有相似线程工厂的实现 。5 线程名很重要 线程名很重要,线程名很重要,线程名很重要 ,重要的事件说三遍。咱们看到 RocketMQ 中,无论是单线程抽象类还是多线程的封装都会配置线程名 ,因为通过线程名,非常容易定位问题,从而大大晋升解决问题的效率。 定位的媒介常见有两种:日志文件和堆栈记录。▍一、日志文件常常解决业务问题的同学,肯定都常常与日志打交道。查看 ERROR 日志,追溯到执行线程, 要是线程池隔离做的好,根本能够判断出哪种业务场景出了问题;通过查看线程打印的日志,推断线程调度是否失常,比方有的定时工作线程打印了开始,没有打印完结,推论以后线程可能曾经挂掉或者阻塞。▍二、堆栈记录jstack 是 java 虚拟机自带的一种堆栈跟踪工具 ,次要用来查看 Java 线程的调用堆栈,线程快照蕴含以后 java 虚拟机内每一条线程正在执行的办法堆栈的汇合,能够用来剖析线程问题。 jstack -l 过程pid笔者查看线程堆栈,个别关注如下几点:以后 jvm 过程中的线程数量和线程分类是否在预期的范畴内;零碎接口超时或者定时工作进行的异样场景下 ,剖析堆栈中是否有锁未开释,或者线程始终期待网络通讯响应;剖析 jvm 过程中哪个线程占用的 CPU 最高。6 总结本文是RocketMQ 系列文章的开篇,和敌人们简略聊聊 RocketMQ 源码里创立线程的技巧。单线程抽象类 ServiceThread 使用者只须要实现业务逻辑以及定义线程名即可 ,不须要写冗余的代码。线程池封装适当封装,定义线程工厂,并合理配置线程池参数。线程名很重要文件日志,堆栈记录配合线程名能大大晋升解决问题的效率。 RocketMQ 的多线程编程技巧很多,比方线程通信,并发管制,线程模型等等,后续的文章会一一为大家展示。如果我的文章对你有所帮忙,还请帮忙点赞、在看、转发一下,你的反对会激励我输入更高质量的文章,非常感谢! ...

September 7, 2022 · 1 min · jiezi

关于rocketmq:RocketMqRocketMq-基本扫盲

引言本篇是RocketMq扫盲,并不会讲到各个组件实现的细节内容,这里从整体全局的角度看对于RocketMq的整体设计。 理论知识略显枯燥乏味,能够大抵理解一些基本概念之后,间接上手源代码以及参考官网文档理解各个组件的细节和设计思路,Rocket各个子组件绝对比拟独立,能够拆分繁多子组件一一攻破。 一、根本介绍一句话介绍RocketMQ RocketMQ作为一款纯java、分布式、队列模型的开源消息中间件,反对事务音讯、程序音讯、批量音讯、定时音讯、音讯回溯等支流音讯队列。 关键词 纯JAVA编写分布式队列模型。反对事务音讯、程序音讯、批量音讯、音讯回溯等等。开源,为 Apach 基金会 顶级我的项目之一。二、RocketMQ 常见问题这些常见问题贯通整个音讯队列的学习,不同的音讯队列有不同的解决方案,须要进行横向和纵向比照。 Broker是如何进行分片存储的?Broker的内部消息如何实现主从同步?RocketMq如何确保MessageId唯一性?百万音讯沉积的低提早如何做到的?RocketMQ 如何保障音讯不失落?如何放弃音讯的程序生产?如何避免音讯反复生产问题?三、什么是音讯队列In computer science, message queues and mailboxes are software-engineeringcomponents typically used for inter-process communication (IPC), or for inter-threadcommunication within the same process. They use a queue for messaging – the passing ofcontrol or of content. Group communication systems provide similar kinds of functionality.在计算机科学类畛域,音讯队列和邮箱都是软件工程组件,通常用于计算机外部同一过程或者同一过程的线程内通信(IPC),它们通过队列来传输管制信息或者内容,群组通信提供相似性能。 上面是来自 《数据密集型利用零碎设计》 第四章的局部介绍内容。 音讯队列最早是由一些商用免费软件管制,后续随着呈现各种开源软件kafka、activeMQ、HornetQ、RabbitMQ等风行,音讯队列逐步走进互联网的视线。 和RPC异步通信比照,音讯队列有上面几个特点: 音讯队列能够充当缓冲关照单方的解决能力。防止发送方须要晓得接管方IP和地址的问题。反对一个音讯发给多个接管方。逻辑上的发送方和接管方拆散。简略来说音讯队列就是应用队列来通信的组件,在最后的定义外面,它仅仅用用于双端通信,然而并没有非凡的规定和要求。 然而到了当初,音讯队列更多指消息中间件,而消息中间件并不只有音讯队列,同时音讯队列自身的职责也在逐步简单和变动。 3.1 为什么须要音讯队列很简略,因为传统的WEB架构对于几百的用户量根本入不敷出,然而一旦上万、上千万、上亿的时候,就必须要借助音讯队列来帮忙零碎解耦,音讯队列是重要的高并发和高可用组件。 你也能够简略的了解音讯队列就是抗"揍"的。 3.1.1 异步解决(异步)异步这个概念其实很早就呈现了,异步解决通常用于实现须要疾速响应同时须要解决重要业务的状况,比方购物之后用户想早点看到付款后果,这时候就外部零碎可能须要调用商户服务,用户服务,订单服务,积分服务、结算服务配合。 这些业务会造成造成一条很长的链路,不可能等到业务全副实现才对客户响应,客户仅仅想要看到的是本人的购物是否胜利。 音讯队列异步解决的次要作用是缩小业务申请申请阻塞,进步业务解决能力的同时做到疾速的响应,业务零碎能够更加专一业务自身,同时利用队列实现不同业务的“转接”解决。 ...

September 1, 2022 · 2 min · jiezi

关于rocketmq:RocketMQ源码解析topic创建机制

RocketMQ Topic创立机制RocketMQ Topic创立机制分为两种:一种主动创立,一种手动创立。能够通过设置broker的配置文件来禁用或者容许主动创立。默认是开启的容许主动创立autoCreateTopicEnable=true/false 上面会联合源码来深度剖析一下主动创立和手动创立的过程。 主动Topic默认状况下,topic不必手动创立,当producer进行音讯发送时,会从nameserver拉取topic的路由信息,如果topic的路由信息不存在,那么会默认拉取broker启动时默认创立好名为“TBW102”的Topic,这定义在org.apache.rocketmq.common.MixAll类中// Will be created at broker when isAutoCreateTopicEnable public static final String AUTO_CREATE_TOPIC_KEY_TOPIC = "TBW102"; 复制代码主动创立开关是下BrokerConfig类中有一个公有变量:@ImportantFieldprivate boolean autoCreateTopicEnable = true;复制代码这变量能够通过配置文件配置来进行批改,代码中的默认值为true,所以在默认的状况下Rocket MQ是会主动创立Topic的。在Broker启动,会调用TopicConfigManager的构造方法,在构造方法中定义了一系列RocketMQ零碎内置的一些零碎Topic(这里只关注一下TBW102):{ // MixAll.AUTO_CREATE_TOPIC_KEY_TOPIC if (this.brokerController.getBrokerConfig().isAutoCreateTopicEnable()) { String topic = MixAll.AUTO_CREATE_TOPIC_KEY_TOPIC; TopicConfig topicConfig = new TopicConfig(topic); this.systemTopicList.add(topic); topicConfig.setReadQueueNums(this.brokerController.getBrokerConfig() .getDefaultTopicQueueNums()); //8 topicConfig.setWriteQueueNums(this.brokerController.getBrokerConfig() .getDefaultTopicQueueNums()); //8 int perm = PermName.PERM_INHERIT | PermName.PERM_READ | PermName.PERM_WRITE; topicConfig.setPerm(perm); this.topicConfigTable.put(topicConfig.getTopicName(), topicConfig);}}复制代码这里有 this.brokerController.getBrokerConfig().isAutoCreateTopicEnable() 这样一段代码,在开启容许主动创立的时候,会把以后Topic的信息存入topicConfigTable变量中。而后通过发送定期发送心跳包把Topic和Broker的信息发送到NameServer的RouteInfoManager中进行保留。在BrokerController中定义了这样的一个定时工作来执行这个心跳包的发送:this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { try { BrokerController.this.registerBrokerAll(true, false, brokerConfig.isForceRegister()); } catch (Throwable e) { log.error("registerBrokerAll Exception", e); } } }, 1000 * 10, Math.max(10000, Math.min(brokerConfig.getRegisterNameServerPeriod(), 60000)), TimeUnit.MILLISECONDS);复制代码这里就阐明了如何把每个Broker的零碎自定义的Topic注册到NameServer。接下来看在发送过程中如何从NameServer获取Topic的路由信息:DefaultMQProducerImpl.sendDefaultImplprivate SendResult sendDefaultImpl( ...

August 19, 2022 · 3 min · jiezi

关于rocketmq:一张图进阶-RocketMQ-消息存储

前言三此君看了好几本书,看了很多遍源码整顿的 一张图进阶 RocketMQ 图片,对于 RocketMQ 你只须要记住这张图!感觉不错的话,记得点赞关注哦。【重要】视频在 B 站同步更新,欢送围观,轻轻松松涨姿态。一张图进阶 RocketMQ-音讯存储(视频版) https://www.bilibili.com/vide... 本文是“一张图进阶 RocketMQ”第 5 篇,对 RocketMQ 不理解的同学能够先看看后面 4 期: 一张图进阶 RocketMQ-整体架构一张图进阶 RocketMQ - NameServer一张图进阶 RocketMQ - 音讯发送一张图进阶 RocketMQ - 通信机制后面两期咱们次要分享了 RocketMQ 是如何将音讯发送进来的,当初音讯曾经被 Netty 送上路了,接力棒曾经交给了 Broker。如果咱们本人来实现 Broker 会怎么实现呢?首先必定得把音讯存起来吧,不然宕机了,音讯失落了,那就离大谱了。 可是音讯要以什么构造存储呢?二进制、JSON、PB?从性能上来看必定都是能够的,那 RocketMQ 到底是怎么搞的? 解决了存储构造问题,那音讯存到哪里呢?数据库,本地文件,还是对象存储服务器?从性能的角度必定也都是能够的。可是,哪家数据库能够反对单机十万级吞吐量?那我间接通通存到数据库得了,瞎折腾些啥。难道存在本地文件就能够了?咱们本人实现不能够,然而 RocketMQ 能够,那 RocketMQ 有什么黑科技呢? 所以咱们明天就来聊一聊 Broker 如何存储音讯,【首先明确咱们的指标】咱们须要先理解 RocketMQ 的存储构造,也就是音讯是如何组织的。理解了存储构造,咱们能力更好的了解存储流程,不然咱们不晓得为什么流程是这样的。最初咱们须要理解有哪些机制撑持 RocketMQ 单机十万级吞吐量。 存储架构音讯在 Broker 上的存储构造如上图,所有相干文件放在 ROCKETMQ_HOME 下,有哪些文件呢?寄存音讯自身的 CommitLog,以及音讯的索引文件 ConsumeQueue 和 IndexFile: CommitLog从物理构造上来看,所有的音讯都存储在 CommitLog 外面,其实就是所有的音讯依照“音讯在 CommitLog 各字段示意图”所示,挨个按顺序存储到文件中。 单个 CommitLog 文件大小默认 1G ,文件名长度为 20 位,右边补零,残余为起始偏移量。比方 00000000000000000000 代表了第一个文件,起始偏移量为 0,文件大小为 1G=1073741824;当第一个文件写满了,第二个文件为 00000000001073741824,起始偏移量为 1073741824,以此类推。音讯次要是程序写入日志文件,当文件满了,写入下一个文件。CommitLog 程序写,能够大大提高写入效率。 ...

July 28, 2022 · 3 min · jiezi

关于rocketmq:一张图进阶-RocketMQ-通信机制

前 言三此君看了好几本书,看了很多遍源码整顿的 一张图进阶 RocketMQ 图片,对于 RocketMQ 你只须要记住这张图!感觉不错的话,记得点赞关注哦。【重要】视频在 B 站同步更新,欢送围观,轻轻松松涨姿态。一张图进阶 RocketMQ-通信机制(视频版) https://www.bilibili.com/vide... 本文是“一张图进阶 RocketMQ”第 4 篇,对 RocketMQ 不理解的同学能够先看看后面三期: 一张图进阶 RocketMQ-整体架构一张图进阶 RocketMQ - NameServer一张图进阶 RocketMQ - 音讯发送上一期分享了 RocketMQ 生产者启动流程及同步音讯发送流程,咱们晓得了在通信层是基于 Netty 将消息传递给 Broker 进行存储的。如果对 Netty 齐全不理解咱们就很难真正了解 RocketMQ,所以明天咱们简略的聊一聊 Netty 根本流程,而后剖析 RocketMQ 的通信机制,最初通过异步音讯发送来串联 RocketMQ 通信机制。 Netty 介绍Netty 有很多概念,等介绍完概念大家都困了,咱们就不过多介绍了,间接联合示例来看看 Netty 的根底流程,可能帮忙咱们更好的了解 RocketMQ 即可。 Netty 服务端启动初始化两个线程组 BossGroup & WorkerGroup,别离用于解决客户端连贯及网络读写。Netty 客户端启动初始化一个线程组, 用于解决申请及返回后果。客户端 connect 到 Netty 服务端,创立用于 传输数据的 Channel。Netty 服务端的 BossGroup 解决客户端的连贯申请,而后把剩下的工作交给 WorkerGroup。连贯建设好了,客户端就能够利用这个连贯发送数据给 Netty 服务端。Netty WorkerGroup 中的线程应用 Pipeline(蕴含多个处理器 Handler) 对数据进行解决。Netty 服务端的解决完申请后,返回后果也通过 Pipeline 解决。Netty 服务端通过 Channel 将数据返回给客户端。客户端通过 Channel 接管到数据,也通过 Pipeline 进行解决。 ...

July 27, 2022 · 4 min · jiezi

关于rocketmq:一张图进阶-RocketMQ-消息发送

前 言三此君看了好几本书,看了很多遍源码整顿的 一张图进阶 RocketMQ 图片,对于 RocketMQ 你只须要记住这张图!感觉不错的话,记得点赞关注哦。 【重要】视频在 B 站同步更新,欢送围观,轻轻松松涨姿态。一张图进阶 RocketMQ-音讯发送(视频版) https://www.bilibili.com/vide... 本文是“一张图进阶 RocketMQ” 系列第 3 篇,对 RocketMQ 不理解的同学能够先看看三此君的 一张图进阶 RocketMQ-整体架构,一张图进阶 RocketMQ - NameServer。在理解了 RocketMQ 的整体架构之后,咱们来深刻的剖析下生产者音讯发送的设计与实现。本文从一个生产者示例开始,以两行代码为切入点,逐渐分析生产者启动流程以及同步音讯发送流程。 生产者示例音讯发送分为同步音讯、异步音讯和单向音讯,简略来说: 同步音讯:音讯发送之后会期待 Broker 响应,并把响应后果传递给业务线程,整个过程业务线程在期待。异步音讯:调用异步发送 API,Producer 把音讯发送申请放进线程池就返回。逻辑解决,网络申请都在线程池中进行,等后果解决完之后回调业务定义好的回调函数。单向音讯:只负责发送音讯,不论发送后果。咱们先来回顾下同步音讯发送的例子: public class SyncProducer { public static void main(String[] args) throws Exception { // 实例化音讯生产者Producer DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name"); // 设置NameServer的地址 producer.setNamesrvAddr("localhost:9876"); // 启动Producer实例 producer.start(); // 创立音讯,并指定Topic,Tag和音讯体 Message msg = new Message("sancijun","order", "orderId", "我肯定会关注三此君".getBytes("UTF-8")); // 发送音讯到一个Broker SendResult sendResult = producer.send(msg); // 通过sendResult返回音讯是否胜利送达 System.out.printf("%s%n", sendResult); // 如果不再发送音讯,敞开Producer实例。 producer.shutdown(); }}首先,实例化一个生产者 producer,并通知它 NameServer 的地址,这样生产者能力从 NameServer 获取路由信息。而后 producer 得做一些初始化(这是很要害的步骤),它要和 NameServer 通信,要先初始化通信模块等。producer 曾经筹备好了,那得筹备好要发的内容,把 "我肯定会关注三此君" 发送到 Topic=”sanicjun“。内容筹备好,那 producer 就能够把音讯发送进来了。producer 怎么晓得 Broker 地址呢?他会去 NameServer 获取路由信息,失去 Broker 的地址是 localhost:10909,而后通过网络通信将音讯发送给 Broker。生产者发送的音讯通过网络传输给 Broker,Broker 须要对音讯依照肯定的构造进行存储。存储实现之后,把存储后果告知生产者。其中有两个要害的中央:producer.start() 及 producer.send(),也就是生产者初始化及音讯发送。咱们以这两行代码为切入点,看看 RocketMQ Producer 的设计与实现。 ...

July 26, 2022 · 2 min · jiezi

关于rocketmq:一张图进阶-RocketMQ-NameServer

前言三此君看了好几本书,看了很多遍源码整顿的一张图进阶 RocketMQ 图片,对于 RocketMQ 你只须要记住这张图!感觉不错的话,记得点赞关注哦。 【重要】视频在 B 站同步更新,欢送围观,轻轻松松涨姿态。一张图进阶 RocketMQ-NameServer(视频版) https://www.bilibili.com/vide... 本文是《一张图进阶 RocketMQ》系列的第 2 篇,明天次要聊一聊 RocketMQ 集群元数据管理。因为 Producer、Consumer 和 Broker 都须要和 NameServer 交互,负责的三此君不得不先和大家唠一唠 NameServer 是何方神圣。 《RocketMQ 整体架构》中有说道 NameServer 是集群的元数据管理核心,那它到底治理了哪些元数据?咱们来看看 NameServer 外面都穿了什么,看完了记得关注、转发、点赞、珍藏哦。 集群元数据简略来说,NameServer 负责集群元数据的增删改查。先不论这个增删改查是怎么实现的,咱们甚至能够了解就是数据库的增删改查,然而咱们肯定要晓得这些元数据都长什么样子。能力晓得 Producer、Consumer 及 Broker 是如何依据这些数据进行音讯收发的。 如图所示,二主二从的 Broker 集群相干的元数据信息,包含 topicQueueTable、BrokerAddrTable、ClusterAddrTable、brokerLiveInfo、FilterServer (临时不必关注,图中未画出)。 HashMap<String topic, List<QueueData>> topicQueueTable:Key 是 Topic 的名称,它存储了所有 Topic 的属性信息。Value 是个 QueueData 列表,长度等于这个 Topic 数据存储的 Master Broker 的个数,QueueData 里存储着 Broker 的名称、读写 queue 的数量、同步标识等。HashMap<String BrokerName, BrokerData> brokerAddrTable:这个构造存储着一个 BrokerName 对应的属性信息,包含所属的 Cluster 名称,一个 Master Broker 和多个 Slave Broker 的地址信息HashMap<String ClusterName, Set<String BrokerName>> clusterAddrTable:存储的是集群中 Cluster 的信息,就是一个 Cluster 名称对应一个由 BrokerName 组成的汇合。HashMap<String BrokerAddr, BrokerLiveInfo> brokerLiveTable:Key 是 BrokerAddr 对应着一台机器,BrokerLiveTable 存储的内容是这台 Broker 机器的实时状态,包含上次更新状态的工夫戳,NameServer 会定期检查这个工夫戳,超时没有更新就认为这个 Broker 有效了,将其从 Broker 列表里革除。HashMap<String BrokerAddr, List<String> FilterServer> filterServerTable:Key 是 Broker 的地址,Value 是和这个 Broker 关联的多个 FilterServer 的地址。Filter Server 是过滤服务器,是 RocketMQ 的一种服务端过滤形式,一个 Broker 能够有一个或多个 Filter Server。工作流程而后咱们来看一下 NameServer 简略的工作流程,其余角色会被动向 NameServer 上报状态,依据上报音讯里的申请码做相应的解决,更新存储的对应信息。 ...

July 25, 2022 · 1 min · jiezi

关于rocketmq:一张图进阶-RocketMQ-NameServer

前言三此君看了好几本书,看了很多遍源码整顿的一张图进阶 RocketMQ 图片,对于 RocketMQ 你只须要记住这张图!感觉不错的话,记得点赞关注哦。 【重要】视频在 B 站同步更新,欢送围观,轻轻松松涨姿态。一张图进阶 RocketMQ-NameServer(视频版) https://www.bilibili.com/vide... 本文是《一张图进阶 RocketMQ》系列的第 2 篇,明天次要聊一聊 RocketMQ 集群元数据管理。因为 Producer、Consumer 和 Broker 都须要和 NameServer 交互,负责的三此君不得不先和大家唠一唠 NameServer 是何方神圣。 《RocketMQ 整体架构》中有说道 NameServer 是集群的元数据管理核心,那它到底治理了哪些元数据?咱们来看看 NameServer 外面都穿了什么,看完了记得关注、转发、点赞、珍藏哦。 集群元数据简略来说,NameServer 负责集群元数据的增删改查。先不论这个增删改查是怎么实现的,咱们甚至能够了解就是数据库的增删改查,然而咱们肯定要晓得这些元数据都长什么样子。能力晓得 Producer、Consumer 及 Broker 是如何依据这些数据进行音讯收发的。 如图所示,二主二从的 Broker 集群相干的元数据信息,包含 topicQueueTable、BrokerAddrTable、ClusterAddrTable、brokerLiveInfo、FilterServer (临时不必关注,图中未画出)。 HashMap<String topic, List<QueueData>> topicQueueTable:Key 是 Topic 的名称,它存储了所有 Topic 的属性信息。Value 是个 QueueData 列表,长度等于这个 Topic 数据存储的 Master Broker 的个数,QueueData 里存储着 Broker 的名称、读写 queue 的数量、同步标识等。HashMap<String BrokerName, BrokerData> brokerAddrTable:这个构造存储着一个 BrokerName 对应的属性信息,包含所属的 Cluster 名称,一个 Master Broker 和多个 Slave Broker 的地址信息HashMap<String ClusterName, Set<String BrokerName>> clusterAddrTable:存储的是集群中 Cluster 的信息,就是一个 Cluster 名称对应一个由 BrokerName 组成的汇合。HashMap<String BrokerAddr, BrokerLiveInfo> brokerLiveTable:Key 是 BrokerAddr 对应着一台机器,BrokerLiveTable 存储的内容是这台 Broker 机器的实时状态,包含上次更新状态的工夫戳,NameServer 会定期检查这个工夫戳,超时没有更新就认为这个 Broker 有效了,将其从 Broker 列表里革除。HashMap<String BrokerAddr, List<String> FilterServer> filterServerTable:Key 是 Broker 的地址,Value 是和这个 Broker 关联的多个 FilterServer 的地址。Filter Server 是过滤服务器,是 RocketMQ 的一种服务端过滤形式,一个 Broker 能够有一个或多个 Filter Server。工作流程而后咱们来看一下 NameServer 简略的工作流程,其余角色会被动向 NameServer 上报状态,依据上报音讯里的申请码做相应的解决,更新存储的对应信息。 ...

July 25, 2022 · 1 min · jiezi

关于rocketmq:rocketmq如何解决高可用问题

架构官网架构图 复制数据主从复制,其实就是把主节点数据复制到从节点。 主动切换如果主节点挂了,从节点会主动降职为主节点。 版本区别高版本4.5和以上,反对主动切换。 低版本4.5以下,不反对主动切换。 主动切换-实现原理1.主节点挂了 2.抉择一个从节点 1)一个从节点 如果只有一个从节点,就抉择这个从节点降职为主节点。 2)多个从节点 如果有多个从节点,就选举,过半以上节点抉择了某个从节点,该节点才会降职为主节点。 小结高可用的外围 1.有一个备胎节点 2.实现主动切换性能 读写数据主节点写和读。 从节点最佳实际是不写也不读,只备胎。 从节点默认是不读,然而也能够设置为可读——然而最好不要这么做,因为徒增复杂度,容易引起故障。 生产数据如果主节点挂了, 1.高版本 从节点会主动降职为主节点,而后就可写可读。 2.低版本 从节点不会降职为主节点。那主节点的没有来得及生产的数据怎么办?消费者会从备胎节点读数据,这正是从节点的作用。 然而,此时,从节点是不能写数据的。所以,相当于主节点集群数量少了一个节点,就像dubbo集群服务一样,少了一个节点,是没有影响的。然而,如果剩下的主节点比拟少,比方总共只有2个,而后还挂了一个,这个时候就会存在单点故障。

July 22, 2022 · 1 min · jiezi

关于rocketmq:RocketMQ系列二RocketMQ监控告警一站式搭建应用

试验简介 钻研RocketMQ的同学都晓得,RocketMQ的生态目前并不是很欠缺,包含官网的文档资料也无限,官网的Console存在一些Bug,页面的款式有的也有问题,然而正是因为这些起因,咱们针对RocketMQ能做的事有很多,不像Kafka,生态曾经很欠缺,一些企业也对Kafka做了开源的产品,最近开始做基于K8S的RocketMQ治理控制台,须要对每个集群有欠缺的监控告警,接下来就简略阐明一下做RocketMQ的监控有哪些步骤。 试验地址:https://developer.aliyun.com/... 试验详情 一、试验环境阐明 启动namesrv。察看到启动胜利的日志后, ctrl + c,终止日志输入。cd /usr/local/services/5-rocketmq/namesrv-01 restart.sh 启动broker批改broker配置项brokerIP1为试验公网IP,启动broker。察看到启动胜利的日志后, ctrl + c,终止日志输入。 操作命令如下: cd /usr/local/services/5-rocketmq/broker-01 vim ./conf/broker.conf restart.sh 二、如何编译和部署RocketMQ (本教程会以如何利用RocketMQ Exporter源码编译并打包,部署一个Exporter实例。) 装置git,jdk, maven等工具(以后试验环境曾经装置好。参考或者baidu/google)jdk装置 jdk 下载:https://www.oracle.com/hk/java/technologies/javase/javase8-archive-downloads.html jdk 装置 macos:https://juejin.cn/post/6844903878694010893 windows:https://www.runoob.com/w3cnote/windows10-java-setup.html maven装置 maven 下载二进制:https://dist.apache.org/repos/dist/release/maven/maven-3/ maven 装置(windows + macos):https://www.runoob.com/maven/maven-setup.html 如果对于国外网站拜访慢, 能够配置maven国内镜像:https://cloud.tencent.com/developer/article/1452479 git装置 ( 可选 , 不装置的话间接下载4.9.3源代码:https://github.com/apache/rocketmq/releases) 下载地址:https://git-scm.com/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E5%AE%89%E8%A3%85-Git2.下载代码 在本人电脑上, 进入命令行, 抉择一个保留源码的目录, 这里我把源码保留到 /tiger/tmp为例 2.1 创立代码保留目录(已创立则不操作)并进入代码保留目录: mkdir -p /tiger/tmpcd /tiger/tmp 2.2 克隆master代码 git clone https://github.com/apache/roc...or git clone https://gitee.com/mirrors_apa... ...

July 22, 2022 · 1 min · jiezi

关于rocketmq:RocketMQ-系列一入门级使用演示

流动链接 试验简介Apache RocketMQ™ 是一个对立的音讯引擎零碎, 也是一个轻量级的数据处理平台. 当你遇到以下相似问题而大刀阔斧时,RocketMQ能够帮忙你解决: 重试音讯、死信音讯、事物音讯反对音讯轨迹追踪音讯过滤IPv6反对ACL反对主、正本主动高牢靠全面的监控反对治理平台原生反对Request-Reply模式反对本教程会以如何利用源码编译、打包、部署、理论应用RocketMQ。 试验实操一、如何下载、编译最新版RocketMQ (本教程会以如何利用源码编译并打包RocketMQ为例, 演示如何下载、编译任意版本的RocketMQ.) 1. 装置git,jdk, maven等工具(参考baidu/google)jdk装置 jdk 下载:https://www.oracle.com/hk/java/technologies/javase/javase8-archive-downloads.htmljdk 装置 macos:https://juejin.cn/post/6844903878694010893windows:https://www.runoob.com/w3cnote/windows10-java-setup.htmlmaven装置 maven 下载二进制:https://dist.apache.org/repos/dist/release/maven/maven-3/maven 装置(windows + macos):https://www.runoob.com/maven/maven-setup.html如果对于国外网站拜访慢, 能够配置maven国内镜像:https://cloud.tencent.com/developer/article/1452479git装置 ( 可选 , 不装置的话间接下载4.9.3源代码:https://github.com/apache/rocketmq/releases) 下载地址:https://git-scm.com/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E5%AE%89%E8%A3%85-Git2.下载最新release代码(这里以git为例,如果没有装置git间接从github release页面下载)在本人电脑上, 进入命令行, 抉择一个保留源码的目录, 这里我把源码保留到 /tiger/tmp为例 2.1 创立代码保留目录(已创立则不操作)并进入代码保留目录: mkdir -p /tiger/tmpcd /tiger/tmp2.2 克隆代码 git clone --branch release-4.9.3 https://github.com/apache/rocketmq.git2.3 进入源码根目录: cd rocketmq3. 编译和打包源码编译打包胜利后, 咱们执行: cd distribution/targetls -l二、如何部署一个简略的RocketMQ集群 (本教程将演示如何利用编译后果,部署一个1Namesrv + 1Broker的RocketMQ集群) 1. 找到上一章节的编译打包后果cd /tiger/tmp/rocketmq/distribution/target/ls -l 2. 装置Namesrv, Broker创立部署长期目录 (已创立则疏忽)mkdir -p /tiger/rocketmq/namesrv1mkdir -p /tiger/rocketmq/broker1拷贝rocketmq-4.9.4-SNAPSHOT外面的内容,别离拷贝到 /tiger/rocketmq/namesrv1, /tiger/rocketmq/broker1,cp -R /tiger/tmp/rocketmq/distribution/target/rocketmq-4.9.4-SNAPSHOT/rocketmq-4.9.4-SNAPSHOT/* /tiger/rocketmq/namesrv1cp -R /tiger/tmp/rocketmq/distribution/target/rocketmq-4.9.4-SNAPSHOT/rocketmq-4.9.4-SNAPSHOT/* /tiger/rocketmq/broker1查看后果如下cd /tiger/rocketmq/ls -l3. 批改日志配置、jvm配置等其余配置批改namesrv配置 ...

July 14, 2022 · 2 min · jiezi

关于rocketmq:RocketMQ之消费者启动与消费流程

vivo 互联网服务器团队 - Li Kui一、简介1.1 RocketMQ 简介RocketMQ是由阿里巴巴开源的分布式消息中间件,反对程序音讯、定时音讯、自定义过滤器、负载平衡、pull/push音讯等性能。RocketMQ次要由 Producer、Broker、Consumer 、NameServer四局部组成,其中Producer 负责生产音讯,Consumer 负责生产音讯,Broker 负责存储音讯。NameServer充当名字路由服务,整体架构图如下所示: roducer:负责生产音讯,个别由业务零碎生产音讯,可通过集群形式部署。RocketMQ提供多种发送形式,同步发送、异步发送、程序发送、单向发送。同步和异步形式均须要Broker返回确认信息,单向发送不须要。Consumer:负责生产音讯,个别是后盾零碎负责异步生产,可通过集群形式部署。一个音讯消费者会从Broker服务器拉取音讯、并将其提供给应用程序。提供pull/push两者生产模式。Broker Server:负责存储音讯、转发音讯。RocketMQ零碎中负责接管从生产者发送来的音讯并存储、同时为消费者的拉取申请作筹备,存储音讯相干的元数据,包含消费者组、生产进度偏移和主题和队列音讯等。Name Server:名字服务,充当路由音讯的提供者。生产者或消费者可能通过名字服务查找各主题相应的Broker IP列表。多个NameServer实例组成集群,互相独立,没有信息替换。本文基于Apache RocketMQ 最新版本次要讲述RocketMQ的消费者机制,剖析其启动流程、pull/push机制,音讯ack机制以及定时音讯和程序音讯的不同。 1.2 工作流程(1)启动NameServer。 NameServer起来后监听端口,期待Broker、Producer、Consumer连上来,相当于一个路由控制中心。 (2)启动Broker。 跟所有的NameServer放弃长连贯,定时发送心跳包。心跳包中蕴含以后Broker信息(IP+端口等)以及存储所有Topic信息。注册胜利后,NameServer集群中就有Topic跟Broker的映射关系。 (3)创立Topic。 创立Topic时须要指定该Topic要存储在哪些Broker上,也能够在发送音讯时主动创立Topic。 (4)Producer发送音讯。 启动时先跟NameServer集群中的其中一台建设长连贯,并从NameServer中获取以后发送的Topic存在哪些Broker上,轮询从队列列表中抉择一个队列,而后与队列所在的Broker建设长连贯从而向Broker发消息。 (5)Consumer生产音讯。 跟其中一台NameServer建设长连贯,获取以后订阅Topic存在哪些Broker上,而后间接跟Broker建设连贯通道,开始生产音讯。 二、消费者启动流程官网给出的消费者实现代码如下所示: public class Consumer { public static void main(String[] args) throws InterruptedException, MQClientException { // 实例化消费者 DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("TestConsumer"); // 设置NameServer的地址 consumer.setNamesrvAddr("localhost:9876"); // 订阅一个Topic,以及Tag来过滤须要生产的音讯 consumer.subscribe("Test", "*"); // 注册回调实现类来解决从broker拉取回来的音讯 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"); }}上面让咱们来剖析消费者在启动中每一阶段中做了什么吧,let’s go. ...

July 12, 2022 · 5 min · jiezi

关于rocketmq:RocketMQ源码分析1NameServer启动

1.NameServer是什么? NameServer是一个非常简单的Topic路由注册核心,其角色相似Dubbo中的zookeeper,反对Broker的动静注册与发现。次要包含两个性能: Broker治理,NameServer承受Broker集群的注册信息并且保留下来作为路由信息的根本数据。而后提供心跳检测机制,查看Broker是否还存活;路由信息管理,每个NameServer将保留对于Broker集群的整个路由信息和用于客户端查问的队列信息。而后Producer和Conumser通过NameServer就能够晓得整个Broker集群的路由信息,从而进行音讯的投递和生产。NameServer通常也是集群的形式部署,各实例间互相不进行信息通信。Broker是向每一台NameServer注册本人的路由信息,所以每一个NameServer实例下面都保留一份残缺的路由信息。当某个NameServer因某种原因下线了,Broker依然能够向其它NameServer同步其路由信息,Producer和Consumer依然能够动静感知Broker的路由的信息 2.NameServer启动入口类public class NamesrvStartup { private static InternalLogger log;private static Properties properties = null;private static CommandLine commandLine = null;public static void main(String[] args) { main0(args);}public static NamesrvController main0(String[] args) { try { NamesrvController controller = createNamesrvController(args); //TODO:NameServer启动 start(controller); String tip = "The Name Server boot success. serializeType=" + RemotingCommand.getSerializeTypeConfigInThisServer(); log.info(tip); System.out.printf("%s%n", tip); return controller; } catch (Throwable e) { e.printStackTrace(); System.exit(-1); } return null;}复制代码晓得了入口,那接下来咱们就剖析下它是如何启动的,启动的过程中都做了什么?从下面的局部代码中,不难看出,它首先就是创立NamesrvController对象,而后启动(start()办法),那咱们就一步一步走进去看看细节.3.创立 NamesrvController 对象 不要看代码比拟多,其实次要就是创立3个对象,请往后看 public static NamesrvController createNamesrvController(String[] args) throws IOException, JoranException { ...

May 12, 2022 · 2 min · jiezi

关于rocketmq:RocketMQ-消息消费过程

上篇讲到音讯从broker拉取后,会把音讯存入PullRequest的processQueue中。此时就会有其余线程,对这些音讯进行生产。 其余线程包含程序生产和并发生产。 并发生产并发生产中,用于生产音讯的线程叫做ConsumeRequest,每一个ConsumeRequest默认生产32条音讯,如果须要生产的音讯超过32条,就会创立多个ConsumeRequest。这些ConsumeRequest就会放入线程池中期待运行。 ConsumeRequest线程运行的时候,会调用监听器,拿到音讯生产的后果,并发生产的后果有两种,RECONSUME_LATER和CONSUME_SUCCESS。 RECONSUME_LATER阐明须要从新生产,但播送模式下只打印正告级别的日志,集群模式下,从新封装音讯成ConsumeRequest,再放入线程池,5s后再进行生产。 同时,也须要跟broker说,这个音讯生产失败了。broker创立重试主题,并设置音讯重试次数,当音讯拉取超过重试次数后,就会进入DLQ队列中。 有哪些音讯须要从新生产?比方这次音讯有10个,ackIndex是2,那下标为3到9的音讯,都要从新生产。如果这个后果是CONSUME_LATER,ackIndex就会被设置为-1,那就是所有的音讯,都要从新生产。 NSUME_SUCCESS阐明生产胜利,依据ackIndex计算一下胜利和失败的信息。 对于RECONSUME_LATER或NSUME_SUCCESS的音讯解决完后,就要把音讯从ProcessQueue中移除,并更新OffsetStore里的音讯生产进度。 OffsetStore里的音讯生产进度会由一个定时工作,每5s会提交给broker,而后broker再更新偏移量。所以即使音讯生产胜利了,然而还没提交给broker的时候,宕机了,那下次还是从之前的偏移量开始读取数据,导致音讯反复生产。 程序生产程序生产中,也是封装一个ConsumeRequest,这个ConsumeRequest和并发生产的ConsumeRequest不同,他并没有音讯列表,所以音讯间接从ProcessQueue拿。 线程生产的时候,首先会获取一个锁。这个锁的粒度是音讯队列,也就是说,一个音讯生产队列,只能有一个线程进行生产。 获取锁后,就从ProcessQueue把音讯拿进去。 如果ProcessQueue有音讯,那还须要拿到ProcessQueue的ConsumeLock。 而后进行音讯生产,并通过监听器拿到生产后果的状态,并开释锁。 如果后果是SUCCESS,则更新ProcessQueue中的音讯生产信息。 如果后果是SUSPEND_CURRENT_QUEUE_A_MOMENT,则会查看音讯的重试次数。 如果这个重试次数超过容许的最大重试次数,就会把这个音讯发送给broker,且寄存在死信队列中。 如果没有超过,则会持续从新生产,并累加生产次数。 最初,再更新OffsetStore里的音讯生产进度。 OffsetStore的解决,和并发生产一样。 线程拿到messageQueueLock锁后,并不会始终持有这个锁,默认生产60s,就会开释这个锁。

May 6, 2022 · 1 min · jiezi

关于rocketmq:RocketMQ-消息拉取

上一篇提到,因为音讯队列负载机制,会往pullRequestQueue队列放入一个个的PullRequest。 这些PullRequest会有一个专门的线程,把它取出来并封装成服务端的一个Request,发送给broker。 在发送服务端之前,须要晓得broker的地址在哪,须要从什么偏移量开始拉取(偏移量集群模式存broker,播送模式存本地),一次性拉多少数据。 broker收到申请后,就会从commitlog中,依据偏移量把所须要的音讯给取出来,因为broker的主从同步,这边返回音讯的时候,也会告知下一次拉取是从主broker拉取还是slave的broker拉取数据。 从commit拉取音讯的时候,会有这几个状况(上面疏忽offsetCheckInSlave判断): 以后音讯队列并没有音讯,则下次拉取音讯的时候,如果这个broker是主节点,还是这个偏移量。如果这个broker是从节点,则下次间接从0开始。 以后音讯队列有音讯,然而咱们拉取的偏移量比队列里最小的偏移量还小,比方咱们须要拉取100的数据,然而音讯队列的最小偏移量是500。则下次拉取音讯的时候,如果这个broker是主节点,还是这个偏移量。如果这个broker是从节点,则下次间接从500开始。 如果咱们咱们拉取的偏移量刚好等于音讯队列里最大的偏移量,所以咱们也没有数据能够生产了,那下次拉取的时候,还是100这个偏移量。 如果咱们咱们拉取的偏移量大于音讯队列里最大的偏移量,所以咱们也没有数据能够生产了,那下次拉取的时候,这里还要判断音讯队列里最小的偏移量是否等于0。 失常状况下,咱们须要的音讯是在音讯队列里最小的偏移量和最大的偏移量之间,那就间接把数据从commitlog中取出来并返回。 生产端接管broker响应后,就会依据下面各个状况的偏移量进行更新,并且把音讯存入PullRequest的processQueue中。 并且再把PullRequest放入pullRequestQueue队列中,期待下次拉取。 processQueue的音讯如果来不及生产,会始终的沉积,所以PullRequest在拉取音讯的时候,会先判断processQueue里的音讯数量是否曾经超过1000,如果超过了,则以后不拉取,并放入pullRequestQueue队列中,50ms后才能够持续拉取。 此外还会判断processQueue中音讯的大小、processQueue中队列最大偏移量与最小偏离量的间距,如果超过了阈值,也会放入pullRequestQueue队列中,50ms后才能够持续拉取。

April 26, 2022 · 1 min · jiezi

关于rocketmq:RocketMQ-写在消息拉取前

消费者生产的时候,须要指定去生产哪个Topic的音讯,比方TopicTest。 和生产者一样,消费者也须要去NameServer拉取TopicTest对应的元数据信息。 如上图所示,TopicTest对应这两个broker,每个broker都有两个MessageQueue,那消费者从哪里拉取数据呢? 比方咱们有两个消费者,消费者在启动的时候,会向每个broker发送心跳包,这样每一个broker都有消费者的信息,咱们也能够从任意一个broker获取所有消费者的信息。 既然有了元数据信息,还有消费者的信息,那就能够通过调配算法,晓得以后这个消费者去哪个MessageQueue拉取数据。 目前RocketMQ默认提供6种调配算法,包含平均分配(AllocateMessageQueueAveragely)、均匀轮询调配(AllocateMessageQueueAveragelyByCircle)、一致性hash(AllocateMessageQueueConsistentHash)、依据配置(AllocateMessageQueueByConfig)、依据Broker部署机房名(AllocateMessageQueueByMachineRoom)、就近机房模式(AllocateMachineRoomNearby)。 默认是平均分配,比方有4个MessageQueue,那两个消费者各2个。消费者1调配的是MessageQueue0和MessageQueue1,消费者2调配MessageQueue2和MessageQueue3。 如果是均匀轮询调配,那消费者1调配1个,消费者2调配1个,消费者1再调配一个,消费者2再调配一个,最终消费者1调配的是MessageQueue0和MessageQueue2,消费者2调配MessageQueue1和MessageQueue3。 咱们接着平均分配进行往下说,以消费者1为例。 此时曾经晓得消费者从MessageQueue0和MessageQueue1来生产,那从哪里开始生产呢? 生产的模式又分为从队列最新偏移量开始生产(CONSUME_FROM_LAST_OFFSET)、从头开始生产(CONSUME_FROM_FIRST_OFFSET)、从消费者启动的工夫戳对应的生产进度开始生产(CONSUME_FROM_TIMESTAMP)。 依据以上的信息,咱们就能够整合一个申请信息PullRequest,包含生产组、偏移量、MessageQueue、ProcessQueue(这个前面来解释),寄存在pullRequestQueue链表里。

March 28, 2022 · 1 min · jiezi

关于rocketmq:RocketMQ-消费者启动流程

消费者在发送音讯的时候,有一个分组的概念。也就是说,有一个或者多个消费者的名称,是同一个,这些名称一样的消费者,组成了一个生产组。 比方下图中,两个group_a是一个生产组,两个group_b是一个生产组。 当broker有音讯a进来的时候,那这个音讯都会同时被这两个生产组所生产。 咱们也看到了每个生产组是有多个消费者的,如果音讯被group_a生产的时候,两个消费者是怎么生产的呢? 这里就波及到了集群模式与播送模式两种生产模式。 在集群模式下,这个音讯只能被生产组内一个消费者进行生产。比方group_a是集群模式,那此时只能一个消费者进行生产。 在播送模式下,这个音讯会被生产组下的每一个消费者进行生产比方group_b是播送模式,那此时两个消费者都进行生产。 生产组和生产模式这么重要,所以消费者启动的时候,就须要去校验生产组和生产模式。 构建Topic订阅信息,比方这个消费者须要订阅TopicTest的音讯,就是在这里构建的。这个步骤中,Topic订阅信息是一个map,所以会存在多个的。 根底信息筹备好了,就开始初始化MQClientInstance、RebalanceImple(音讯从新负载实现类)。 初始化音讯进度,这里依据生产模式会有两种状况: 一个是集群模式,因为生产组的几个消费者只能一个进行生产,如果这个消费者挂了,那另外一个消费者须要从之前的地位开始生产,所以这个进度不能保留在本地,须要保留在broker中,最终从broker拉取生产进度。 另外一个是集群模式,消费者的音讯本人保护本人的进度,所以这个进度保留在本人的本地,最终从本地拉取生产进度。 如果音讯是程序生产的,那创立一个程序音讯生产服务,如果不是,则创立并发音讯生产服务,这两个前面来讲细节。 创立好后启动相应的音讯生产服务。 启动消费者客户端,这里的启动跟生产者是一样的,包含音讯从新负载的启动等。

March 27, 2022 · 1 min · jiezi

关于rocketmq:RocketMQ-过期文件的删除

在音讯一直的发送给Broker,Broker又一直的把音讯贮存在Commitlog文件、ConsumeQueue文件、IndexFile文件里,磁盘总会有用完的那一刻。 因为Commitlog文件、ConsumeQueue文件都是程序写,也就是说,所有的写操作要么是往最初一个文件里写,要么写满了创立一个新的文件,所以之前的文件都不会被更新,那就能够删除比拟久之前的文件,这些比拟久的文件,就是过期文件。 删除过期文件的时候,须要留神的是,Broker并不会去判断这里的音讯是否曾经生产,反正删就完事了。 在Broker启动的时候,就会启动一个定时工作,提早60s,而后每10s就开始去判断开释要删除过期文件。 对于删除过期的机会包含以下3种: 默认凌晨4点。这个也比拟好了解,这个时候用的人也比拟少,删除对系统的影响就降到最小。磁盘空间有余。当磁盘空间有余的时候,就要删除过期文件以提供更多的空间进去接管音讯。人工触发,指人为的染指去删除。 删除的文件是过期文件,那哪些文件是过期的呢? 首先是保留工夫,默认72小时,也就是3天,超过3天的数据,是须要删除的。 如果这个文件被其余线程援用了,此时就不会进行删除,记录第一次删除的工夫戳,退出本次工作,等120s后,就会把文件的援用减1000,再强制删除。 如果以后删除的文件数量,曾经超过了能够删除的最大批量数,则退出本次工作。 在删除的过程中,会存在删除多个文件的状况,每个文件之间,还有一个工夫距离,比方第一个文件删除完后,须要等100ms再删除第二个文件。

March 21, 2022 · 1 min · jiezi

关于rocketmq:RocketMQ-文件不一致的解决方案

后面提到了ConsumeQueue和IndexFile是通过ReputMessageService线程异步同步commitlog日志文件信息的,如果Commitlog日志文件写入胜利后,Broker宕机,那就没方法进行同步了,导致Commitlog、ConsumeQueue、IndexFile文件数据不统一。那RocketMQ是怎么解决的呢? stroe目录下的有一个文件,叫abort。 这个abort文件,在Broker启动的时候,会创立,如果是失常退出的话,那就会销毁。 所以失常状况下,abort文件只会存在运行期,进行的时候,是没有这个文件的。 当Broker启动的时候,如果没有abort文件,阐明是失常退出。如果发现存在abort文件,那就是之前是异样退出的,可能Commitlog、ConsumeQueue、IndexFile文件呈现数据不统一的状况,此时就须要进行修数。 stroe目录下的还有一个文件,叫checkpoint(同abort图),存储着Commitlog文件刷盘工夫点、ConsumeQueue文件刷盘工夫点、IndexFile文件刷盘工夫点,这些信息用于异样退出时的判断解决。 加载IndexFile的时候,如果是异样退出,IndexFile文件刷盘工夫点小于该索引文件最大的音讯工夫戳该文件将立刻销毁。 对于ConsumeQueue文件来说,文件复原机制又分为异样进行、失常进行。异样进行就是用abort文件来判断。 如果是失常进行,从倒数第三个开始复原,有余三个的,间接第一个遍历,找到以后Commitlog日志文件的刷新指针和提交指针,并删除ConsumeQueue文件中这个指针后的所有文件。 剩下的就是通过偏移量,通过转发音讯复原ConsumeQueue文件和IndexFile文件的内容,这个步骤同上一篇。 假如有4个Commitlog日志文件,倒数第三个就是2,而后从2开始查找。因为是失常进行的,所以预留3个Commitlog日志文件来校验是不会出问题的。 如果是异样进行,间接读取最初一个文件,而后向前遍历到第一个正确存储的文件,剩下的步骤就和失常进行的差不多。 同样假如是4个Commitlog日志文件,因为是异样退出的,所以不晓得在哪里呈现问题,就只能从最初一个开始往前面遍历查找,直至找到正确的文件。

March 20, 2022 · 1 min · jiezi

关于rocketmq:RocketMQ-消息消费队列与索引文件

在store目录下,除了commilog目录,还有consumequeue和index目录。 consumequeue是音讯生产队列存储目录,比方咱们建了一个TopicTest,有四个MessageQueue,那在consumequeue目录下,就有一个TopicTest目录,TopicTest目录下还有0,1,2,3四个目录,对应着MessageQueue的个数。这些数字的上面的文件,就是实际上的数据。 后面曾经讲了音讯都是落在了commitlog日志文件中,咱们生产的时候,却是依据topic来的,如果须要一个个的从commitlog日志文件中遍历某一个topic,那这个效率就十分低下了,所以就有了一个ConsumeQueue来记录每一个topic在commitlog的地位。 另外一个index是音讯索引文件存储目录,通过Hash索引机制为音讯建设索引,RocketMQ会将音讯索引键与音讯偏移量映射关系写入到IndexFile。 既然ConsumeQueue和IndexFile都是关联着commilog日志文件,那咱们写入commitlog日志文件的时候,ConsumeQueue和IndexFile是什么时候写入的呢? broker启动的时候,还会启动一个线程,叫做ReputMessageService,负责把commitlog日志的更新事件流传进来,他记录commitlog从哪个偏移量开始转发音讯给ConsumeQueue和IndexFile。 每隔1ms,这个线程就会通过偏移量去commitlog日志文件里看看是否有新的音讯进来,如果有,就会把音讯进行转发,因为须要写入ConsumeQueue和IndexFile,所以这里就有两个转发器。 CommitLogDispatcherBuildConsumeQueue会依据topic以及队列的ID,获取对应的ConsumeQueue文件(能够参考下面的目录树),把数据写入其中。 CommitLogDispatcherBuildIndex则会构建索引键,再写入数据。

March 20, 2022 · 1 min · jiezi

关于rocketmq:RocketMQ-刷盘机制

下面讲了音讯到内存的过程,为了音讯不失落,是须要长久化到磁盘上的,这里又分为同步刷屏和异步刷屏。 在内存里解决后,就开始开释锁,解决完的后果叫result。所以到了刷屏的阶段,可能会有一个或者多个的result须要解决。 同步刷盘同步刷盘的服务,也是一个线程,专门用来解决这些result。他保护着requestsWrite和requestsRead的链表。requestsWrite是放须要解决的result,requestsRead是放筹备解决的result。 每个result须要同步刷盘服务解决的时候,都会放入requestsWrite,因为requestsWrite并不是线程平安的,所以这里也须要加锁和开释锁的操作。 下面提过,这个服务也是一个线程,这个线程就是用来解决requestsRead的。 这个线程每解决完requestsRead就会期待最多10ms的工夫,直至被从新唤醒。 所以requestsWrite有数据的时候,就会唤醒这个线程,这个线程就会把requestsWrite和requestsRead里的数据进行替换。 因为写入requestsWrite是并发的,所以替换的时候,可能会有多个result到requestsRead里。 替换完后,就开始把requestsRead里的数据进行落盘,这个线程并不影响requestsWrite的写入。 异步刷盘异步刷盘,简略的说,就是不等落盘就间接返回。 同样的,异步刷盘也有一个线程的服务,每隔一段时间就开始进行刷盘。然而这里也有一个小区别,就是是否开启堆外内存机制。 如果没有开启,那音讯是追加到与物理文件映射的内存中,而后写入到磁盘。 如果开启了,那会创立一个一个commitlog一样大小的堆外内存,所以音讯会追加到这个堆外内存中,而后是提交到与物理文件映射的内存中,最初写入到磁盘。

March 19, 2022 · 1 min · jiezi

关于rocketmq:RocketMQ-消息发送存储流程

后面曾经讲了音讯是怎么发送给broker的,那broker接管到音讯后,是怎么解决的? 为了保障音讯不失落,RocketMQ会把音讯进行长久化,也就是说,会把音讯写入commitlog的日志,这个目录是在store上面。每个日志大小为1G,所以commitlog会有多个磁盘文件,每个文件名是音讯的物理偏移量。 当broker接管到音讯的时候,首先会进行一些判断,比方只能master能够写入,Topic的长度限度为256个字符,音讯属性长度限度为65536个字符等。 等验证通过后,开始进行写数据,因为可能存在并发问题,所以每次写的时候,都须要申请锁。 申请到锁后,开始往commitlog里写数据,如果是刚开始写入日志文件的时候,此时commitlog并没有文件,所以就会创立一个大小为1G名字为00000000000000000000的日志文件。如果曾经存在多个日志文件,那间接取最初一个日志文件,因为日志文件写完才会创立一个新的日志文件,那最初一个日志文件就是以后须要写入的。 拿到日志文件后,咱们须要将音讯追加到这个文件里,此时须要晓得以后文件的写指针,如果是刚创立的文件,那写指针就是0。 这个写指针是不能大于文件的大小,如果超过了,阐明文件已写满,那是不能往里面写数据的。 另外一个还须要判断的是,以后音讯是否够放这个文件,也就写指针+音讯的长度(mq还会预留其余空间),是否会超过这个文件的大小,如果不会,就写入这个文件,如果会,那就会创立一个新的文件进行写入。 写入后,就更新下面的写指针,也就是说,最初的写指针就是以后写指针+音讯的大小。最初开释锁,让其余线程进行写入。 其实到了这一个步骤,音讯并没有写到磁盘上,还只是追加到内存,后续再来提刷屏的过程。

March 19, 2022 · 1 min · jiezi

关于rocketmq:RocketMQ-MessageQueue是怎么指定的

比照kafka -- 分区是怎么指定的 kafka是有Partition的概率,发送音讯的时候,跟进分区规定进行抉择哪个Partition,每个Partition都有本人对应的broker。 RocketMQ有MessageQueue,相似于Partition,发送音讯的时候,也是先抉择MessageQueue,而后依据MessageQueue对应的broker信息发送给对应的broker。 MessageQueue抉择有两种,启用Broker故障提早机制不启用Broker故障提早机制,默认不启用。 不启用Broker故障提早机制刚开始的时候,会有一个随机值,比方5,而后对MessageQueue的个数进行取模,为1,而后就对第二个MessageQueue的broker进行发送数据。 下一个音讯,这个随机值就会加1,取模后为2,对第三个MessageQueue的broker进行发送数据。 简略的说,就是轮询。 因为broker可能会呈现问题,比方MessageQueue-0对应的broker为broker-a,MessageQueue-1也是broker-a。 那发送给MessageQueue-0的broker-a失败的时候,会把broker-a记录下来,重试的时候,会持续迭代,此时是MessageQueue-1,broker也是broker-a,那就会往下轮询,去找MessageQueue-2,通过MessageQueue-2进行发送音讯。 这样的机制,就会防止重试到同一个broker,减低失败的可能性。 启用Broker故障提早机制MQ发送音讯的时候,会记录这个音讯发送的工夫,如果broker-a是50毫秒,那下次发送就能够间接发,如果是550毫秒,那须要等30秒后才能够发给broker-a,规定同下表。 那比方是800毫秒呢,因为是介于550到1000间接,所以算550这个档,也就是提早30秒。 如果是broker故障等导致失败,则会间接当作3万毫秒,相当于600秒内不能发送这个broker。 在这种机制下,每次发送音讯的时候,也是通过轮询的,然而轮询拿出一个MessageQueue,就会去判断这个broker-a是否在延迟时间内,如果还须要期待,那就持续轮询下一个MessageQueue。 如果都在延迟时间内,那就找一个绝对可用的,如果也没有绝对可用的,那就持续轮询,间接返回对应的MessageQueue。

March 2, 2022 · 1 min · jiezi

关于rocketmq:RocketMQ-元数据的拉取

拉取的机会和Kafka -- 元数据的拉取机会的相似,分为发消息的时候以及定时工作。 拉取的流程获取topic的时候,先从缓存获取是否有这个topic对应的路由数据,如果有,就间接返回。 如果本地缓存,那须要通过网络申请(Netty)去NameServer获取topic的路由数据。 如果从NameServer拿到topic信息的话,就会把这个topic数据寄存在缓存中,以供下次间接取用,防止频繁的拉取。 在放入缓存的时候,还会跟缓存里的数据进行比照,如果跟缓存里不统一,就会更新broker的地址以及MessageQueue(这个前面讲)。 和kafka比照Kafka -- 元数据的拉取流程 共同点按需拉取。也就是说,须要哪些topic的信息,就拉取这些topic。所以在定时工作触发拉取元数据音讯的时候,会读取内存里有哪些topic信息,依据这些topic的信息去拉取元数据。先判断内存里是否有topic信息,如果有,间接用,如果没有或者不可用去服务端拉取并更新内存。不同点kafka能够批量拉取topic的信息,RocketMQ是一个个topic去拉,性能上比kakfa低。kafka的发送音讯、拉取元数据是有专门的线程来解决的(发送音讯也是这个线程),因为是异步的,所以用休眠-唤醒的模式来告诉拉取后果。RocketMQ是以后线程解决,会有多线程须要拉取topic信息的状况,须要加锁。kafka会对5分钟内不必的topic进行删除,RoeketMQ如同没有??

March 2, 2022 · 1 min · jiezi

关于rocketmq:聊聊rocketmqstreams的ILeaseService

序本文次要钻研一下rocketmq-streams的ILeaseService ILeaseService/** * 通过db实现租约和锁,能够更轻量级,缩小其余中间件的依赖 应用主备场景,只有一个实例运行,当以后实例挂掉,在肯定工夫内,会被其余实例接手 也能够用于全局锁 */public interface ILeaseService { /** * 默认锁定工夫 */ static final int DEFALUT_LOCK_TIME = 60 * 5; /** * 查看某用户以后工夫是否具备租约。这个办法是纯内存操作,无性能开销 * * @return true,租约无效;false,租约有效 */ boolean hasLease(String name); /** * 申请租约,会启动一个线程,不停申请租约,直到申请胜利。 申请胜利后,每 租期/2 续约。 如果目前被其余租户获取租约,只有在对方租约生效,后才容许新的租户获取租约 * * @param name 租约名称,无特殊要求,雷同名称会竞争租约 */ void startLeaseTask(String name); /** * 申请租约,会启动一个线程,不停申请租约,直到申请胜利。 申请胜利后,每 租期/2 续约。 如果目前被其余租户获取租约,只有在对方租约生效,后才容许新的租户获取租约 * * @param name 租约名称,无特殊要求,雷同名称会竞争租约 * @param callback 当第一获取租约时,回调此函数 */ void startLeaseTask(final String name, ILeaseGetCallback callback); /** * 申请租约,会启动一个线程,不停申请租约,直到申请胜利。 申请胜利后,每 租期/2 续约。 如果目前被其余租户获取租约,只有在对方租约生效,后才容许新的租户获取租约 * * @param name 租约名称,无特殊要求,雷同名称会竞争租约 * @param leaseTermSecond 租期,在租期内能够做业务解决,单位是秒 * @param callback 当第一获取租约时,回调此函数 */ void startLeaseTask(final String name, int leaseTermSecond, ILeaseGetCallback callback); /** * 申请锁,无论胜利与否,立即返回。如果不开释,最大锁定工夫是5分钟 * * @param name 业务名称 * @param lockerName 锁名称 * @return 是否桎梏胜利 */ boolean lock(String name, String lockerName); /** * 申请锁,无论胜利与否,立即返回。默认锁定工夫是5分钟 * * @param name 业务名称 * @param lockerName 锁名称 * @param lockTimeSecond 如果不开释,锁定的最大工夫,单位是秒 * @return 是否桎梏胜利 * @return */ boolean lock(String name, String lockerName, int lockTimeSecond); /** * 申请锁,如果没有则期待,等待时间能够指定,如果是-1 则有限期待。如果不开释,最大锁定工夫是5分钟 * * @param name 业务名称 * @param lockerName 锁名称 * @param waitTime 没获取锁时,最大期待多长时间,如果是-1 则有限期待 * @return 是否桎梏胜利 */ boolean tryLocker(String name, String lockerName, long waitTime); /** * 申请锁,如果没有则期待,等待时间能够指定,如果是-1 则有限期待。如果不开释,最大锁定工夫是lockTimeSecond * * @param name 业务名称 * @param lockerName 锁名称 * @param waitTime 没获取锁时,最大期待多长时间,如果是-1 则有限期待 * @param lockTimeSecond 如果不开释,锁定的最大工夫,单位是秒 * @return 是否桎梏胜利 */ boolean tryLocker(String name, String lockerName, long waitTime, int lockTimeSecond); /** * 开释锁 * * @param name * @param lockerName * @return */ boolean unlock(String name, String lockerName); /** * 对于曾经获取锁的,能够通过这个办法,始终持有锁。 和租约的区别是,当开释锁后,无其余实例抢占。无奈实现主备模式 * * @param name 业务名称 * @param lockerName 锁名称 * @param lockTimeSecond 租期,这个办法会主动续约,如果不被动开释,会始终持有锁 * @return 是否胜利获取锁 */ boolean holdLock(String name, String lockerName, int lockTimeSecond); /** * 是否持有锁,不会申请锁。如果以前申请过,且未过期,返回true,否则返回false * * @param name 业务名称 * @param lockerName 锁名称 * @return */ boolean hasHoldLock(String name, String lockerName); List<LeaseInfo> queryLockedInstanceByNamePrefix(String name, String lockerNamePrefix);}ILeaseService接口定义了hasLease、startLeaseTask、lock、tryLocker、unlock、holdLock、hasHoldLock、queryLockedInstanceByNamePrefix办法BasedLesaseImplpublic abstract class BasedLesaseImpl implements ILeaseService { private static final Log LOG = LogFactory.getLog(BasedLesaseImpl.class); private static final String CONSISTENT_HASH_PREFIX = "consistent_hash_"; private static final AtomicBoolean syncStart = new AtomicBoolean(false); private static final int synTime = 120; // 5分钟的一致性hash同步工夫太久了,改为2分钟 protected ScheduledExecutorService taskExecutor = null; protected int leaseTerm = 300 * 2; // 租约工夫 // protected transient JDBCDriver jdbcDataSource = null; protected ILeaseStorage leaseStorage; protected volatile Map<String, Date> leaseName2Date = new ConcurrentHashMap<>(); // 每个lease name对应的租约到期工夫 public BasedLesaseImpl() { taskExecutor = new ScheduledThreadPoolExecutor(10); } /** * lease_name: consistent_hash_ip, lease_user_ip: ip,定时刷新lease_info表,查看一致性hash环的节点状况 * * @param name * @return */ @Override public boolean hasLease(String name) { // 内存中没有租约信息则示意 没有租约 Date leaseEndTime = leaseName2Date.get(name); if (leaseEndTime == null) { // LOG.info("内存中依据 " + name + "没有查问到租约信息,示意没有租约"); return false; } // LOG.info("查问是否有租约 name:" + name + " ,以后工夫:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) // + " 租约到期工夫 " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(leaseEndTime)); // 有租约工夫,并且租约工夫大于以后工夫,示意有租约信息 if (new Date().before(leaseEndTime)) { return true; } return false; } private final Map<String, AtomicBoolean> startLeaseMap = new HashMap<>(); @Override public void startLeaseTask(final String name) { startLeaseTask(name, this.leaseTerm, null); } @Override public void startLeaseTask(final String name, ILeaseGetCallback callback) { startLeaseTask(name, this.leaseTerm, callback); } @Override public void startLeaseTask(final String name, int leaseTerm, ILeaseGetCallback callback) { ApplyTask applyTask = new ApplyTask(leaseTerm, name, callback); startLeaseTask(name, applyTask, leaseTerm / 2, true); } /** * 启动定时器,定时执行工作,确保工作可重入 * * @param name * @param runnable 具体任务 * @param scheduleTime 调度工夫 * @param startNow 是否立即启动一次 */ protected void startLeaseTask(final String name, Runnable runnable, int scheduleTime, boolean startNow) { AtomicBoolean isStartLease = startLeaseMap.get(name);//屡次调用,只启动一次定时工作 if (isStartLease == null) { synchronized (this) { isStartLease = startLeaseMap.get(name); if (isStartLease == null) { isStartLease = new AtomicBoolean(false); startLeaseMap.put(name, isStartLease); } } } if (isStartLease.compareAndSet(false, true)) { if (startNow) { runnable.run(); } taskExecutor.scheduleWithFixedDelay(runnable, 0, scheduleTime, TimeUnit.SECONDS); } } //......}BasedLesaseImpl申明实现了ILeaseService,它依赖ILeaseStorage,startLeaseTask办法会创立ApplyTask,而后以固定距离调度执行ApplyTask /** * 续约工作 */ protected class ApplyTask implements Runnable { protected String name; protected int leaseTerm; protected ILeaseGetCallback callback; public ApplyTask(int leaseTerm, String name) { this(leaseTerm, name, null); } public ApplyTask(int leaseTerm, String name, ILeaseGetCallback callback) { this.name = name; this.leaseTerm = leaseTerm; this.callback = callback; } @Override public void run() { try { // LOG.info("LeaseServiceImpl name: " + name + "开始获取租约..."); AtomicBoolean newApplyLease = new AtomicBoolean(false); Date leaseDate = applyLeaseTask(leaseTerm, name, newApplyLease); if (leaseDate != null) { leaseName2Date.put(name, leaseDate); LOG.info("LeaseServiceImpl, name: " + name + " " + getSelfUser() + " 获取租约胜利, 租约到期工夫为 " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(leaseDate)); } else { // fix.2020.08.13 这时name对应的租约可能还在有效期内,或者本机还持有租约,须要remove // leaseName2Date.remove(name); LOG.info("LeaseServiceImpl name: " + name + " " + getSelfUser() + " 获取租约失败 "); } if (newApplyLease.get() && callback != null) { callback.callback(leaseDate); } } catch (Exception e) { LOG.error(" LeaseServiceImpl name: " + name + " " + getSelfUser() + " 获取租约出现异常 ", e); } } } /** * 申请租约,如果当期租约无效,间接更新一个租约周期,如果以后租约有效,先查问是否有无效的租约,如果有申请失败,否则间接申请租约 */ protected Date applyLeaseTask(int leaseTerm, String name, AtomicBoolean newApplyLease) { // 计算下一次租约工夫 = 以后工夫 + 租约时长 Date nextLeaseDate = DateUtil.addSecond(new Date(), leaseTerm); // 1 如果曾经有租约,则更新租约工夫(内存和数据库)即可 if (hasLease(name)) { // LOG.info("用户已有租约,更新数据库和内存中的租约信息"); // 更新数据库 LeaseInfo leaseInfo = queryValidateLease(name); if (leaseInfo == null) { LOG.error("LeaseServiceImpl applyLeaseTask leaseInfo is null"); return null; } // fix.2020.08.13,与本机ip相等且满足一致性hash调配策略,才续约,其余状况为null String leaseUserIp = leaseInfo.getLeaseUserIp(); if (!leaseUserIp.equals(getSelfUser())) { return null; } leaseInfo.setLeaseEndDate(nextLeaseDate); updateLeaseInfo(leaseInfo); return nextLeaseDate; } // 2 没有租约状况 判断是否能够获取租约,只有租约没有被其他人获取,则阐明有无效租约 boolean success = canGetLease(name); if (!success) { // 示意被其余机器获取到了无效的租约 // LOG.info("其余机器获取到了无效的租约"); return null; } // 3 没有租约而且能够获取租约的状况,则尝试应用数据库原子更新的形式获取租约,保障只有一台机器胜利获取租约,而且能够运行 boolean flag = tryGetLease(name, nextLeaseDate); if (flag) { // 获取租约胜利 newApplyLease.set(true); return nextLeaseDate; } return null; } ApplyTask外部调用applyLeaseTask,如果已有租约则更新租约工夫,没有租约则判断是否能够获取租约,能够则执行tryGetLeasetryGetLease /** * 更新数据库,占用租期并更新租期工夫 * * @param time */ protected boolean tryGetLease(String name, Date time) { // LOG.info("尝试获取租约 lease name is : " + name + " 下次到期工夫: " // + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(time)); LeaseInfo validateLeaseInfo = queryValidateLease(name); if (validateLeaseInfo == null) {// 这里有两种状况 1 数据库外面没有租约信息 2 数据库外面有租约信息然而曾经过期 Integer count = countLeaseInfo(name); if (count == null || count == 0) {// 示意当初数据库外面没有任何租约信息,插入租约胜利则示意获取胜利,失败示意在这一时刻其余机器获取了租约 // LOG.info("数据库中临时没有租约信息,尝试原子插入租约:" + name); // fix.2020.08.13,通过一致性hash计算,该名字的工作不应该在本机执行,间接返回,无需插入。只有调配到hash执行权限的机器才能够插入并获取租约 if (!getSelfUser().equals(getConsistentHashHost(name))) { return false; } validateLeaseInfo = new LeaseInfo(); validateLeaseInfo.setLeaseName(name); validateLeaseInfo.setLeaseUserIp(getSelfUser()); validateLeaseInfo.setLeaseEndDate(time); validateLeaseInfo.setStatus(1); validateLeaseInfo.setVersion(1); if (insert(validateLeaseInfo)) { LOG.info("数据库中临时没有租约信息,原子插入胜利,获取租约胜利:" + name); return true; } else { LOG.info("数据库中临时没有租约信息,原子插入失败,曾经被其余机器获取租约:" + name); return false; } } else { // 示意数据库外面有一条然而有效,这里须要两台机器依照version进行原子更新,更新胜利的获取租约 // LOG.info("数据库中有一条有效的租约信息,尝试依据版本号去原子更新租约信息:" + name); LeaseInfo inValidateLeaseInfo = queryInValidateLease(name); if (inValidateLeaseInfo == null) {// 阐明这个时候另外一台机器获取胜利了 LOG.info("另外一台机器获取胜利了租约:" + name); return false; } // fix.2020.08.13,机器重启之后,该名字的工作曾经不调配在此机器上执行,间接返回,无需更新数据库 if (!getSelfUser().equals(getConsistentHashHost(name))) { return false; } inValidateLeaseInfo.setLeaseName(name); inValidateLeaseInfo.setLeaseUserIp(getSelfUser()); inValidateLeaseInfo.setLeaseEndDate(time); inValidateLeaseInfo.setStatus(1); boolean success = updateDBLeaseInfo(inValidateLeaseInfo); if (success) { LOG.info("LeaseServiceImpl 原子更新租约胜利,以后机器获取到了租约信息:" + name); } else { LOG.info("LeaseServiceImpl 原子更新租约失败,租约被其余机器获取:" + name); } return success; } } else { // 判断是否是本人获取了租约,如果是本人获取了租约则更新工夫(内存和数据库), // 这里是为了解决机器重启的状况,机器重启,内存中没有租约信息,然而实际上该用户是有租约权限的 // fix.2020.08.13,租约的ip与本机ip相等,且满足一致性hash策略,才会被本机执行 String leaseUserIp = validateLeaseInfo.getLeaseUserIp(); if (leaseUserIp.equals(getSelfUser())) { // 如果当期用户有租约信息,则更新数据库 validateLeaseInfo.setLeaseEndDate(time); boolean hasUpdate = updateLeaseInfo(validateLeaseInfo); if (hasUpdate) { LOG.info( "LeaseServiceImpl机器重启状况,以后用户有租约信息,并且更新数据库胜利,租约信息为 name :" + validateLeaseInfo.getLeaseName() + " ip : " + validateLeaseInfo.getLeaseUserIp() + " 到期工夫 : " + new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss").format(validateLeaseInfo.getLeaseEndDate())); return true; } else { LOG.info("LeaseServiceImpl 机器重启状况,以后用户有租约信息,并且更新数据库失败,示意失去租约:" + name); return false; } } // LOG.info("LeaseServiceImpl 租约被其余机器获取,租约信息为 name :" + validateLeaseInfo.getLeaseName() + " ip : " // + validateLeaseInfo.getLeaseUserIp() + " 到期工夫 : " // + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(validateLeaseInfo.getLeaseEndDate())); return false; } } protected LeaseInfo queryValidateLease(String name) { //String sql = "SELECT * FROM lease_info WHERE lease_name ='" + name + "' and status=1 and lease_end_time>now()"; //// LOG.info("LeaseServiceImpl query validate lease sql:" + sql); //return queryLease(name, sql); return leaseStorage.queryValidateLease(name); } tryGetLease先通过queryValidateLease查问租约信息,若没有租约则插入,若过期则依据版本号更新,若已有租约则判断是否是本人获取了租约,是则更新租约信息LeaseServiceImplpublic class LeaseServiceImpl extends BasedLesaseImpl { private static final Log LOG = LogFactory.getLog(LeaseServiceImpl.class); private transient ConcurrentHashMap<String, HoldLockTask> holdLockTasks = new ConcurrentHashMap(); protected ConcurrentHashMap<String, HoldLockFunture> seizeLockingFuntures = new ConcurrentHashMap<>(); //如果是抢占锁状态中,则不容许申请锁 public LeaseServiceImpl() { super(); } /** * 尝试获取锁,能够期待waitTime,如果到点未返回,则间接返回。如果是-1,则始终期待 * * @param name 业务名称 * @param lockerName 锁名称 * @param waitTime 等待时间,是微秒单位 * @return */ @Override public boolean tryLocker(String name, String lockerName, long waitTime) { return tryLocker(name, lockerName, waitTime, ILeaseService.DEFALUT_LOCK_TIME); } @Override public boolean tryLocker(String name, String lockerName, long waitTime, int lockTimeSecond) { long now = System.currentTimeMillis(); boolean success = lock(name, lockerName, lockTimeSecond); while (!success) { if (waitTime > -1 && (System.currentTimeMillis() - now > waitTime)) { break; } success = lock(name, lockerName, lockTimeSecond); if (success) { return success; } try { Thread.sleep(100); } catch (InterruptedException e) { LOG.error("LeaseServiceImpl try locker error", e); } } return success; } @Override public boolean lock(String name, String lockerName) { return lock(name, lockerName, ILeaseService.DEFALUT_LOCK_TIME); } @Override public boolean lock(String name, String lockerName, int leaseSecond) { lockerName = createLockName(name, lockerName); Future future = seizeLockingFuntures.get(lockerName); if (future != null && ((HoldLockFunture)future).isDone == false) { return false; } Date nextLeaseDate = DateUtil.addSecond(new Date(), leaseSecond);// 默认锁定5分钟,用完须要立即开释.如果工夫不同步,可能导致锁失败 return tryGetLease(lockerName, nextLeaseDate); } @Override public boolean unlock(String name, String lockerName) { // LOG.info("LeaseServiceImpl unlock,name:" + name); lockerName = createLockName(name, lockerName); LeaseInfo validateLeaseInfo = queryValidateLease(lockerName); if (validateLeaseInfo == null) { LOG.warn("LeaseServiceImpl unlock,validateLeaseInfo is null,lockerName:" + lockerName); } if (validateLeaseInfo != null && validateLeaseInfo.getLeaseUserIp().equals(getSelfUser())) { validateLeaseInfo.setStatus(0); updateDBLeaseInfo(validateLeaseInfo); } HoldLockTask holdLockTask = holdLockTasks.remove(lockerName); if (holdLockTask != null) { holdLockTask.close(); } leaseName2Date.remove(lockerName); return false; } /** * 如果有锁,则始终持有,如果不能获取,则完结。和租约不同,租约是没有也会尝试重试,一备对方挂机,本人能够接手工作 * * @param name * @param secondeName * @param lockTimeSecond 获取锁的工夫 * @return */ @Override public boolean holdLock(String name, String secondeName, int lockTimeSecond) { if (hasHoldLock(name, secondeName)) { return true; } synchronized (this) { if (hasHoldLock(name, secondeName)) { return true; } String lockerName = createLockName(name, secondeName); Date nextLeaseDate = DateUtil.addSecond(new Date(), lockTimeSecond); boolean success = tryGetLease(lockerName, nextLeaseDate);// 申请锁,锁的工夫是leaseTerm if (!success) { return false; } leaseName2Date.put(lockerName, nextLeaseDate); if (!holdLockTasks.containsKey(lockerName)) { HoldLockTask holdLockTask = new HoldLockTask(lockTimeSecond, lockerName, this); holdLockTask.start(); holdLockTasks.putIfAbsent(lockerName, holdLockTask); } } return true; } /** * 是否持有锁,不拜访数据库,间接看本地 * * @param name * @param secondeName * @return */ @Override public boolean hasHoldLock(String name, String secondeName) { String lockerName = createLockName(name, secondeName); return hasLease(lockerName); } @Override public List<LeaseInfo> queryLockedInstanceByNamePrefix(String name, String lockerNamePrefix) { String leaseNamePrefix = MapKeyUtil.createKey(name, lockerNamePrefix); return queryValidateLeaseByNamePrefix(leaseNamePrefix); } //......}LeaseServiceImpl继承了BasedLesaseImpl,tryLocker办法会依据等待时间循环执行lock,lock办法则执行tryGetLease,unlock办法则更新租约信息,同时移除内存记录;holdLock则通过hasHoldLock判断是否持有锁,若有则返回,没有则执行tryGetLeaseILeaseStoragepublic interface ILeaseStorage { /** * 更新lease info,须要是原子操作,存储保障多线程操作的原子性 * * @param leaseInfo 租约表数据 * @return */ boolean updateLeaseInfo(LeaseInfo leaseInfo); /** * 统计这个租约名称下,LeaseInfo对象个数 * * @param leaseName 租约名称,无特殊要求,雷同名称会竞争租约 * @return */ Integer countLeaseInfo(String leaseName); /** * 查问有效的的租约 * * @param leaseName 租约名称,无特殊要求,雷同名称会竞争租约 * @return */ LeaseInfo queryInValidateLease(String leaseName); /** * 查问无效的的租约 * * @param leaseName 租约名称,无特殊要求,雷同名称会竞争租约 * @return */ LeaseInfo queryValidateLease(String leaseName); /** * 按前缀查问无效的租约信息 * * @param namePrefix * @return */ List<LeaseInfo> queryValidateLeaseByNamePrefix(String namePrefix); /** * 减少租约 * * @param leaseInfo 租约名称,无特殊要求,雷同名称会竞争租约 */ void addLeaseInfo(LeaseInfo leaseInfo);}ILeaseStorage接口定义了updateLeaseInfo、countLeaseInfo、queryInValidateLease、queryValidateLease、queryValidateLeaseByNamePrefix、addLeaseInfo办法DBLeaseStoragepublic class DBLeaseStorage implements ILeaseStorage { private static final Log LOG = LogFactory.getLog(DBLeaseStorage.class); protected JDBCDriver jdbcDataSource; private String url; protected String userName; protected String password; protected String jdbc; public DBLeaseStorage(String jdbc, String url, String userName, String password) { this.jdbc = jdbc; this.url = url; this.userName = userName; this.password = password; jdbcDataSource = DriverBuilder.createDriver(jdbc, url, userName, password); } @Override public boolean updateLeaseInfo(LeaseInfo leaseInfo) { String sql = "UPDATE lease_info SET version=version+1,status=#{status},gmt_modified=now()"; String whereSQL = " WHERE id=#{id} and version=#{version}"; if (StringUtil.isNotEmpty(leaseInfo.getLeaseName())) { sql += ",lease_name=#{leaseName}"; } if (StringUtil.isNotEmpty(leaseInfo.getLeaseUserIp())) { sql += ",lease_user_ip=#{leaseUserIp}"; } if (leaseInfo.getLeaseEndDate() != null) { sql += ",lease_end_time=#{leaseEndDate}"; } sql += whereSQL; sql = SQLUtil.parseIbatisSQL(leaseInfo, sql); try { int count = getOrCreateJDBCDataSource().update(sql); boolean success = count > 0; if (success) { synchronized (this) { leaseInfo.setVersion(leaseInfo.getVersion() + 1); } } else { System.out.println(count); } return success; } catch (Exception e) { LOG.error("LeaseServiceImpl updateLeaseInfo excuteUpdate error", e); throw new RuntimeException("execute sql error " + sql, e); } } @Override public Integer countLeaseInfo(String leaseName) { String sql = "SELECT count(*) as c FROM lease_info WHERE lease_name = '" + leaseName + "' and status = 1"; try { List<Map<String, Object>> rows = getOrCreateJDBCDataSource().queryForList(sql); if (rows == null || rows.size() == 0) { return null; } Long value = (Long) rows.get(0).get("c"); return value.intValue(); } catch (Exception e) { throw new RuntimeException("execute sql error " + sql, e); } } @Override public LeaseInfo queryInValidateLease(String leaseName) { String sql = "SELECT * FROM lease_info WHERE lease_name ='" + leaseName + "' and status=1 and lease_end_time<'" + DateUtil.getCurrentTimeString() + "'"; LOG.info("LeaseServiceImpl queryInValidateLease builder:" + sql); return queryLease(leaseName, sql); } @Override public LeaseInfo queryValidateLease(String leaseName) { String sql = "SELECT * FROM lease_info WHERE lease_name ='" + leaseName + "' and status=1 and lease_end_time>now()"; return queryLease(leaseName, sql); } @Override public List<LeaseInfo> queryValidateLeaseByNamePrefix(String namePrefix) { String sql = "SELECT * FROM lease_info WHERE lease_name like '" + namePrefix + "%' and status=1 and lease_end_time>now()"; try { List<LeaseInfo> leaseInfos = new ArrayList<>(); List<Map<String, Object>> rows = getOrCreateJDBCDataSource().queryForList(sql); if (rows == null || rows.size() == 0) { return null; } for (Map<String, Object> row : rows) { LeaseInfo leaseInfo = convert(row); leaseInfos.add(leaseInfo); } return leaseInfos; } catch (Exception e) { throw new RuntimeException("execute sql error " + sql, e); } } @Override public void addLeaseInfo(LeaseInfo leaseInfo) { String sql = " REPLACE INTO lease_info(lease_name,lease_user_ip,lease_end_time,status,version,gmt_create,gmt_modified)" + " VALUES (#{leaseName},#{leaseUserIp},#{leaseEndDate},#{status},#{version},now(),now())"; sql = SQLUtil.parseIbatisSQL(leaseInfo, sql); try { getOrCreateJDBCDataSource().execute(sql); } catch (Exception e) { LOG.error("LeaseServiceImpl execute sql error,sql:" + sql, e); throw new RuntimeException("execute sql error " + sql, e); } } protected JDBCDriver getOrCreateJDBCDataSource() { if (this.jdbcDataSource == null || !this.jdbcDataSource.isValidate()) { synchronized (this) { if (this.jdbcDataSource == null || !this.jdbcDataSource.isValidate()) { this.jdbcDataSource = DriverBuilder.createDriver(this.jdbc, this.url, this.userName, this.password); } } } return jdbcDataSource; } protected LeaseInfo queryLease(String name, String sql) { try { List<Map<String, Object>> rows = getOrCreateJDBCDataSource().queryForList(sql); if (rows == null || rows.size() == 0) { return null; } return convert(rows.get(0)); } catch (Exception e) { throw new RuntimeException("execute sql error " + sql, e); } } protected LeaseInfo convert(Map<String, Object> map) { LeaseInfo leaseInfo = new LeaseInfo(); leaseInfo.setId(getMapLongValue("id", map)); leaseInfo.setCreateTime(getMapDateValue("gmt_create", map)); leaseInfo.setLeaseEndDate(getMapDateValue("lease_end_time", map)); leaseInfo.setLeaseName(getMapValue("lease_name", map, String.class)); leaseInfo.setLeaseUserIp(getMapValue("lease_user_ip", map, String.class)); Integer status = getMapValue("status", map, Integer.class); if (status != null) { leaseInfo.setStatus(status); } leaseInfo.setUpdateTime(getMapDateValue("gmt_modified", map)); Long version = getMapLongValue("version", map); if (version != null) { leaseInfo.setVersion(version); } return leaseInfo; } @SuppressWarnings("unchecked") private <T> T getMapValue(String fieldName, Map<String, Object> map, Class<T> integerClass) { Object value = map.get(fieldName); if (value == null) { return null; } return (T) value; } private Long getMapLongValue(String fieldName, Map<String, Object> map) { Object value = map.get(fieldName); if (value == null) { return null; } if (value instanceof Long) { return (Long) value; } if (value instanceof BigInteger) { return ((BigInteger) value).longValue(); } return null; } private Date getMapDateValue(String fieldName, Map<String, Object> map) { Object value = map.get(fieldName); if (value == null) { return null; } if (value instanceof Date) { return (Date) value; } if (value instanceof String) { return DateUtil.parseTime(((String) value)); } return null; }}DBLeaseStorage实现了ILeaseStorage接口,应用jdbc实现了其办法小结rocketmq-streams的LeaseService基于db实现了租约和锁,可用于主备场景切换。 ...

February 21, 2022 · 11 min · jiezi

关于rocketmq:RocketMQ学习十五消费进度提交

音讯拉取形式 通过队列负载机制后,会调配给以后消费者一些队列,留神一个生产组能够订阅多个主题,正如下面 pullRequestQueue 中所示,topic_test、topic_test2 这两个主题都调配了一个队列。轮流从 pullRequestQueue 中取出一个 PullRequest 对象,依据该对象中的拉取偏移量向 Broker 发动拉取申请,默认拉取 32 条,可通过 pullBatchSize 参数进行扭转,该办法不仅会返回音讯列表,还会返更改 PullRequest 对象中的下一次拉取的偏移量。接管到 Broker 返回的音讯后,会首先放入 ProccessQueue(解决队列),该队列的内部结构为 TreeMap,key 寄存的是音讯在音讯生产队列(consumequeue)中的偏移量,而 value 为具体的音讯对象。而后将拉取到的音讯提交到生产组外部的线程池,并立刻返回,并将 PullRequest 对象放入到 pullRequestQueue 中,而后取出下一个 PullRequest 对象持续反复音讯拉取的流程,从这里能够看出,音讯拉取与音讯生产是不同的线程。音讯生产组线程池解决完一条音讯后,会将音讯从 ProccessQueue 中删除,而后会向 Broker 汇报音讯生产进度,以便下次重启时能从上一次生产的地位开始生产。音讯生产进度提交 通过下面拉取音讯流程咱们晓得,音讯生产组线程池在解决完一条音讯后,会将音讯从 ProccessQueue 中移除,并向 Broker 汇报音讯生产进度。 那请大家思考一下上面这个问题: 例如当初解决队列中有 5 条音讯,并且是线程池并发生产,那如果音讯偏移量为 3 的音讯(3:msg3)先于偏移量为 0、1、2 的音讯解决完,那向 Broker 如何汇报音讯生产进度呢?如果提交 msg3 的偏移量为音讯生产进度,那汇报结束后如果消费者产生内存溢出等问题导致 JVM 异样退出,msg1 的音讯还未解决,而后重启消费者,因为音讯生产进度文件中存储的是 msg3 的音讯偏移量,会持续从 msg3 开始生产,会造成音讯失落。RocketMQ 采取的形式是解决完 msg3 之后,会将 msg3 从音讯解决队列中移除,但在向 Broker 汇报音讯生产进度时是取 ProceeQueue 中最小的偏移量为音讯生产进度,即汇报的音讯生产进度是 0。 ...

February 9, 2022 · 1 min · jiezi

关于rocketmq:RocketMQ学习十三消息的PUSH与PULL消费方式

在 RocketMQ里生产形式虽有PUSH与PULL两种,但实现机制实为 PULL 模式,PUSH 模式是一种伪推送,是对 PULL 模式的封装,每拉去一批音讯后,提交到生产端的线程池(异步),而后马上向 Broker 拉取音讯,即实现相似“推”的成果。 在 RocketMQ 中绝大数场景中,通常会抉择应用 PUSH 模式,具体起因下方也会进行阐明。 上面别离介绍下两者的关联。 一,PUSH与PULL别离是什么 PUSH指的是客户端与服务端建设好网络长连贯,当服务端有数据时立刻通过连贯将数据推送给客户端。PULL指的是客户端被动向服务端申请,拉取数据。二,PUSH与PULL的特点 PUSH的一个特点是及时,一旦有数据服务端立刻将数据推送给客户端;对客户端来说比拟敌对,毋庸解决无数据的情景;不过服务端并不知道客户端的解决能力,如果客户端解决能力会造成音讯沉积在客户端的问题。PULL因为是客户端被动去服务端拉取数据,所以不存在音讯沉积问题;但什么时候有数据客户端是无奈感知的,所以拉取工夫距离不好管制,距离长音讯生产不及时;距离短会呈现有效拉取的申请。在PULL模式下为了保障生产的实时性,采起了长轮询音讯服务器拉取音讯的形式,每隔肯定工夫客户端向服务端发动一次申请,如果有数据则取回进行生产,如果服务端没数据客户端线程会阻塞,阻塞工夫为15S,有数据了就会被唤醒。长轮询还是由consumer发动的,因而就算broker端有大量数据也不会被动推送给consumer。对于长轮询的实现在PullRequestHoldService类里。三,PUSH与PULL的实现 先看看PULL应用的一个示例: import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;import org.apache.rocketmq.client.consumer.PullResult;import org.apache.rocketmq.common.message.MessageExt;import org.apache.rocketmq.common.message.MessageQueue;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Set;import java.util.concurrent.CountDownLatch;import java.util.concurrent.TimeUnit;public class PullConsumerTest { public static void main(String[] args) throws Exception { Semaphore semaphore = new Semaphore(); Thread t = new Thread(new Task(semaphore)); t.start(); CountDownLatch cdh = new CountDownLatch(1); try { //程序运行 120s cdh.await(120 * 1000, TimeUnit.MILLISECONDS); } finally { semaphore.running = false; } } /** * 音讯拉取外围实现逻辑 */ static class Task implements Runnable { Semaphore s = new Semaphore(); public Task(Semaphore s ) { this.s = s; } public void run() { try { DefaultMQPullConsumer consumer = new DefaultMQPullConsumer("dw_pull_consumer"); consumer.setNamesrvAddr("127.0.01:9876"); consumer.start(); Map<MessageQueue, Long> offsetTable = new HashMap<MessageQueue, Long>(); Set<MessageQueue> msgQueueList = consumer. fetchSubscribeMessageQueues("TOPIC_TEST"); // 获取该 Topic 的所有队列 if(msgQueueList != null && !msgQueueList.isEmpty()) { boolean noFoundFlag = false; while(this.s.running) { if(noFoundFlag) { // 没有找到音讯,暂停一下生产 Thread.sleep(1000); } for( MessageQueue q : msgQueueList ) { PullResult pullResult = consumer.pull(q, "*", decivedPulloffset(offsetTable , q, consumer) , 3000); System.out.println("pullStatus:" + pullResult.getPullStatus()); switch (pullResult.getPullStatus()) { case FOUND: doSomething(pullResult.getMsgFoundList()); break; case NO_MATCHED_MSG: break; case NO_NEW_MSG: case OFFSET_ILLEGAL: noFoundFlag = true; break; default: continue ; } //提交位点 consumer.updateConsumeOffset(q, pullResult.getNextBeginOffset()); } System.out.println("balacne queue is empty: " + consumer. fetchMessageQueuesInBalance("TOPIC_TEST").isEmpty()); } } else { System.out.println("end,because queue is enmpty"); } consumer.shutdown(); System.out.println("consumer shutdown"); } catch (Throwable e) { e.printStackTrace(); } } } /** 拉取到音讯后具体的解决逻辑 */ private static void doSomething(List<MessageExt> msgs) { System.out.println("本次拉取到的音讯条数:" + msgs.size()); } public static long decivedPulloffset(Map<MessageQueue, Long> offsetTable, MessageQueue queue, DefaultMQPullConsumer consumer) throws Exception { long offset = consumer.fetchConsumeOffset(queue, false); if(offset < 0 ) { offset = 0; } System.out.println("offset:" + offset); return offset; } static class Semaphore { public volatile boolean running = true; }}音讯的拉取实现次要在工作 Task 的 run 办法中,重点看下: ...

February 9, 2022 · 2 min · jiezi

关于rocketmq:RocketMQ学习十三顺序消息延时消息与消息过滤

如题所示,本文围绕程序音讯,延时音讯与音讯过滤来开展。 一,程序音讯 RocketMQ只能保障队列级别的音讯有序,如果要实现某一类音讯的程序执行,就必须将这类音讯发送到同一个队列,能够在音讯发送时应用 MessageQueueSelector,通过指定sharding key进而将同一类音讯发送到同一队列里,这样在CommitLog文件里音讯的程序就与发送时统一了。broker端抉择发送的队列能够参考之前是的文章:RocketMQ学习五-抉择队列等个性。 上面再说下生产端的解决。 程序音讯生产的事件监听器是MessageListenerOrderly。咱们晓得PullMessageService依据偏移量拉取一批音讯后会存入ProcessQueue中,而后应用线程池进行解决。要保障生产端对单队列中的音讯程序解决,故在多线程场景下须要依照音讯生产队列进行加锁。程序生产在生产端的并发度并不取决生产端线程池的大小,而是取决于分给给消费者的队列数量,故如果一个 Topic 是用在程序生产场景中,倡议消费者的队列数设置增多,能够适当为非程序生产的 2~3 倍,这样有利于进步生产端的并发度,不便横向扩容。 生产端的横向扩容或 Broker 端队列个数的变更都会触发音讯生产队列的从新负载,并发生产时一个生产队列有可能被多个消费者同时生产,但程序生产时并不会呈现这种状况,因为程序音讯不仅仅在生产音讯时会锁定音讯生产队列,在调配到音讯队列时,能从该队列拉取音讯还须要在 Broker 端申请该生产队列的锁,即同一个工夫只有一个消费者能拉取该队列中的音讯,确保程序生产的语义。 流程: PullMessageService单线程的从Broker获取音讯PullMessageService将音讯增加到ProcessQueue中(ProcessMessage是一个音讯的缓存),之后提交一个生产工作到ConsumeMessageOrderServiceConsumeMessageOrderService多线程执行,每个线程在生产音讯时须要拿到MessageQueue的锁拿到锁之后从ProcessQueue中获取音讯那如果程序生产的过程中生产失败了怎么解决呢?并发生产模式在生产失败是有重试机制,默认重试 16 次,而且重试时是先将音讯发送到 Broker,而后再次拉取到音讯,这种机制就会丢失其生产的程序性。还有如果一条音讯如果始终不能生产胜利,其音讯生产进度就会始终无奈向前推动,即会造成音讯积压景象,所以程序生产时咱们肯定要捕获异样。 二,延时音讯 在开源版本的RocketMQ中延时音讯并不反对任意工夫的延时,目前默认设置为:1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h,从1s到2h别离对应着等级1到18,而阿里云中的付费版本是能够反对40天内的任何时刻(毫秒级别)。 延时音讯的流程图: Producer在本人发送的音讯上设置好须要延时的级别(比方设置3等级的提早:message.setDelayTimeLevel(3))。Broker发现此音讯是延时音讯(音讯的 delayLevel 大于0),将Topic进行替换成延时Topic(SCHEDULE_TOPIC_XXXX),每个延时级别都会作为一个独自的queue(delayLevel-1),将本人的Topic作为额定信息存储(CommitLog#putMessage办法里)。构建ConsumerQueue定时工作定时每隔1s扫描每个延时级别的ConsumerQueue。拿到ConsumerQueue中的CommitLog的Offset,获取音讯,判断是否曾经达到执行工夫如果达到,那么将音讯的Topic复原,进行从新投递。如果没有达到则提早没有达到的这段时间执行工作。三,音讯过滤 RocketMQ反对SQL过滤与TAG过滤两种形式。 SQL过滤:在broker端进行,能够缩小无用数据的网络传输但broker压力会大,性能低,反对应用SQL语句简单的过滤逻辑。TAG过滤:在broker与consumer端进行,减少无用数据的网络传输但broker压力小,性能高,只反对简略的过滤。SQL过滤先不剖析了,能够参考文章:RocketMQ源码解析:音讯过滤是如何实现的?TAG过滤的流程大略是,broker获取对应ConsuemrQueue里hashcode(tag)后依据生产端传入的tag进行比拟,如果不匹配则将此音讯跳过;如果匹配生产端还要进行一次tag的比拟,因为会有可能呈现了hash抵触。 broker端的过滤: //查问音讯入口 public GetMessageResult getMessage(final String group, final String topic, final int queueId, final long offset, final int maxMsgNums, final MessageFilter messageFilter) { //tag过滤,在consumerQueue里 if (messageFilter != null && !messageFilter.isMatchedByConsumeQueue(isTagsCodeLegal ? tagsCode : null, extRet ? cqExtUnit : null)) { if (getResult.getBufferTotalSize() == 0) { status = GetMessageStatus.NO_MATCHED_MESSAGE; } continue; } //tag过滤,在commitlog里 if (messageFilter != null && !messageFilter.isMatchedByCommitLog(selectResult.getByteBuffer().slice(), null)) { if (getResult.getBufferTotalSize() == 0) { status = GetMessageStatus.NO_MATCHED_MESSAGE; } // release... selectResult.release(); continue; }}consumer过滤: ...

February 8, 2022 · 1 min · jiezi

关于rocketmq:RocketMQ学习十二Consumer学习

生产端的次要性能有启动消费者,敞开消费者,同步或异步推或者拉取音讯,获取指定音讯生产队列的生产进度,获取以后正在解决的音讯生产队列,订阅主题,勾销订阅,注册并发生产模式监听器等性能。在 RocketMQ 的外部实现原理中,其实现机制为 PULL 模式,而 PUSH 模式是一种伪推送,是对 PULL 模式的封装。每拉去一批音讯后,提交到生产端的线程池(异步),而后马上向 Broker 拉取音讯,即实现相似“推”的成果。为什么要对拉取的根底上再封装出推送呢?次要是拉取模式对使用者不敌对,对使用者要求比拟高,封装成推送后的API简略。 本文会介绍DefaultMQPushConsumer类的一些特色,大略会从: DefaultMQPushConsumer的外围参数PUSH 模型音讯拉取机制音讯生产进度提交这些方面动手。 DefaultMQPushConsumer外围参数 String consumerGroup:生产组的名称,在 RocketMQ 中,对于生产组来说,一个生产组就是一个独立的隔离单位。 MessageModel messageModel:音讯组音讯生产模式,在 RocketMQ 中反对集群模式、播送模式。集群模式值得是一个生产组内多个消费者独特生产一个 Topic 中的音讯,即一条音讯只会被集群内的某一个消费者解决;而播送模式是指一个生产组内的每一个消费者负责 Topic 中的所有音讯。 ConsumeFromWhere consumeFromWhere:一个消费者首次启动时(即生产进度管理器中无奈查问到该生产组的进度)时从哪个地位开始生产的策略,可选值如下所示: CONSUME_FROM_LAST_OFFSET:从最新的音讯开始生产。CONSUME_FROM_FIRST_OFFSET:从最新的位点开始生产。CONSUME_FROM_TIMESTAMP:从指定的工夫戳开始生产,这里的实现思路是从 Broker 服务器寻找音讯的存储工夫小于或等于指定工夫戳中最大的音讯偏移量的音讯,从这条音讯开始生产。 AllocateMessageQueueStrategy allocateMessageQueueStrategy:音讯队列负载算法。次要解决的问题是音讯生产队列在各个消费者之间的负载平衡策略,之前的文章RocketMQ学习一-RocketMQ初探已有介绍。 OffsetStore offsetStore:音讯进度存储管理器,该属性为公有属性,不能通过 API 进行批改,该参数次要是依据生产模式在外部主动创立,RocketMQ 在播送音讯、集群生产两种模式下音讯生产进度的存储策略会有所不同。 集群模式:RocketMQ 会将音讯生产进度存储在 Broker 服务器,存储门路为 ${ROCKET_HOME}/store/config/ consumerOffset.json 文件中。(还记得如何定位CommitLog里的音讯吗?RocketMQ学习十-消息日志文件及音讯检索)播送模式:RocketMQ 会将音讯生产进存在在生产端所在的机器上,存储门路为 ${user.home}/.rocketmq_offsets 中。音讯生产进度,首先应用 topic@consumerGroup 为键,其值是一个 Map,键为 Topic 的队列序列,值为以后的音讯生产位点。 PUSH 模型音讯拉取机制 下图是RocketMQ 音讯拉取执行模型其外围关键点如下: 通过队列负载机制后,会调配给以后消费者一些队列,留神一个生产组能够订阅多个主题,正如下面 pullRequestQueue 中所示,topic_test、topic_test2 这两个主题都调配了一个队列。轮流从 pullRequestQueue 中取出一个 PullRequest 对象,依据该对象中的拉取偏移量向 Broker 发动拉取申请,默认拉取 32 条,可通过 pullBatchSize 参数进行扭转,该办法不仅会返回音讯列表,还会返更改 PullRequest 对象中的下一次拉取的偏移量。接管到 Broker 返回的音讯后,会首先放入 ProccessQueue(解决队列),该队列的内部结构为 TreeMap,key 寄存的是音讯在音讯生产队列(consumequeue)中的偏移量,而 value 为具体的音讯对象。而后将拉取到的音讯提交到生产组外部的线程池,并立刻返回,并将 PullRequest 对象放入到 pullRequestQueue 中,而后取出下一个 PullRequest 对象持续反复音讯拉取的流程,从这里能够看出,音讯拉取与音讯生产是不同的线程。音讯生产组线程池解决完一条音讯后,会将音讯从 ProccessQueue 中删除,而后会向 Broker 汇报音讯生产进度,以便下次重启时能从上一次生产的地位开始生产。音讯生产进度提交 ...

February 7, 2022 · 1 min · jiezi

关于rocketmq:RocketMQ学习十二消息刷盘

数据存储这一块,RocketMQ应用的是文件编程模型。为了进步文件的写入性能通常会引入内存映射机制,数据先写入页缓存而后再择机将页缓存数据刷盘到磁盘,写入波及性能与数据可靠性是必须要思考的。针对刷盘策略个别会有同步刷盘与异步刷盘,RocketMQ也是如此,默认应用异步刷盘。先来简略看下RocketMQ刷盘操作的代码块: try { //We only append data to fileChannel or mappedByteBuffer, never both. if (writeBuffer != null || this.fileChannel.position() != 0) { this.fileChannel.force(false); } else { // 正文4.8.1:同步落盘 this.mappedByteBuffer.force(); } } catch (Throwable e) { log.error("Error occurred when force data to disk.", e); }能够看到刷盘其实就是调用了MappedByteBuffer的force办法。 同步刷盘 同步刷盘指的 Broker 端收到音讯发送者的音讯后,先写入内存,而后将内容长久化到磁盘后才向客户端返回音讯发送胜利。刷盘要分两条线进行剖析: 第一条线是broker在启动的时候会启动一个刷盘线程,调用门路为:BrokerController#start()->DefaultMessageStore#start()->CommitLog#start()->GroupCommitService#start()->MappedFileQueue#flush();第二条线是broker在接管到音讯后加载或更新MappedFile而后存入MappedFileQueue,调用门路为:SendMessageProcessor#processRequest()->DefaultMessageStore#putMessage()->CommitLog#putMessage()->CommitLog#handleDiskFlush()->GroupCommitRequest#waitForFlush().第一条线的刷盘线程会在一个while循环里每距离10ms执行一次刷盘操作,刷盘胜利后会唤醒第二条线里中期待响应的线程,在第二条线里组装好MappedFileQueue(CopyOnWriteArrayList类型)之后便会调用countDownLatch的await办法期待刷盘线程的执行。 //broker接管到音讯组装好MappedFileQueue后期待刷盘线程执行 public void handleDiskFlush(AppendMessageResult result, PutMessageResult putMessageResult, MessageExt messageExt) { // Synchronization flush if (FlushDiskType.SYNC_FLUSH == this.defaultMessageStore.getMessageStoreConfig().getFlushDiskType()) { final GroupCommitService service = (GroupCommitService) this.flushCommitLogService; if (messageExt.isWaitStoreMsgOK()) { GroupCommitRequest request = new GroupCommitRequest(result.getWroteOffset() + result.getWroteBytes()); service.putRequest(request); //期待刷盘线程执行 boolean flushOK = request.waitForFlush(this.defaultMessageStore.getMessageStoreConfig().getSyncFlushTimeout()); if (!flushOK) { log.error("do groupcommit, wait for flush failed, topic: " + messageExt.getTopic() + " tags: " + messageExt.getTags() + " client address: " + messageExt.getBornHostString()); putMessageResult.setPutMessageStatus(PutMessageStatus.FLUSH_DISK_TIMEOUT); } } else { service.wakeup(); } } // Asynchronous flush // 正文4.8.2:异步刷盘 else { if (!this.defaultMessageStore.getMessageStoreConfig().isTransientStorePoolEnable()) { flushCommitLogService.wakeup(); } else { commitLogService.wakeup(); } } } for (GroupCommitRequest req : this.requestsRead) { // There may be a message in the next file, so a maximum of // two times the flush boolean flushOK = false; for (int i = 0; i < 2 && !flushOK; i++) { //以后已刷盘指针大于该条音讯对应的物理偏移量阐明已刷完 flushOK = CommitLog.this.mappedFileQueue.getFlushedWhere() >= req.getNextOffset(); if (!flushOK) { //刷盘操作 CommitLog.this.mappedFileQueue.flush(0); } } //唤醒期待刷盘的线程 req.wakeupCustomer(flushOK); } 对于同步刷盘须要提一下就是每次刷盘并非只刷写一条音讯,而是一组音讯。 ...

February 7, 2022 · 2 min · jiezi

关于rocketmq:RocketMQ学习十一offset管理

通过上一篇咱们曾经晓得只有先从ConsumerQueue里获取到了CommitLog物理偏移量后才能够疾速的从CommintLog里找到对应的message。那ConsumerQueue里的信息又是如何定位的呢?这里就波及到咱们明天的主题:offset治理。在RocketMQ中,音讯生产实现后须要将offset存储下来,offset用来治理每个生产队列的不同生产组的生产进度,依据生产模式的不同又有所差别: 在播送模式下,因为每条音讯会被生产组内所有的消费者生产,同生产组的消费者互相独立,生产进度要独自存储,会以文本文件的模式存储在客户端,对应的数据结构为LocalFileOffsetStore在集群模式下,同一条音讯只会被同一个生产组生产一次,生产进度会参加到负载平衡中,故生产进度是须要共享的,另外,消费者产生异样或重启为了保障能够从上一次生产的中央持续进行生产,这时的offset是对立保留到broker服务端的。对应的数据结构为RemoteBrokerOffsetStore。理论应用RocketMQ大多是集群模式,这里也只针对集群模式下的offset治理形容。 一,Broker服务端offset存储与加载 rocketMQ的broker端中,offset的是以json的模式长久化到磁盘文件中,文件门路为${user.home}/store/config/consumerOffset.json。其内容示例如下: { "offsetTable": { "test-topic@test-group": { "0": 88526, "1": 88528 } }}broker端启动后,会调用BrokerController.initialize()办法,办法中会对offset进行加载,加载办法consumerOffsetManager.load()。获取文件内容后,最初会将其序列化为一个ConsumerOffsetManager对象,这个对象中要害的一个属性是 ConcurrentMap<String,ConcurrentMap<Integer, Long>> offsetTableoffsetTable的,key的模式为topic@group(每个topic下不同生产组的生产进度),value也是一个ConcurrentMap,key为queueId,value为生产位移(这里不是offset而是位移)。通过对全局ConsumerOffsetManager对象就能够对各个topic下不同生产组的生产位移进行获取与治理。 commitLog与offset producer发送音讯到broker之后,会将音讯具体内容长久化到commitLog文件中,再散发到topic下的生产队列consume Queue,消费者提交生产申请时,broker从该consumer负责的生产队列中依据申请参数起始offset获取待生产的音讯索引信息,再从commitLog中获取具体的音讯内容返回给consumer。在这个过程中,consumer提交的offset为本次申请的起始生产地位,即beginOffset;consume Queue中的offset定位了commitLog中具体音讯的地位。 nextBeginOffset 对于consumer的生产申请解决(PullMessageProcessor.processRequest()),除了待生产的音讯内容,broker在responseHeader(PullMessageResponseHeader)附带上以后生产队列的最小offset(minOffset)、最大offset(maxOffset)、及下次拉取的起始offset(nextBeginOffset,也就是上图里的consumerOffset)。 minOffset、maxOffset是以后生产队列consumeQueue记录的最小及最大的offset信息。nextBeginOffset是consumer下次拉取音讯的offset信息,即consumer对该consumeQueue的生产进度。其中nextBeginOffset是consumer在下一轮音讯拉取时offset的重要依据,无论当次拉取的音讯生产是否失常,nextBeginOffset都不会回滚,这是因为rocketMQ对生产异样的音讯的解决是将音讯从新发回broker端的重试队列(会为每个topic创立一个重试队列,以%RERTY%结尾),达到重试工夫后将音讯投递到重试队列中进行生产重试。对生产异样的解决不是通过offset回滚,这使得客户端简化了offset的治理。 二,Consumer客户端offset初始化 consumer启动过程中(Consumer主函数默认调用DefaultMQPushConsumer.start()办法)依据MessageModel(播送与集群模式)抉择对应的offsetStore,而后调用offsetStore.load()对offset进行加载,LocalFileOffsetStore是对本地文件的加载,而RemotebrokerOffsetStore是没有本地文件的,因而load()办法没有实现。在rebalance实现对messageQueue的调配之后会对messageQueue对应的生产地位offset进行更新。 /** RebalanceImpl *//**doRebalance() -> rebalanceByTopic() -> updateProcessQueueTableInRebalance() -> computePullFromWhere()*/private boolean updateProcessQueueTableInRebalance(final String topic, final Set<MessageQueue> mqSet, final boolean isOrder) { // (省略局部代码)负载平衡获取以后consumer负责的音讯队列后对processQueue进行筛选,删除processQueue不必要的messageQueue // 获取topic下consumer音讯拉取列表,List<PullRequest> List<PullRequest> pullRequestList = new ArrayList<PullRequest>(); for (MessageQueue mq : mqSet) { if (!this.processQueueTable.containsKey(mq)) { if (isOrder && !this.lock(mq)) { log.warn("doRebalance, {}, add a new mq failed, {}, because lock failed", consumerGroup, mq); continue; } // 删除messageQueue旧的offset信息 this.removeDirtyOffset(mq); ProcessQueue pq = new ProcessQueue(); // 获取nextOffset,即更新以后messageQueue对应申请的offset long nextOffset = this.computePullFromWhere(mq); if (nextOffset >= 0) { ProcessQueue pre = this.processQueueTable.putIfAbsent(mq, pq); if (pre != null) { log.info("doRebalance, {}, mq already exists, {}", consumerGroup, mq); } else { log.info("doRebalance, {}, add a new mq, {}", consumerGroup, mq); PullRequest pullRequest = new PullRequest(); pullRequest.setConsumerGroup(consumerGroup); pullRequest.setNextOffset(nextOffset); pullRequest.setMessageQueue(mq); pullRequest.setProcessQueue(pq); pullRequestList.add(pullRequest); changed = true; } } else { log.warn("doRebalance, {}, add new mq failed, {}", consumerGroup, mq); } } } }Push模式下,computePullFromWhere()办法的实现类为RebalancePushImpl.class。依据配置信息consumeFromWhere进行不同的操作。ConsumeFromWhere 这个参数的含意是,首次启动从何处开始生产。更精确的表述是,如果查问不到音讯生产进度时,从什么中央开始生产。 ...

February 6, 2022 · 3 min · jiezi

关于rocketmq:RocketMQ学习十消息存储

下面是从官网上找的一张图,producer发送音讯后,由broker生成CommitLog与ConsumerQueue文件,而后consumer依据ConsumerQueue里获取音讯在commitLog里的起始物理地址+message size后,拿到音讯进行生产。上面别离介绍下CommitLog,ConsumerQueue与IndexFile这三个文件。 一,CommitLog文件 存储Producer端写入的音讯主体内容,音讯内容不是定长的。单个文件大小默认1G ,文件名长度为20位,右边补零,残余为起始偏移量,这里的偏移量也就是文件大小的字节示意,这样的益处是只有咱们晓得了音讯的偏移量就能很快晓得该音讯在哪个文件里了。假如某音讯物理偏移量是1073741830,则绝对的偏移量是6(6 = 1073741830 - 1073741824),于是判断出该音讯位于第二个commitLog文件上。它有上面几个个性: 程序写入,一个文件写满再写下一个刷盘前会有一个mappedFile内存映射文件,音讯是先写入到这个内存映射文件中,而后依据刷盘策略写到硬盘中header+body格局,其中header是定长的,记录了音讯的长度读取音讯时先解析出header,从中获取到音讯长度,接着读取音讯体二,ConsumerQueue文件 在介绍ConsumerQueue文件之前须要先提一下生产进度,它是Broker治理每个一个消费者生产topic的进度。这个进度有可能是失常生产后产生的进度,也可能是重置的生产进度,这两种情景下,消费者都会上报进度而后Broker进行记录。之所以要治理生产进度是为了确保消费者在失常状态,重启,异样敞开状况下能精确的接着上一次生产的进度进行生产,也就是确保音讯能够‘至多生产一次’,所以消费者须要有幂等措施确保不会反复生产,这个后续再提。再回到ConsumerQueue文件上,这个文件里记录着某个音讯投递到某个队列里的地位信息,咱们晓得音讯是存在CommitLog文件里的,但必须要先获取音讯的偏移量而后再依据偏移量去CommitLog里进行查问,而音讯的偏移量是记录在ConsumerQueue文件里的,也能够这样了解:ConsumerQueue是CommitLog的一个索引文件。 ConsumerQueue是依照topic维度存储的,每个topic默认4个队列,外面寄存的consumequeue文件。外面记录了一个Topic下的队列里音讯在CommitLog中的起始物理偏移量offset,音讯大小size和音讯Tag的HashCode值。 它有如下几个个性: 每个topic默认为4个队列单个队列下最大可有30W个条目,每个ConsumeQueue文件(条目)大小约5.72Mconsumequeue文件采取定长设计,共20个字节。使之能够应用相似拜访数组的形式疾速定位数据。ConsumerQueue不存音讯的tag而是存tag的hashCode次要是为了保障条目标固定长度。这样咱们定位一条音讯的流程就有2步: 先读ConsumeQueue失去音讯在CommitLog中所在的offset再通过offset找到CommitLog对应的音讯内容消费者通过broker保留的offset(offsetTable.offset json文件中保留的ConsumerQueue的下标)能够在ConsumeQueue中获取音讯,下一章再具体写。 ConsumerQueue除了根本的音讯检索外还有两个作用: 通过tag过滤音讯。过滤tag是也是通过遍历ConsumeQueue来实现的(先比拟hash(tag)符合条件的再到consumer比拟tag原文)ConsumeQueue还能借助于操作系统的PageCache进行缓存晋升检索性能三,IndexFile文件音讯还能够通过key或工夫进行检索,当然咱们也不可能间接从CommitLog里查寻,而是须要借助IndexFile。IndexFile里的文件名fileName是以创立时的工夫戳命名的,固定的单个IndexFile文件大小约为400M,一个IndexFile能够保留 2000W个索引,IndexFile的底层存储设计为在文件系统中实现HashMap构造,故rocketmq的索引文件其底层实现为hash索引。 IndexFile生成原理每当一个新的音讯的index进来,首先取MessageKey的hashCode,而后用hashCode对slot总数取模,失去应该放到哪个slot中,slot总数零碎默认500W个。只有是取hash就必然面临hash抵触的问题,跟HashMap一样,IndexFile也是应用一个链表构造来解决hash抵触。只是这里跟HashMap略微有点区别的中央是,slot中放的是最新index的指针,也就是发生冲突后最新的放在slot里,这个是因为个别查问的时候必定是优先查最近的音讯。每个slot中放的指针值是索引在indexFile中的偏移量,每个索引大小是20字节,所以依据以后索引是这个文件中的第几个(偏移量),就很容易定位到索引的地位。而后每个索引都保留了跟它同一个slot的前一个索引的地位,以此类推造成一个链表的构造。IndexFil组成 IndexFile由三局部组成:1)索引文件由索引文件头IndexHeader。头文件由40个字节的数据组成,次要内容有: //8位 该索引文件的第一个音讯(Message)的存储工夫(落盘工夫)this.byteBuffer.putLong(beginTimestampIndex, this.beginTimestamp.get());//8位 该索引文件的最初一个音讯(Message)的存储工夫(落盘工夫)this.byteBuffer.putLong(endTimestampIndex, this.endTimestamp.get());//8位 该索引文件第一个音讯(Message)的在CommitLog(音讯存储文件)的物理地位偏移量(能够通过该物理偏移间接获取到该音讯)this.byteBuffer.putLong(beginPhyoffsetIndex, this.beginPhyOffset.get());//8位 该索引文件最初一个音讯(Message)的在CommitLog(音讯存储文件)的物理地位偏移量this.byteBuffer.putLong(endPhyoffsetIndex, this.endPhyOffset.get());//4位 该索引文件目前的hash slot的个数this.byteBuffer.putInt(hashSlotcountIndex, this.hashSlotCount.get());//4位 索引文件目前的索引个数this.byteBuffer.putInt(indexCountIndex, this.indexCount.get());2)槽位Slot紧临着IndexHeader,默认slot是500万个,每个固定大小为4byte,slot中存着一个int值,示意以后slot下最新的一个index序号。在计算对应的槽位时,会先算出MessageKey的hashCode,而后用Hashcode对slot的总数进行取模,决定该音讯key的地位,slot的总数默认是500W个。只有取hash就必然面临着hash抵触的问题,indexfile也是采纳链表构造来解决hash抵触(留神,500w个slot很大,另外抵触的情景个别不会很大,所以没有应用红黑树)。slot的值对应以后slot下最新的那个index的序号,index中存储了以后slot下、以后index的前一个index序号,这就把slot下的所有index链起来了 //slot的数据寄存地位 40 + keyHash %(500W)* 4int absSlotPos = IndexHeader.INDEX_HEADER_SIZE + slotPos * hashSlotSize;//Slot Table 4字节//记录该slot以后index,如果hash抵触(即absSlotPos统一)作为下一次该slot新增的前置indexthis.mappedByteBuffer.putInt(absSlotPos, this.indexHeader.getIndexCount()); 3)音讯的索引内容 //Index Linked list//topic+message key的hash值this.mappedByteBuffer.putInt(absIndexPos, keyHash);//音讯在CommitLog的物理文件地址, 能够间接查问到该音讯(索引的外围机制)this.mappedByteBuffer.putLong(absIndexPos + 4, phyOffset);//音讯的落盘工夫与header里的beginTimestamp的差值(为了节俭存储空间,如果间接存message的落盘工夫就得8bytes)this.mappedByteBuffer.putInt(absIndexPos + 4 + 8, (int) timeDiff);//9、记录该slot上一个index//hash抵触解决的要害之处, 雷同hash值上一个音讯索引的index(如果以后音讯索引是该hash值的第一个索引,则prevIndex=0, 也是音讯索引查找时的进行条件),每个slot地位的第一个音讯的prevIndex就是0的this.mappedByteBuffer.putInt(absIndexPos + 4 + 8 + 4, slotValue); ...

February 6, 2022 · 3 min · jiezi

关于rocketmq:RocketMQ学习九Broker分析

一,Broker缓存的数据 Broker次要缓存了路由信息,蕴含producer表,consumer表,consumerGroup表和topic表。这些信息是在ProducerManager,ConsumerManager,SubscriptionGroupManager,TopicConfigManager这几个类里进行治理的。 ProducerManager//producer列表HashMap<String /* group name */, HashMap<Channel, ClientChannelInfo>> groupChannelTablegroupChannelTable:各ProducerGroup中别离有哪些存活的Producer连贯;每个连贯的Producer最初一次发来心跳的工夫ConsumerManager//consumer列表ConcurrentMap<String/* Group */, ConsumerGroupInfo> consumerTableconsumerTable:每个ConsumerGroup中别离有哪些存活的Consumer连贯,别离订阅了哪些Topic,订阅的每个Topic应用什么过滤条件(TAG)。SubscriptionGroupManager//ConsumerGroup表ConcurrentMap<String, SubscriptionGroupConfig> subscriptionGroupTablesubscriptionGroupTable:各ConsumerGroup的消费行为特点,例如:生产失败后的最大重试次数;重试队列个数;如果从MasterBroker生产迟缓,切换到哪个Slave Broker进行生产TopicConfigManager//topic列表ConcurrentMap<String, TopicConfig> topicConfigTabletopicConfigTable:散布在以后Broker上的各Topic分片的配置信息,如:蕴含的读/写Queue的数量;是否有读/写权限二,Broker启动设计 创立BrokerController创立BrokerController类是在BrokerStartup#createBrokerController办法里进行的。先是进行参考解析,完了创立BrokerController类,紧接着调用其initialize办法,外面的逻辑次要有:1)加载topic,consumer生产进度,订阅关系与consumer过滤的配置,并会加载音讯的日志文件2)再创立一个netty服务监听10909这个VIP端口3)初始化一系列线程池,而后在registerProcessor办法里将这些线程池与处理器进行关联,为当前不同的业务应用不同的线程池,也就是线程隔离4)启动一些定时工作,比方记录Broker状态,生产进度长久化等5)最初进行权限校验初始化和Rpc调用钩子相干服务,这些服务加载形式是Java的SPI形式进行的。启动Broker public void start() throws Exception { //启动音讯存储相干的工作 if (this.messageStore != null) { this.messageStore.start(); } //启动broker服务器 if (this.remotingServer != null) { this.remotingServer.start(); } //启动给音讯发送者应用的netty服务 if (this.fastRemotingServer != null) { this.fastRemotingServer.start(); } //启动监控SSL连贯文件的服务 if (this.fileWatchService != null) { this.fileWatchService.start(); } //启动内部API的客户端 if (this.brokerOuterAPI != null) { this.brokerOuterAPI.start(); } //启动pull模式相干的服务 if (this.pullRequestHoldService != null) { this.pullRequestHoldService.start(); } //启动心跳检测服务 if (this.clientHousekeepingService != null) { this.clientHousekeepingService.start(); } //启动音讯过滤服务 if (this.filterServerManager != null) { this.filterServerManager.start(); } //如果没启动DLegerCommitLog ,就将Broker注册到NameServer上 if (!messageStoreConfig.isEnableDLegerCommitLog()) { startProcessorByHa(messageStoreConfig.getBrokerRole()); handleSlaveSynchronize(messageStoreConfig.getBrokerRole()); } /*向namesrv注册*/ this.registerBrokerAll(true, false, true); this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { try { BrokerController.this.registerBrokerAll(true, false, brokerConfig.isForceRegister()); } catch (Throwable e) { log.error("registerBrokerAll Exception", e); } } }, 1000 * 10, Math.max(10000, Math.min(brokerConfig.getRegisterNameServerPeriod(), 60000)), TimeUnit.MILLISECONDS); if (this.brokerStatsManager != null) { this.brokerStatsManager.start(); } if (this.brokerFastFailure != null) { this.brokerFastFailure.start(); } }messageStore服务:解决音讯的存储相干的日志,比方CommitLog,ConsumeQueue等remotingServer服务:解决客户端producer&consumer的申请fastRemotingServer服务:默认端口可能存在多用,可能会造成业务阻塞。新开一个VIP端口专门进行音讯解决。不过4.5版本之后默认已敞开,是为了妆容之前版本。fileWatchService服务:启动监控服务连贯时用到的SSL连贯文件的服务brokerOuterAPI服务:RocketMQ控制台跟Broker交互时候的客户端pullRequestHoldService服务:解决push模式生产,或者提早生产的服务clientHousekeepingService服务:心跳连贯用的服务filterServerManager服务:过滤音讯服务transactionalMessageCheckService服务:定期检查和处理事务音讯服务slaveSynchronize服务:主从路由信息同步服务netty服务端的启动这里能够参考之前文章三大点4小点里的服务端的创立参考文章:Broker局部之Broker启动过程BrokerStartup(2) ...

February 3, 2022 · 1 min · jiezi

关于rocketmq:RocketMQ学习八NameServer分析

NameServer作为RocketMQ的注册核心,次要存储了哪些信息,如何存储,信息有变更时如何告诉进来的?上面咱们带着这几个问题一起剖析下NameServer 一,NameServer记录的信息NameServer作为RocketMQ的注册核心,次要存储着队列与Topic关系列表,broker列表,集群与broker关系列表,以后存活的broker列表 public class RouteInfoManager { //Broker向NameServer上报心跳的最大工夫 private final static long BROKER_CHANNEL_EXPIRED_TIME = 1000 * 60 * 2; private final ReadWriteLock lock = new ReentrantReadWriteLock(); //主题与队列的关系列表,其中队列里有读队列与写队列 private final HashMap<String/* topic */, List<QueueData>> topicQueueTable; private final HashMap<String/* brokerName */, BrokerData> brokerAddrTable;//所有broker信息 private final HashMap<String/* clusterName */, Set<String/* brokerName */>> clusterAddrTable;//每个集群包含哪些broker //以后存活broker,因为nameserver 10S扫描一次所以会有提早 private final HashMap<String/* brokerAddr */, BrokerLiveInfo> brokerLiveTable; //记录每个Broker别离应用了哪些音讯过滤服务器 private final HashMap<String/* brokerAddr */, List<String>/* Filter Server */> filterServerTable;}topicQueueTable:各topic属于那个broker上;每个topic下有多少队列;各队列的读写数量及权限brokerAddrTable:各broker id与address关系,会辨别broker的master与slaveclusterAddrTable:各cluster蕴含的brokerbrokerLiveTable:以后存活的broker,会有肯定的提早filterServerTable:每个Broker别离应用了哪些音讯过滤服务器如果下面的数据产生了变动NameServer如何解决呢?答案是通过BrokerHousekeepingService进行监听,它实现了ChannelEventListener接口,如果通道产生了变动,比方Nameserver与 Broker的连贯通道在敞开、通道发送异样、通道闲暇时,会触发BrokerHousekeepingService的回调,而后移除相应的Broker。 ...

February 2, 2022 · 1 min · jiezi

关于rocketmq:RocketMQ学习七Netty的应用

一,RocketMQ中Remoting通信机制 RocketMQ音讯队列集群次要包含NameServe、Broker(Master/Slave)、Producer、Consumer4个角色,根本通信流程如下: Broker启动后将本人注册至NameServer的操作;随后每隔30s工夫定期向NameServer上报Topic路由信息;音讯生产者Producer在发送音讯时须要依据Msg的Topic从本地缓存的TopicPublishInfoTable获取路由信息(如果没有会从NameServer上从新拉取);Producer依据(2)中获取的路由信息抉择一个队列(MessageQueue)进行音讯发送;Broker作为音讯的接收者收音讯并落盘存储。音讯消费者Consumer依据2)中获取的路由信息,并再实现客户端的负载平衡后,抉择其中的某一个或者某几个音讯队列来拉取音讯并进行生产。二,RocketMQ中Remoting通信模块API RemotingService:为顶层接口。次要办法有: void start(); void shutdown(); void registerRPCHook(RPCHook rpcHook);RemotingServer/RemotingClient:近程服务器/客户端根底接口,两者中的办法根本相似: /** * requestCode 命令编码 * processor RocketMQ 申请业务处理器,例如音讯发送的处理器为 SendMessageProcessor,PullMessageProcessor 为音讯拉取的业务处理器。 * executor 线程池,NettyRequestProcessor 具体业务逻辑在该线程池中执行 */ void registerProcessor(final int requestCode, final NettyRequestProcessor processor, final ExecutorService executor); void registerDefaultProcessor(final NettyRequestProcessor processor, final ExecutorService executor); int localListenPort(); //依据申请编码获取对应的申请业务处理器与线程池 Pair<NettyRequestProcessor, ExecutorService> getProcessorPair(final int requestCode); RemotingCommand invokeSync(final Channel channel, final RemotingCommand request, final long timeoutMillis) throws InterruptedException, RemotingSendRequestException, RemotingTimeoutException; void invokeAsync(final Channel channel, final RemotingCommand request, final long timeoutMillis, final InvokeCallback invokeCallback) throws InterruptedException, RemotingTooMuchRequestException, RemotingTimeoutException, RemotingSendRequestException; void invokeOneway(final Channel channel, final RemotingCommand request, final long timeoutMillis) throws InterruptedException, RemotingTooMuchRequestException, RemotingTimeoutException, RemotingSendRequestException;RomotingClient次要是Producer发送音讯与Consumer拉取音讯时用到;RomotingServer次要是Broker进行回调,获取Consumer状态等的时候用到。这里重点须要关注下registerProcessor注册命令处理器这个办法。RocketMQ 会依照业务逻辑进行拆分,例如音讯发送、音讯拉取等每一个网络操作会定义一个申请编码(requestCode),而后每一个类型对应一个业务处理器 NettyRequestProcessor,并能够依照不同的 requestCode 定义不同的线程池,实现不同申请的线程池隔离。 ...

January 31, 2022 · 4 min · jiezi

关于rocketmq:RocketMQ学习六消息发送错误与解决方案

本文次要提到上面两类谬误及解决方案: 音讯发送超时System busy、Broker busy一,音讯发送谬误 音讯发送超时,通常客户端的日志如下: 客户端报音讯发送超时,通常第一狐疑的对象是 RocketMQ 服务器,是不是 Broker 性能呈现了抖动,无奈抗住以后的量。那咱们如何来排查 RocketMQ 以后是否有性能瓶颈呢?首先咱们执行如下命令查看 RocketMQ 音讯写入的耗时散布状况: cd /${USER.HOME}/logs/rocketmqlogs/grep -n 'PAGECACHERT' store.log | more输入后果如下所示:RocketMQ 会每一分钟打印前一分钟内音讯发送的耗时状况散布,咱们从这里就能窥探 RocketMQ 音讯写入是否存在明细的性能瓶颈,其区间如下: [<=0ms] 小于 0ms,即奥妙级别的[0~10ms] 小于 10ms 的个数[10~50ms] 大于 10ms 小于 50ms 的个数其余区间显示,绝大多数会落在奥妙级别实现,依照笔者的教训如果 100~200ms 及以上的区间超过 20 个后,阐明 Broker 的确存在肯定的瓶颈,如果只是少数几个,阐明这个是内存或 PageCache 的抖动,问题不大。在 RocketMQ broker 中还存在疾速失败机制,即当 Broker 收到客户端的申请后会将音讯先放入队列,而后程序执行,如果一条音讯队列中期待超过 200ms 就会启动疾速失败,向客户端返回 [TIMEOUT_CLEAN_QUEUE]broker busy的谬误。另外,Producer客户端如果那个时候正好呈现了垃圾回收也是有可能造成音讯发送超时的问题。 针对音讯发送超时咱们应该如何应答呢?一般来说咱们能够缩小音讯发送的超时工夫,减少重试次数,并减少疾速失败的最大期待时长。具体通过maxWaitTimeMillsInQueue进行配置,一般来说超时工夫调为500ms-1000ms。须要提一下的是超时工夫在不同版本间含意是不同的,在4.3.0(不含4.3.0)以下的版本,超时工夫指的是单次发送的工夫;而在4.3.0及以上版本中超时工夫指的是所有重试的总的超时工夫。 二,System busy、Broker busy 在应用 RocketMQ 中,如果 RocketMQ 集群达到 1W/tps 的压力负载程度,System busy、Broker busy 就会是大家常常会遇到的问题。例如如下图所示的异样栈。纵观 RocketMQ 与 System busy、Broker busy 相干的谬误关键字,总共蕴含如下 5 个: ...

January 29, 2022 · 1 min · jiezi

关于rocketmq:RocketMQ学习五选择队列等特性

本文次要波及的内容有发送音讯时: 程序音讯之队列抉择机制RocketMQ keyRocketMQ tagRocketMQ msgId程序音讯之队列抉择机制 很多业务场景下须要保障音讯的程序解决,比方订单流转到不同状态都会向同一个topic发送音讯,但消费者在进行生产时心愿依照订单的的变动程序进行解决,如果不管制的话音讯会发送到topic里的不同队列里去,这样消费者就没方法进行程序生产了。本文先只剖析Producer是如何发送程序音讯的,至于Consumer的解决当前再进行剖析。咱们晓得RocketMQ是反对队列级别的程序音讯的,那么在发送音讯的时候只有做到将须要程序生产的音讯按程序都发送到一个队列里就能够了。RocketMQ在音讯发送时提供了自定义的队列负载机制,音讯发送的默认队列负载机制为轮询,那如何进行队列抉择呢?RocketMQ 提供了如下 API(这里只举了其中一个API的例子): SendResult send(final Message msg, final MessageQueueSelector selector, final Object arg) throws MQClientException, RemotingException, MQBrokerException, InterruptedException;应用示例: public static void main(String[] args) throws UnsupportedEncodingException { try { MQProducer producer = new DefaultMQProducer("please_rename_unique_group_name"); producer.start(); String[] tags = new String[] {"TagA", "TagB", "TagC", "TagD", "TagE"}; for (int i = 0; i < 100; i++) { int orderId = i % 10; Message msg = new Message("TopicTestjjj", tags[i % tags.length], "KEY" + i, ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET)); SendResult sendResult = producer.send(msg, new MessageQueueSelector() { @Override public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) { Integer id = (Integer) arg; int index = id % mqs.size(); return mqs.get(index); } }, orderId); System.out.printf("%s%n", sendResult); } producer.shutdown(); } catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) { e.printStackTrace(); } }在发送音讯的时候,咱们指定了抉择队列的实现,传入参考arg(能够是orderId或userId等)而后对整个队列取模,这样就能够做到同一个arg都会发到同一个队列里了。 ...

January 29, 2022 · 2 min · jiezi

关于rocketmq:RocketMQ-理论篇

RocketMQ是一个纯Java、分布式、队列模型的开源消息中间件,是阿里参考Kafka特点研发的一个队列模型的消息中间件,后开源给apache基金会。 我之前写过 RabbitMQ 的文章,毕竟先入为主,后续在介绍 RocketMQ 的性能时,可能会交叉地拿 RabbitMQ 做比拟。以后公司内的消息中间件选型,也是从 RabbitMQ 转为了 RocketMQ,技术总监通知我的理由也很简略,因为 RocketMQ 的分布式集群可用性更高,运维更简略。 也确实,抛去为大数据而生的 Kafka 不说,这二者除了在架构和应用形式上差距很大,但在理论利用中的性能、成果上差距不大。有人说 RabbitMQ 的响应速度更快,有人说 RocketMQ 的数据吞吐量更高,但也是差距不大,各有千秋。国内也没有多少公司有那么大的体量,对性能那么较真。 1. 根本组件1.1. 名词概念Name ServerName Server 是 RocketMQ 集群的协调者,集群的各个组件是通过 Name Server 获取各种属性和地址信息的。次要性能包含两局部: 各个 Broker 定期上报本人的状态信息到 Name Server,维持心跳。各个客户端,包含Producer、Consumer,以及命令行工具,通过 Name Server 获取 Broker 等最新的状态信息。所以,在启动 Broker、生产者和消费者之前,必须通知它们 Name Server 的地址。为了进步可靠性,倡议启动多个 Name Server 组成集群,独自部署。因而在产线中,能够动静增减 Name Server 集群中节点的数量。 能够把 Name Server 类比成 Kafka 中的 ZooKeeper,那为什么不间接用 ZooKeeper 呢?因为 RocketMQ 只能用到 ZooKeeper 的少部分性能,间接用会显得太重,就本人开发了相较而言更轻量级、更满足本身个性的 Name Server。 BrokerBroker 次要负责音讯的存储、投递和查问以及服务高可用保障,说白了就是 RocketMQ 的服务器。Broker 是中间件的外围,相对不能挂,更是要保障它的可靠性,通常会搭建主从高可用架构,因而 Broker 有分 Master Broker(BrokerId 为0)和 Slave Broker(BrokerId 非0)。 ...

January 28, 2022 · 3 min · jiezi

关于rocketmq:RocketMQ学习四消息发送高可用设计

咱们晓得RocketMQ的NameServer并非强统一而是最终一致性的,也就是客户端隔一段时间定时去获取Broker信息,如果Broker一段时间内呈现了故障,客户端并不能马上感应到,那RocketMQ如何做到音讯发送的高可用呢?大抵能够从上面三个方面来开展: 重试机制顺次更换队列躲避己故障的Broker一,重试机制在发送音讯过程中有这样一段代码: int timesTotal = communicationMode == CommunicationMode.SYNC ? 1 + this.defaultMQProducer.getRetryTimesWhenSendFailed() : 1; int times = 0; for (; times < timesTotal; times++) { ... }如果是同步发送的形式默认会重试3次,重试次数也能够通过retryTimesWhenSendFailed进行配置。 二,顺次更换队列在selectOneMessageQueue办法在里,会从sendWhichQueue获得上一次应用过的队列的索引,这个sendWhichQueue是一个ThreadLocalIndex类型,外面有一个ThreadLocal,队列索引就是存储在这个ThreadLocal里,获得索引后会有一个自增的动作,而后再依据新的索引获取新的队列。举个例子:有一个名为broker-a的broker,外面有队列q1,q2,q3,q4,如果上一次应用的是队列q1那么索引自增后会抉择q2作为新的发送队列。通过这样的形式会让这4个队列的负载尽量保持一致。代码如下: int index = tpInfo.getSendWhichQueue().getAndIncrement(); for (int i = 0; i < tpInfo.getMessageQueueList().size(); i++) { int pos = Math.abs(index++) % tpInfo.getMessageQueueList().size(); if (pos < 0) pos = 0; //获取一个队列,队列里记录着所属broker MessageQueue mq = tpInfo.getMessageQueueList().get(pos); //判断 broker 是否生效 if (latencyFaultTolerance.isAvailable(mq.getBrokerName())) { //第一次获取会是空;获取的broker是无效的且还是上一次获取应用过的 if (null == lastBrokerName || mq.getBrokerName().equals(lastBrokerName)) return mq; } }躲避己故障的Broker ...

January 28, 2022 · 2 min · jiezi

关于rocketmq:RocketMQ学习三消息发送方式

RocketMQ 反对同步、异步、Oneway 三种音讯发送形式。 同步:客户端发动一次音讯发送后会同步期待服务器的响应后果。异步:客户端发动一下音讯发动申请后不期待服务器响应后果而是立刻返回,这样不会阻塞客户端子线程,当客户端收到服务端(Broker)的响应后果后会主动调用回调函数。Oneway:客户端发动音讯发送申请后并不会期待服务器的响应后果,也不会调用回调函数,即不关怀音讯的最终发送后果。这里重点介绍下异步与同步。 异步音讯 每一个音讯发送者实例(DefaultMQProducer)外部会创立一个异步音讯发送线程池,默认线程数量为 CPU 核数,线程池外部持有一个有界队列,默认长度为 5W,并且会管制异步调用的最大并发度,默认为 65536,其能够通过参数 clientAsyncSemaphoreValue 来配置。客户端使线程池将音讯发送到服务端,服务端解决实现后,返回构造并依据是否产生异样调用 SendCallback 回调函数下面是发送异步音讯的过程,上面再从源码上剖析下。 public void start() throws MQClientException { this.defaultMQProducerImpl.start(); if (null != traceDispatcher) { try { traceDispatcher.start(this.getNamesrvAddr()); } catch (MQClientException e) { log.warn("trace dispatcher start failed ", e); } } }这个是Producer服务的启动入口。接着看DefaultMQProducerImpl类: public void start(final boolean startFactory) throws MQClientException { ... if (startFactory) { //启动MQClientInstance mQClientFactory.start(); } ...}public void start(final boolean startFactory) throws MQClientException { ... this.mQClientFactory = MQClientManager.getInstance().getAndCreateMQClientInstance(this.defaultMQProducer, rpcHook); ... }在getAndCreateMQClientInstance办法里会创立MQClientInstance实例,接着在MQClientInstance创立过程上又会创立DefaultMQProducerImpl对象,这时会创立一个异步音讯发送线程池。 ...

January 27, 2022 · 2 min · jiezi

关于rocketmq:RocketMQ-应用篇

本文偏重解说 RocketMQ 的理论利用,对于实践局部,在另外一篇文章中再做探讨。在此不多说,间接进入实战吧。 1. 配置通常开发间接依赖 rocketmq-spring-boot-starter 即可,starter 中蕴含了所有所需的依赖,如: rocketmq-client:封装了客户端的应用程序,还蕴含了netty的通信服务。rocketmq-acl:拜访权限管制服务。starter 还提供了很多现成封装类,如:RocketMQTemplate.java、RocketMQListener.java、RocketMQUtil.java 等,在利用开发时会常常用到。 倡议间接用上述的 rocketmq-spring-boot-starter,见过有公司为了外部兼容,本人封装了一个服务代替官网的 starter。但这个服务除了减少局部自定义程序外,其余的类和办法都是照拷贝 starter 的。当后续 rocketmq-spring-boot-starter 降级了,或修复bug、或拓展性能,公司外部的服务就很难降级了,除非再从头拷贝一遍。当公司外部没有相应的体量,倡议不要学大厂本人封装根底服务,否则容易欲罢不能。 pom依赖 <dependency> <groupId>org.apache.rocketmq</groupId> <artifactId>rocketmq-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency>写文章时,starter 最新的版本是 2.2.0,对应的 rocketmq-client、rocketmq-acl版本是 4.8.0。 认准版本好很重要,因为 rocketmq-spring-boot-starter 始终在疾速迭代中,很多类和办法,在新版本中都会扭转,例如下文会提到的tag、音讯事务等,这是也是为什么不倡议公司外部封装 starter。 application# rocketmq 配置项,对应 RocketMQProperties 配置类rocketmq: name-server: 127.0.0.1:9876 # RocketMQ Namesrv # Producer 配置项 producer: group: koala-dev-event-centre-group # 生产者分组 send-message-timeout: 3000 # 发送音讯超时工夫,单位:毫秒。默认为 3000 。 compress-message-body-threshold: 4096 # 消息压缩阀值,当音讯体的大小超过该阀值后,进行消息压缩。默认为 4 * 1024B max-message-size: 4194304 # 音讯体的最大容许大小。。默认为 4 * 1024 * 1024B retry-times-when-send-failed: 2 # 同步发送音讯时,失败重试次数。默认为 2 次。 retry-times-when-send-async-failed: 2 # 异步发送音讯时,失败重试次数。默认为 2 次。 retry-next-server: false # 发送音讯给 Broker 时,如果发送失败,是否重试另外一台 Broker 。默认为 false access-key: # Access Key ,可浏览 https://github.com/apache/rocketmq/blob/master/docs/cn/acl/user_guide.md 文档 secret-key: # Secret Key enable-msg-trace: true # 是否开启音讯轨迹性能。默认为 true 开启。可浏览 https://github.com/apache/rocketmq/blob/master/docs/cn/msg_trace/user_guide.md 文档 customized-trace-topic: RMQ_SYS_TRACE_TOPIC # 自定义音讯轨迹的 Topic 。默认为 RMQ_SYS_TRACE_TOPIC 。 # Consumer 配置项 consumer: listeners: # 配置某个生产分组,是否监听指定 Topic 。构造为 Map<消费者分组, <Topic, Boolean>> 。默认状况下,不配置示意监听。 erbadagang-consumer-group: topic1: false # 敞开 test-consumer-group 对 topic1 的监听生产rocketmq 配置很多,除了根底无关server的配置以外,还有acl、producer、consumer等。但通常一个服务内会有多个consumer,倡议在代码中实现。而producer如果只有一个,能够配置。 ...

January 27, 2022 · 4 min · jiezi

关于rocketmq:RocketMQ学习四顺序消费消息过滤临时

13章因为 RocketMQ 只提供了音讯队列的部分有序,故如果要实现某一类音讯的程序执行,就必须将这类音讯发送到同一个队列,故这里在音讯发送时应用了 MessageQueueSelector,并且应用用户账户进行队列负载,这样同一个账户的音讯就会账号余额变更的程序达到队列,而后队列中的音讯就能被程序生产。那 RocketMQ 程序生产是如何实现的?队列从新负载时还能放弃程序生产吗?程序生产会反复生产吗?音讯过滤,Tag底层实现 所谓的音讯积压:就是 Broker 端以后队列无效数据最大的偏移量(brokerOffset)与音讯生产端的以后解决进度(consumerOffset)之间的差值,即示意以后须要生产但没有生产的音讯。须要晓得这两个偏移量具体详情 RocketMQ 生产端限流机制 RocketMQ 服务端性能自查技巧,也就是看Broker的性能,个别是看日志文件里‘PAGECACHERT’关键字 系统配置 48C256G,集群架构为 4 主 4 从,200 个线程、音讯大小为 3K、主题为 16 个队列,TPS可达12W左右 17章调优倡议18章平滑运维 RoketMQ源码剖析https://my.oschina.net/wangsh...https://www.jianshu.com/u/c5a... 23章音讯轨迹25章Nameserver设计原理及相干问题 CompletableFuture剖析 27commitlog 文件consumequeuemmap,ByteBuffer剖析程序写 28刷盘分同步与异步文件复原机制 29如何应用Netty创立客户端,建设连贯,发送音讯,服务端如何解决音讯线程隔离机制

January 18, 2022 · 1 min · jiezi

关于rocketmq:RocketMQ学习三Consumer临时

联合https://blog.csdn.net/prestig...作者文章生产端的次要性能有启动消费者,敞开消费者,同步或异步推或者拉取音讯,获取指定音讯生产队列的生产进度,获取以后正在解决的音讯生产队列,订阅主题,勾销订阅,注册并发生产模式监听器等性能。在 RocketMQ 的外部实现原理中,其实现机制为 PULL 模式,而 PUSH 模式是一种伪推送,是对 PULL 模式的封装。每拉去一批音讯后,提交到生产端的线程池(异步),而后马上向 Broker 拉取音讯,即实现相似“推”的成果。为什么要对拉取的根底上再封装出推送呢?次要是拉取模式对使用者不敌对,对使用者要求比拟高,封装成推送后的API简略。 别离介绍拉取与推送的具体流程。 RocketMQ 不保障音讯反复生产起因剖析(第9章) ConsumeFromWhere 注意事项(10章)订阅关系不统一导致音讯失落 Lite Pull 与 PUSH 模式之比照(11章)长轮询实现原理PUSH 与 PULL 模式选型(12章)

January 16, 2022 · 1 min · jiezi

关于rocketmq:RocketMQ学习二Producer临时

Namespace,顾名思义,命名空间,为音讯发送者、音讯消费者编入到一个命名空间中。在笔者的了解中,Namespace 的引入,有点相似 RocketMQ 反对多环境、多标签、全链路压测场景。一言以蔽之,Namespace 次要为音讯发送者、音讯消费者进行分组,底层的逻辑是扭转 Topic 的名称。 音讯轨迹:反对跟踪音讯发送、音讯生产的全过程,即能跟踪音讯的发送 IP、存储服务器,什么时候被哪个消费者生产。ACL:拜访权限管制,即能够 Topic 音讯发送、订阅进行受权,只有受权用户能力发送音讯到指定 Topic。 msgId的生成:https://www.cnblogs.com/allen...https://www.cnblogs.com/linli... 音讯发送形式RocketMQ 反对同步、异步、Oneway 三种发送形式。 同步:客户端发动一次音讯发送后会同步期待服务器的响应后果。异步:客户端发动一下音讯发动申请后不期待服务器响应后果而是立刻返回,这样不会阻塞客户端子线程,当客户端收到服务端(Broker)的响应后果后会主动调用回调函数。Oneway:客户端发动音讯发送申请后并不会期待服务器的响应后果,也不会调用回调函数,即不关怀音讯的最终发送后果。 队列抉择机制RocketMQ 反对队列级别的程序生产,故咱们只须要在音讯发送的时候如果将同一个订单号的不同的音讯发送到同一个队列,这样在生产的时候,咱们就能依照程序进行解决。 SendResult send(final Message msg, final MessageQueueSelector selector, final Object arg) throws MQClientException, RemotingException, MQBrokerException, InterruptedException;指定selector RocketMQ key的应用场景,能够依据key查问音讯(控制台,接口)。还有依据音讯偏移量、音讯全局惟一 msgId查问,这都是kafka所没有的 RocketMQ 能够为 Topic 设置 Tag(标签),这样生产端能够对 Topic 中的音讯基于 Tag 进行过滤,即选择性的对 Topic 中的音讯进行解决。发送音讯时能够依据事件不同流程为音讯设置不同的Tag,而生产端进行生产时能够只生产合乎本人Tag的音讯,API如下:void subscribe(final String topic, final String subExpression) throws MQClientException; RocketMQ msgId与offsetMsgId生成规定 音讯发送高可用设计与故障躲避机制(第5章)主要参数是sendLatencyFaultEnable 第6章,音讯发送谬误及罕用解决方案音讯发送超时剖析,很可能是网络超时疾速失败导致的谬误为 system_busy,并不会触发重试能够将超时等待时间设置短一点,这样容易触发,进而重试 System busy、Broker busy起因及应答策略 PageCache 压力较大-应用堆外内存发送线程池积压的回绝策略-扩容Broker 端疾速失败-使用者进行解决,比方重试

January 15, 2022 · 1 min · jiezi

关于rocketmq:RocketMQ学习一RocketMQ初探

RocketMQ架构图 次要组件如下:NameServerNameServer集群,Topic的路由注册核心,为客户端依据Topic提供路由服务,从而为客户端获取对应的Broker进而向Broker发送音讯。NamerServer将Topic信息保留在内存里。NameServer之间的节点不通信,NameServer集群里的路由信息采纳的是最终一致性。NameServer对于RokcetMQ好比ZK对于Kafka。Broker音讯存储服务器,分Master与Slave,Master承当读写操作,Slave作为一个备份。每30s Master与Slave会向NameServer发送心跳包,心跳包里有Broker上所有的Topic路由信息。Broker会将Topic信息长久化。Client音讯客户端,包含Producer与Consumer。个别状况下同一时间一个客户端只会连贯一台NameServer,只有在异样的时候才会尝试连贯另外一台。客户端每30s向NamerServer发动Topic的路由信息查问 音讯订阅模型RocketMQ音讯生产模式采纳的是公布与订阅模式。 Topic: 一类音讯的汇合,不同类型的音讯归属于不同的主题ConsumerGroup:音讯生产组,一个生产单位的汇合,生产组启动时须要订阅须要生产的Topic。一个Topic能够被多个生产组订阅,同样一个生产组也能够订阅多个主题。一个生产组有多个消费者。

January 13, 2022 · 1 min · jiezi

关于rocketmq:浅谈RocketMQ与Kafka有什么区别

为了不便大家更好的选型,小编整顿一份RocketMQ与Kafka的比照文档,心愿能够帮忙到大家。 数据可靠性RocketMQ反对异步实时刷盘,同步刷盘,同步Replication,异步ReplicationKafka应用异步刷盘形式,异步Replication 总结:RocketMQ的同步刷盘在单机可靠性上比Kafka更高,不会因为操作系统Crash,导致数据失落。同时同步Replication也比Kafka异步Replication更牢靠,数据齐全无单点。另外Kafka的Replication以topic为单位,反对主机宕机,备机主动切换,然而这里有个问题,因为是异步Replication,那么切换后会有数据失落,同时Leader如果重启后,会与曾经存在的Leader产生数据抵触。开源版本的RocketMQ不反对Master宕机,Slave主动切换为Master,阿里云版本的RocketMQ反对主动切换个性。 性能比照Kafka单机写入TPS约在百万条/秒,音讯大小10个字节RocketMQ单机写入TPS单实例约7万条/秒,单机部署3个Broker,能够跑到最高12万条/秒,音讯大小10个字节。 总结:Kafka的TPS跑到单机百万,次要是因为Producer端将多个小音讯合并,批量发向Broker。 RocketMQ为什么没有这么做? Producer通常应用Java语言,缓存过多音讯,GC是个很重大的问题Producer调用发送音讯接口,音讯未发送到Broker,向业务返回胜利,此时Producer宕机,会导致音讯失落,业务出错Producer通常为分布式系统,且每台机器都是多线程发送,咱们认为线上的零碎单个Producer每秒产生的数据量无限,不可能上万。缓存的性能齐全能够由下层业务实现。 单机反对的队列数 Kafka单机超过64个队列/分区,Load会产生显著的飙高景象,队列越多,load越高,发送音讯响应工夫变长RocketMQ单机反对最高5万个队列,Load不会产生显著变动 队列多有什么益处? 单机能够创立更多Topic,因为每个Topic都是由一批队列组成Consumer的集群规模和队列数成正比,队列越多,Consumer集群能够越大 音讯投递实时性Kafka应用短轮询形式,实时性取决于轮询间隔时间RocketMQ应用长轮询,同Push形式实时性统一,音讯的投递延时通常在几个毫秒。 生产失败重试Kafka生产失败不反对重试RocketMQ生产失败反对定时重试,每次重试间隔时间顺延 总结:例如充值类利用,大数据培训以后时刻调用运营商网关,充值失败,可能是对方压力过多,稍后在调用就会胜利,如支付宝到银行扣款也是相似需要。这里的重试须要牢靠的重试,即失败重试的音讯不因为Consumer宕机导致失落。 严格的音讯程序Kafka反对音讯程序,然而一台Broker宕机后,就会产生音讯乱序RocketMQ反对严格的音讯程序,在程序音讯场景下,一台Broker宕机后,发送音讯会失败,然而不会乱序 Mysql Binlog散发须要严格的音讯程序 定时音讯Kafka不反对定时音讯RocketMQ反对两类定时音讯 开源版本RocketMQ仅反对定时Level阿里云ONS反对定时Level,以及指定的毫秒级别的延时工夫 分布式事务音讯Kafka不反对分布式事务音讯阿里云ONS反对分布式定时音讯,将来开源版本的RocketMQ也有打算反对分布式事务音讯 音讯查问Kafka不反对音讯查问RocketMQ反对依据Message Id查问音讯,也反对依据音讯内容查问音讯(发送音讯时指定一个Message Key,任意字符串,例如指定为订单Id) 总结:音讯查问对于定位音讯失落问题十分有帮忙,例如某个订单解决失败,是音讯没收到还是收到解决出错了。 音讯回溯Kafka实践上能够依照Offset来回溯音讯RocketMQ反对依照工夫来回溯音讯,精度毫秒,例如从一天之前的某时某分某秒开始从新生产音讯 总结:典型业务场景如consumer做订单剖析,然而因为程序逻辑或者依赖的零碎产生故障等起因,导致明天生产的音讯全副有效,须要从新从昨天零点开始生产,那么以工夫为终点的音讯重放性能对于业务十分有帮忙。 生产并行度Kafka的生产并行度依赖Topic配置的分区数,如分区数为10,那么最多10台机器来并行生产(每台机器只能开启一个线程),或者一台机器生产(10个线程并行生产)。即生产并行度和分区数统一。 RocketMQ生产并行度分两种状况 程序生产形式并行度同Kafka完全一致乱序形式并行度取决于Consumer的线程数,如Topic配置10个队列,10台机器生产,每台机器100个线程,那么并行度为1000。 音讯轨迹Kafka不反对音讯轨迹阿里云ONS反对音讯轨迹 开发语言敌对性Kafka采纳Scala编写RocketMQ采纳Java语言编写 Broker端音讯过滤Kafka不反对Broker端的音讯过滤RocketMQ反对两种Broker端音讯过滤形式 依据Message Tag来过滤,相当于子topic概念向服务器上传一段Java代码,能够对音讯做任意模式的过滤,甚至能够做Message Body的过滤拆分。 音讯沉积能力实践上Kafka要比RocketMQ的沉积能力更强,不过RocketMQ单机也能够反对亿级的音讯沉积能力,咱们认为这个沉积能力曾经齐全能够满足业务需要。 开源社区活跃度Kafka社区更新较慢RocketMQ的github社区有250个集体、公司用户注销了联系方式,QQ群超过1000人。 商业反对Kafka原开发团队成立新公司,目前暂没有相干产品看到RocketMQ在阿里云上曾经凋谢公测近半年,目前以云服务模式收费供大家商用,并向用户承诺99.99%的可靠性,同时彻底解决了用户本人搭建MQ产品的运维复杂性问题 小标题成熟度Kafka在日志畛域比拟成熟RocketMQ在阿里团体外部有大量的利用在应用,每天都产生海量的音讯,并且顺利反对了屡次天猫双十一海量音讯考验,是数据削峰填谷的利器。

December 30, 2021 · 1 min · jiezi

关于rocketmq:我与消息队列的八年情缘

谈起音讯队列,心田还是会有些波澜。 音讯队列,缓存,分库分表是高并发解决方案三剑客,而音讯队列是我最喜爱,也是思考最多的技术。 我想依照上面的四个阶段分享我与音讯队列的故事,同时也是对我技术成长经验的回顾。 初识:ActiveMQ进阶:Redis&RabbitMQ升华:MetaQ钟情:RocketMQ1 初识ActiveMQ1.1 异步&解耦2011年初,我在一家互联网彩票公司做研发。 我负责的是用户核心零碎,提供用户注册,查问,批改等根底性能。用户注册胜利之后,须要给用户发送短信。 因为原来都是面向过程编程,我就把新增用户模块和发送短信模块都揉在一起了。 起初都还好,但问题缓缓的显现出来。 短信渠道不够稳固,发送短信会达到5秒左右,这样用户注册接口耗时很大,影响前端用户体验;短信渠道接口发生变化,用户核心代码就必须批改了。但用户核心是外围零碎。每次上线都必要谨小慎微。这种感觉很顺当,非核心性能影响到外围零碎了。第一个问题,我能够采取线程池的办法来做,次要是异步化。但第二个问题却让我束手无措。 于是我向技术经理求教,他通知我引入音讯队列去解决这个问题。 将发送短信性能独自拆成独立的Job服务;用户核心用户注册胜利后,发送一条音讯到音讯队列,Job服务收到音讯调用短信服务发送短信即可。 这时,我才明确: 音讯队列最外围的性能就是<font color="red">异步</font>和<font color="red">解耦</font>。 1.2 调度核心彩票零碎的业务是比较复杂的。在彩票订单的生命周期里,通过创立,拆分子订单,出票,算奖等诸多环节。每一个环节都须要不同的服务解决,每个零碎都有本人独立的表,业务性能也绝对独立。如果每个利用都去批改订单主表的信息,那就会相当凌乱了。 公司的架构师设计了<font color="red">调度核心</font>的服务,调度核心的职责是保护订单外围状态机,订单返奖流程,彩票外围数据生成。 调度核心通过音讯队列和出票网关,算奖服务等零碎传递和替换信息。 这种设计在那个时候青涩的我的眼里,几乎就是水滴vs人类舰队,降维打击。 随着我对业务了解的不断深入,我隐约感觉:“好的架构是简洁的,也是应该易于保护的”。 当彩票业务日均千万交易额的时候,调度核心的研发保护人员也只有两个人。调度核心的源码里业务逻辑,日志,代码标准都是极好的。 在我日后的程序人生里,我也会下意识模拟调度核心的编码方式,“不玩奇技淫巧,代码是给人浏览的”。 1.3 重启大法随着彩票业务的爆炸增长,每天的音讯量从30万激增到150~200万左右,所有看起来仿佛很安稳。 某一天双色球投注截止,调度核心无奈从音讯队列中生产数据。音讯总线处于只能发,不能收的状态下。 整个技术团队都处于极度的焦虑状态,“要是出不了票,那可是几百万的损失呀,要是用户中了两个双色球?那可是千万呀”。大家急得像热锅上的蚂蚁。 这也是整个技术团队第一次遇到生产沉积的状况,大家都没有教训。 首先想到的是多部署几台调度核心服务,部署实现之后,调度核心生产了几千条音讯后还是Hang住了。 这时,架构师只能采纳重启的策略。你没有看错,就是重启大法。说起来真的很羞愧,但过后真的只能采纳这种形式。 调度核心重启后,生产了一两万后又Hang住了。只能又重启一次。来来回回继续20屡次,像挤牙膏一样。而且随着出票截止工夫的邻近,这种思维上的缓和和恐惧感更加强烈。终于,通过1小时的手工一直重启,音讯终于生产完了。 我过后正好在读毕玄老师的《分布式java利用根底与实际》,猜测是不是线程阻塞了,于是我用Jstack命令查看堆栈状况。 果然不出所料,线程都阻塞在提交数据的办法上。 咱们马上和DBA沟通,发现oracle数据库执行了十分多的大事务,每次大的事务执行都须要30分钟以上,导致调度核心的调度出票线程阻塞了。 技术部起初采取了如下的计划躲避沉积问题: 生产者发送音讯的时候,将超大的音讯拆分成多批次的音讯,缩小调度核心执行大事务的几率;数据源配置参数,如果事务执行超过肯定时长,主动抛异样,回滚。1.4 复盘Spring封装的ActiveMQ的API十分简洁易用,应用过程中真的十分难受。 受限于过后彩票技术团队的技术水平和视线,咱们在应用ActiveMQ中遇到了一些问题。 高吞吐下,沉积到肯定音讯量易Hang住;技术团队发现在吞吐量特地高的场景下,如果音讯沉积越大,ActiveMQ有较小几率会Hang住的。 出票网关的音讯量特地大,有的音讯并不需要马上生产,然而为了躲避音讯队列Hang住的问题,出票网关生产数据的时候,先将音讯先长久化到本地磁盘,生成本地XML文件,而后异步定时执行音讯。通过这种形式,咱们大幅度晋升了出票网关的生产速度,根本杜绝了出票网关队列的沉积。 但这种形式感觉也挺怪的,生产音讯的时候,还要本地再存储一份数据,音讯存储在本地,如果磁盘坏了,也有丢音讯的危险。 高可用机制待欠缺咱们采纳的master/slave部署模式,一主一从,服务器配置是4核8G 。 这种部署形式能够同时运行两个ActiveMQ, 只容许一个slave连贯到Master下面,也就是说只能有2台MQ做集群,这两个服务之间有一个数据备份通道,利用这个通道Master向Slave单向地数据备份。 这个计划在理论生产线上不不便, 因为当Master挂了之后, Slave并不能主动地接管Client发来的请来,须要手动干涉,且要进行Slave再重启Master能力复原负载集群。 还有一些很诡异丢音讯的事件,生产者发送音讯胜利,但master控制台查问不到,但slave控制台居然能查问到该音讯。 但消费者没有方法生产slave上的音讯,还得通过人工染指的形式去解决。 2 进阶Redis&RabbitMQ2014年,我在艺龙网从事红包零碎和优惠券系统优化相干工作。 2.1 Redis能够做音讯队列吗酒店优惠券计算服务应用的是初代流式计算框架Storm。Storm这里就不具体介绍,能够参看上面的逻辑图: 这里咱们的Storm集群的水源头(数据源)是redis集群,应用list数据结构实现了音讯队列的push/pop性能。 流式计算的整体流程: 酒店信息服务发送酒店信息到Redis集群A/B;Storm的spout组件从Redis集群A/B获取数据, 获取胜利后,发送tuple音讯给Bolt组件;Bolt组件收到音讯后,通过经营配置的规定对数据进行荡涤;最初Storm把解决好的数据发送到Redis集群C;入库服务从Redis集群C获取数据,存储数据到数据库;搜寻团队扫描数据库表,生成索引。 这套流式计算服务每天解决千万条数据,解决得还算顺利。但计划在团队外部还是有不同声音: storm的拓扑降级时候,或者优惠券服务重启的时候,偶然呈现丢音讯的状况。但音讯的失落,对业务来讲没有那么敏感,而且咱们也提供了手工刷新的性能,也在业务的容忍范畴内;团队须要常常关注Redis的缓存使用量,放心Redis队列沉积, 导致out of memory;架构师认为搜寻团队间接扫描数据库不够解耦,倡议将Redis集群C替换成Kafka,搜寻团队从kafka间接生产音讯,生成索引;我认为应用Redis做音讯队列应该满足如下条件: 容忍小概率音讯失落,通过定时工作/手工触发达到最终统一的业务场景;音讯沉积概率低,有相干的报警监控;消费者的生产模型要足够简略。2.2 RabbitMQ是管子不是池子RabbitMQ是用erlang语言编写的。RabbitMQ满足了我的两点需要: 高可用机制。艺龙外部是应用的镜像高可用模式,而且这种模式在艺龙曾经应用了较长时间了,稳定性也失去了肯定的验证。我负责的红包零碎里,RabbitMQ每天的吞吐也在百万条音讯左右,音讯的发送和生产都还挺完满。优惠券服务原应用SqlServer,因为数据量太大,技术团队决定应用分库分表的策略,应用公司自主研发的分布式数据库DDA。 因为是第一次应用分布式数据库,为了测试DDA的稳定性,咱们模仿发送1000万条音讯到RabbitMQ,而后优惠券重构服务生产音讯后,依照用户编号hash到不同的mysql库。 RabbitMQ集群模式是镜像高可用,3台服务器,每台配置是4核8G 。 咱们以每小时300万条音讯的速度发送音讯,最开始1个小时生产者和消费者体现都很好,但因为消费者的速度跟不上生产者的速度,导致音讯队列有积压状况产生。第三个小时,音讯队列已沉积了500多万条音讯了, 生产者发送音讯的速度由最开始的2毫秒激增到500毫秒左右。RabbitMQ的控制台已血溅当场,标红报警。 ...

December 16, 2021 · 2 min · jiezi

关于rocketmq:RocketMQ-50-POP-消费模式探秘

简介: POP Consumer—使客户端无状态,更轻量! 作者:凯易&耘田 前言:随着 RocketMQ 5.0 preview 的公布,5.0 的重大个性逐渐与大家见面。POP Consumer 作为 5.0 的一大个性,POP 生产模式展示了一种全新的生产模式。其具备的轻量级,无状态,无队列独占等特点,对于音讯积压场景,Streaming 生产场景等都十分敌对。在介绍 POP Consumer 之前,咱们先回顾一下目前应用较多的 Push Consumer。 Push Consumer相熟 RocketMQ 的同学对 Push Consumer 必定不会生疏,客户端生产个别都会应用这种生产模式,应用这种生产模式也比较简单。咱们只需简略设置,并在回调办法 ConsumeMessage 中写好业务逻辑即可,启动客户端利用就能够失常生产音讯了。 public class PushConsumer { public static void main(String[] args) throws InterruptedException, MQClientException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("CID_JODIE_1"); consumer.subscribe("test_topic", "*"); consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); 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"); }}那么 Push Consumer 是如何生产音讯的呢? ...

November 12, 2021 · 4 min · jiezi

关于rocketmq:基于消息队列-RocketMQ-的大型分布式应用上云最佳实践

作者|绍舒审核&校对:岁月、佳佳编辑&排版:雯燕 前言音讯队列是分布式互联网架构的重要基础设施,在以下场景都有着重要的利用: 利用解耦削峰填谷异步告诉分布式事务大数据处理并波及互动直播、挪动互联网&物联网,IM 实时通信、Cache 同步、日志监控等多个畛域。 而本文次要围绕着商业版本的音讯队列 RocketMQ,和开源版本 RocketMQ 进行比拟,并联合一些实际中的场景来展现大型分布式应用的上云最佳实际。 外围能力商业版本音讯队列 RocketMQ 相比拟开源版本 RocketMQ 和其余竞品,次要有以下几点劣势。 开箱即用、功能丰富高性能、有限扩大能力可观测、免运维能力高 SLA 和稳定性保障 开箱即用、功能丰富音讯队列 RocketMQ 提供了定时、事务、程序等多类型音讯的反对,且反对播送、集群两种生产模式;另外在协定层面,提供 TCP/HTTP 多协定反对,还提供了 TAG/SQL 属性过滤性能,极大水平地拓宽了用户的应用场景。 高性能、有限拓展能力音讯队列 RocketMQ 禁受了阿里外围电商历年双十一洪峰的考验,反对千万级 TPS 音讯收发和亿级音讯沉积的能力,并且可能为音讯提供毫秒级端到端提早保障,另外还提供分级存储,反对海量音讯的任意保留工夫。 可观测、免运维能力音讯队列 RocketMQ 提供了一个可观测性大盘,反对细粒度数据大盘,提供了音讯全链路生命周期追踪和查问能力,对各个指标提供了相应的监控报警性能;此外,还提供了音讯回溯和死信队列性能,可能保障用户的音讯可能随时回溯生产。 高 SLA 和稳定性保障音讯队列 RocketMQ 的稳定性是咱们一贯、继续、稳固投入的重要畛域,提供了高可用部署和多正本写入性能;另外也反对同城多 AZ 容灾和异地多活。 产品剖面接下来,咱们会从以上的产品外围能力中筛选几个剖面,并且联合具体的场景和实际来做进一步的介绍。 多音讯类型反对高可用程序音讯商业版本音讯队列 RocketMQ 应用的程序音讯咱们称之为高可用程序音讯。在介绍高可用程序音讯之前,首先简要介绍下开源版本 RocketMQ 的程序音讯。 程序音讯分为两种类型,全局程序音讯和分区程序音讯。 全局程序音讯:在 RocketMQ 存储层只会调配一个分区,也就是说全局程序 Topic 的可用性跟繁多正本的可用性强相干,且不具备可扩大的能力。分区程序音讯:所有音讯依据 Sharding Key 进行分区。同一个分区内的音讯依照严格的 FIFO 程序进行公布和生产。Sharding Key 是程序音讯中用来辨别不同分区的关键字段。下图是分区程序音讯的利用场景,order ID 即为此时程序音讯的 Sharding Key。 能够看到,无论是全局程序音讯还是分区程序音讯,都依赖了繁多分区人造的 FIFO 个性来保障程序,因而程序性也只能在同一个分区内保障,当此分区所在的正本不可用时,程序音讯并不具备重试到其余正本的能力,此时音讯的程序性就难以失去保障。 为了解决这一问题,咱们设计并实现了高可用程序音讯。 高可用程序音讯有以下几个特点: ...

November 4, 2021 · 2 min · jiezi

关于rocketmq:RoketMq源码Consumer的构造

DefaultMQPushConsumer的结构实例代码 // 设置group DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("test_consumer_group"); // 注册生产监听 consumer.registerMessageListener(new MessageListenerConcurrently() { public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) { System.out.println(" Receive New Messages: " + msgs); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } }); // 启动 consumer.start();consumer结构 public DefaultMQPushConsumer(final String namespace, final String consumerGroup, RPCHook rpcHook, AllocateMessageQueueStrategy allocateMessageQueueStrategy) { this.consumerGroup = consumerGroup; this.namespace = namespace; // 设置调配策略 this.allocateMessageQueueStrategy = allocateMessageQueueStrategy; defaultMQPushConsumerImpl = new DefaultMQPushConsumerImpl(this, rpcHook);}defaultMQPushConsumerImpl的结构 public DefaultMQPushConsumerImpl(DefaultMQPushConsumer defaultMQPushConsumer, RPCHook rpcHook) { this.defaultMQPushConsumer = defaultMQPushConsumer; this.rpcHook = rpcHook; // 有一个pull的产生异样后提早多久再接着解决的一个工夫 this.pullTimeDelayMillsWhenException = defaultMQPushConsumer.getPullTimeDelayMillsWhenException();}总结consumer结构次要是结构外部的defaultMQPushConsumerImpl,而后须要理解一下调配的策略,有一个细节,就是这个pull过程中产生异样了的一个提早解决的工夫 ...

October 25, 2021 · 1 min · jiezi

关于rocketmq:基于-RocketMQ-的基金数字化陪伴体系的架构实践

作者|伍振河 本文以博时基金的金融场景为案例,论述 RocketMQ 在晋升客户陪伴效率和丰盛金融场景化能力等方面的晋升作用。 行业背景基金公司的外围业务次要分为两局部,一部分是投研线业务,即投资治理和行业钻研业务,它体现了基金公司外围竞争力。另一部分是市场线业务,即基金公司利用自身渠道和市场能力实现基金销售并做好客户服务。 博时基金管作为中国边疆首批成立的五家基金治理公司之一,截至 2021 年 6 月 30 日,博时基金公司共治理 276 只公募基金,治理资产总规模逾 15482 亿元人民币,累计分成逾 1465 亿元人民币。 随着互联网技术倒退,基金销售渠道更加多元化,线上成为基金销售重要渠道。相比传统基金客户,线上渠道具备客户基数大,程度参差不齐的特点。对于那些还不成熟的客户,咱们须要做好陪伴,让他们了解危险,了解投资。 RocketMQ 在陪伴体系中的利用1、陪伴场景概述博时基金建设了一套全方位多层次陪伴体系,从用户层面、市场层面和产品层面为用户提供投前、投中、投后的有温度的投资陪伴体验。 每个陪伴场景的达成,须要公司多个部门不同团队协同配合来实现。依赖与投研、合规、经营、大数据等上下游多个零碎。但这些零碎可能采纳不同技术架构,实现形式各异,如果采纳同步调用形式来实现协同,耦合度太高,不利于将来扩大。 2、RocketMQ 解耦异构零碎RocketMQ 提供高效牢靠的消息传递个性和公布订阅机制,非常适合用于这种上下游异构零碎间的解耦。咱们把原来基于文件、邮件的合作形式全副线上化、流程化和机制化,大大晋升了陪伴输入效率。对于这种波及多方零碎的合作,须要对音讯进行正当地归类,以便进行过滤和索引。RocketMQ 提供的 Topic 和 Tags 就是用来做这件事的。 3、Topic 和 Tags 最佳实际Topic 与 Tag 作为业务上用来归类的标识,别离属于一级分类和二级分类,这种层次化的分类标识与企业组织架构比拟相似,能够联合起来实现音讯过滤。举个例子,对于陪伴零碎的 Topic,经营零碎订阅经营类音讯,咱们给这类音讯打上 TagA 的标签,客服零碎订阅客服类音讯 TagB,陪伴编排零碎订阅编排类音讯 TagC,合规零碎须要对经营和陪伴音讯进行合规审查,因而它须要订阅 TagA 和 TagC,最初是数据中心,所有的音讯都要解决,因而它须要监听所有 Tag。 RocketMQ 事务音讯的金融利用场景1、金融场景概述接下来,咱们解说一下典型的金融场景--优惠购。在博时基金 APP 上申购基金能够享受低至 0 折的费率优惠,具体业务怎么样实现?这里有有两种形式,第一种先充值博时钱包,底层是替客户购买了一笔货币基金,而后再用博时钱包购买指标基金。这种形式须要用户操作两次,比拟繁琐,容易引起客单散失。另外一种形式就是优惠购,把两步购买基金封装成一次事务操作。对投资者来说,开启优惠购服务后,操作少一步,投资更简略! 2、畛域事件实践模型畛域事件是指业务流程的一个步骤将导致进一步的业务操作,比方说登录事件,比方说基金购买事件等。在畛域模型外面,畛域事件事务采纳的是最终一致性,区别于强一致性,它是弱一致性的一种。在畛域模型映射到微服务零碎架构时,微服务之间的数据不用要求强统一,因而畛域事件能够解耦微服务。根据是否跨微服务,能够分为两种场景: 第一种场景:当畛域事件产生在同一个微服务。因为大部分事件产生在同一个过程内,本身能够很好地管制事务。但如果一个事件须要同时更新多个聚合,依照 DDD 中一次事务只更新一个聚合的准则,就须要引入事件总线,就是 eventbus 这种模式。 第二种场景:跨微服务。畛域事件产生在微服务之间的场景比拟多,事件处理的机制也更加简单。跨微服务的事件能够推动业务流程或者数据在不同的子域或微服务间间接流转,因而须要一个协调者来推动全局事务。跨微服务的事件机制要总体思考事件构建、公布和订阅、事件数据长久化、消息中间件、分布式事务机制等,其中具备事务音讯性能的消息中间件是这个解决方案的外围组件。 3、布式事务计划比照在博时基金的业务场景下,须要解决的问题是事务一致性与服务解耦度之间的矛盾,因而咱们的指标是让主从事务解耦,保障外围逻辑稳固,同时不因为解耦而就义最终一致性。因而,过后做出了几种不同的解决方案: 第一种计划:最常见一般音讯+异步对账,这个计划的问题是无奈保障主事务的执行和入队同时胜利,须要时效性低的对账弥补解决,一致性只是较高。第二种计划:本地音讯表,比照上一种做法,它由业务将写入音讯表放到主事务中,把主事务和入队变成一个原子操作,而后业务读取入队记录,本人投递给从事务。它的毛病是主事务和音讯表在存储上是耦合的,没有解耦度。第三种计划:引入 XA 事务,是个两阶段提交的协定,实现难度较大。而且面临两个问题:一是这是一种同步阻塞协定,有锁占用导致并发不会太高,另外就是 XA 事务过程中,在参与者投赞成票后,如果协调者产生故障,节点不分明应该提交还是停止,只能期待协调者复原。这时候可能会呈现业务中断。第四种计划:TCC,专门解决分布式事务的 TCC,只侧重于一致性,无解耦度,也是不可行。第五种计划:事务音讯,它能同时兼顾解耦度和一致性,是最合适的模式。最终咱们抉择了 RocketMQ 的事务音讯作为分布式事务的解决方案。 ...

October 25, 2021 · 1 min · jiezi

关于rocketmq:与顶级互联网公司技术大佬面对面聊聊RocketMQ

作为由阿里巴巴捐献的Apache顶级云原生消息中间件,RocketMQ 立足于在线交易链路,帮忙企业实现异步解耦和削峰填谷以及 IoT 边缘数据以及 C 端用户行为数据采集传输和集成等泛滥性能。咱们能够在十分多的大规模落地场景看到它的身影:电商物流的交易系统、在线教育课程零碎、大型游戏信令零碎以及银行交易系统;非在线业务的场景里,大量车联网、电商网站。 随着音讯基础架构的云原生化演进,RocketMQ 也开始摸索音讯畛域的后处理场景,以及音讯的流式解决和轻计算,帮忙用户实现音讯的就近计算和剖析,并将全面拥抱 Serverless 和 EDA。那么,当初一线大厂在哪些新场景在应用RocketMQ,社区最近又有新的性能演进?那些想要理解这些,那就快来加入 Apache RocketMQ MeetUp - 北京站 线下流动吧! 流动现场你不仅可能失去多个干货案例剖析,更能取得多种神秘小礼物。更重要的是,你能结交100余位与你气味相投的开发者小伙伴!别再犹豫!立刻海报扫码报名吧! 流动名称:Apache RocketMQ Meetup - 北京站流动工夫:2021年10月16日 14:00 - 17:00流动地址:北京市朝阳区霄云路35号-氪空间(霄云路社区)流动亮点:(1)深刻针对多云异构存储,如何打造云原生消息中间件(2)全面解说RocketMQ Go语言重构设计思路和实现细节(3)深底解析RocketMQ Streams,让实时计算更加简略(4)理解架构新趋势,解读Event-Driven架构摸索与实际(5)理解rocketmq 5.0 音讯、事件、流交融解决平台的演进方向 点击链接(https://www.huodongxing.com/e...),即可报名取票!

September 22, 2021 · 1 min · jiezi

关于rocketmq:聊一下-RocketMQ-的消息存储二

本文应用「署名 4.0 国内 (CC BY 4.0)」许可协定,欢送转载、或从新批改应用,但须要注明起源。 [署名 4.0 国内 (CC BY 4.0)]本文作者: Nicksxs创立工夫: 2021-09-12本文链接: 聊一下 RocketMQ 的音讯存储二CommitLog 是 rocketmq 的服务端,也就是 broker 存储音讯的的文件,跟 kafka 一样,也是程序写入,当然音讯是变长的,生成的规定是每个文件的默认1G =1024 * 1024 * 1024,commitlog的文件名fileName,名字长度为20位,右边补零,残余为起始偏移量;比方00000000000000000000代表了第一个文件,起始偏移量为0,文件大小为1G=1 073 741 824Byte;当这个文件满了,第二个文件名字为00000000001073741824,起始偏移量为1073741824, 音讯存储的时候会程序写入文件,当文件满了则写入下一个文件,代码中的定义 private int mapedFileSizeCommitLog = 1024 * 1024 * 1024; 本地跑个 demo 验证下,也是这样,这里微妙有几个比拟奇妙的点(个人观点),首先文件就刚好是 1G,并且依照大小偏移量去生成下一个文件,这样获取音讯的时候按大小算一下就晓得在哪个文件里了, 代码中写入 CommitLog 的逻辑能够从这开始看 public PutMessageResult putMessage(final MessageExtBrokerInner msg) { msg.setStoreTimestamp(System.currentTimeMillis()); msg.setBodyCRC(UtilAll.crc32(msg.getBody())); AppendMessageResult result = null; StoreStatsService storeStatsService = this.defaultMessageStore.getStoreStatsService(); String topic = msg.getTopic(); int queueId = msg.getQueueId(); final int tranType = MessageSysFlag.getTransactionValue(msg.getSysFlag()); if (tranType == MessageSysFlag.TRANSACTION_NOT_TYPE || tranType == MessageSysFlag.TRANSACTION_COMMIT_TYPE) { if (msg.getDelayTimeLevel() > 0) { if (msg.getDelayTimeLevel() > this.defaultMessageStore.getScheduleMessageService().getMaxDelayLevel()) { msg.setDelayTimeLevel(this.defaultMessageStore.getScheduleMessageService().getMaxDelayLevel()); } topic = ScheduleMessageService.SCHEDULE_TOPIC; queueId = ScheduleMessageService.delayLevel2QueueId(msg.getDelayTimeLevel()); MessageAccessor.putProperty(msg, MessageConst.PROPERTY_REAL_TOPIC, msg.getTopic()); MessageAccessor.putProperty(msg, MessageConst.PROPERTY_REAL_QUEUE_ID, String.valueOf(msg.getQueueId())); msg.setPropertiesString(MessageDecoder.messageProperties2String(msg.getProperties())); msg.setTopic(topic); msg.setQueueId(queueId); } } long eclipseTimeInLock = 0; MappedFile unlockMappedFile = null; MappedFile mappedFile = this.mappedFileQueue.getLastMappedFile(); putMessageLock.lock(); try { long beginLockTimestamp = this.defaultMessageStore.getSystemClock().now(); this.beginTimeInLock = beginLockTimestamp; msg.setStoreTimestamp(beginLockTimestamp); if (null == mappedFile || mappedFile.isFull()) { mappedFile = this.mappedFileQueue.getLastMappedFile(0); } if (null == mappedFile) { log.error("create mapped file1 error, topic: " + msg.getTopic() + " clientAddr: " + msg.getBornHostString()); beginTimeInLock = 0; return new PutMessageResult(PutMessageStatus.CREATE_MAPEDFILE_FAILED, null); } result = mappedFile.appendMessage(msg, this.appendMessageCallback); switch (result.getStatus()) { case PUT_OK: break; case END_OF_FILE: unlockMappedFile = mappedFile; mappedFile = this.mappedFileQueue.getLastMappedFile(0); if (null == mappedFile) { log.error("create mapped file2 error, topic: " + msg.getTopic() + " clientAddr: " + msg.getBornHostString()); beginTimeInLock = 0; return new PutMessageResult(PutMessageStatus.CREATE_MAPEDFILE_FAILED, result); } result = mappedFile.appendMessage(msg, this.appendMessageCallback); break; case MESSAGE_SIZE_EXCEEDED: case PROPERTIES_SIZE_EXCEEDED: beginTimeInLock = 0; return new PutMessageResult(PutMessageStatus.MESSAGE_ILLEGAL, result); case UNKNOWN_ERROR: beginTimeInLock = 0; return new PutMessageResult(PutMessageStatus.UNKNOWN_ERROR, result); default: beginTimeInLock = 0; return new PutMessageResult(PutMessageStatus.UNKNOWN_ERROR, result); } eclipseTimeInLock = this.defaultMessageStore.getSystemClock().now() - beginLockTimestamp; beginTimeInLock = 0; } finally { putMessageLock.unlock(); } if (eclipseTimeInLock > 500) { log.warn("[NOTIFYME]putMessage in lock cost time(ms)={}, bodyLength={} AppendMessageResult={}", eclipseTimeInLock, msg.getBody().length, result); } if (null != unlockMappedFile && this.defaultMessageStore.getMessageStoreConfig().isWarmMapedFileEnable()) { this.defaultMessageStore.unlockMappedFile(unlockMappedFile); } PutMessageResult putMessageResult = new PutMessageResult(PutMessageStatus.PUT_OK, result); storeStatsService.getSinglePutMessageTopicTimesTotal(msg.getTopic()).incrementAndGet(); storeStatsService.getSinglePutMessageTopicSizeTotal(topic).addAndGet(result.getWroteBytes()); handleDiskFlush(result, putMessageResult, msg); handleHA(result, putMessageResult, msg); return putMessageResult; }后面也看到在CommitLog 目录下是有大小为 1G 的文件组成,在实现逻辑中,其实是通过 org.apache.rocketmq.store.MappedFileQueue ,外部是存的一个MappedFile的队列,对于写入的场景每次都是通过org.apache.rocketmq.store.MappedFileQueue#getLastMappedFile() 获取最初一个文件,如果还没有创立,或者最初这个文件曾经满了,那就调用 org.apache.rocketmq.store.MappedFileQueue#getLastMappedFile(long) ...

September 13, 2021 · 5 min · jiezi

关于rocketmq:RocketMQ使用指南

RocketMQ 的介绍RocketMQ 作为目前支流的消息中间件之一, 而音讯队列次要利用于以下场景: 异步(不须要同步期待)解耦(利用之间不相互依赖)削峰(防止流量激增导致系统性能问题)RocketMQ 具备以下个性: 音讯的订阅与公布(音讯队列的基本功能)程序音讯(生产的程序与发送的程序统一,包含全局有序和分区有序,全局有序的topic只有一个音讯队列,实用场景:性能要求不高,所有音讯须要严格有序。分区有序的所有音讯依据 sharding key进行分区。同一个分区内的音讯依照 FIFO 程序进行公布和生产。Sharding key 是音讯中用来辨别不同分区的关键字段,和一般音讯的 Key 是齐全不同的概念。 实用场景:性能要求高,对于某一类音讯须要有序,同时有一个sharding key 作为分区字段。)定时音讯(音讯发送到broker后,不会立刻被生产,期待特定工夫投递给真正的topic。)事务音讯(利用本地事务和发送音讯操作能够被定义到全局事务中,要么同时胜利,要么同时失败。通过事务音讯能达到分布式事务的最终统一。)音讯重试(消费者生产失败时,RocketMQ 提供重试机制使得音讯能够被再次生产。)流量管制(当Broker解决音讯的能力达到瓶颈时,通过回绝生产者的发送申请进行流量管制,当消费者的音讯能力达到瓶颈时,通过升高音讯的拉取频率进行流量管制)死信队列(当一条音讯达到最大重试次数后,若生产仍然失败,则表明消费者在失常状况下无奈正确地生产该音讯,此时,音讯队列不会立即将音讯抛弃,而是将其发送到该消费者对应的非凡队列中。)RocketMQ 的概念音讯(MESSAGE)零碎之间相互传递的音讯,每个音讯都属于某一个主题,每个音讯应用惟一的Message ID进行标识,同时音讯能够带有标签(TAG)和 键(KEY)。标签(TAG)每条音讯能够携带标签,用于同一主题下辨别不同类型的音讯。键(KEY)除了标签,RocketMQ的音讯还能够带上KEY,能够有多个KEY。主题(TOPIC)音讯对应的主题,用于示意音讯的类别,一个主题能够对应多条音讯,生产者生产音讯时须要制订主题,消费者生产音讯时也须要制订主题生产者(PRODUCER)发送音讯的利用称为生产者。生产者组(PRODUCER GROUP)同一类的生产者的汇合,每个生存者组蕴含多个生产者。消费者(CONSUMER)生产音讯的利用称为消费者。消费者组(CONSUMER GROUP)同一类的消费者的汇合,每个消费者组蕴含多个消费者。代理服务器(BROKER)音讯直达角色,负责存储音讯、转发音讯。接管从生产者发送来的音讯并存储、同时为消费者的拉取申请作筹备。也存储音讯相干的元数据,包含消费者组、生产进度偏移和主题和队列音讯等。名字服务器(NAME SERVER)相似于注册核心,消费者和生产者的注册和发现都依赖于名字服务器,同时会保留broker的信息, 生产者或消费者可能通过名字服务查找各主题相应的Broker IP列表。多个Namesrv实例组成集群,但互相独立,没有信息替换。消费者获取音讯的形式: 拉取式生产 利用通常被动调用Consumer的拉音讯办法从Broker服务器拉音讯、主动权由利用管制。一旦获取了批量音讯,利用就会启动生产过程。推动式生产 该模式下Broker收到数据后会被动推送给生产端,该生产模式个别实时性较高。生产模式: 集群生产 集群生产模式下,雷同Consumer Group的每个Consumer实例均匀摊派音讯。播送生产 播送生产模式下,雷同Consumer Group的每个Consumer实例都接管全量的音讯。RocketMQ 的架构设计整体架构整个RocketMQ 的架构如下: 能够整个架构由四局部组成,别离是Producer, Conusmer, Name Server, Broker。整个RocketMQ 的工作流程如下: 启动NameServer,NameServer起来后监听端口,期待Broker、Producer、Consumer连上来,相当于一个路由控制中心。Broker启动,跟所有的NameServer放弃长连贯,定时发送心跳包。心跳包中蕴含以后Broker信息(IP+端口等)以及存储所有Topic信息。注册胜利后,NameServer集群中就有Topic跟Broker的映射关系。收发音讯前,先创立Topic,创立Topic时须要指定该Topic要存储在哪些Broker上,也能够在发送音讯时主动创立Topic。Producer发送音讯,启动时先跟NameServer集群中的其中一台建设长连贯,并从NameServer中获取以后发送的Topic存在哪些Broker上,轮询从队列列表中抉择一个队列,而后与队列所在的Broker建设长连贯从而向Broker发消息。Consumer跟Producer相似,跟其中一台NameServer建设长连贯,获取以后订阅Topic存在哪些Broker上,而后间接跟Broker建设连贯通道,开始生产音讯。音讯存储 CommitLog:音讯主体以及元数据的存储主体,存储Producer端写入的音讯主体内容,音讯内容不是定长的。单个文件大小默认1G, 文件名长度为20位,右边补零,残余为起始偏移量,比方00000000000000000000代表了第一个文件,起始偏移量为0,文件大小为1G=1073741824;当第一个文件写满了,第二个文件为00000000001073741824,起始偏移量为1073741824,以此类推。音讯次要是程序写入日志文件,当文件满了,写入下一个文件;ConsumeQueue:音讯生产队列,引入的目标次要是进步音讯生产的性能,因为RocketMQ是基于主题topic的订阅模式,音讯生产是针对主题进行的,如果要遍历commitlog文件中依据topic检索音讯是十分低效的。Consumer即可依据ConsumeQueue来查找待生产的音讯。其中,ConsumeQueue(逻辑生产队列)作为生产音讯的索引,保留了指定Topic下的队列音讯在CommitLog中的起始物理偏移量offset,音讯大小size和音讯Tag的HashCode值。consumequeue文件能够看成是基于topic的commitlog索引文件,故consumequeue文件夹的组织形式如下:topic/queue/file三层组织构造,具体存储门路为:$HOME/store/consumequeue/{topic}/{queueId}/{fileName}。同样consumequeue文件采取定长设计,每一个条目共20个字节,别离为8字节的commitlog物理偏移量、4字节的音讯长度、8字节tag hashcode,单个文件由30W个条目组成,能够像数组一样随机拜访每一个条目,每个ConsumeQueue文件大小约5.72M;IndexFile:IndexFile(索引文件)提供了一种能够通过key或工夫区间来查问音讯的办法。Index文件的存储地位是:$HOME \store\index${fileName},文件名fileName是以创立时的工夫戳命名的,固定的单个IndexFile文件大小约为400M,一个IndexFile能够保留 2000W个索引,IndexFile的底层存储设计为在文件系统中实现HashMap构造,故rocketmq的索引文件其底层实现为hash索引。RocketMQ采纳的是混合型的存储构造,Broker单个实例下所有的队列共用一个日志数据文件(即为CommitLog)来存储。RocketMQ的混合型存储构造(多个Topic的音讯实体内容都存储于一个CommitLog中)针对Producer和Consumer别离采纳了数据和索引局部相拆散的存储构造,Producer发送音讯至Broker端,而后Broker端应用同步或者异步的形式对音讯刷盘长久化,保留至CommitLog中。RocketMQ应用Broker端的后盾服务线程—ReputMessageService不停地散发申请并异步构建ConsumeQueue(逻辑生产队列)和IndexFile(索引文件)数据。 音讯过滤后面有提到Consumer端订阅音讯是通过ConsumeQueue拿到音讯的索引,而后再从CommitLog外面读取真正的音讯实体内容,ConsumeQueue的存储构造如下,能够看到其中有8个字节存储的Message Tag的哈希值,基于Tag的音讯过滤正是基于这个字段值的。 RocketMQ 反对两种音讯过滤形式: Tag过滤形式:Consumer端在订阅音讯时除了指定Topic还能够指定TAG,如果一个音讯有多个TAG,能够用||分隔。其中,Consumer端会将这个订阅申请构建成一个 SubscriptionData,发送一个Pull音讯的申请给Broker端。Broker端从RocketMQ的文件存储层—Store读取数据之前,会用这些数据先构建一个MessageFilter,而后传给Store。Store从 ConsumeQueue读取到一条记录后,会用它记录的音讯tag hash值去做过滤,因为在服务端只是依据hashcode进行判断,无奈准确对tag原始字符串进行过滤,故在音讯生产端拉取到音讯后,还须要对音讯的原始tag字符串进行比对,如果不同,则抛弃该音讯,不进行音讯生产 (如果存在tag的hashcode统一,则可能导致失落音讯)。SQL92的过滤形式:这种形式的大抵做法和下面的Tag过滤形式一样,只是在Store层的具体过滤过程不太一样,真正的 SQL expression 的构建和执行由rocketmq-filter模块负责的。每次过滤都去执行SQL表达式会影响效率,所以RocketMQ应用了BloomFilter防止了每次都去执行。SQL92的表达式上下文为音讯的属性。音讯查问RocketMQ反对两种形式查问: 依照Message Id查问音讯。RocketMQ中的MessageId的长度总共有16字节,其中蕴含了音讯存储主机地址(IP地址和端口),音讯Commit Log offset。“依照MessageId查问音讯”在RocketMQ中具体做法是:Client端从MessageId中解析出Broker的地址(IP地址和端口)和Commit Log的偏移地址后封装成一个RPC申请后通过Remoting通信层发送(业务申请码:VIEW_MESSAGE_BY_ID)。Broker端走的是QueryMessageProcessor,读取音讯的过程用其中的 commitLog offset 和 size 去 commitLog 中找到真正的记录并解析成一个残缺的音讯返回。依照Message Key查问音讯。“依照Message Key查问音讯”,次要是基于RocketMQ的IndexFile索引文件来实现的。RocketMQ的索引文件逻辑构造,相似JDK中HashMap的实现。索引文件的具体构造如下:IndexFile索引文件为用户提供通过“依照Message Key查问音讯”的音讯索引查问服务。如果音讯的properties中设置了UNIQ_KEY这个属性,就用 topic + “#” + UNIQ_KEY的value作为 key 来做写入操作。如果音讯设置了KEYS属性(多个KEY以空格分隔),也会用 topic + “#” + KEY 来做索引。其中的索引数据蕴含了Key Hash/CommitLog Offset/Timestamp/Next Index offset 这四个字段,一共20 Byte。SlotTable中保留每个Slot对应的链表的头指针,NextIndexOffset 保留的是下一个节点的地址。依照Message Key查问音讯”的形式,RocketMQ的具体做法是,次要通过Broker端的QueryMessageProcessor业务处理器来查问,读取音讯的过程就是用topic和key找到IndexFile索引文件中的一条记录,依据其中的commitLog offset从CommitLog文件中读取音讯的实体内容。事务音讯RocketMQ采纳了2PC的思维来实现了提交事务音讯,同时减少一个弥补逻辑来解决二阶段超时或者失败的音讯,如下图所示。上图阐明了事务音讯的大抵计划,其中分为两个流程:失常事务音讯的发送及提交、事务音讯的弥补流程。 ...

August 29, 2021 · 4 min · jiezi

关于rocketmq:RocketMq-修改日志保存目录-无需修改代码

介绍rocketmq默认打印的日志是user.dir目录 因为某些起因须要调整这个目录以升高主磁盘IO占用 所以须要手动调整一下 批改原理是在JVM参数下面加上-Duser.home参数来指定用户根目录地位达到批改日志地位的作用 JAVA_OPT="${JAVA_OPT} -Duser.home=新目录" 步骤一共须要批改两个文件bin/runnamesrv.sh和bin/runserver.sh都是在 JAVA_OPT="${JAVA_OPT} -Djava.ext.dirs=${JAVA_HOME}/jre/lib/ext:${BASE_DIR}/lib:${JAVA_HOME}/lib/ext"前面增加 #rewrite user home JAVA_OPT="${JAVA_OPT} -Duser.home=新目录"重启namesrv,broker即可

July 23, 2021 · 1 min · jiezi

关于rocketmq:顺序消息的实现RocketMQ知识体系5

咱们晓得,kafka 如果要保障程序生产,必须保障音讯保留到同一个patition上,而且为了有序性,只能有一个消费者进行生产。这种状况下,Kafka 就进化成了繁多队列,毫无并发性可言,极大升高零碎性能。那么对于对业务比拟敌对的RocketMQ 是如何实现的呢?首先,咱们循序渐进的来理解下程序音讯的实现。 程序音讯业务应用场景1、电商场景中传递订单状态。 2、同步mysql 的binlong 日志,数据库的操作是有程序的。 3、其余音讯之间有先后的依赖关系,后一条音讯须要依赖于前一条音讯的处理结果的状况。 等等。。。 消息中间件中的程序音讯程序音讯(FIFO 音讯)是 MQ 提供的一种严格依照程序进行公布和生产的音讯类型。程序音讯由两个局部组成:程序公布和程序生产。 程序音讯蕴含两种类型: 分区程序:一个Partition(queue)内所有的音讯依照先进先出的程序进行公布和生产 全局程序:一个Topic内所有的音讯依照先进先出的程序进行公布和生产.然而全局程序极大的升高了零碎的吞吐量,不合乎mq的设计初衷。 那么折中的方法就是抉择分区程序。 【部分程序生产】如何保障程序在MQ的模型中,程序须要由3个阶段去保障: 音讯被发送时放弃程序音讯被存储时放弃和发送的程序统一音讯被生产时放弃和存储的程序统一发送时放弃程序意味着对于有程序要求的音讯,用户应该在同一个线程中采纳同步的形式发送。存储放弃和发送的程序统一则要求在同一线程中被发送进去的音讯A和B,存储时在空间上A肯定在B之前。而生产放弃和存储统一则要求音讯A、B达到Consumer之后必须依照先A后B的程序被解决。 第一点,音讯程序发送,多线程发送的音讯无奈保障有序性,因而,须要业务方在发送时,针对同一个业务编号(如同一笔订单)的音讯须要保障在一个线程内程序发送,在上一个音讯发送胜利后,在进行下一个音讯的发送。对应到mq中,音讯发送办法就得应用同步发送,异步发送无奈保障程序性。 第二点,音讯顺序存储,mq的topic下会存在多个queue,要保障音讯的顺序存储,同一个业务编号的音讯须要被发送到一个queue中。对应到mq中,须要应用MessageQueueSelector来抉择要发送的queue,即对业务编号进行hash,而后依据队列数量对hash值取余,将音讯发送到一个queue中。 第三点,音讯程序生产,要保障音讯程序生产,同一个queue就只能被一个消费者所生产,因而对broker中生产队列加锁是无奈防止的。同一时刻,一个生产队列只能被一个消费者生产,消费者外部,也只能有一个生产线程来生产该队列。即,同一时刻,一个生产队列只能被一个消费者中的一个线程生产。 RocketMQ中程序的实现【Producer端】 Producer端确保音讯程序惟一要做的事件就是将音讯路由到特定的分区,在RocketMQ中,通过MessageQueueSelector来实现分区的抉择。 /** * 音讯队列选择器 */public interface MessageQueueSelector { /** * 抉择音讯队列 * * @param mqs 音讯队列 * @param msg 音讯 * @param arg 参数 * @return 音讯队列 */ MessageQueue select(final List<MessageQueue> mqs, final Message msg, final Object arg);}List<MessageQueue> mqs:音讯要发送的Topic下所有的分区Message msg:音讯对象额定的参数:用户能够传递本人的参数比方如下实现就能够保障雷同的订单的音讯被路由到雷同的分区: long orderId = ((Order) object).getOrderId;return mqs.get(orderId % mqs.size());【Consumer端】 ...

July 23, 2021 · 5 min · jiezi

关于rocketmq:同程旅行基于-RocketMQ-高可用架构实践

简介: 咱们在几年前决定引入 MQ 时,市场上曾经有不少成熟的解决方案,比方 RabbitMQ , ActiveMQ,NSQ,Kafka 等。思考到稳定性、保护老本、公司技术栈等因素,咱们抉择了 RocketMQ。 背景介绍为何抉择 RocketMQ咱们在几年前决定引入 MQ 时,市场上曾经有不少成熟的解决方案,比方 RabbitMQ , ActiveMQ,NSQ,Kafka 等。思考到稳定性、保护老本、公司技术栈等因素,咱们抉择了 RocketMQ : 纯 Java 开发,无依赖,应用简略,呈现问题能 hold ;通过阿里双十一考验,性能、稳定性能够保障;性能实用,发送端:同步、异步、单边、延时发送;生产端:音讯重置,重试队列,死信队列;社区沉闷,出问题能及时沟通解决。应用状况次要用于削峰、解耦、异步解决;已在火车票、机票、酒店等外围业务宽泛应用,扛住微小的微信入口流量;在领取、订单、出票、数据同步等外围流程宽泛应用;每天 1000+ 亿条音讯周转。下图是 MQ 接入框架图 因为公司技术栈起因,client sdk 咱们提供了 java sdk ;对于其余语言,收敛到 http proxy ,屏蔽语言细节,节约保护老本。依照各大业务线,对后端存储节点进行了隔离,互相不影响。 MQ 双核心革新 之前单机房呈现过网络故障,对业务影响较大。为保障业务高可用,同城双核心革新提上了日程。 为何做双核心 单机房故障业务可用;保证数据牢靠:若所有数据都在一个机房,一旦机房故障,数据有失落危险;横向扩容:单机房容量无限,多机房可分担流量。双核心计划 做双核心之前,对同城双核心计划作了些调研,次要有冷(热)备份、双活两种。(过后社区 Dledger 版本还没呈现,Dledger 版本齐全可做为双核心的一种可选计划。) 1)同城冷(热)备份 两个独立的 MQ 集群, 用户流量写到一个主集群,数据实时同步到备用集群,社区有成熟的 RocketMQ Replicator 计划,须要定期同步元数据,比方主题,生产组,生产进度等。 2)同城双活 两个独立 MQ 集群,用户流量写到各自机房的 MQ 集群,数据互相不同步。 平时业务写入各自机房的 MQ 集群,若一个机房挂了,能够将用户申请流量全副切到另一个机房,音讯也会生产到另一个机房。 对于双活计划,须要解决 MQ 集群域名。 1)若两个集群用一个域名,域名能够动静解析到各自机房。此形式要求生产、生产必须在同一个机房。如果生产在 idc1 ,生产在 idc2 ,这样生产、生产各自连贯一个集群,没法生产数据。 ...

July 2, 2021 · 2 min · jiezi

关于rocketmq:一次-RocketMQ-顺序消费延迟的问题定位

一次 RocketMQ 程序生产提早的问题定位问题背景与景象昨晚收到了利用报警,发现线上某个业务生产音讯提早了 54s 多(从音讯发送到MQ 到被生产的距离): 2021-06-30T23:12:46.756 message processing is incredibly delayed! (Current delay time: 54725, incredible delay count in 10 seconds: 5677) 查看 RocketMQ 的监控,发现的确产生了比拟多的音讯积压: 从 RocketMQ-Console 下面查看 Topic 的消费者: 这个 Topic,业务要求是须要有序的。所以在发送的时候,指定了业务 Key,并且生产的时候,应用的是程序生产模式。 咱们应用了 RocketMQ 集群,有三个 Broker,对于这个 Topic,每个 Broker 下面都有 8 个 ReadQueue 和 WriteQueue。这里简略提一下 ReadQueue 和 WriteQueue 的意思: 在 RocketMQ 中,音讯发送时应用 WriteQueue 个数返回路由信息,而音讯生产时依照 ReadQueue 个数返回路由信息。在物理文件层面,只有 WriteQueue 才会创立文件。举个例子:设置 WriteQueueNum = 8,ReadQueueNum = 4,会创立 8 个文件夹,代表 0 1 2 3 4 5 6 7 这 8 个队列,但在音讯生产时,路由信息只返回 4,在具体拉取音讯时,就只会生产0 1 2 3 这 4 个队列中的音讯,4 5 6 7 压根就没有被生产。反过来,如果设置 WriteQueueNum = 4,ReadQueueNum = 8,在生产音讯时只会往0 1 2 3中生产音讯,生产音讯时则会从0 1 2 3 4 5 6 7 所有的队列中生产,当然 4 5 6 7中压根就没有音讯 ,假如生产是 Group 生产,Group 中有两个消费者,事实上只有第一个消费者在真正的生产音讯(0 1 2 3),第二个消费者压根就生产不到音讯(4 5 6 7)。个别咱们都会设置这两个值雷同,只有在须要缩容 topic 的队列数量的时候,才会设置他们不同。 ...

July 1, 2021 · 3 min · jiezi

关于rocketmq:全网首发纯手打RocketMQ笔记帮你解决95以上的问题

音讯队列 RocketMQ 是阿里巴巴团体基于高可用分布式集群技术,自主研发的云正式商用的业余消息中间件,既可为分布式应用零碎提供异步解耦和削峰填谷的能力,同时也具备互联网利用所需的海量音讯沉积、高吞吐、牢靠重试等个性,是阿里巴巴双 11 应用的外围产品。 6月初偶尔从敌人那里失去一份纯手打“RocketMQ笔记”(貌似全网都没有这份学习笔记,中大奖了,哈哈哈),花了十天工夫刷了第一遍,感觉挺不错的,明天拿进去献个宝。全网首发,纯手打“RocketMQ笔记”,帮你解决95%以上的问题! 留神:这份纯手打“RocketMQ笔记”,有百来页,一篇文章只够介绍大略的内容,更多的细节无奈全副展现进去,但 残缺的原件 已整顿在此!纯手打“RocketMQ笔记”第一节:RocketMQ介绍1.1 外围概念(主题、生产者、消费者、音讯)1.2 RocketMQ的设计理念和指标(设计理念、设计指标) 第二节:RocketMQ中音讯的发送2.1 单向[OneWay]发送(代码演示、Producer Group、Producer实例、Message Key、Tag)2.2 牢靠同步发送(代码演示、Message ID、SendStatus、Queue)2.3 牢靠异步发送(代码演示)2.4 RocketMQ中音讯发送的衡量 第三节:RocketMQ音讯生产3.1 集群音讯和播送生产(基本概念)3.2 场景比照(集群音讯模式、播送音讯模式、应用集群模式模仿播送)3.3 生产形式(推模式、拉模式) 第四节:深刻音讯发送4.1 音讯生产者流程4.2 批量音讯发送4.3 音讯重试机制(躲避准则) 第五节:深刻音讯模式5.1 拉模式5.2 推模式(长轮询、流量管制、音讯队列负载与从新散布机制、音讯确认、音讯进度存储、推模式总结)第六节:程序音讯6.1 全局程序音讯(实用场景、示例)6.2 局部程序音讯第七节:延时音讯7.1 概念介绍7.2 实用场景7.3 应用形式 第八节:死信队列8.1 概念介绍8.2 实用场景(死信音讯的个性、死信队列的个性)第九节:生产幂等9.1 什么是生产幂等9.2 须要解决的场景9.3 解决办法第十节:音讯过滤10.1 概念介绍10.2 表达式过滤10.3 类过滤第十一节:RocketMQ存储构造概要设计11.1 音讯存储构造(CommitLog、ConsumeQueue、IndexFile、Config、其余)11.2 内存映射11.3 文件刷盘机制(异步刷盘形式、同步刷盘形式、总结)11.4 过期文件删除(过期判断、删除条件) 第十二节:RocketMQ中的事务音讯12.1 事务音讯实现思维12.2 两阶段提交12.3 事务状态回查机制12.4 代码实现 第十三节:RocketMQ主从同步(HA)机制13.1 RocketMQ集群部署模式(集群部署模式:单master模式、多master模式、多master多Slave异步复制模式、多master多slave同步双写模式、多主模式与数据反复;装置部署过程)13.2 主从复制原理13.3 读写拆散机制13.4 与Spring集成(Pom文件、生产者、消费者)13.5 与SpringBoot集成 第14节:限时订单实战14.1 什么是限时订单14.2 如何实现限时订单(限时订单的流程、限时订单实现的要害、轮询数据库、Java自身的提供的解决方案、从零碎可用性角度思考、从零碎伸缩性角度思考)14.3 用RocketMQ实现限时订单(延时音讯:概念介绍、实用场景;外围的代码局部) 第十五节:RocketMQ源码剖析15.1 RocketMQ整体架构15.2 NameServer15.3 RocketMQ服务启动15.4 源码剖析之音讯的前因后果(音讯的生产:Client中的音讯发送、Broker中音讯的生产、Broker中更新音讯队列和索引文件;音讯的生产:Client中的音讯者启动流程、音讯的拉取、音讯的生产) ...

May 30, 2021 · 1 min · jiezi

关于rocketmq:快手基于RocketMQ的在线消息系统建设实践

简介: 快手须要建设一个次要面向在线业务的音讯零碎作为 Kafka 的补充,低提早、高并发、高可用、高牢靠的分布式消息中间件 RocketMQ 正是咱们所需的。 作者:黄理 黄理,10多年软件开发和架构教训,热衷于代码和性能优化,开发和参加过多个开源我的项目。曾在淘宝任业务架构师多年,以后在快手负责在线音讯零碎建设工作。 为什么建设在线音讯零碎在引入RocketMQ之前,快手曾经在大量的应用Kafka了,但并非所有状况下Kafka都是最合适的,比方以下场景: 业务心愿个别生产失败当前能够重试,并且不梗塞后续其它音讯的生产。业务心愿音讯能够提早一段时间再投递。业务须要发送的时候保障数据库操作和音讯发送是统一的(也就是事务发送)。为了排查问题,有的时候业务须要肯定的单个音讯查问能力。为了应答以上这类场景,咱们须要建设一个次要面向在线业务的音讯零碎,作为Kafka的补充。在考查的一些消息中间件中,RocketMQ和业务需要匹配度比拟高,同时部署构造简略,应用的公司也比拟多,于是最初咱们就采纳了RocketMQ。 部署模式和落地策略在一个已有的体系内落地一个开软软件,通常大略有两种形式: 形式一,在开源软件的根底上做深度批改,很容易实现公司内须要的定制性能。但和社区开源版本各奔前程,当前如何降级? 形式二,尽量不批改社区版本(或缩小不兼容的批改),而是在它的外围或者下层进一步包装来实现公司外部须要的定制性能。 注:上图形式一的图画的比拟极其,实际上很多公司是形式一、形式二联合的。 咱们抉择了形式二。最早的时候,咱们应用的是4.5.2版本,起初社区4.7版本大幅减小了同步复制的提早,正好咱们的部署模式就是同步复制,于是就很轻松的降级了4.7系列,享受了新版本的红利。 在部署集群的时候,还会面临很多部署策略的抉择: • 大集群 vs 小集群 • 抉择正本数 • 同步刷盘 vs 异步刷盘 • 同步复制  vs 异步复制 • SSD vs 机械硬盘 大集群会有更好的性能弹性,而小集群具备更好的隔离型,此外小集群能够不须要跨可用区/IDC部署,所以会有更好的健壮性,咱们十分看重稳定性,因而抉择了小集群。集群同步复制异步刷盘,首选SSD。 客户端封装策略如上所述,咱们没有在Rocketmq外面做深度批改,所以须要提供一个SDK来提供公司内的须要的定制性能,这个SDK大略是这样的: 对外只提供最根本的API,所有拜访必须通过咱们提供的接口。简洁的API就像冰山的一个角,除了对外的简略接口,上面所有的货色都能够降级更换,而不会毁坏兼容性。 业务开发起来也很简略,只有须要提供Topic(全局惟一)和Group就能够生产和生产,不必提供环境、Name Server地址等。SDK外部会依据Topic解析出集群Name Server的地址,而后连贯相应的集群。生产环境和测试环境环境会解析出不同的地址,从而实现了隔离。 上图分为3层,第二层是通用的,第三层才对应具体的MQ实现,因而,实践上能够更换为其它消息中间件,而客户端程序不须要批改。 SDK外部集成了热变更机制,能够在不重启client的状况下做动静配置,比方下发路由策略(更换集群name server的地址,或者连贯到别的集群去),Client的线程数、超时工夫等。通过maven强制更新机制,能够保障业务应用的SDK基本上是最新的。 集群负载平衡 & 机房灾备所有的Topic默认都调配到两个可用区,生产者和消费者会同时连贯至多两个独立集群(散布在不同的可用区),如下图: 生产者同时连贯两个集群,如果可用区A呈现故障,流量就会主动切换到可用区B的集群2去。咱们开发了一个小组件来实现自适应的集群负载平衡,它蕴含以下能力: • 千万级OPS • 灵便的权重调整策略 • 健康检查反对/事件告诉 • 并发度管制(主动升高响应慢的服务器的申请数) • 资源优先级(相似Envoy,实现本地机房优先,或是被调服务器很多的时候选取一个子集来调用) • 主动优先级治理 • 增量热变更 实际上它并不仅仅用于音讯生产者,而是一个通用的主调方负载平衡类库,能够在github上找到: https://github.com/PhantomThief/simple-failover-java 外围的SimpleFailover接口和PriorityFailover类没有传递第三方依赖,非常容易整合。 多样的音讯性能提早音讯提早音讯是十分重要的业务性能,不过RocketMQ内置的提早音讯只能反对几个固定的提早级别,所以咱们又开发了独自的Delay Server来调度提早音讯: ...

April 8, 2021 · 1 min · jiezi

关于rocketmq:阿里的-RocketMQ-如何让双十一峰值之下-0-故障

简介: 2020 年双十一交易峰值达到 58.3 W 笔/秒,消息中间件 RocketMQ 持续数年 0 故障丝般顺滑地完满反对了整个团体大促的各类业务安稳。 作者 | 愈安起源 | 阿里巴巴云原生公众号 2020 年双十一交易峰值达到 58.3 W 笔/秒,消息中间件 RocketMQ 持续数年 0 故障丝般顺滑地完满反对了整个团体大促的各类业务安稳。2020 年双十一大促中,消息中间件 RocketMQ 产生了以下几个方面的变动: 云原生化实际:实现运维层面的云原生化革新,实现 Kubernetes 化。性能优化:音讯过滤优化交易集群性能晋升 30%。全新的生产模型:对于提早敏感业务提供新的生产模式,升高因公布、重启等场景下导致的生产提早。云原生化实际背景Kubernetes 作为目前云原生化技术栈实际中重要的一环,其生态曾经逐渐建设并日益丰盛。目前,服务于团体外部的 RocketMQ 集群领有微小的规模以及各种历史因素,因而在运维方面存在相当一部分痛点,咱们心愿可能通过云原生技术栈来尝试找到对应解决方案,并同时实现降本提效,达到无人值守的自动化运维。 消息中间件早在 2016 年,通过外部团队提供的中间件部署平台实现了容器化和自动化公布,整体的运维比 2016 年前曾经有了很大的进步,然而作为一个有状态的服务,在运维层面依然存在较多的问题。 中间件部署平台帮咱们实现了资源的申请,容器的创立、初始化、镜像装置等一系列的根底工作,然而因为中间件各个产品都有本人不同的部署逻辑,所以在利用的公布上,就是各利用本人的定制化了。中间件部署平台的开发也不齐全理解团体内 RocketMQ 的部署过程是怎么的。 因而在 2016 年的时候,部署平台须要咱们去亲自实现消息中间件的利用公布代码。尽管部署平台大大晋升了咱们的运维效率,甚至还能实现一键公布,然而这样的计划也有不少的问题。比拟显著的就是,当咱们的公布逻辑有变动的时候,还须要去批改部署平台对应的代码,须要部署平台降级来反对咱们,用最近比拟风行的一个说法,就是相当不云原生。 同样在故障机替换、集群缩容等操作中,存在局部人工参加的工作,如切流,沉积数据的确认等。咱们尝试过在部署平台中集成更多消息中间件本人的运维逻辑,不过在其余团队的工程里写本人的业务代码,的确也是一个不太敌对的实现计划,因而咱们心愿通过 Kubernetes 来实现消息中间件本人的 operator 。咱们同样心愿利用云化后云盘的多正本能力来升高咱们的机器老本并升高主备运维的复杂程度。 通过一段时间的跟进与探讨,最终再次由外部团队承当了建设云原生利用运维平台的工作,并依靠于中间件部署平台的教训,借助云原生技术栈,实现对有状态利用自动化运维的冲破。 实现 整体的实现计划如上图所示,通过自定义的 CRD 对消息中间件的业务模型进行形象,将原有的在中间件部署平台的业务公布部署逻辑下沉到消息中间件本人的 operator 中,托管在外部 Kubernetes 平台上。该平台负责所有的容器生产、初始化以及团体内所有线上环境的基线部署,屏蔽掉 IaaS 层的所有细节。 Operator 承当了所有的新建集群、扩容、缩容、迁徙的全副逻辑,包含每个 pod 对应的 brokerName 主动生成、配置文件,依据集群不同性能而配置的各种开关,元数据的同步复制等等。同时之前一些人工的相干操作,比方切流时候的流量察看,下线前的沉积数据察看等也全副集成到了 operator 中。当咱们有需要从新批改各种运维逻辑的时候,也再也不必去依赖通用的具体实现,批改本人的 operator 即可。 ...

April 7, 2021 · 2 min · jiezi

关于rocketmq:记一次RocketMQ消息消费异常

记一次RocketMQ 音讯曾经生产然则cosumer offset没有更新的问题 发现问题: 开发中在我的项目重启时会反复生产音讯,但其实音讯曾经生产过了。 查找问题: 1.RocketMq console查看,发现订阅组音讯提早 2.从音讯看message Detail 对应的consumerGroup trackType为 not conume yet 3.我的项目日志也没有任何谬误日志,然而依据相干业务查询数据库发现数据曾经解决实现 4.业务代码断点,没有抛出任何异样,通过resend message也能失常生产 5.狐疑是不是rocketMq 更新offset的定时工作没有启动 然而通过源码断点MQClientInstance 定时工作失常,只是每次更新的offset都是原offet 6.看看是不是生产的时候出了问题 因为是用的spring-boot整合的client,跟踪consumer源码,代码在DefaultRocketMQListenerContainer.handleMessage办法中 然而一切正常,再往上跟踪到DefaultMessageListenerConcurrently public class DefaultMessageListenerConcurrently implements MessageListenerConcurrently { @SuppressWarnings("unchecked") @Override public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) { for (MessageExt messageExt : msgs) { log.debug("received msg: {}", messageExt); try { long now = System.currentTimeMillis(); handleMessage(messageExt); long costTime = System.currentTimeMillis() - now; log.debug("consume {} cost: {} ms", messageExt.getMsgId(), costTime); } catch (Exception e) { ...

January 27, 2021 · 1 min · jiezi

关于rocketmq:RocketMQ-简介

文章首发于公众号《程序员果果》地址 : http://blog.itwolfed.com/blog/97简介RocketMQ是由阿里捐献给Apache的一款低提早、高并发、高可用、高牢靠的分布式消息中间件。经验了淘宝双十一的洗礼。RocketMQ既可为分布式应用零碎提供异步解耦和削峰填谷的能力,同时也具备互联网利用所需的海量音讯沉积、高吞吐、牢靠重试等个性。 外围概念Topic:音讯主题,一级音讯类型,生产者向其发送音讯。Message:生产者向Topic发送并最终传送给消费者的数据音讯的载体。音讯属性:生产者能够为音讯定义的属性,蕴含Message Key和Tag。Message Key:音讯的业务标识,由音讯生产者(Producer)设置,惟一标识某个业务逻辑。Message ID:音讯的全局惟一标识,由音讯队列RocketMQ零碎主动生成,惟一标识某条音讯。Tag:音讯标签,二级音讯类型,用来进一步辨别某个Topic下的音讯分类Producer:也称为音讯发布者,负责生产并发送音讯至Topic。Consumer:也称为音讯订阅者,负责从Topic接管并生产音讯。分区:即Topic Partition,物理上的概念。每个Topic蕴含一个或多个分区。生产位点:每个Topic会有多个分区,每个分区会统计以后音讯的总条数,这个称为最大位点MaxOffset;分区的起始地位对应的地位叫做起始位点MinOffset。Group:一类生产者或消费者,这类生产者或消费者通常生产或生产同一类音讯,且音讯公布或订阅的逻辑统一。Group ID:Group的标识。队列:个Topic下会由一到多个队列来存储音讯。Exactly-Once投递语义:Exactly-Once投递语义是指发送到音讯零碎的音讯只能被Consumer解决且仅解决一次,即便Producer重试音讯发送导致某音讯反复投递,该音讯在Consumer也只被生产一次。集群生产:一个Group ID所标识的所有Consumer均匀摊派生产音讯。例如某个Topic有9条音讯,一个Group ID有3个Consumer实例,那么在集群生产模式下每个实例均匀摊派,只生产其中的3条音讯。播送生产:一个Group ID所标识的所有Consumer都会各自生产某条音讯一次。例如某个Topic有9条音讯,一个Group ID有3个Consumer实例,那么在播送生产模式下每个实例都会各自生产9条音讯。定时音讯:Producer将音讯发送到音讯队列RocketMQ服务端,但并不冀望这条音讯立马投递,而是推延到在以后工夫点之后的某一个工夫投递到Consumer进行生产,该音讯即定时音讯。延时音讯:Producer将音讯发送到音讯队列RocketMQ服务端,但并不冀望这条音讯立马投递,而是提早肯定工夫后才投递到Consumer进行生产,该音讯即延时音讯。事务音讯:RocketMQ提供相似X/Open XA的散布事务性能,通过音讯队列RocketMQ的事务音讯能达到分布式事务的最终统一。程序音讯:RocketMQ提供的一种依照程序进行公布和生产的音讯类型,分为全局程序音讯和分区程序音讯。全局程序音讯:对于指定的一个Topic,所有音讯依照严格的先入先出(FIFO)的程序进行公布和生产。分区程序音讯:对于指定的一个Topic,所有音讯依据Sharding Key进行区块分区。同一个分区内的音讯依照严格的FIFO程序进行公布和生产。Sharding Key是程序音讯中用来辨别不同分区的关键字段,和一般音讯的Message Key是齐全不同的概念。音讯沉积:Producer曾经将音讯发送到音讯队列RocketMQ的服务端,但因为Consumer生产能力无限,未能在短时间内将所有音讯正确生产掉,此时在音讯队列RocketMQ的服务端保留着未被生产的音讯,该状态即音讯沉积。音讯过滤:Consumer能够依据音讯标签(Tag)对音讯进行过滤,确保Consumer最终只接管被过滤后的音讯类型。音讯过滤在音讯队列RocketMQ的服务端实现。音讯轨迹:在一条音讯从Producer收回到Consumer生产处理过程中,由各个相干节点的工夫、地点等数据汇聚而成的残缺链路信息。通过音讯轨迹,您能清晰定位音讯从Producer收回,经由音讯队列RocketMQ服务端,投递给Consumer的残缺链路,不便定位排查问题。重置生产位点:以时间轴为坐标,在音讯长久化存储的工夫范畴内(默认3天),从新设置Consumer对已订阅的Topic的生产进度,设置实现后Consumer将接管设定工夫点之后由Producer发送到音讯队列RocketMQ服务端的音讯。死信队列:死信队列用于解决无奈被失常生产的音讯。当一条音讯首次生产失败,音讯队列RocketMQ会主动进行音讯重试;达到最大重试次数后,若生产仍然失败,则表明Consumer在失常状况下无奈正确地生产该音讯。此时,音讯队列RocketMQ不会立即将音讯抛弃,而是将这条音讯发送到该Consumer对应的非凡队列中。音讯队列RocketMQ将这种失常状况下无奈被生产的音讯称为死信音讯(Dead-Letter Message),将存储死信音讯的非凡队列称为死信队列(Dead-Letter Queue)。 音讯收发模型音讯队列RocketMQ反对公布和订阅模型,音讯生产者利用创立Topic并将音讯发送到Topic。消费者利用创立对Topic的订阅以便从其接管音讯。通信能够是一对多(扇出)、多对一(扇入)和多对多。具体通信如下图所示。 生产者集群:用来示意发送音讯利用,一个生产者集群下蕴含多个生产者实例,能够是多台机器,也能够是一台机器的多个过程,或者一个过程的多个生产者对象。一个生产者集群能够发送多个Topic音讯。发送分布式事务音讯时,如果生产者中途意外宕机,音讯队列RocketMQ服务端会被动回调生产者集群的任意一台机器来确认事务状态。 消费者集群:用来示意生产音讯利用,一个消费者集群下蕴含多个消费者实例,能够是多台机器,也能够是多个过程,或者是一个过程的多个消费者对象。一个消费者集群下的多个消费者以均摊形式生产音讯。如果设置的是播送形式,那么这个消费者集群下的每个实例都生产全量数据。 一个消费者集群对应一个Group ID,一个Group ID能够订阅多个Topic,如上图中的Group 2所示。Group和Topic的订阅关系能够通过间接在程序中设置即可。 利用场景削峰填谷:诸如秒杀、抢红包、企业开门红等大型流动时皆会带来较高的流量脉冲,或因没做相应的爱护而导致系统超负荷甚至解体,或因限度太过导致申请大量失败而影响用户体验,音讯队列RocketMQ可提供削峰填谷的服务来解决该问题。异步解耦:交易系统作为淘宝和天猫主站最外围的零碎,每笔交易订单数据的产生会引起几百个上游业务零碎的关注,包含物流、购物车、积分、流计算剖析等等,整体业务零碎宏大而且简单,音讯队列RocketMQ可实现异步通信和利用解耦,确保主站业务的连续性。程序收发:细数日常中须要保障程序的利用场景十分多,例如证券交易过程工夫优先准则,交易系统中的订单创立、领取、退款等流程,航班中的旅客登机音讯解决等等。与先进先出FIFO(First In First Out)原理相似,音讯队列RocketMQ提供的程序音讯即保障音讯FIFO。分布式事务一致性:交易系统、领取红包等场景须要确保数据的最终一致性,大量引入音讯队列RocketMQ的分布式事务,既能够实现零碎之间的解耦,又能够保障最终的数据一致性。大数据分析:数据在“流动”中产生价值,传统数据分析大多是基于批量计算模型,而无奈做到实时的数据分析,利用阿里云音讯队列RocketMQ与流式计算引擎相结合,能够很不便的实现业务数据的实时剖析。分布式缓存同步:天猫双11大促,各个分会场目不暇接的商品须要实时感知价格变动,大量并发拜访数据库导致会场页面响应工夫长,集中式缓存因带宽瓶颈,限度了商品变更的拜访流量,通过音讯队列RocketMQ构建分布式缓存,实时告诉商品数据的变动。下文先以用户注册为场景阐明音讯队列RocketMQ如何实现以下性能: 异步解耦分布式事务的数据一致性音讯的程序收发最初,再以电商的秒杀场景和价格同步场景别离阐明音讯队列RocketMQ所实现的削峰填谷和大规模机器的缓存同步。 异步解耦传统解决最常见的一个场景是用户注册后,须要发送注册邮件和短信告诉,以告知用户注册胜利。传统的做法有以下两种: 串行形式 数据流动如下所述: 您在注册页面填写账号和明码并提交注册信息,这些注册信息首先会被写入注册零碎。注册信息写入注册零碎胜利后,再发送申请至邮件告诉零碎。邮件告诉零碎收到申请后向用户发送邮件告诉。邮件告诉零碎接管注册零碎申请后再向上游的短信告诉零碎发送申请。短信告诉零碎收到申请后向用户发送短信告诉。以上三个工作全副实现后,才返回注册后果到客户端,用户能力应用账号登录。 假如每个工作耗时别离为50 ms,则用户须要在注册页面期待总共150 ms能力登录。 并行形式 数据流动如下所述: 用户在注册页面填写账号和明码并提交注册信息,这些注册信息首先会被写入注册零碎。注册信息写入注册零碎胜利后,再同时发送申请至邮件和短信告诉零碎。邮件和短信告诉零碎收到申请后别离向用户发送邮件和短信告诉。以上两个工作全副实现后,才返回注册后果到客户端,用户能力应用账号登录。 假如每个工作耗时别离为50 ms,其中,邮件和短信告诉并行实现,则用户须要在注册页面期待总共100 ms能力登录。 异步解耦对于用户来说,注册性能理论只须要注册零碎存储用户的账户信息后,该用户便能够登录,后续的注册短信和邮件不是即时须要关注的步骤。 对于注册零碎而言,发送注册胜利的短信和邮件告诉并不一定要绑定在一起同步实现,所以理论当数据写入注册零碎后,注册零碎就能够把其余的操作放入对应的音讯队列RocketMQ中而后马上返回用户后果,由音讯队列RocketMQ异步地进行这些操作。 数据流动如下所述: 用户在注册页面填写账号和明码并提交注册信息,这些注册信息首先会被写入注册零碎。注册信息写入注册零碎胜利后,再发送音讯至音讯队列RocketMQ。音讯队列RocketMQ会马上返回响应给注册零碎,注册实现。用户可立刻登录。上游的邮件和短信告诉零碎订阅音讯队列RocketMQ的此类注册申请音讯,即可向用户发送邮件和短信告诉,实现所有的注册流程。用户只需在注册页面期待注册数据写入注册零碎和音讯队列RocketMQ的工夫,即期待55 ms即可登录。 异步解耦是音讯队列RocketMQ的次要特点,次要目标是缩小申请响应工夫和解耦。次要的实用场景就是将比拟耗时而且不须要即时(同步)返回后果的操作作为音讯放入音讯队列。同时,因为应用了音讯队列RocketMQ,只有保障音讯格局不变,音讯的发送方和接管方并不需要彼此分割,也不须要受对方的影响,即解耦。 分布式事务的数据一致性注册零碎注册的流程中,用户入口在网页注册零碎,告诉零碎在邮件系统,两个零碎之间的数据须要放弃最终统一。 一般音讯解决如上所述,注册零碎和邮件告诉零碎之间通过音讯队列进行异步解决。注册零碎将注册信息写入注册零碎之后,发送一条注册胜利的音讯到音讯队列RocketMQ,邮件告诉零碎订阅音讯队列RocketMQ的注册音讯,做相应的业务解决,发送注册胜利或者失败的邮件。 流程阐明如下: 注册零碎发动注册。注册零碎向音讯队列RocketMQ发送注册音讯胜利与否的音讯。 2.1. 音讯发送胜利,进入3。2.2. 音讯发送失败,导致邮件告诉零碎未收到音讯队列RocketMQ发送的注册胜利与否的音讯,而无奈发送邮件,最终邮件告诉零碎和注册零碎之间的状态数据不统一。 邮件告诉零碎收到音讯队列RocketMQ的注册胜利音讯。邮件告诉零碎发送注册胜利邮件给用户。在这样的状况下,尽管实现了零碎间的解耦,上游零碎不须要关怀上游零碎的业务处理结果;然而数据一致性不好解决,如何保障邮件告诉零碎状态与注册零碎状态的最终统一。 事务音讯解决此时,须要利用音讯队列RocketMQ所提供的事务音讯来实现零碎间的状态数据一致性。 流程阐明如下: ...

January 6, 2021 · 1 min · jiezi

关于rocketmq:RocketMQ三RocketMQ整合SpringCloud实现分布式事务

事务音讯的原理 上面来看 RocketMQ 的事务音讯是如何来发送“可靠消息”的,只须要以下三步: 发送半音讯(半音讯不会发送给消费者)执行本地事务提交音讯 实现事务音讯发送后,消费者就能够以失常的形式来生产数据。 RocketMQ 的主动重发机制在绝大多数状况下,都能够保障音讯被正确生产。 如果音讯最终生产失败了,还能够由人工解决进行托底。 下面剖析的是失常状况下的执行流程。上面再来看两种谬误状况: 事务执行失败时回滚音讯服务器无奈得悉音讯状态时,须要被动回查音讯状态回滚: 音讯回查: 生产者package demo8;import org.apache.rocketmq.client.exception.MQClientException;import org.apache.rocketmq.client.producer.LocalTransactionState;import org.apache.rocketmq.client.producer.TransactionListener;import org.apache.rocketmq.client.producer.TransactionMQProducer;import org.apache.rocketmq.client.producer.TransactionSendResult;import org.apache.rocketmq.common.message.Message;import org.apache.rocketmq.common.message.MessageExt;import java.util.Scanner;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.Executors;public class Producer { public static void main(String[] args) throws MQClientException { TransactionMQProducer p = new TransactionMQProducer("producer-demo8"); p.setNamesrvAddr("192.168.64.151:9876;192.168.64.152:9876"); p.setExecutorService(Executors.newFixedThreadPool(5)); p.setTransactionListener(new TransactionListener() { ConcurrentHashMap<String, LocalTransactionState> localTx = new ConcurrentHashMap<>(); /* 在这里执行本地事务 */ @Override public LocalTransactionState executeLocalTransaction(Message message, Object o) { System.out.println("执行本地事务"); if (Math.random()<0.333) { System.out.println("本地事务执行胜利, 按回车提交事务音讯"); new Scanner(System.in).nextLine(); localTx.put(message.getTransactionId(), LocalTransactionState.COMMIT_MESSAGE); return LocalTransactionState.COMMIT_MESSAGE; } else if (Math.random()<0.666) { System.out.println("本地事务执行失败, 按回车回滚事务音讯"); new Scanner(System.in).nextLine(); localTx.put(message.getTransactionId(), LocalTransactionState.ROLLBACK_MESSAGE); return LocalTransactionState.ROLLBACK_MESSAGE; } else { System.out.println("本地事务执行状况未知, 按回车持续"); new Scanner(System.in).nextLine(); localTx.put(message.getTransactionId(), LocalTransactionState.UNKNOW); return LocalTransactionState.UNKNOW; } } /* 回查办法 检测频率默认1分钟,可通过在broker.conf文件中设置transactionCheckInterval的值来扭转默认值,单位为毫秒。 */ @Override public LocalTransactionState checkLocalTransaction(MessageExt messageExt) { System.out.println("服务器正在回查音讯状态"); LocalTransactionState s = localTx.get(messageExt.getTransactionId()); if (s == null || s == LocalTransactionState.UNKNOW) { s = LocalTransactionState.ROLLBACK_MESSAGE; } return s; } }); p.start(); String topic = "Topic8"; while (true) { System.out.print("输出音讯,用逗号分隔多条音讯: "); String[] a = new Scanner(System.in).nextLine().split(","); for (String s : a) { Message msg = new Message(topic, s.getBytes()); System.out.println("---------发送半音讯-----------"); TransactionSendResult r = p.sendMessageInTransaction(msg, null); System.out.println("事务音讯发送后果: "+ r.getLocalTransactionState().name()); } } }} 消费者package demo8;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;/*如果返回 RECONSUME_LATER, 服务器会期待一会再重试发送音讯音讯属性默认设置 DELAY=6, 等待时间为 2 分钟, org/apache/rocketmq/store/config/MessageStoreConfig.java this.messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h"; */public class Consumer { public static void main(String[] args) throws Exception { DefaultMQPushConsumer c = new DefaultMQPushConsumer("consumer-demo8"); c.setNamesrvAddr("192.168.64.151:9876:192.168.64.152:9876"); c.subscribe("Topic8", "*"); c.registerMessageListener(new MessageListenerConcurrently() { @Override public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext ctx) { for (MessageExt msg : list) { System.out.println(new String(msg.getBody()) + " - " + msg); } if (Math.random()<0.5) { System.out.println("音讯解决实现"); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } else { System.out.println("音讯解决失败, 要求服务器稍后重试发送音讯"); return ConsumeConcurrentlyStatus.RECONSUME_LATER; } } }); c.start(); System.out.println("开始生产数据"); }}筹备订单我的项目案例新建 rocketmq-dtx 工程新建 Empty Project:工程命名为 rocketmq-dtx,寄存到任意文件夹下: ...

December 14, 2020 · 13 min · jiezi

关于rocketmq:RocketMQ二RocketMQ整合Springboot

RocketMQ原生API收发音讯代码样例pom文件新建 maven 我的项目或 module,增加 rocketmq-client 依赖。 <?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>cn.tedu</groupId> <artifactId>demo1</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.apache.rocketmq</groupId> <artifactId>rocketmq-client</artifactId> <version>4.7.1</version> </dependency> <dependency> <groupId>org.apache.rocketmq</groupId> <artifactId>rocketmq-store</artifactId> <version>4.7.1</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build></project>同步音讯 同步音讯发送要保障强一致性,发到master的音讯向slave复制后,才会向生产者发送反馈信息。 这种可靠性同步地发送形式应用的比拟宽泛,比方:重要的音讯告诉,短信告诉。 生产者package demo1;import org.apache.rocketmq.client.producer.DefaultMQProducer;import org.apache.rocketmq.client.producer.SendResult;import org.apache.rocketmq.common.message.Message;import java.util.Scanner;/*发送同步音讯 */public class Producer { public static void main(String[] args) throws Exception { /* group 雷同的生产者成为一个生产者组 标识发送同一类音讯的Producer,通常发送逻辑统一。 发送一般音讯的时候,仅标识应用,并无特地用途。 若发送事务音讯,发送某条音讯的producer-A宕机, 使得事务音讯始终处于PREPARED状态并超时, 则broker会回查同一个group的其余producer, 确认这条音讯应该commit还是rollback。 但开源版本并不齐全反对事务音讯(阉割了事务回查的代码)。????? */ DefaultMQProducer p = new DefaultMQProducer("producer-demo1"); /* 连贯nameserver集群, 取得注册的broker信息 */ p.setNamesrvAddr("192.168.64.151:9876:192.168.64.152:9876"); p.start(); /* 主题相当于是音讯的分类, 一类音讯应用一个主题 */ String topic = "Topic1"; /* tag 相当于是音讯的二级分类, 在一个主题下, 能够通过 tag 再对音讯进行分类 */ String tag = "TagA"; while (true) { System.out.print("输出音讯,用逗号分隔多条音讯: "); String[] a = new Scanner(System.in).nextLine().split(","); for (String s : a) { Message msg = new Message(topic, tag, s.getBytes()); //一级分类, 二级分类, 音讯内容 SendResult r = p.send(msg);// 发送音讯后会失去服务器反馈, 蕴含: smsgId, sendStatus, queue, queueOffset, offsetMsgId System.out.println(r); } } }}消费者消费者的要点: ...

December 13, 2020 · 8 min · jiezi

关于rocketmq:RocketMQ一RocketMQ简介

RocketMQ装置装置配置 jdk81. 上传jdk压缩文件 将文件jdk-8u212-linux-x64.tar.gz上传到 /root 目录 2. 解压缩 执行解压命令 # 将jdk解压到 /usr/local/ 目录tar -xf jdk-8u212-linux-x64.tar.gz -C /usr/local/# 切换到 /usr/local/ 目录, 显示列表, 查看解压缩的jdk目录cd /usr/localll3. 配置环境变量 批改 /etc/profile 配置文件, 配置环境变量 vim /etc/profile# 在文件开端增加以下内容:export JAVA_HOME=/usr/local/jdk1.8.0_212export PATH=$JAVA_HOME/bin:$PATH批改完后, 让环境变量立刻失效 source /etc/profile4. 验证 java -version----------------------------------------------------------------java version "1.8.0_212"Java(TM) SE Runtime Environment (build 1.8.0_212-b10)Java HotSpot(TM) 64-Bit Server VM (build 25.212-b10, mixed mode)javac -version---------------javac 1.8.0_212装置 RocketMQ1. 下载 rocketmq 二进制文件wget https://mirror.bit.edu.cn/apache/rocketmq/4.7.0/rocketmq-all-4.7.0-bin-release.zip2. 解压缩 rocketmq将 rocketmq 解压到 /usr/local/ 目录 ...

December 11, 2020 · 3 min · jiezi

关于rocketmq:RocketMQ本地开发调试环境搭建

前言发现公司这边的消息中间件采纳了aliyun的RocketMQ服务,相熟开源的同学都晓得,RocketMQ是国内最早一批募捐Apache并胜利毕业的我的项目。架构设计参考了kafka的模式,所以如果你理解kafka的架构,对于RocketMQ就能够驾轻就熟了,尽管参考了kafka,然而RocketMQ也有很多的降级,比方Broker的注册和发现就采纳了外部的NameServer,没有引入更多的第三方依赖,而且增加了诸如音讯回溯、事务音讯、延时音讯等特色性能。因为之前没有接触过RocketMQ(之前始终用的kafka和RabbitMQ),筹备钻研一番,也为了前面集成spring boot metrics监控RocketMQ客户端信息做筹备。钻研一个开源我的项目,最好的办法就是Debug,所以记录下本地搭建RocketMq的调试环境过程 生成安装包我的项目地址:https://github.com/apache/rocketmq ,从这个地址下载我的项目后,导入到IDEA开发工具,执行mvn install,生成装置RocketMQ包,生成胜利后,在distribution模块下,会有如下目录,这个目录等下会用到 启动NameServer找到namesrv模块,运行NamesrvStartup的main办法,这个时候会提醒你,须要设置ROCKETMQ_HOME,提示信息如下: 这个时候就须要第一步生成的目录,拷贝/Users/kl/githubnamespace/rocketmq/distribution/target/rocketmq-4.7.1/rocketmq-4.7.1目录,在IDEA的运行设置界面,增加如下参数: -Drocketmq.home.dir=/Users/kl/githubnamespace/rocketmq/distribution/target/rocketmq-4.7.1/rocketmq-4.7.1 如: 而后在启动,就能够胜利启动了 启动broker装置启动NameServer的模式,找到borker模块,设置好ROCKETMQ_HOME,在用雷同的形式采纳-D形式,配置下NameServer的地址,如: -Drocketmq.namesrv.addr=127.0.0.1:9876 而后启动即可,此时一个残缺的跑在IDEA中的单节点架构的RocketMQ服务就搭建好了 装置RocketMQ Console为了更好的察看理解RocketMQ的性能,能够装置一个web治理控制台,这个须要用到另一个我的项目 我的项目地址:https://github.com/apache/rocketmq-externals/tree/master/rocketmq-console 装置胜利后,就能够通过web页面查问producer发送的message信息,关上浏览器,输出:http://localhost:8080。就能够按到如下页面: 纵情的Debug所有准备就绪后,能够找到我的项目的example模块,外面内置了各种个性性能的应用案例,接下来就能够一个一个案例Runing起来,纵情的Deubg

November 24, 2020 · 1 min · jiezi

关于rocketmq:Rocketmq-引起的磁盘告警排查

早上收到服务器的磁盘告警,查看发现是es日志比拟多,具体看索引由rocketmq的store日志造成。排查:1、因为rocketmq的日志对立收集到elk,不便排查。首先定位到是es造成磁盘告警,查看es的索引数据,疾速找到具体是哪个索引日志失常状况下,rocketmq的store索引数据在40M左右。 2、查看rocketmq集群挂载的日志,只有一个slave节点的store日志始终在输入warn日志,如下: 3、因为是从节点,重启对业务不会有影响,先重启试试,后果是么用。网上搜寻一番,https://issues.apache.org/jir... 据说可能是commitlog 数据损坏,那这样没方法了,因为是slave节点能够间接删除commitlog数据,再重启会主动从master同步过去。store日志恢复正常。

November 19, 2020 · 1 min · jiezi

关于rocketmq:SpringBoot整合RocketMQ测试用例

1、在阿里云后盾创立GroupID以及topic。2、springboot我的项目引入rocketMQ依赖以及在application.yml文件中退出MQ服务器配置。3、创立监听listener4、创立配置生产者与消费者5、创立推送音讯工具类6、创立Controller测试

September 29, 2020 · 1 min · jiezi

关于rocketmq:使用Spring-Cloud-Stream玩转RabbitMQRocketMQ和Kafka

前一章咱们讲了《SpringBoot RabbitMQ音讯队列的重试、超时、延时、死信队列》,从代码层面援用了十分多的rabbit特色代码,如:rabbitTemplate.convertAndSend(), @RabbitListener(queues = "xxx")等,都是很简略的代码看起来一切都是正当的,但隐约感觉代码受到了入侵。 业务的倒退对MQ的依赖越来越重,位置也越来越高,对它的需要也越来越多。比方程序生产,事务音讯,回溯生产等,性能方面也有更高要求。越来越多的趋势揭示咱们有更好MQ计划。 如果咱们将“MQ从Rabbit替换成Rocket”的计划提上议程,就会发放这是一个十分盛大的工程。以前好多服务都是用的有RabbitMQ的特色代码,如果要替换相当于所有服务的代码都要较大的更新,这带来的经营危险是微小的,须要十分多的开发测试资源的投入。 那回头来讲,咱们最开始应用rabbitmq的时候能不能尽量暗藏特色代码吗,为当前的降级替换保留可能性。 这个时候就须要应用Spring Cloud的子组件Spring Cloud Stream。它是一个构建音讯驱动微服务的框架,提供一套音讯订阅生产的规范为不同供应商的消息中间件进行集成。目前官网提供Kafka和RabbitMQ的集成实现,而阿里也实现对RocketMQ的集成。 一、 Spring Cloud Stream简介 Spring Cloud Stream利用由第三方的中间件组成。利用间的通信通过输出通道(input channel)和输入通道(output channel)实现。这些通道是由Spring Cloud Stream 注入的。而通道与内部的代理的连贯又是通过Binder实现的。 二、 RabbitMQ集成1. 引入包<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId></dependency>2. 设置音讯输入输出通道public interface Source { String OUTPUT = "myOutput"; @Output(OUTPUT) MessageChannel message();}public interface Sink { String INPUT = "myInput"; @Input(INPUT) SubscribableChannel sub1();}输入通道为音讯的发送方,输出通道为音讯的接管方 myOutput,myInput为通道名,后续通过配置文件进行个性配置,切记两个通道的绑定最好是离开定义,不然有可能产生莫名谬误 3. 音讯个性配置spring cloud: stream: bindings: myOutput: destination: login-user myInput: # 通道名,对应代码中的生产监听组 destination: login-user # exchange group: logined-member # 生产组 rabbit: bindings: myOutput: producer: routing-key-expression: headers.routingKey # 发送端路由key delayed-exchange: true # 开启延时队列 myInput: consumer: binding-routing-key: login.user.succeed # 生产监听路由表达式 delayed-exchange: true # 开启延时队列 auto-bind-dlq: true # 绑定死信队列 republish-to-dlq: true # 重投到死信队列并带有报错信息1) destination音讯的主题名在Rabbit中用来定义exchange以及成为queue的一部分 ...

September 27, 2020 · 2 min · jiezi

关于rocketmq:RocketMQ学习笔记一-初遇篇

在MQ引论中,我曾经介绍了MQ的一些概念和用途,没看过的能够先看一下。这次我抉择介绍的是RocketMQRocket MQ的概念一图胜千言,都在图里了。 从下面的图,咱们能够看出主题是一类音讯的汇合,每条音讯必须属于一个主题。RocketMQ中的每个音讯领有惟一的Message ID,且能够携带具备业务标识的key,当然你也能够抉择不给。标签(tag)是更细一级的划分,用于同一主题下辨别不同类型的音讯。 同一类生产者形成生产者小组,同一类消费者形成消费者小组。消费者生产的形式也有两种: 推动式生产该模式下Broker收到数据后会被动推送给生产端,该生产模式个别实时性较高。拉取式生产Consumer生产的一种类型,利用通常被动调用Consumer的拉音讯办法从Broker服务器拉音讯、主动权由利用管制。一旦获取了批量音讯,利用就会启动生产过程。咱们讲过RocketMQ是能够集群的,所以当多台Broker的时候,咱们就不能按IP和端口去找了,咱们用broker的名字去找,让nameServer去问,相似于注册核心。 架构图: 有了下面的根底概念,咱们来开始写个Hello world吧。 装置与配置RocketMQ对内存要求比拟高,默认是8G来着,就算你改了默认的配置,改成2G, 基本上也跑不起来,晓得假相的我眼泪掉下来,我的阿里云服务器学生版只有2G,过后改了配置,我还有疑难为啥跑不起来。Rocket MQ 是阿里巴巴开源的,文档也比拟丰盛,咱们依照文档走就行。 咱们本次下载4.40.release版的而后上传到Linux上 -- 上传rz rocketmq-all-4.4.0-bin-release.zip-- 解压unzip rocketmq-all-4.4.0-bin-release.zip-- 名字太长改下名mv rocketmq-all-4.4.0-bin-release.zip rocketmq次要配置的配置的 nameServer 哪台主机是nameServermaster: 谁是主节点/etc/hosts 域名映射在hosts文件中退出以下配置 -- 将来会搭建集群,mqmater是主节点,mqnameserver1是一个,将来会有多个47.101.136.147 mqmaster147.101.136.147 mqnameserver1-- 长久化到哪里mkdir mqstorecd mqstore-- 日志mkdir commitlog-- 生产队列mkdir consumequeue-- 索引mkdir index-- 进入配置的文件夹cd conf 两主两从异步 cd 2m-2s-async vi broker-a.properties namesrvAddr = mqnameserver1:9876 分号隔开默认主题数量 autoCreateTopicEnable =truedefaultTopicQueueNums = 4-- 对外裸露的接口listenPort = 10911-- 无用文件保留工夫deleteWhen = 04-- 最多保留多长时间fileReservedTime = 48-- 存储门路storePathRootDir=/usr/rocketmq/mqstore-- 提交日志storePathCommitLog=/usr/rocketmq/mqstore/commitlog-- 生产门路storePathConsuQueue=/usr/rocketmq/mqstore/commitlog/consumequeue-- 索引门路storePathIndex=/usr/rocketmq/mqstore/commitlog/consumequeue-- 音讯的最大数量maxMessageSize=65536-- 主从同步,这是指明以后音讯队列是异步,而且是主节点brokerRole=ASYCN_MASTER-- 同步到硬盘,异步刷新flushDiskType=ASYNC_FLUSH-- 0 示意mastter 大于0是从brokerId = 0-- 批量替换 这是配置RocketMq的日志门路,这个请在conf文件夹下配置sed -i 's#${user.home}#/usr/rocketmq/#g' *.xml设置启动参数: ...

September 26, 2020 · 2 min · jiezi

关于rocketmq:常见消息中间件之RocketMQ

前言 RocketMQ是一款分布式、队列模型的消息中间件,由阿里巴巴自主研发的一款实用于高并发、高可靠性、海量数据场景的消息中间件。晚期开源2.X版本名为MetaQ;2015年迭代3.X版本,更名为RocketMQ,2016年奉献给Apache,通过一年多的孵化,最终成为Apache的顶级开源我的项目之一。RocketMQ是在Kafka的根底上倒退起来的,它的诞生参考借鉴了Apache Kafka(前面的文章我会独自介绍Kafka)。起因是随着阿里巴巴业务的倒退,他们发现Kafka对于具体的业务场景反对的不欠缺,于是阿里巴巴的团队借鉴Kafka的设计思路,并联合本身“双十一”场景,自行开发了更贴合本人业务场景的RocketMQ,对Kafka进行了正当的扩大和API丰盛。RocketMQ的音讯路由、存储、集群划分等设计思路与Kafka都极其类似,惟一的不同是 RocketMQ 对于业务个性的反对更欠缺,所以更实用于业务场景。 1 专业术语 每一个技术框架,都有它的专有名词,RocketMQ的专业术语如下: 1)Producer:音讯生产者,负责产生音讯,个别由业务零碎负责产生音讯。 2)Consumer:音讯消费者,负责生产音讯,个别由后盾零碎负责异步生产。 3)Pull Consumer:Consumer的一种,须要被动申请Broker拉取音讯。 4)Push Consumer:Consumer的一种,须要向Consumer对象注册监听。 5)Producer Group:生产者汇合,个别用于发送一类音讯。 6)Consumer Group:消费者汇合,个别用于承受一类音讯进行生产。 7)Broker:MQ音讯服务(中专角色,用于音讯存储和生产生产转发)。 2 能力与反对 1)反对集群模型、负载平衡、程度扩大能力,如上面咱们要讲的集群架构。 2)亿级别的音讯沉积能力。 3)采纳零拷贝的原理、程序写盘、随机读(索引文件)。 4)丰盛的API应用。 5)代码优良,底层通信框架采纳Netty NIO框架。 6)NameServer代替Zookeeper。 7)强调集群无单点,可扩大,任意一点高可用,程度可扩大。 8)音讯失败重试机制、音讯可查问。 9)开源社区活跃度高,足够成熟(通过双十一考验)。 3 外围源码包及性能阐明 如下图,咱们看一下RocketMQ源码包的组成,这有利于咱们当前更深刻的学习。 1)rocketmq-broker 次要的业务逻辑,音讯收发,主从同步,pagecache 2)rocketmq-client 客户端接口,比方生产者和消费者 3)rocketmq-common 专用数据结构等等 4)rocketmq-distribution 编译模块,编译输入等 5)rocketmq-example 示例,比方生产者和消费者 6)rocketmq-fliter 进行Broker过滤的不感兴趣的音讯传输,减小带宽压力 ...

September 23, 2020 · 1 min · jiezi

关于rocketmq:无微不至之RocketMq源码深度讲解1大纲介绍

本系列纲要大抵为: 架构剖析NameServer剖析CommitLog物理队列存储机制同步、异步刷盘剖析ConsumeQueue逻辑队列存储机制Index索引构建机制一般音讯发送流程程程序音讯发送流程事务音讯(二阶段提交)发送流程音讯拉取逻辑剖析提早音讯发送流程并发生产原理剖析(集群策略+播送策略)有序生产原理(集群策略+播送策略)(与程序发送相结合)基于2种生产策略的客户端生产offset存储原理主从同步原理DOWN机重启复原机制详解raft协定Dledger实现原理剖析

September 21, 2020 · 1 min · jiezi

关于rocketmq:大写的服看完这篇你还不懂RocketMQ算我输

目录RocketMQ介绍RocketMQ概念为什么要用RocketMQ? 异步解耦削峰填谷分布式事务最终一致性数据散发RocketMQ架构RocketMQ音讯类型 一般音讯程序音讯定时音讯事务音讯最佳实际 音讯重试音讯过滤生产模式生产幂等本地事务音讯封装参考代码RocketMQ 介绍Apache RocketMQ 是一款 低提早、高并发、高可用、高牢靠的分布式消息中间件。音讯队列 RocketMQ 可为分布式应用零碎提供异步解耦和削峰填谷的能力,同时也具备互联网利用所需的海量音讯沉积、高吞吐、牢靠重试等个性。 RocketMQ 概念Topic:音讯主题,用于将一类的音讯进行归类,比方订单主题,就是所有订单相干的音讯都能够由这个主题去承载,生产者向这个主题发送音讯。生产者:负责生产音讯并发送音讯到 Topic 的角色。消费者:负责从 Topic 接管并生产音讯 的角色。音讯:生产者向 Topic 发送的内容,会被消费者生产。音讯属性:生产者发送的时候能够为音讯自定义一些业务相干的属性,比方 Message Key 和 Tag 等。Group:一类生产者或消费者,这类生产者或消费者通常生产或生产同一类音讯,且音讯公布或订阅的逻辑统一。为什么要应用 RocketMQ?异步解耦随着微服务架构的风行,服务之间的关系梳理十分重要。异步解耦能够升高服务之间的耦合水平,同时也能进步服务的吞吐量。 应用异步解耦的业务场景十分多,因为每个行业的业务都会不太一样,以一些比拟通用的业务来阐明置信大家都能了解。 比方电商行业的下单业务场景,以最简略的下单流程来说,下单流程如下: 锁库存创立订单用户领取扣减库存给用户发送购买短信告诉给用户减少积分告诉商家发货咱们以下单胜利后,用户进行领取,领取实现会有个逻辑叫领取回调,在回调外面须要去做一些业务逻辑。首先来看下同步解决须要破费的工夫,如下图: 下面的下单流程从 3 到 5 都是能够采纳异步流程进行解决,对于用户来说,领取实现后他就不须要关注前面的流程了。后盾缓缓解决就行了,这样就能简化三个步骤,进步回调的解决工夫。 削峰填谷削峰填谷指的是在大流量的冲击下,利用 RocketMQ 能够抗住刹时的大流量,爱护零碎的稳定性,晋升用户体验。 在电商行业,最常见的流量冲击就是秒杀流动了,利用 RocketMQ 来实现一个残缺的秒杀业务还是与很多须要做的工作,不在本文的范畴内,前面有机会能够独自跟大家聊聊。想通知大家的是像诸如此类的场景能够利用 RocketMQ 来扛住高并发,前提是业务场景反对异步解决。 分布式事务最终一致性家喻户晓,分布式事务有 2PC,TCC,最终一致性等计划。其中应用音讯队列来做最终一致性计划是比拟罕用的。 在电商的业务场景中,交易相干的外围业务肯定要确保数据的一致性。通过引入音讯队列 RocketMQ 版的分布式事务,既能够实现零碎之间的解耦,又能够保障最终的数据一致性。 数据散发数据散发指的是能够将原始数据散发到多个须要应用这份数据的零碎中,实现数据异构的需要。最常见的有将数据散发到 ES, Redis 中为业务提供搜寻,缓存等服务。 除了手动通过音讯机制进行数据散发,还能够订阅 Mysql 的 binlog 来散发,在散发这个场景,须要应用 RocketMQ 的程序音讯来保证数据的一致性。 RocketMQ 架构 图片起源阿里云官网文档 Name Server:是一个简直无状态节点,可集群部署,在音讯队列 RocketMQ 版中提供命名服务,更新和发现 Broker 服务。就是一个注册核心。Broker:音讯直达角色,负责存储音讯,转发音讯。分为 Master Broker 和 Slave Broker,一个 Master Broker 能够对应多个 Slave Broker,然而一个 Slave Broker 只能对应一个 Master Broker。Broker 启动后须要实现一次将本人注册至 Name Server 的操作;随后每隔 30s 定期向 Name Server 上报 Topic 路由信息。生产者:与 Name Server 集群中的其中一个节点(随机)建设长链接(Keep-alive),定期从 Name Server 读取 Topic 路由信息,并向提供 Topic 服务的 Master Broker 建设长链接,且定时向 Master Broker 发送心跳。消费者:与 Name Server 集群中的其中一个节点(随机)建设长连贯,定期从 Name Server 拉取 Topic 路由信息,并向提供 Topic 服务的 Master Broker、Slave Broker 建设长连贯,且定时向 Master Broker、Slave Broker 发送心跳。Consumer 既能够从 Master Broker 订阅音讯,也能够从 Slave Broker 订阅音讯,订阅规定由 Broker 配置决定。RocketMQ 音讯类型RocketMQ 反对丰盛的音讯类型,能够满足多场景的业务需要。不同的音讯有不同的利用场景,上面为大家介绍罕用的四种音讯类型。 ...

September 15, 2020 · 2 min · jiezi

关于rocketmq:消息中间件RocketMQ入门

本文转载自 阿里中间件团队博客的文章十分钟入门RocketMQ 本文首先引出消息中间件通常须要解决哪些问题,在解决这些问题当中会遇到什么艰难,Apache RocketMQ作为阿里开源的一款高性能、高吞吐量的分布式消息中间件否能够解决,标准中如何定义这些问题。而后本文将介绍RocketMQ的架构设计,以期让读者疾速理解RocketMQ。 消息中间件须要解决哪些问题?Publish/Subscribe公布订阅是消息中间件的最基本功能,也是绝对于传统RPC通信而言。在此不再详述。 Message Priority标准中形容的优先级是指在一个音讯队列中,每条音讯都有不同的优先级,个别用整数来形容,优先级高的音讯先投递,如果音讯齐全在一个内存队列中,那么在投递前能够依照优先级排序,令优先级高的先投递。因为RocketMQ所有音讯都是长久化的,所以如果依照优先级来排序,开销会十分大,因而RocketMQ没有特意反对音讯优先级,然而能够通过变通的形式实现相似性能,即独自配置一个优先级高的队列,和一个一般优先级的队列, 将不同优先级发送到不同队列即可。 对于优先级问题,能够演绎为2类: 只有达到优先级目标即可,不是严格意义上的优先级,通常将优先级划分为高、中、低,或者再多几个级别。每个优先级能够用不同的topic示意,发消息时,指定不同的topic来示意优先级,这种形式能够解决绝大部分的优先级问题,然而对业务的优先级精确性做了斗争。严格的优先级,优先级用整数示意,例如0 ~ 65535,这种优先级问题个别应用不同topic解决就十分不适合。如果要让MQ解决此问题,会对MQ的性能造成十分大的影响。这里要确保一点,业务上是否的确须要这种严格的优先级,如果将优先级压缩成几个,对业务的影响有多大?Message Order音讯有序指的是一类音讯生产时,能依照发送的程序来生产。例如:一个订单产生了3条音讯,别离是订单创立,订单付款,订单实现。生产时,要依照这个程序生产能力有意义。然而同时订单之间是能够并行生产的。RocketMQ能够严格的保障音讯有序。 Message FilterBroker端音讯过滤在Broker中,依照Consumer的要求做过滤,长处是缩小了对于Consumer无用音讯的网络传输。毛病是减少了Broker的累赘,实现绝对简单。 淘宝Notify反对多种过滤形式,蕴含间接依照音讯类型过滤,灵便的语法表达式过滤,简直能够满足最刻薄的过滤需要。淘宝RocketMQ反对依照简略的Message Tag过滤,也反对依照Message Header、body进行过滤。CORBA Notification标准中也反对灵便的语法表达式过滤。Consumer端音讯过滤这种过滤形式可由利用齐全自定义实现,然而毛病是很多无用的音讯要传输到Consumer端。 Message Persistence消息中间件通常采纳的几种长久化形式: 长久化到数据库,例如Mysql。长久化到KV存储,例如levelDB、伯克利DB等KV存储系统。文件记录模式长久化,例如Kafka,RocketMQ对内存数据做一个长久化镜像,例如beanstalkd,VisiNotify(1)、(2)、(3)三种长久化形式都具备将内存队列Buffer进行扩大的能力,(4)只是一个内存的镜像,作用是当Broker挂掉重启后依然能将之前内存的数据恢复进去。JMS与CORBA Notification标准没有明确阐明如何长久化,然而长久化局部的性能间接决定了整个消息中间件的性能。 RocketMQ充分利用Linux文件系统内存cache来进步性能。 Message Reliablity影响音讯可靠性的几种状况: Broker失常敞开Broker异样CrashOS Crash机器掉电,然而能立刻复原供电状况。机器无奈开机(可能是cpu、主板、内存等关键设备损坏)磁盘设施损坏。(1)、(2)、(3)、(4)四种状况都属于硬件资源可立刻复原状况,RocketMQ在这四种状况下能保障音讯不丢,或者失落大量数据(依赖刷盘形式是同步还是异步)。 (5)、(6)属于单点故障,且无奈复原,一旦产生,在此单点上的音讯全副失落。RocketMQ在这两种状况下,通过异步复制,可保障99%的音讯不丢,然而依然会有极少量的音讯可能失落。通过同步双写技术能够完全避免单点,同步双写势必会影响性能,适宜对音讯可靠性要求极高的场合,例如与Money相干的利用。 RocketMQ从3.0版本开始反对同步双写。 Low Latency Messaging在音讯不沉积状况下,音讯达到Broker后,能立即达到Consumer。RocketMQ应用长轮询Pull形式,可保障音讯十分实时,音讯实时性不低于Push。 At least Once是指每个音讯必须投递一次。RocketMQ Consumer先pull音讯到本地,生产实现后,才向服务器返回ack,如果没有生产肯定不会ack音讯,所以RocketMQ能够很好的反对此个性。 Exactly Only Once发送音讯阶段,不容许发送反复的音讯。生产音讯阶段,不容许生产反复的音讯。只有以上两个条件都满足状况下,能力认为音讯是“Exactly Only Once”,而要实现以上两点,在分布式系统环境下,不可避免要产生微小的开销。所以RocketMQ为了谋求高性能,并不保障此个性,要求在业务上进行去重,也就是说生产音讯要做到幂等性。RocketMQ尽管不能严格保障不反复,然而失常状况下很少会呈现反复发送、生产状况,只有网络异样,Consumer启停等异常情况下会呈现音讯反复。 Broker的Buffer满了怎么办?Broker的Buffer通常指的是Broker中一个队列的内存Buffer大小,这类Buffer通常大小无限,如果Buffer满了当前怎么办?上面是CORBA Notification标准中解决形式: RejectNewEvents 回绝新来的音讯,向Producer返回RejectNewEvents错误码。依照特定策略抛弃已有音讯 AnyOrder - Any event may be discarded on overflow. This is the default setting for this property.FifoOrder - The first event received will be the first discarded.LifoOrder - The last event received will be the first discarded.PriorityOrder - Events should be discarded in priority order, such that lower priority events will be discarded before higher priority events.DeadlineOrder - Events should be discarded in the order of shortest expiry deadline first.RocketMQ没有内存Buffer概念,RocketMQ的队列都是长久化磁盘,数据定期革除。 ...

July 23, 2020 · 1 min · jiezi