diff --git a/base/jvm/README.md b/base/jvm/README.md new file mode 100644 index 0000000..93c933c --- /dev/null +++ b/base/jvm/README.md @@ -0,0 +1,16 @@ +# JVM深入及调优 + + + + + + + + + + + + + + + diff --git a/best-practice/bd-support-project/README.md b/best-practice/bd-support-project/README.md new file mode 100644 index 0000000..e69de29 diff --git a/best-practice/my-ms-project/README.md b/best-practice/my-ms-project/README.md new file mode 100644 index 0000000..d7fe773 --- /dev/null +++ b/best-practice/my-ms-project/README.md @@ -0,0 +1,15 @@ +# 介绍 +- 自己的微服务项目, 整合所有的微服务技术来解决一业务问题 + +## 项目架构 +- 广告系统 +- 电商系统 +- IM系统 +- 活动系统 +- SCRM系统 +- 视频系统 +- 撮合系统 +- 支付系统 +- 搜索系统 +- 办公系统 +- \ No newline at end of file diff --git a/database/mysql/README.md b/database/mysql/README.md new file mode 100644 index 0000000..eb6b845 --- /dev/null +++ b/database/mysql/README.md @@ -0,0 +1,89 @@ +# MySQL 高级 + + +## 1. 系统是怎么和 MySQL 打交道 ? +- MySQL不仅仅是CRUD + +### 1.1 MySQL 驱动 +- mysql 驱动是跟数据库进行的一个网络连接建立工具 +- 不同的语言有不同的驱动使用 + +### 1.2 数据库连接池 +- 作用是减少连接的重复建立和销毁,让线程去执行SQL语句后, 不要销毁这个数据库连接,而是放在池子中进行复用 +- 常见的数据库连接池有: DBCP,C3P0,Druid + +## 2. 执行 SQL 语句, MySQL用了什么架构设计? +- 一个不变的原则: 网络连接必须让线程来处理 +![MySQL架构设计](pic/MySQL架构设计.png) +- SQL 接口: 负责处理接收到的SQL语句 +- 监听请求以及读取请求数据的线程, 把解析好的SQL语句v转交给SQL接口去执行 +- 查询解析器: 按照估计的SQL语法, 对我们发送的 SQL 语句进行解析, 理解其要进行的操作 +- 查询优化器: 生成查询路径树, 然后从里面选择一条最优的查询路径 +- 调用存储引擎, 真正的执行 SQL 语句 +- MySQL 的设计架构中, SQL接口, SQL解析器, 查询优化器其实都是通用的,就是一套组件 +- 存储引擎可以供我们去选择, 选择那种存储引擎来执行SQL。常见的存储引擎: InnoDB, MyISAM, Memory等。 +- 执行器: 执行器会根据我们的优化器生成一套执行计划, 然后不停的调用存储引擎的各种接口去完成SQL语句的执行计划 + +## 3. 用一次数据更新流程, 了解 InnoDB 引擎的架构设计 + +### 3.1 InnoDB 的重要内存结构: 缓冲池 +![缓冲池](pic/缓冲池.png) +- InnoDB 的很重要放在内存里面的组件, 里面会缓冲很多数据, 在查询时候, 如果查的是内存中的数据, 就不用去查盘了 +![缓冲池加载](pic/缓冲池加载.png) +- 引擎在更新语句的时候, 比如 "id=10" 这行数据是否在缓冲池中, 如果不在就从磁盘中加载到缓冲池, 而且还会对这行数据加独占锁 + +### 3.2 undo日志文件: 如何让你更新的数据可以回滚? +![undo日志](pic/undo日志.png) +- 我们开发中可以轻松地利用事务对数据提交的过程进行回滚操作,是因为会把更新前的值写入到undo日志文件中 + +### 3.3 更新 buffer pool 中的缓存数据 +- 当我们要把更新的那行记录从磁盘文件加载到缓冲池, 同时对他加锁, 而且还把更新前的旧值写入undo日志文件之后, 我们才正式开始更新 + 这行记录, 更新的时候, 先会更新缓冲池中的记录, 此时这个数据是脏数据 +- 这里所谓的更新内存缓冲池里的数据, 意思是把内存里的 "id=10" 这行数据的name修改为 "xxx" +- 为什么说是脏数据呢? + - 因为这个时候磁盘上 "id=10" 这行数据的name字段还是以前的值, 但是内存里面这行数据已经改变了, 所以称为脏数据 +![更新buffer-pool中的缓存数据](pic/更新bufferpool中的缓存数据.png) + +### 3.4 Redo Log Buffer: 万一系统宕机, 如何避免数据丢失? +- 在 3.3 中如果此时宕机, 会导致内存中改过的数据丢失, 怎么办? + - 这个时候,就必须把对内存的修改写到一个Redo Log Buffer 中, 这也是内存里的一个缓冲区, 是用来存放 redo 日志的 + - 所谓 redo 日志,就是记录你对数据做了什么修改, 比如对 "id=10这行记录修改了name字段的值为xxx", 这就是一个日志 +![redo日志](pic/redo日志.png) +- 这个 redo 日志其实是用来在 MySQL 突然宕机的时候, 用来恢复你更新过的数据, 现在redo日志还仅仅停留在内存缓冲里 + +### 3.5 如果还没提交事务, MySQL 宕机了怎么办? +- 执行一条SQL语句, 其实也可以是一个独立的事务, 当你提交事务后, SQL语句才算执行结束 +- 此时还没有提交事务, 如果此时 MySQL 崩溃, 必然导致内存里 Buffer Pool 中的修改过的数据都丢失, + 同时你写入 Redo Log Buffer 中的redo日志也会丢失
+![没提交事务宕机](pic/没提交事务宕机.png) +- 此时, 丢数据不要紧, 因为你一条更新语句, 没提交事务, 就代表他没执行成功, 磁盘上的数据没有改变, mysql 重启后, 你的数据无任何变化 + +### 3.6 提交事务的时候将redo日志写入磁盘中 +- 我们提交一个事务, 此时会根据一定的策略把redo日志从 redo log buffer里刷入到磁盘文件里去 +- 这个策略是通过 innodb_flush_log_at_trx_commit 来配置的 +- 当这个值为0时候, 你提交事务的时候,不会把redo log buffer里面的数据刷入磁盘文件,此时可能你都提交事务了, 结果MySQL宕机了, 然后此时内存里的数据全部丢失 + 相当于你提交事务成功了, 但是由于MySQL突然宕机, 导致内存中的数据和redo日志都丢失了
+![redo写磁盘0](pic/redo写磁盘0.png) +- 当这个参数值为1的时候, 你提交事务的时候, 就必须把redo log从内存刷入到磁盘文件中去, 只要事务提交成功, 那么redo log就必然在磁盘里了 +![redo写磁盘1](pic/redo写磁盘1.png) +- 只要提交事务成功, redo日志一定在磁盘文件中了, 此时你肯定会有一条redo日志说了, "我对什么数据进行了一个什么操作" +- 然后哪怕此时buffer pool中更新过的数据还没刷到磁盘里面去,此时内存中的数据是已经更新过的"name=xxx",然后磁盘上的数据是还没更新过的"name=zhangsan" +![redo写磁盘3](pic/redo写磁盘3.png) +- 此时不会丢数据, 因为redo日志中已经记录了操作 +- 所以此时mysql重启后, 可以根据redo日志去恢复之前做的修改 +![redo写磁盘4](pic/redo写磁盘4.png) +- 如果 innodb_flush_log_at_trx_commit 值为2 +- 意思是说, 提交事务的时候, 把redo日志写入磁盘文件对应的os cache缓存里去, 而不是直接进入磁盘文件, 可能1秒后才会把 os cache 里的数据写入到磁盘文件中去 +- 这种模式下,你提交了事务, redo log可能仅仅停留在os cache内存缓存中, 没实际进入磁盘文件, 万一此时你要是机器宕机了, 那么os cache里的redo log就会丢失 + 同样让你感觉提交了事务, 数据丢了 +![redo写磁盘5](pic/redo写磁盘5.png) + +--- +- 总结: 对于redo日志的三种刷盘策略, 我们的通常建议是1, 保证事务提交后, 数据绝对不能丢失 +## 4. 聊聊binlog是什么? + + + + + + diff --git a/database/mysql/pic/MySQL架构设计.png b/database/mysql/pic/MySQL架构设计.png new file mode 100644 index 0000000..619bf2a Binary files /dev/null and b/database/mysql/pic/MySQL架构设计.png differ diff --git a/database/mysql/pic/redo写磁盘0.png b/database/mysql/pic/redo写磁盘0.png new file mode 100644 index 0000000..8b87fc5 Binary files /dev/null and b/database/mysql/pic/redo写磁盘0.png differ diff --git a/database/mysql/pic/redo写磁盘1.png b/database/mysql/pic/redo写磁盘1.png new file mode 100644 index 0000000..bb46a2f Binary files /dev/null and b/database/mysql/pic/redo写磁盘1.png differ diff --git a/database/mysql/pic/redo写磁盘3.png b/database/mysql/pic/redo写磁盘3.png new file mode 100644 index 0000000..378a273 Binary files /dev/null and b/database/mysql/pic/redo写磁盘3.png differ diff --git a/database/mysql/pic/redo写磁盘4.png b/database/mysql/pic/redo写磁盘4.png new file mode 100644 index 0000000..cc1707d Binary files /dev/null and b/database/mysql/pic/redo写磁盘4.png differ diff --git a/database/mysql/pic/redo写磁盘5.png b/database/mysql/pic/redo写磁盘5.png new file mode 100644 index 0000000..1b5b290 Binary files /dev/null and b/database/mysql/pic/redo写磁盘5.png differ diff --git a/database/mysql/pic/redo日志.png b/database/mysql/pic/redo日志.png new file mode 100644 index 0000000..22e5aba Binary files /dev/null and b/database/mysql/pic/redo日志.png differ diff --git a/database/mysql/pic/undo日志.png b/database/mysql/pic/undo日志.png new file mode 100644 index 0000000..cc3e28f Binary files /dev/null and b/database/mysql/pic/undo日志.png differ diff --git a/database/mysql/pic/更新bufferpool中的缓存数据.png b/database/mysql/pic/更新bufferpool中的缓存数据.png new file mode 100644 index 0000000..521226a Binary files /dev/null and b/database/mysql/pic/更新bufferpool中的缓存数据.png differ diff --git a/database/mysql/pic/没提交事务宕机.png b/database/mysql/pic/没提交事务宕机.png new file mode 100644 index 0000000..7310580 Binary files /dev/null and b/database/mysql/pic/没提交事务宕机.png differ diff --git a/database/mysql/pic/缓冲池.png b/database/mysql/pic/缓冲池.png new file mode 100644 index 0000000..de5857c Binary files /dev/null and b/database/mysql/pic/缓冲池.png differ diff --git a/database/mysql/pic/缓冲池加载.png b/database/mysql/pic/缓冲池加载.png new file mode 100644 index 0000000..3e1aaea Binary files /dev/null and b/database/mysql/pic/缓冲池加载.png differ diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-alibaba/.gitignore b/dev-protocol-springcloud/dev-protocol-springcloud-alibaba/.gitignore new file mode 100644 index 0000000..d4beec9 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-alibaba/.gitignore @@ -0,0 +1,46 @@ +### Example user template template +### Example user template + +# IntelliJ project files +.idea +*.iml +out +gen +### Maven template +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +# https://github.com/takari/maven-wrapper#usage-without-binary-jar +.mvn/wrapper/maven-wrapper.jar + +### Java template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-alibaba/README.md b/dev-protocol-springcloud/dev-protocol-springcloud-alibaba/README.md new file mode 100644 index 0000000..c1398ea --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-alibaba/README.md @@ -0,0 +1,82 @@ +# SpringCloud + +## 1. 概念综述 + +- 参考: https://github.com/alibaba/spring-cloud-alibaba/wiki/ + - 版本选择 + - 开源组件 + - Nacos Config + - Nacos Discovery + - Sentinel + - RocketMQ + - Dubbo Spring Cloud + - 商业化组件 + - OSS + - SchedulerX + - SMS + - Example + - Sentinel + - Nacos Config + - Nacos Discovery + - RocketMQ + - OSS + - SMS + - Dubbo Spring Cloud +- 视频: https://coding.imooc.com/class/chapter/358.html#Anchor +- + +## 2. 服务发现-Nacos + + +## 3. 实现负载均衡-Ribbon + + +## 4. 声明式HTTP客户端-Feign + + +## 5. 服务容错-Sentinel + + +## 6. 消息驱动的微服务-Spring Cloud Alibaba RocketMQ + + +## 7. API网关-Spring Cloud Gateway + + +## 8. 微服务的用户认证与授权 + + +## 9. 配置管理-Nacos + + +## 10. 调用链监控-Sleuth + + +## 11. 多维度微服务监控 + + +## 12. 完美融合异构微服务 + + +## 13. Spring Cloud Alibaba升级 + + +## 14. SpringBoot Admin 微服务应用监控 + + +## 15. SpringCloud Sleuth + Zipkin:分布式日志追踪 + + +## 16. 微服务通信 Ribbon + OpenFeign + + +## 17. SpringCloud Netflix Hystrix 实现微服务容错 + + +## 18. Seata:分布式事务解决方案 + + +## 19. 基于 SpringCloud Alibaba Sentinel 实现网关动态限流 + + +## 20. 微服务工程部署与整体可用性验证 \ No newline at end of file diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-alibaba/pom.xml b/dev-protocol-springcloud/dev-protocol-springcloud-alibaba/pom.xml new file mode 100644 index 0000000..b3a0563 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-alibaba/pom.xml @@ -0,0 +1,22 @@ + + + + dev-protocol + org.example + 1.0-SNAPSHOT + ../../pom.xml + + + + 4.0.0 + dev-protocol-springcloud-alibaba + 1.0.0-RELEASE + + + 8 + 8 + + + \ No newline at end of file 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/practice/README.md b/longpolling/practice/README.md new file mode 100644 index 0000000..b33759a --- /dev/null +++ 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 new file mode 100644 index 0000000..cc1aab1 --- /dev/null +++ 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. 数据过滤与性能提升 + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 8527b1a..4d4e5bb 100644 --- a/pom.xml +++ b/pom.xml @@ -37,6 +37,7 @@ mq/rocketmq/springboot-rocketmq dev-protocol-springcloud dev-protocol-springcloud/dev-protocol-springcloud-stream + dev-protocol-springcloud/dev-protocol-springcloud-alibaba longpolling/demo/demo2/dev-protocol-disruptor-demo longpolling/netty/better-netty longpolling/demo/demo3/dev-protocol-netty-client