From 340580bd52f44b7a26209b5d78abd9f797400f11 Mon Sep 17 00:00:00 2001 From: qyx <565485304@qq.com> Date: Sat, 6 Aug 2022 21:57:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BA=86=E6=96=87=E6=A1=A31?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- longpolling/README.md | 39 +++++- longpolling/netty/README.md | 9 -- longpolling/practice/README.md | 55 ++++++++ mq/README.md | 75 +++++++++++ mq/rocketmq/README.md | 227 ++++++++++++++++++++++++++++++++- 5 files changed, 394 insertions(+), 11 deletions(-) diff --git a/longpolling/README.md b/longpolling/README.md index e7e6da4..c745b0c 100644 --- a/longpolling/README.md +++ b/longpolling/README.md @@ -1,3 +1,23 @@ +# 项目介绍 +- demo + - demo1 Disruptor 与 Netty 的整合 + - demo2 Disruptor + - demo3 Netty与 SpringBoot 整合 +- netty + - better-netty Netty 的深入研究和学习 + + + +# 参考文章 +- 阿里云开发者社区 + - 现代IM系统中的消息系统架构 - 实现篇 https://my.oschina.net/yunqi/blog/3078531 + - 现代IM系统中的消息系统架构 - 模型篇 https://developer.aliyun.com/article/701593 + - 现代IM系统中的消息系统架构 - 架构篇 https://developer.aliyun.com/article/698301 +- + + + + # Disruptor @@ -13,4 +33,21 @@ - Memory Barrier -## 3. \ No newline at end of file +## 3. +坐席传来的新消息都存入 localstorage 中,其他页签监听 localstorage 的变化取出数据进行渲染 + +Java 实现 UDP 传输方式同步发送并接收消息 +- https://my.oschina.net/u/3986411/blog/5482852 +JackJiang2020 +- https://my.oschina.net/u/4231722 +JackJiang- +- https://my.oschina.net/jb2011 + + + + + + + + + diff --git a/longpolling/netty/README.md b/longpolling/netty/README.md index b667f05..0ab2fa4 100644 --- a/longpolling/netty/README.md +++ b/longpolling/netty/README.md @@ -1,14 +1,5 @@ # Netty -## 成就点 -- 负责公司千万级别实时在线用户长连消息推送系统 -- 企业社交 Sass 完美解决方案 - -## 主要方案 -- 完美应用示例(十万级): 线程池异步业务 + (Linux 句柄) + 单机Netty + 日志 -- 异步化完美方案(百万级): 线程池异步业务 + (Linux 句柄) + HA高可用 + 日志 -- 多服务端解决方案(千万级): Disruptor异步业务 + (Linux 句柄) + HA高可用 + 日志 - ## 参考资料 - https://www.imooc.com/t/6224286 慕课网牛人教程 - diff --git a/longpolling/practice/README.md b/longpolling/practice/README.md index e69de29..b33759a 100644 --- a/longpolling/practice/README.md +++ b/longpolling/practice/README.md @@ -0,0 +1,55 @@ +# 架构实战方案 + +## 业务特点 +- 消息收发的及时性 +- 消息收发的准确性 +- 消息记录的检索高效 +- 消息的收送高可靠 +- 方便进行演进扩展 +- 无感知停机上线 + +## 成就点 +- 企业级 千万用户实时在线长连消息推送系统 +- 企业级 消息长链接 Sass 架构完美解决方案 + +## 消息推送主要方案 +- 完美应用示例(十万级): 线程池异步业务 + (Linux 句柄) + 单机Netty + 日志 +- 异步化完美方案(百万级): 线程池异步业务 + (Linux 句柄) + HA高可用 + 日志 +- 多服务端解决方案(千万级): Disruptor异步业务 + (Linux 句柄) + HA高可用 + 日志 + +## 消息增量同步方案 +- 数据增量同步方案 https://blog.csdn.net/godlovedaniel/article/details/120220878 +- 观察者模式 消息收发模型和同步方案的一些心得 https://www.imooc.com/article/19165 +- + + + +## 数据架构存储方法 +- MYSQL数同步方案 https://www.jb51.net/article/230528.htm +- + + + +## 消息服务部署方案 + + + + +## 消息服务上线/更新方案 + + + + + + + + + + + + + + + + + diff --git a/mq/README.md b/mq/README.md index e69de29..cc1aab1 100644 --- a/mq/README.md +++ b/mq/README.md @@ -0,0 +1,75 @@ +# 消息中间件相关的知识 + +## 技术调研: Kafka RocketMQ RabbitMQ 技术选型 +- 其实技术调研说白了,就是对一个技术去找到一些业内常用的开源实现,然后对各种不同的实现都进行一些调研,对比一 + 下他们的优劣势,看看谁比较符合我们的需求,谁比较适合我们来使用。 + +### 1. 思考的问题 +- 业内常用的MQ有哪些? +- 每一种MQ各自的表现如何? +- 这些MQ在同等机器条件下,能抗多少QPS(每秒抗几千QPS还是几万QPS)? +- 性能有多高(发送一条消息给他要2ms还是20ms)? +- 可用性能不能得到保证(要是MQ部署的机器挂了怎么办)? +- 他们会不会丢失数据? +- 如果需要的话能否让他们进行线性的集群扩容(就是多加几台机器)? +- 消息中间件经常需要使用的一些功能他们都有吗(比如说延迟消息、事务消息、消息堆积、消息回溯、死信队列,等等)? +- 这些MQ在文档是否齐全?社区是否活跃?在行业内是否广泛运用?是用什么语言编写的? + +### 2. Kafka、RabbitMQ以及RocketMQ的调研对比 + +#### 2.1 Kafka的优势和劣势 +- 优势 + - Kafka的吞吐量几乎是行业里最优秀的,在常规的机器配置下,一台机器 可以达到每秒十几万的QPS,相当的强悍。 + - Kafka性能也很高,基本上发送消息给Kafka都是毫秒级的性能。 + - 可用性也很高,Kafka是可以支持集群部署的,其中部分机器宕机是可以继续运行的。 +- 劣势 + - Kafka比较为人诟病的一点,似乎是丢数据方面的问题,因为Kafka收到消息之后会写入一个磁盘缓冲区里,并没有直接落地到物理 + 磁盘上去,所以要是机器本身故障了,可能会导致磁盘缓冲区里的数据丢失。 + - Kafka另外一个比较大的缺点,就是功能非常的单一,主要是支持发送消息给他,然后从里面消费消息,其他就没有什么额外的高 + 级功能了。所以基于Kafka有限的功能,可能适用的场景并不是很多。 +- 业界实践1 + - 基本行业里的一个标准,是把Kafka用在用户行为日志的采集和传输上,比 如大数据团队要收集APP上用户的一些行为日志, + 这种日志就是用Kafka来收集和传输的。 + - 因为那种日志适当丢失数据是没有关系的,而且一般量特别大,要求吞吐量要高,一般就是收发消息,不需要太多的高级功能, + 所以 Kafka是非常适合这种场景的。 +#### 2.2 RabbitMQ的优势和劣势 +- 优势 + - 在中国使用的企业很多, 尤其是金融行业, 一线互联网大厂,而且直到现在都有很多中小型公司在使用RabbitMQ + - 可以保证数据不丢失,也能保证高可用性,即集群部署的时候部分机器宕机可以继续运行 + - 支持部分高级功 能,比如说死信队列,消息重试之类的 +- 缺点 + - RabbitMQ的吞吐量是比较低的,一般就是每秒几万的级别,所以如果遇到特别特别高并发的情况下,支撑起来是有点困难的。 + - 他进行集群扩展的时候(也就是加机器部署),还比较麻烦。 + - 修改困难,开发语言是erlang,国内很少有精通erlang语言的工程师,因此也没办法去阅读他的源代码,甚至修改他的源代码。 +- 业界表现 + - 很多BAT等一线互联网大厂都切换到使用更加优秀的RocketMQ了,很多中小型公司觉得 RabbitMQ 可以满足自己的需求还在继续使用中。 + - 中小型公司认为 RabbitMQ已经足以满足需求, 不需要部署特别大规模的集群,也没必要去阅读和修改RabbitMQ的源码,迁移复杂。 +#### 2.3 RocketMQ的优势和劣势 +- RocketMQ是阿里开源的消息中间件,久经沙场,非常的靠谱。他几乎同时解决了Kafka和RabbitMQ的缺陷。 +- 优势 + - RocketMQ的吞吐量也同样很高,单机可以达到10万QPS以上。 + - 可以保证高可用性,性能很高。 + - 支持通过配置保证数据绝对不丢失。 + - 可以部署大规模的集群。 + - 支持各种高级的功能,比如说延迟消息、事务消息、消息回溯、死信队列、消息积压,等等。 + - RocketMQ是基于Java开发的,符合国内大多数公司的技术栈,很容易就可以阅读他的源码,甚至是修改他的源码。 + - RocketMQ是非常适合用在Java业务系统架构中的,因为他很高的性能表现,还有他的高阶功能的支持,可以让我们解决各种业务问题。 +- 劣势 + - RocketMQ 的官方文档相对简单一些,但是Kafka和RabbitMQ的官方文档就非常的全面和详细,这可能是RocketMQ目前唯一的缺点。 + - PS: RocketMQ 现在已经交给 Apache 基金会在维护, 除了团队没有硬实力的因素,其他暂时无劣势, 建议解决这个问题, 使用。 +- 使用考虑 + - 如果现在是一个中小型互联网公司,看起来似乎RabbitMQ足以应对需求,但是假设未来成长为一个大公司? + 也许也会有每秒几十万的QPS,也许以后也需要对MQ进行源码的二次开发,那此时RabbitMQ还合适吗? + + + + + + + + + + + + + diff --git a/mq/rocketmq/README.md b/mq/rocketmq/README.md index 6831ecf..24756e2 100644 --- a/mq/rocketmq/README.md +++ b/mq/rocketmq/README.md @@ -21,6 +21,11 @@ - 集群无单点,可扩展,任意一点高可用,水平可扩展 - 消息失败重试机制,消息可查询 - 开源社区活跃、成熟度(经过双十一考验) +- 理解的问题: + - MQ如何集群化部署来支撑高并发访问? + - MQ如果要存储海量消息应该怎么做? + - 高可用保障: 万一Broker宕机了怎么办? + - 数据路由: 怎么知道访问哪个Broker? ### 1.2 概念模型 - Producer: 消息生产者,负责生产消息,一般由业务系统负责产生消息 @@ -280,14 +285,216 @@ ## 6. 双主双从部署 +### 6.1 设计生产的架构 +- NameServer集群化部署, 保证高可用, 每台NameServer实际上都会有完整的集群路由信息,包括所有的Broker节点信息,我们的数据信息 +- 基于Dledger的Broker主从架构部署 + - RocketMQ 4.5以前的那种普通的Master-Slave架构来部署,能在一定程度上保证数据不丢失,也能保证一定的可用性 + - 但是那种方式的缺陷是很明显的,最大的问题就是当Master Broker挂了之后,没办法让Slave Broker自动切换为新的Master Broker,需要手工做一些运维操作,修改配置以及重启机器才行,这个非常麻烦。 + - Dledger技术是要求至少得是一个Master带两个Slave,这样有三个Broke组成一个Group,也就是作为一个分组来运行。一旦 Master宕机,他就可以从剩余的两个Slave中选举出来一个新的Master对外提供服务。 +- Broker是如何跟NameServer进行通信的 + - Broker会每隔30秒发送心跳到所有的NameServer 上去,然后每个NameServer都会每隔10s检查一次有没有哪个Broker超过120s没发送心跳的,如果有,就认为那个Broker已经宕机 了,从路由信息里要摘除这个Broker。 + - RocketMQ的实现中,采用的是TCP长连接进行通信。 + - Broker会跟每个NameServer都建立一个TCP长连接,然后定时通过TCP长连接发送心跳请求过去 +- 使用MQ的系统都要多机器集群部署 + - 无论作为生产者还是消费者的系统,都应 该多机器集群化部署,保证他自己本身作为生产者或者消费者的高可用性 +- MQ的核心数据模型:Topic到底是什么? + - 就是一个数据集合的意思 + - 系统如果要往MQ里写入消息或者获取消息,首先得创建一些Topic,作为数据集合存放不同类型的消息 +- Topic作为一个数据集合是怎么在Broker集群里存储的 + - 我们可以在创建Topic的时候指定让他里面的数据分散存储在多台Broker机器上,比如一个Topic里有1000万条数据,此时有2台 Broker,那么就可以让每台Broker上都放500万条数据 +- 生产者系统是如何将消息发送给Broker的? + - 生产者一定是投递消息到Master Broker的,然后Master Broker会同步数据给他的Slave Brokers,实现 一份数据多份副本,保证Master故障的时候数据不丢失,而且可以自动把Slave切换为Master提供服务。 +- 消费者是如何从Broker上拉取消息的? + - 消费者系统可能会从Master Broker拉取消息,也可能从Slave Broker拉取消息,都有可能,一切都看具体 情况。 + +### 6.2 部署机器配置 +- NameServer:3台机器,每台机器都是8核CPU + 16G内存 + 500G磁盘 + 千兆网卡 +- Broker:3台机器,每台机器都是24核CPU(两颗x86_64 cpu,每颗cpu是12核) + 48G内存 + 1TB磁盘 + 千兆网卡 +- 生产者:2台机器,每台机器都是4核CPU + 8G内存 + 500GB磁盘 + 千兆网卡 +- 消费者:2台机器,每台机器都是4核CPU + 8G内存 + 500GB磁盘 + 千兆网卡 + +- NameServer是核心的路由服务,所以给8核16G的较高配置的机器,但是他一般就是承载Broker注册和心跳、系统的路由表拉取等请 求,负载其实很低,因此不需要特别高的机器配置,部署三台也可以实现高可用的效果了。 +- Broker是最负载最高的,未来要承载高并发写入和海量数据存储,所以把最高配置的机器都会留给他,这里用3台机器组成一个“单 Master + 双Slave”的集群。 +- 生产者和消费者机器都是临时用来测试的,而且一般他们都是业务系统,只会部署在标准的4核8G的机器配置下。 + +### 6.3 参数调整 +- PS: 中间件系统在压测或者上生产之前,需要对三大块参数进行调整:OS内核参数、JVM参数以及中间件核心参数 +- PS: OS内核参数主要调整的地方都是跟磁盘IO、网络通信、内存管理以及线程管理有关的,需要适当调节大小 +- PS: JVM参数需要我们去中间件系统的启动脚本中寻找他的默认JVM参数,然后根据机器的情况,对JVM的堆内存大小,新生代大 小,Direct Buffer大小,等等,做出一些调整,发挥机器的资源 +- PS: 中间件核心参数主要也是关注其中跟网络通信、磁盘IO、线程数量、内存 管理相关的,根据机器资源,适当可以增加网络通信线程,控制同步刷磁盘或者异步刷磁盘,线程数量有多少,内存中一些队列的大小 - +--- +#### A. 对RocketMQ集群进行OS内核参数的调整 +(1) vm.overcommit_memory +- “vm.overcommit_memory”这个参数有三个值可以选择,0、1、2。 + - 0: 如果值是0的话,在你的中间件系统申请内存的时候,os内核会检查可用内存是否足够,如果足够的话就分配内存给你,如果感觉剩余 内存不是太够了,干脆就拒绝你的申请,导致你申请内存失败,进而导致中间件系统异常出错。
+ - 1: 一般需要将这个参数的值调整为1,意思是把所有可用的物理内存都允许分配给你,只要有内存就给你来用,这样可以避免申请内存失败的问题。 +> 可以用如下命令修改: echo 'vm.overcommit_memory=1' >> /etc/sysctl.conf + +(2) vm.max_map_count +- 这个参数的值会影响中间件系统可以开启的线程的数量,同样也是非常重要的 + - 如果这个参数过小,有的时候可能会导致有些中间件无法开启足够的线程,进而导致报错,甚至中间件系统挂掉。 + - 他的默认值是65536,但是这个值有时候是不够的,比如大数据团队的生产环境部署的Kafka集群曾经有一次就报出过这个异常,说无法开启足够多的线程,直接导致Kafka宕机了。 + - 因此建议可以把这个参数调大10倍,比如655360这样的值,保证中间件可以开启足够多的线程。 +> 可以用如下命令修改:echo 'vm.max_map_count=655360' >> /etc/sysctl.conf + +(3) vm.swappiness +- 这个参数是用来控制进程的swap行为的,这个简单来说就是os会把一部分磁盘空间作为swap区域,然后如果有的进程现在可能不是太 活跃, + 就会被操作系统把进程调整为睡眠状态,把进程中的数据放入磁盘上的swap区域,然后让这个进程把原来占用的内存空间腾出 来,交给其他活跃运行的进程来使用。 + - 如果这个参数的值设置为0,意思就是尽量别把任何一个进程放到磁盘swap区域去,尽量大家都用物理内存。 + - 如果这个参数的值是100,那么意思就是尽量把一些进程给放到磁盘swap区域去,内存腾出来给活跃的进程使用。 + - 默认这个参数的值是60,有点偏高了,可能会导致我们的中间件运行不活跃的时候被迫腾出内存空间然后放磁盘swap区域去。 + - 因此通常在生产环境建议把这个参数调整小一些,比如设置为10,尽量用物理内存,别放磁盘swap区域去。 +> 可以用如下命令修改:echo 'vm.swappiness=10' >> /etc/sysctl.conf + +(4) ulimit +- 这个是用来控制linux上的最大文件链接数的,默认值可能是1024,一般肯定是不够的,因为你在大量频繁的读写磁盘文件的时候,或 者是进行网络通信的时候,都会跟这个参数有关系 + - 对于一个中间件系统而言肯定是不能使用默认值的,如果你采用默认值,很可能在线上会出现如下错误:error: too many open files +> 因此通常建议用如下命令修改这个值:echo 'ulimit -n 1000000' >> /etc/profile + +(5) 总结: +- 要调整的东西,无非都是跟磁盘文件IO、网络通信、内存管理、线程数量有关系的, 因为我们的中间件系统在运行的时候无非就是跟这些打交道 + - 中间件系统肯定要开启大量的线程(跟vm.max_map_count有关) + - 而且要进行大量的网络通信和磁盘IO(跟ulimit有关) + - 然后大量的使用内存(跟vm.swappiness和vm.overcommit_memory有关) +- 所以对OS内核参数的调整,往往也就是围绕跟中间件系统运行最相关的一些东西 + +#### B. 对JVM参数进行调整 +- 在rocketmq/distribution/target/apache-rocketmq/bin目录下,就有对应的启动脚本,比如: + - mqbroker是用来启动Broker的 + - mqnamesvr是用来启动NameServer的 +- 用mqbroker来举例,我们查看这个脚本里的内容,最后有如下一行: + - sh ${ROCKETMQ_HOME}/bin/runbroker.sh org.apache.rocketmq.broker.BrokerStartup $@ + - 这一行内容就是用runbroker.sh脚本来启动一个JVM进程,JVM进程刚开始执行的main类就是 org.apache.rocketmq.broker.BrokerStartup +- runbroker.sh脚本,在里面可以看到如下内容: + +```shell +JAVA_OPT="${JAVA_OPT} -server -Xms8g -Xmx8g -Xmn4g" +JAVA_OPT="${JAVA_OPT} -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 - XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0" +JAVA_OPT="${JAVA_OPT} -verbose:gc -Xloggc:/dev/shm/mq_gc_%p.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps - XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy" +JAVA_OPT="${JAVA_OPT} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m" JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow" +JAVA_OPT="${JAVA_OPT} -XX:+AlwaysPreTouch" +JAVA_OPT="${JAVA_OPT} -XX:MaxDirectMemorySize=15g" +JAVA_OPT="${JAVA_OPT} -XX:-UseLargePages -XX:-UseBiasedLocking" +JAVA_OPT="${JAVA_OPT} -Djava.ext.dirs=${JAVA_HOME}/jre/lib/ext:${BASE_DIR}/lib" +#JAVA_OPT="${JAVA_OPT} -Xdebug -Xrunjdwp:transport=dt_socket,address=9555,server=y,suspend=n" JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}" +JAVA_OPT="${JAVA_OPT} -cp ${CLASSPATH}" +``` +- 在上面的内容中,其实就是在为启动Broker设置对应的JVM参数和其他一些参数 +- PS: 狸猫技术窝的《从0开始带你成为JVM实战高手》这个专栏, 可以去学习MQ相关的内容 +--- +- **[了解]** -server:这个参数就是说用服务器模式启动,这个没什么可说的,现在一般都是如此 +- **[重点调整]** -Xms8g -Xmx8g -Xmn4g:这个就是很关键的一块参数了,也是重点需要调整的,就是默认的堆大小是8g内存,新生代是4g内存, 如果我们的高配物理机是48g内存的 + - 所以这里完全可以给他们翻几倍,比如给堆内存20g,其中新生代给10g,甚至可以更多一些,当然要留一些内存给操作系统来用,但是要注意最小不能低于1g +- **[重点调整]** -XX:+UseG1GC -XX:G1HeapRegionSize=16m:这几个参数也是至关重要的,这是选用了G1垃圾回收器来做分代回收,对新生代 和老年代都是用G1来回收 + - 这里把G1的region大小设置为了16m,这个因为机器内存比较多,所以region大小可以调大一些给到16m,不然用2m的region,会导致region数量过多的 +- **[了解]** -XX:G1ReservePercent=25:这个参数是说,在G1管理的老年代里预留25%的空闲内存,保证新生代对象晋升到老年代的时候有足 够空间,避免老年代内存都满了,新生代有对象要进入老年代没有充足内存了 + - 默认值是10%,略微偏少,这里RocketMQ给调大了一些 +- **[了解]** -XX:InitiatingHeapOccupancyPercent=30:这个参数是说,当堆内存的使用率达到30%之后就会自动启动G1的并发垃圾回收,开始尝试回收一些垃圾对象 + - 默认值是45%,这里调低了一些,也就是提高了GC的频率,但是避免了垃圾对象过多,一次垃圾回收耗时过长的问题 +- **[重点调整]** -XX:SoftRefLRUPolicyMSPerMB=0:这个参数默认设置为0了 + - 在JVM优化专栏中,救火队队长讲过这个参数引发的案例,其实建议这个参数不要设置为0,避免频繁回收一些软引用的Class对象,这里可以调整为比如1000 +- **[了解]** -verbose:gc -Xloggc:/dev/shm/mq_gc_%p.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps - XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy -XX:+UseGCLogFileRotation - XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m:这一堆参数都是控制GC日志打印输出的,确定了gc日志文件的地址,要 打印哪些详细信息,然后控制每个gc日志文件的大小是30m,最多保留5个gc日志文件。 +- **[了解]** -XX:-OmitStackTraceInFastThrow:这个参数是说,有时候JVM会抛弃一些异常堆栈信息,因此这个参数设置之后,就是禁用这个特性,要把完整的异常堆栈信息打印出来 +- **[了解]** -XX:+AlwaysPreTouch:这个参数的意思是我们刚开始指定JVM用多少内存,不会真正分配给他,会在实际需要使用的时候再分配给 他 + - 所以使用这个参数之后,就是强制让JVM启动的时候直接分配我们指定的内存,不要等到使用内存的时候再分配 +- **[可以调整]** -XX:MaxDirectMemorySize=15g:这是说RocketMQ里大量用了NIO中的direct buffer,这里限定了direct buffer最多申请多少, 如果你机器内存比较大,可以适当调大这个值,如果有朋友不了解direct buffer是什么,可以自己查阅一些资料。 +- **[了解]** -XX:-UseLargePages -XX:-UseBiasedLocking:这两个参数的意思是禁用大内存页和偏向锁,这两个参数对应的概念每个要说清楚 都得一篇文章,所以这里大家直接知道人家禁用了两个特性即可。 +--- +- 总结: + - RocketMQ默认的JVM参数是采用了G1垃圾回收器,默认堆内存大小是8G + - 这个其实完全可以根据大家的机器内存来调整,你可以增大一些也是没有问题的,然后就是一些G1的垃圾回收的行为参数做了调整, 这个一般我们不用去动,然后就是对GC日志打印做了设置,这个一般也不用动。 + - 其余的就是禁用一些特性,开启一些特性,这些都直接维持RocketMQ的默认值即可 + +#### C. 对RocketMQ核心参数进行调整 +- 在下面的目录里有dledger的示例配置文件:rocketmq/distribution/target/apache- rocketmq/conf/dledger +- 在这里主要是有一个较为核心的参数:sendMessageThreadPoolNums=16 + - 这个参数的意思就是RocketMQ内部用来发送消息的线程池的线程数量,默认是16 + - 其实这个参数可以根据你的机器的CPU核数进行适当增加,比如机器CPU是24核的,可以增加这个线程数量到24或者30,都是可以 的 + +### 6.4 压测 +- 我们在压测的时候一方面要关注RocketMQ能抗下多少TPS,一方面还要关注RocketMQ部署的几台机器的资源使用率和负载情况 +- 我们平时做压测,主要关注的还是要压测出来一个最合适的最高负载。 + - 意思就是在RocketMQ的TPS和机器的资源使用率和负载之间取得一个平衡。 +- 以下压测过程以及压测结果,都是根据我们之前真实的RocketMQ压测报告总结而来,非常的有代表性,大家完全可以结合我们之前说的机器配置来参考一下 +#### (1) RocketMQ的TPS和消息延时 +- 我们让两个Producer不停的往RocketMQ集群发送消息,每个Producer所在机器启动了**80个线程**,相当于每台机器有80个线程并发的往RocketMQ集群写入消息。 +- RocketMQ集群是**1主2从**组成的一个dledger模式的高可用集群,只有一个Master Broker会接收消息的写入。有2个Cosumer不停的从RocketMQ集群消费数据。[只主节点会接到数据的成功写入] +- **每条数据的大小是500个字节**,这个非常关键,大家一定要牢记这个数字,因为这个数字是跟后续的**网卡流量**有关的。 +- 一条消息从Producer生产出来到经过RocketMQ的Broker存储下来,再到被Consumer消费,基本上这个**时间跨度不会超过1秒钟**,这些这个性能是正常而且可以接受的。 +- 同时在RocketMQ的管理工作台中可以看到,Master Broker的TPS(也就是每秒处理消息的数量),可以稳定的达到7万左右,也就是 每秒可以稳定处理7万消息。 +#### (2) cpu负载情况 +- 检查一下Broker机器上的CPU负载,可以通过top、uptime等命令来查看 +- 执行top命令就可以看到cpu load和cpu使用率,这就代表了cpu的负载情况 + - 在你执行了top命令之后,往往可以看到如下一行信息: + - load average:12.03,12.05,12.08 + - 类似上面那行信息代表的是cpu在1分钟、5分钟和15分钟内的cpu负载情况 + - 比如我们一台机器是24核的,那么上面的12意思就是有12个核在使用中。换言之就是还有12个核其实还没使用,cpu还是有很大余力的。 +- 这个cpu负载其实是比较好的,因为并没有让cpu负载达到极限。 + +#### (3) 内存使用率 +- 使用free命令就可以查看到内存的使用率 +- 根据当时的测试结果,机器上48G的内存,仅仅使用了一部分,还剩下很大一部分内存都是 空闲可用的,或者是被RocketMQ用来进行磁盘数据缓存了 +- 所以内存负载是很低的 + +#### (4) JVM GC频率 +- 使用jstat命令就可以查看RocketMQ的JVM的GC频率,基本上新生代每隔几十秒会垃圾回收一次,每次回收过后存活的对象很少,几乎不进入老年代 + +#### (5) 磁盘IO负载 +- 首先可以用top命令查看一下IO等待占用CPU时间的百分比,你执行top命令之后,会看到一行类似下面的东西: +- Cpu(s): 0.3% us, 0.3% sy, 0.0% ni, 76.7% id, 13.2% wa, 0.0% hi, 0.0% si +- 在这里的13.2% wa,说的就是磁盘IO等待在CPU执行时间中的百分比 +- 如果这个比例太高,说明CPU执行的时候大部分时间都在等待执行IO,也就说明IO负载很高,导致大量的IO等待 +- 这个当时我们压测的时候,是在40%左右,说明IO等待时间占用CPU执行时间的比例在40%左右,这是相对高一些,但还是可以接受的,只不过如果继续让这个比例提高上去, + 就很不靠谱了,因为说明磁盘IO负载可能过高了。 + +#### (6) 网卡流量 +- 使用如下命令可以查看服务器的网卡流量: +- sar -n DEV 1 2 +- 通过这个命令就可以看到每秒钟网卡读写数据量了。当时我们的服务器使用的是千兆网卡,千兆网卡的理论上限是每秒传输128M数 据,但是一般实际最大值是每秒传输100M数据。 +- 因此当时我们发现的一个问题就是,在RocketMQ处理到每秒7万消息的时候,每条消息500字节左右的大小的情况下,每秒网卡传输 数据量已经达到100M了,就是已经达到了网卡的一个极限值了。 +- 因为一个Master Broker服务器,每秒不光是通过网络接收你写入的数据,还要把数据同步给两个Slave Broker,还有别的一些网络通 信开销。 +- 因此实际压测发现,每条消息500字节,每秒7万消息的时候,服务器的网卡就几乎打满了,无法承载更多的消息了。 + +#### (7) 针对压测的一点小总结 +- 通过本次压测做一些总结: + - 当我们使用平均大小为500字节的消息时,最多就是做到RocketMQ单台服务器每秒7万的TPS,而且这个时候cpu负载、内存负 载、jvm gc负载、磁盘io负载,基本都还在正常范围内 + - 只不过这个时候网卡流量基本已经打满了,无法再提升TPS了 + - 因此在这样的一个机器配置下,RocketMQ一个比较靠谱的TPS就是7万左右 + +#### 总结 +- 到底应该如何压测: + - 应该在TPS和机器的cpu负载、内存使用率、jvm gc频率、磁盘io负载、网络流量负载之间取得一个平衡,尽量让 TPS尽可能的提高,同时让机器的各项资源负载不要太高。 +- 实际压测过程: + - 采用几台机器开启大量线程并发读写消息,然后观察TPS、cpu load(使用top命令)、内存使用率(使用free命 令)、jvm gc频率(使用jstat命令)、磁盘io负载(使用top命令)、网卡流量负载(使用sar命令),不断增加机器和线程,让TPS不 断提升上去,同时观察各项资源负载是否过高。 +- 生产集群规划: + - 根据公司的后台整体QPS来定,稍微多冗余部署一些机器即可,实际部署生产环境的集群时,使用高配置物理机,同时 合理调整os内核参数、jvm参数、中间件核心参数,如此即可 ## 7. 购物车、订单与支付场景抗压实战 -> 可以移步到 ## 8. RocketMQ分布式事务消息 +### 8.1 生产案例:从 RocketMQ 全链路分析一下为什么用户支付后没有收到红包? +- 用户支付之后红包到底为什么没发送出去呢? + - 其实原因有多种可能,比如订单系统推送消息到MQ就失败了,压根儿就没推送过去; + - 或者是消息确实推送到MQ了,但是结果MQ自己机器故障,把消息搞丢了; + - 或者是红包系统拿到了消息,但是他把消息搞丢了,结果红包还没来得及发。 + - 如果真的在生产环境里要搞明白这个问题,就必须要打更多的日志去一点点分析消息到底是在哪个环节丢失了? + - 如果订单系统推送了消息,结果红包系统连消息都没收到,那可能消息根本就没发到MQ去,或者MQ自己搞丢了消息。 + - 如果红包系统收到了消息,结果红包没派发,那么就是红包系统搞丢了消息。 + + + + + + + + + + + + @@ -298,3 +505,21 @@ ## 10. 数据过滤与性能提升 + + + + + + + + + + + + + + + + + +