文章列表

详解分布式系统的缓存设计

作者:微信小助手

<section style="font-size: 15px;line-height: 1.9;letter-spacing: 0.75px;box-sizing: border-box;" data-mpa-powered-by="yiban.io"> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <blockquote class="js_blockquote_wrap" data-type="2" data-url="" data-author-name="" data-content-utf8-length="25" data-source-title="" data-text="作者:vivo互联网服务器团队-Zhang Peng" data-editid="i96t8lqfx7clgxughs"> <section class="js_blockquote_digest"> <section> 作者:vivo互联网服务器团队-Zhang Peng </section> </section> </blockquote> <p><br></p>​ </section> <section style="margin-top: 10px;margin-bottom: 10px;text-align: center;box-sizing: border-box;" powered-by="xiumi.us"> <section style="padding: 3px;display: inline-block;border-bottom: 1px solid rgb(65, 94, 255);font-size: 17px;color: rgb(65, 94, 255);box-sizing: border-box;"> <p style="box-sizing: border-box;">一、缓存简介</p> </section> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">1.1 什么是缓存</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">缓存就是数据交换的缓冲区。缓存的本质是一个内存 Hash。缓存是一种利用空间换时间的设计,其目标就是更快、更近:极大的提高。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;">将数据写入/读取速度更快的存储(设备);</p></li> <li><p style="white-space: normal;box-sizing: border-box;">将数据缓存到离应用最近的位置;</p></li> <li><p style="white-space: normal;box-sizing: border-box;">将数据缓存到离用户最近的位置。</p></li> </ul> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">缓存是用于存储数据的硬件或软件的组成部分,以使得后续更快访问相应的数据。缓存中的数据可能是提前计算好的结果、数据的副本等。典型的应用场景:有 cpu cache, 磁盘 cache 等。本文中提及到缓存主要是指互联网应用中所使用的缓存组件。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">缓存命中率是缓存的重要度量指标,命中率越高越好。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <blockquote class="js_blockquote_wrap" data-type="2" data-url="" data-author-name="" data-content-utf8-length="24" data-source-title="" data-text="缓存命中率 = 从缓存中读取次数 / 总读取次数" data-editid="39doa6w6levseih5hc"> <section class="js_blockquote_digest"> <section> 缓存命中率 = 从缓存中读取次数 / 总读取次数 </section> </section> </blockquote> <p><br></p>​ </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">1.2 何时需要缓存</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">引入缓存,会增加系统的复杂度。所以,引入缓存前,需要先权衡是否值得,考量点如下:</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">CPU 开销</strong> - 如果应用某个计算需要消耗大量 CPU,可以考虑缓存其计算结果。典型场景:复杂的、频繁调用的正则计算;分布式计算中间状态等。</p></li> <li><p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">IO 开销</strong> - 如果数据库连接池比较繁忙,可以考虑缓存其查询结果。</p></li> </ul> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">在数据层引入缓存,有以下几个好处:</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;">提升数据读取速度。</p></li> <li><p style="white-space: normal;box-sizing: border-box;">提升系统扩展能力,通过扩展缓存,提升系统承载能力。</p></li> <li><p style="white-space: normal;box-sizing: border-box;">降低存储成本,Cache+DB 的方式可以承担原有需要多台 DB 才能承担的请求量,节省机器成本。</p></li> </ul> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">1.3 缓存的基本原理</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">根据业务场景,通常缓存有以下几种使用方式:</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;">懒汉式(读时触发):先查询 DB 里的数据, 然后把相关的数据写入 Cache。</p></li> <li><p style="white-space: normal;box-sizing: border-box;">饥饿式(写时触发):写入 DB 后, 然后把相关的数据也写入 Cache。</p></li> <li><p style="white-space: normal;box-sizing: border-box;">定期刷新:适合周期性的跑数据的任务,或者列表型的数据,而且不要求绝对实时性。</p></li> </ul> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">1.4 缓存淘汰策略</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">缓存淘汰的类型:</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;"><strong>1)基于空间</strong>:设置缓存空间大小。</p> <p style="white-space: normal;box-sizing: border-box;"><strong>2)基于容量</strong>:设置缓存存储记录数。</p> <p style="white-space: normal;box-sizing: border-box;"><strong>3)基于时间</strong></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;">TTL(Time To Live,即存活期)缓存数据从创建到过期的时间。</p></li> <li><p style="white-space: normal;box-sizing: border-box;">TTI(Time To Idle,即空闲期)缓存数据多久没被访问的时间。</p></li> </ul> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">缓存淘汰算法:</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">1)FIFO:先进先出</strong>。在这种淘汰算法中,先进入缓存的会先被淘汰。这种可谓是最简单的了,但是会导致我们命中率很低。试想一下我们如果有个访问频率很高的数据是所有数据第一个访问的,而那些不是很高的是后面再访问的,那这样就会把我们的首个数据但是他的访问频率很高给挤出。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">2)LRU:最近最少使用算法</strong>。在这种算法中避免了上面的问题,每次访问数据都会将其放在我们的队尾,如果需要淘汰数据,就只需要淘汰队首即可。但是这个依然有个问题,如果有个数据在 1 个小时的前 59 分钟访问了 1 万次(可见这是个热点数据),再后一分钟没有访问这个数据,但是有其他的数据访问,就导致了我们这个热点数据被淘汰。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">3)LFU:最近最少频率使用</strong>。在这种算法中又对上面进行了优化,利用额外的空间记录每个数据的使用频率,然后选出频率最低进行淘汰。这样就避免了 LRU 不能处理时间段的问题。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">这三种缓存淘汰算法,实现复杂度一个比一个高,同样的命中率也是一个比一个好。而我们一般来说选择的方案居中即可,即实现成本不是太高,而命中率也还行的 LRU。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="margin-top: 10px;margin-bottom: 10px;text-align: center;box-sizing: border-box;" powered-by="xiumi.us"> <section style="padding: 3px;display: inline-block;border-bottom: 1px solid rgb(65, 94, 255);font-size: 17px;color: rgb(65, 94, 255);box-sizing: border-box;"> <p style="box-sizing: border-box;">二、缓存的分类</p> </section> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">缓存从部署角度,可以分为客户端缓存和服务端缓存。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">客户端缓存</strong></p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;">HTTP 缓存</p></li> <li><p style="white-space: normal;box-sizing: border-box;">浏览器缓存</p></li> <li><p style="white-space: normal;box-sizing: border-box;">APP 缓存(1、<span style="box-sizing: border-box;">Android&nbsp; 2、</span><span style="box-sizing: border-box;">IOS)</span></p></li> </ul> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">服务端缓存</strong></p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;">CDN 缓存:存放 HTML、CSS、JS 等静态资源。</p></li> <li><p style="white-space: normal;box-sizing: border-box;">反向代理缓存:动静分离,只缓存用户请求的静态资源。</p></li> <li><p style="white-space: normal;box-sizing: border-box;">数据库缓存:数据库(如 MySQL)自身一般也有缓存,但因为命中率和更新频率问题,不推荐使用。</p></li> <li><p style="white-space: normal;box-sizing: border-box;">进程内缓存:缓存应用字典等常用数据。</p></li> <li><p style="white-space: normal;box-sizing: border-box;">分布式缓存:缓存数据库中的热点数据。</p></li> </ul> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">其中,CDN 缓存、反向代理缓存、数据库缓存一般由专职人员维护(运维、DBA)。<span style="box-sizing: border-box;">后端开发一般聚焦于进程内缓存、分布式缓存。</span></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">2.1 HTTP 缓存</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">2.2 CDN 缓存</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">CDN 将数据缓存到离用户物理距离最近的服务器,使得用户可以就近获取请求内容。CDN 一般缓存静态资源文件(页面,脚本,图片,视频,文件等)。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">国内网络异常复杂,跨运营商的网络访问会很慢。为了解决跨运营商或各地用户访问问题,可以在重要的城市,部署 CDN 应用。使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="321" data-backw="578" data-ratio="0.555" data-s="300,640" data-type="png" data-w="1000" style="width: 100%;height: auto !important;" src="/upload/247e743d029289a30b219cd4a3a82e46.png"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;"><span style="font-size: 14px;">图片引用自:</span><span style="font-size: 14px;">Why use a CDN</span><br></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">2.1.1 CDN 原理</strong></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">CDN 的基本原理是广泛采用各种缓存服务器,将这些缓存服务器分布到用户访问相对集中的地区或网络中,在用户访问网站时,利用全局负载技术将用户的访问指向距离最近的工作正常的缓存服务器上,由缓存服务器直接响应用户请求。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">1)未部署 CDN 应用前的网络路径:</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">请求</strong>:本机网络(局域网)=&gt; 运营商网络 =&gt; 应用服务器机房</p></li> <li><p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">响应</strong>:应用服务器机房 =&gt; 运营商网络 =&gt; 本机网络(局域网)</p></li> </ul> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">在不考虑复杂网络的情况下,从请求到响应需要经过 3 个节点,6 个步骤完成一次用户访问操作。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">2)部署 CDN 应用后网络路径:</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">请求</strong>:本机网络(局域网) =&gt; 运营商网络</p></li> <li><p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">响应</strong>:运营商网络 =&gt; 本机网络(局域网)</p></li> </ul> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;">在不考虑复杂网络的情况下,从请求到响应需要经过 2 个节点,2 个步骤完成一次用户访问操作。与不部署 CDN 服务相比,减少了 1 个节点,4 个步骤的访问。极大的提高了系统的响应速度。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">2.1.2 CDN 特点</strong></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">优点</strong></p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">本地 Cache 加速</strong>:提升访问速度,尤其含有大量图片和静态页面站点;</p></li> <li><p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">实现跨运营商的网络加速</strong>:消除了不同运营商之间互联的瓶颈造成的影响,实现了跨运营商的网络加速,保证不同网络中的用户都能得到良好的访问质量;</p></li> <li><p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">远程加速</strong>:远程访问用户根据 DNS 负载均衡技术智能自动选择 Cache 服务器,选择最快的 Cache 服务器,加快远程访问的速度;</p></li> <li><p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">带宽优化</strong>:自动生成服务器的远程 Mirror(镜像)cache 服务器,远程用户访问时从 cache 服务器上读取数据,减少远程访问的带宽、分担网络流量、减轻原站点 WEB 服务器负载等功能。</p></li> <li><p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">集群抗攻击</strong>:广泛分布的 CDN 节点加上节点之间的智能冗余机制,可以有效地预防黑客入侵以及降低各种 D.D.o.S 攻击对网站的影响,同时保证较好的服务质量。</p></li> </ul> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">缺点</strong></p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">不适宜缓存动态资源</strong></p></li> </ul> <blockquote class="js_blockquote_wrap" data-type="2" data-url="" data-author-name="" data-content-utf8-length="31" data-source-title="" data-text="解决方案:主要缓存静态资源,动态资源建立多级缓存或准实时同步;" data-editid="g83cdwz26gr602zzeo"> <section class="js_blockquote_digest"> <section> 解决方案:主要缓存静态资源,动态资源建立多级缓存或准实时同步; </section> </section> </blockquote> <p><br></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">存在数据的一致性问题</strong></p></li> </ul> <blockquote class="js_blockquote_wrap" data-type="2" data-url="" data-author-name="" data-content-utf8-length="67" data-source-title="" data-text="1.解决方案(主要是在性能和数据一致性二者间寻找一个平衡)。2.设置缓存失效时间(1 个小时,过期后同步数据)。3.针对资源设置版本号。" data-editid="2iadhcf3w7gy1t47i8"> <section class="js_blockquote_digest"> <section> <p style="white-space: normal;box-sizing: border-box;">1.解决方案(主要是在性能和数据一致性二者间寻找一个平衡)。</p> <p style="white-space: normal;box-sizing: border-box;">2.设置缓存失效时间(1 个小时,过期后同步数据)。</p> <p style="white-space: normal;box-sizing: border-box;">3.针对资源设置版本号。</p> </section> </section> </blockquote>​​ </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">2.2 反向代理缓存</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">反向代理(Reverse Proxy)方式是指以代理服务器来接受 internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给 internet 上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="340" data-backw="578" data-ratio="0.5885558583106267" data-s="300,640" data-type="png" data-w="1101" style="width: 100%;height: auto !important;" src="/upload/725f290a6b675ad529959202e88ef4b7.png"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">2.2.1 反向代理缓存原理</strong></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">反向代理位于应用服务器同一网络,处理所有对 WEB 服务器的请求。反向代理缓存的原理:</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;">如果用户请求的页面在代理服务器上有缓存的话,代理服务器直接将缓存内容发送给用户。</p></li> <li><p style="white-space: normal;box-sizing: border-box;">如果没有缓存则先向 WEB 服务器发出请求,取回数据,本地缓存后再发送给用户。</p></li> </ul> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">这种方式通过降低向 WEB 服务器的请求数,从而降低了 WEB 服务器的负载。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">反向代理缓存一般针对的是静态资源,而将动态资源请求转发到应用服务器处理。常用的缓存应用服务器有 Varnish,Ngnix,Squid。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">2.2.2 反向代理缓存比较</strong></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">常用的代理缓存有 Varnish,Squid,Ngnix,简单比较如下:</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;">Varnish 和 Squid 是专业的 cache 服务,Ngnix 需要第三方模块支持;</p></li> <li><p style="white-space: normal;box-sizing: border-box;">Varnish 采用内存型缓存,避免了频繁在内存、磁盘中交换文件,性能比 Squid 高;</p></li> <li><p style="white-space: normal;box-sizing: border-box;">Varnish 由于是内存 cache,所以对小文件如 css、js、小图片的支持很棒,后端的持久化缓存可以采用的是 Squid 或 ATS;</p></li> <li><p style="white-space: normal;box-sizing: border-box;">Squid 功能全而大,适合于各种静态的文件缓存,一般会在前端挂一个 HAProxy 或 Ngnix 做负载均衡跑多个实例;</p></li> <li> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">Nginx 采用第三方模块 ncache 做的缓冲,性能基本达到 Varnish,一般作为反向代理使用,可以实现简单的缓存。</p> </section></li> </ul> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="margin-top: 10px;margin-bottom: 10px;text-align: center;box-sizing: border-box;" powered-by="xiumi.us"> <section style="padding: 3px;display: inline-block;border-bottom: 1px solid rgb(65, 94, 255);font-size: 17px;color: rgb(65, 94, 255);box-sizing: border-box;"> <p style="box-sizing: border-box;">三、进程内缓存</p> </section> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">进程内缓存是指应用内部的缓存,标准的分布式系统,一般有多级缓存构成。本地缓存是离应用最近的缓存,一般可以将数据缓存到硬盘或内存。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">硬盘缓存</strong>:将数据缓存到硬盘中,读取时从硬盘读取。原理是直接读取本机文件,减少了网络传输消耗,比通过网络读取数据库速度更快。可以应用在对速度要求不是很高,但需要大量缓存存储的场景。</p></li> <li><p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">内存缓存</strong>:直接将数据存储到本机内存中,通过程序直接维护缓存对象,是访问速度最快的方式。</p></li> </ul> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">常见的本地缓存实现方案:HashMap、Guava Cache、Caffeine、Ehcache。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><span style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;">3.1 ConcurrentHashMap</span></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">最简单的进程内缓存可以通过 JDK 自带的 HashMap 或 ConcurrentHashMap 实现。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;">适用场景:不需要淘汰的缓存数据。</p></li> <li> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">缺点:无法进行缓存淘汰,内存会无限制的增长。</p> </section></li> </ul> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">3.2&nbsp;LRUHashMap</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">可以通过继承 LinkedHashMap 来实现一个简单的 LRUHashMap。重写 removeEldestEntry 方法,即可完成一个简单的最近最少使用算法。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">缺点:</p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;">锁竞争严重,性能比较低。</p></li> <li><p style="white-space: normal;box-sizing: border-box;">不支持过期时间。</p></li> <li> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">不支持自动刷新。</p> </section></li> </ul> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">3.3&nbsp; Guava Cache</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">解决了LRUHashMap 中的几个缺点。Guava Cache 采用了类似 ConcurrentHashMap 的思想,分段加锁,减少锁竞争。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">Guava Cache 对于过期的 Entry 并没有马上过期(也就是并没有后台线程一直在扫),而是通过进行读写操作的时候进行过期处理,这样做的好处是避免后台线程扫描的时候进行全局加锁。直接通过查询,判断其是否满足刷新条件,进行刷新。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">3.4&nbsp; Caffeine</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">Caffeine 实现了 W-TinyLFU(LFU + LRU 算法的变种),其命中率和读写吞吐量大大优于 Guava Cache。其实现原理较复杂,可以参考你应该知道的缓存进化史。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">3.5 Ehcache</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">EhCache 是一个纯 Java 的进程内缓存框架,具有快速、精干等特点,是 Hibernate 中默认的 CacheProvider。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">优点</strong></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;">快速、简单;</p></li> <li><p style="white-space: normal;box-sizing: border-box;">支持多种缓存策略:LRU、LFU、FIFO 淘汰算法;</p></li> <li><p style="white-space: normal;box-sizing: border-box;">缓存数据有两级:内存和磁盘,因此无需担心容量问题;</p></li> <li><p style="white-space: normal;box-sizing: border-box;">缓存数据会在虚拟机重启的过程中写入磁盘;</p></li> <li><p style="white-space: normal;box-sizing: border-box;">可以通过 RMI、可插入 API 等方式进行分布式缓存;</p></li> <li><p style="white-space: normal;box-sizing: border-box;">具有缓存和缓存管理器的侦听接口;</p></li> <li><p style="white-space: normal;box-sizing: border-box;">支持多缓存管理器实例,以及一个实例的多个缓存区域;</p></li> <li><p style="white-space: normal;box-sizing: border-box;">提供 Hibernate 的缓存实现。</p></li> </ul> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">缺点</strong></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;">使用磁盘 Cache 的时候非常占用磁盘空间;</p></li> <li><p style="white-space: normal;box-sizing: border-box;">不保证数据的安全;</p></li> <li><p style="white-space: normal;box-sizing: border-box;">虽然支持分布式缓存,但效率不高(通过组播方式,在不同节点之间同步数据)。</p></li> </ul> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">3.6 进程内缓存对比</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">常用进程内缓存技术对比:</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="340" data-backw="578" data-ratio="0.5877502944640753" data-s="300,640" data-type="png" data-w="849" style="width: 100%;height: auto !important;" src="/upload/4e1c728059aff9612a61fe25b1ec8b08.png"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> </section> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">ConcurrentHashMap</strong>:比较适合缓存比较固定不变的元素,且缓存的数量较小的。虽然从上面表格中比起来有点逊色,但是其由于是 JDK 自带的类,在各种框架中依然有大量的使用,比如我们可以用来缓存我们反射的 Method,Field 等等;也可以缓存一些链接,防止其重复建立。在 Caffeine 中也是使用的 ConcurrentHashMap 来存储元素。</p></li> </ul> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">LRUMap</strong>:如果不想引入第三方包,又想使用淘汰算法淘汰数据,可以使用这个。</p></li> </ul> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">Ehcache</strong>:由于其 jar 包很大,较重量级。对于需要持久化和集群的一些功能的,可以选择 Ehcache。需要注意的是,虽然 Ehcache 也支持分布式缓存,但是由于其节点间通信方式为 rmi,表现不如 Redis,所以一般不建议用它来作为分布式缓存。</p></li> </ul> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">Guava Cache</strong>:Guava 这个 jar 包在很多 Java 应用程序中都有大量的引入,所以很多时候其实是直接用就好了,并且其本身是轻量级的而且功能较为丰富,在不了解 Caffeine 的情况下可以选择 Guava Cache。</p></li> </ul> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">Caffeine</strong>:其在命中率,读写性能上都比 Guava Cache 好很多,并且其 API 和 Guava cache 基本一致,甚至会多一点。在真实环境中使用 Caffeine,取得过不错的效果。</p></li> </ul> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">总结一下:如果不需要淘汰算法则选择 ConcurrentHashMap,如果需要淘汰算法和一些丰富的 API,推荐选择。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="margin-top: 10px;margin-bottom: 10px;text-align: center;box-sizing: border-box;" powered-by="xiumi.us"> <section style="padding: 3px;display: inline-block;border-bottom: 1px solid rgb(65, 94, 255);font-size: 17px;color: rgb(65, 94, 255);box-sizing: border-box;"> <p style="box-sizing: border-box;">四、分布式缓存</p> </section> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">分布式缓存解决了进程内缓存最大的问题:如果应用是分布式系统,节点之间无法共享彼此的进程内缓存。<span style="box-sizing: border-box;">分布式缓存的应用场景:</span></p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;">缓存经过复杂计算得到的数据。</p></li> <li><p style="white-space: normal;box-sizing: border-box;">缓存系统中频繁访问的热点数据,减轻数据库压力。</p></li> </ul> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">不同分布式缓存的实现原理往往有比较大的差异。本文主要针对 Memcached 和 Redis 进行说明。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">4.1 Memcached</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="wh

Spring Boot 使用 Disruptor 做内部高性能消息队列

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.6;word-break: break-word;overflow-wrap: break-word;text-align: left;padding: 5px;font-size: 16px;color: rgb(53, 53, 53);word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;font-family: Roboto, Oxygen, Ubuntu, Cantarell, PingFangSC-light, PingFangTC-light, &quot;Open Sans&quot;, &quot;Helvetica Neue&quot;, sans-serif;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">工作中遇到项目使用Disruptor做消息队列,对你没看错,不是Kafka,也不是rabbitmq。Disruptor有个最大的优点就是快,还有一点它是开源的哦,下面做个简单的记录.</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU3MDAzNDg1MA==&amp;mid=2247512889&amp;idx=1&amp;sn=3f4600c571d97f1311def99c103365c2&amp;chksm=fcf76cf4cb80e5e2034beb924b8ba5dc0c76af6526d24313c978ede2e193ef13b18189edad1d&amp;scene=21#wechat_redirect" textvalue="你已选中了添加链接的内容" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="inset: auto;margin: 20px 0px;"><img class="rich_pages wxw-img" data-ratio="0.8298653610771114" src="/upload/ead6d7cb5f1f53a15748231a0a9285ef.png" data-type="png" data-w="817" style="border-radius: 6px;display: block;margin: 0px;max-width: 95%;object-fit: contain;"></span></a> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);"><strong style="font-weight: 700;color: rgb(248, 57, 41);">Disruptor介绍</strong></span></h2> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: decimal;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> Disruptor 是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题(在性能测试中发现竟然与I/O操作处于同样的数量级)。基于 Disruptor 开发的系统单线程能支撑每秒 600 万订单,2010 年在 QCon 演讲后,获得了业界关注。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> Disruptor是一个开源的Java框架,它被设计用于在生产者—消费者(producer-consumer problem,简称PCP)问题上获得尽量高的吞吐量(TPS)和尽量低的延迟。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 从功能上来看,Disruptor 是实现了“队列”的功能,而且是一个有界队列。那么它的应用场景自然就是“生产者-消费者”模型的应用场合了。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> Disruptor是LMAX在线交易平台的关键组成部分,LMAX平台使用该框架对订单处理速度能达到600万TPS,除金融领域之外,其他一般的应用中都可以用到Disruptor,它可以带来显著的性能提升。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 其实Disruptor与其说是一个框架,不如说是一种设计思路,这个设计思路对于存在“并发、缓冲区、生产者—消费者模型、事务处理”这些元素的程序来说,Disruptor提出了一种大幅提升性能(TPS)的方案。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> Disruptor的github主页:https://github.com/LMAX-Exchange/disruptor </section></li> </ol> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);"><strong style="font-weight: 700;color: rgb(248, 57, 41);">Disruptor 的核心概念</strong></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">先从了解 Disruptor 的核心概念开始,来了解它是如何运作的。下面介绍的概念模型,既是领域对象,也是映射到代码实现上的核心对象。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);"><strong style="font-weight: 700;color: rgb(248, 57, 41);">Ring Buffer</strong></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">如其名,环形的缓冲区。曾经 RingBuffer 是 Disruptor 中的最主要的对象,但从3.0版本开始,其职责被简化为仅仅负责对通过 Disruptor 进行交换的数据(事件)进行存储和更新。在一些更高级的应用场景中,Ring Buffer 可以由用户的自定义实现来完全替代。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);"><strong style="font-weight: 700;color: rgb(248, 57, 41);">Sequence Disruptor</strong></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">通过顺序递增的序号来编号管理通过其进行交换的数据(事件),对数据(事件)的处理过程总是沿着序号逐个递增处理。一个 Sequence 用于跟踪标识某个特定的事件处理者( RingBuffer/Consumer )的处理进度。虽然一个 AtomicLong 也可以用于标识进度,但定义 Sequence 来负责该问题还有另一个目的,那就是防止不同的 Sequence 之间的CPU缓存伪共享(Flase Sharing)问题。(注:这是 Disruptor 实现高性能的关键点之一,网上关于伪共享问题的介绍已经汗牛充栋,在此不再赘述)。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);"><strong style="font-weight: 700;color: rgb(248, 57, 41);">Sequencer</strong></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">Sequencer 是 Disruptor 的真正核心。此接口有两个实现类 SingleProducerSequencer、MultiProducerSequencer ,它们定义在生产者和消费者之间快速、正确地传递数据的并发算法。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);"><strong style="font-weight: 700;color: rgb(248, 57, 41);">Sequence Barrier</strong></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">用于保持对RingBuffer的 main published Sequence 和Consumer依赖的其它Consumer的 Sequence 的引用。Sequence Barrier 还定义了决定 Consumer 是否还有可处理的事件的逻辑。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);"><strong style="font-weight: 700;color: rgb(248, 57, 41);">Wait Strategy</strong></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">定义 Consumer 如何进行等待下一个事件的策略。(注:Disruptor 定义了多种不同的策略,针对不同的场景,提供了不一样的性能表现)</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);"><strong style="font-weight: 700;color: rgb(248, 57, 41);">Event</strong></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">在 Disruptor 的语义中,生产者和消费者之间进行交换的数据被称为事件(Event)。它不是一个被 Disruptor 定义的特定类型,而是由 Disruptor 的使用者定义并指定。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);"><strong style="font-weight: 700;color: rgb(248, 57, 41);">EventProcessor</strong></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">EventProcessor 持有特定消费者(Consumer)的 Sequence,并提供用于调用事件处理实现的事件循环(Event Loop)。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);"><strong style="font-weight: 700;color: rgb(248, 57, 41);">EventHandler</strong></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">Disruptor 定义的事件处理接口,由用户实现,用于处理事件,是 Consumer 的真正实现。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);"><strong style="font-weight: 700;color: rgb(248, 57, 41);">Producer</strong></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">即生产者,只是泛指调用 Disruptor 发布事件的用户代码,Disruptor 没有定义特定接口或类型。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU3MDAzNDg1MA==&amp;mid=2247512889&amp;idx=1&amp;sn=3f4600c571d97f1311def99c103365c2&amp;chksm=fcf76cf4cb80e5e2034beb924b8ba5dc0c76af6526d24313c978ede2e193ef13b18189edad1d&amp;scene=21#wechat_redirect" textvalue="你已选中了添加链接的内容" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="inset: auto;margin: 20px 0px;"><img class="rich_pages wxw-img" data-ratio="0.6650124069478908" src="/upload/91ecfad375c2c3627859fb32c62e6900.png" data-type="png" data-w="806" style="border-radius: 6px;display: block;margin: 0px;max-width: 95%;object-fit: contain;"></span></a> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);"><strong style="font-weight: 700;color: rgb(248, 57, 41);">案例-demo</strong></span></h2> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 通过下面8个步骤,你就能将Disruptor Get回家啦: </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 1、添加pom.xml依赖 </section></li> </ul> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oexqmcDw0weKQa3AT91gGibCzcn7gTKBF908ibBgmbHfGPVTc2naYj2VoJRcac69ac5FsowsAI4ibYGliatBqP7ibrMzib/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="line-height: 26px;">&lt;<span style="color: #e45649;line-height: 26px;">dependency</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;">&lt;<span style="color: #e45649;line-height: 26px;">groupId</span>&gt;</span>com.lmax<span style="line-height: 26px;">&lt;/<span style="color: #e45649;line-height: 26px;">groupId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;">&lt;<span style="color: #e45649;line-height: 26px;">artifactId</span>&gt;</span>disruptor<span style="line-height: 26px;">&lt;/<span style="color: #e45649;line-height: 26px;">artifactId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;">&lt;<span style="color: #e45649;line-height: 26px;">version</span>&gt;</span>3.3.4<span style="line-height: 26px;">&lt;/<span style="color: #e45649;line-height: 26px;">version</span>&gt;</span><br><span style="line-height: 26px;">&lt;/<span style="color: #e45649;line-height: 26px;">dependency</span>&gt;</span><br></code></pre> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 2、消息体Model </section></li> </ul> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oexqmcDw0weKQa3AT91gGibCzcn7gTKBF908ibBgmbHfGPVTc2naYj2VoJRcac69ac5FsowsAI4ibYGliatBqP7ibrMzib/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #a0a1a7;font-style: italic;line-height: 26px;">/**<br>&nbsp;*&nbsp;消息体<br>&nbsp;*/</span><br><span style="color: #4078f2;line-height: 26px;">@Data</span><br><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span>&nbsp;<span style="color: #c18401;line-height: 26px;">MessageModel</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a626a4;line-height: 26px;">private</span>&nbsp;String&nbsp;message;<br>}<br></code></pre> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 3、构造EventFactory </section></li> </ul> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oexqmcDw0weKQa3AT91gGibCzcn7gTKBF908ibBgmbHfGPVTc2naYj2VoJRcac69ac5FsowsAI4ibYGliatBqP7ibrMzib/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span>&nbsp;<span style="color: #c18401;line-height: 26px;">HelloEventFactory</span>&nbsp;<span style="color: #a626a4;line-height: 26px;">implements</span>&nbsp;<span style="color: #c18401;line-height: 26px;">EventFactory</span>&lt;<span style="color: #c18401;line-height: 26px;">MessageModel</span>&gt;&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #4078f2;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;MessageModel&nbsp;<span style="color: #4078f2;line-height: 26px;">newInstance</span><span style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a626a4;line-height: 26px;">return</span>&nbsp;<span style="color: #a626a4;line-height: 26px;">new</span>&nbsp;MessageModel();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 4、构造EventHandler-消费者 </section></li> </ul> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oexqmcDw0weKQa3AT91gGibCzcn7gTKBF908ibBgmbHfGPVTc2naYj2VoJRcac69ac5FsowsAI4ibYGliatBqP7ibrMzib/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@Slf</span>4j<br><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span>&nbsp;<span style="color: #c18401;line-height: 26px;">HelloEventHandler</span>&nbsp;<span style="color: #a626a4;line-height: 26px;">implements</span>&nbsp;<span style="color: #c18401;line-height: 26px;">EventHandler</span>&lt;<span style="color: #c18401;line-height: 26px;">MessageModel</span>&gt;&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #4078f2;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="color: #a626a4;line-height: 26px;">void</span>&nbsp;<span style="color: #4078f2;line-height: 26px;">onEvent</span><span style="line-height: 26px;">(MessageModel&nbsp;event,&nbsp;<span style="color: #a626a4;line-height: 26px;">long</span>&nbsp;sequence,&nbsp;<span style="color: #a626a4;line-height: 26px;">boolean</span>&nbsp;endOfBatch)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a626a4;line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">//这里停止1000ms是为了确定消费消息是异步的</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(<span style="color: #986801;line-height: 26px;">1000</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.info(<span style="color: #50a14f;line-height: 26px;">"消费者处理消息开始"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a626a4;line-height: 26px;">if</span>&nbsp;(event&nbsp;!=&nbsp;<span style="color: #a626a4;line-height: 26px;">null</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.info(<span style="color: #50a14f;line-height: 26px;">"消费者消费的信息是:{}"</span>,event);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #a626a4;line-height: 26px;">catch</span>&nbsp;(Exception&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.info(<span style="color: #50a14f;line-height: 26px;">"消费者处理消息失败"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.info(<span style="color: #50a14f;line-height: 26px;">"消费者处理消息结束"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 5、构造BeanManager </section></li> </ul> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oexqmcDw0weKQa3AT91gGibCzcn7gTKBF908ibBgmbHfGPVTc2naYj2VoJRcac69ac5FsowsAI4ibYGliatBqP7ibrMzib/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #a0a1a7;font-style: italic;line-height: 26px;">/**<br>&nbsp;*&nbsp;获取实例化对象<br>&nbsp;*/</span><br><span style="color: #4078f2;line-height: 26px;">@Component</span><br><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span>&nbsp;<span style="color: #c18401;line-height: 26px;">BeanManager</span>&nbsp;<span style="color: #a626a4;line-height: 26px;">implements</span>&nbsp;<span style="color: #c18401;line-height: 26px;">ApplicationContextAware</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a626a4;line-height: 26px;">private</span>&nbsp;<span style="color: #a626a4;line-height: 26px;">static</span>&nbsp;ApplicationContext&nbsp;applicationContext&nbsp;=&nbsp;<span style="color: #a626a4;line-height: 26px;">null</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #4078f2;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="color: #a626a4;line-height: 26px;">void</span>&nbsp;<span style="color: #4078f2;line-height: 26px;">setApplicationContext</span><span style="line-height: 26px;">(ApplicationContext&nbsp;applicationContext)</span>&nbsp;<span style="color: #a626a4;line-height: 26px;">throws</span>&nbsp;BeansException&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a626a4;line-height: 26px;">this</span>.applicationContext&nbsp;=&nbsp;applicationContext;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="color: #a626a4;line-height: 26px;">static</span>&nbsp;ApplicationContext&nbsp;<span style="color: #4078f2;line-height: 26px;">getApplicationContext</span><span style="line-height: 26px;">()</span>&nbsp;</span>{&nbsp;<span style="color: #a626a4;line-height: 26px;">return</span>&nbsp;applicationContext;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="color: #a626a4;line-height: 26px;">static</span>&nbsp;Object&nbsp;<span style="color: #4078f2;line-height: 26px;">getBean</span><span style="line-height: 26px;">(String&nbsp;name)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a626a4;line-height: 26px;">return</span>&nbsp;applicationContext.getBean(name);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="color: #a626a4;line-height: 26px;">static</span>&nbsp;&lt;T&gt;&nbsp;<span style="line-height: 26px;">T&nbsp;<span style="color: #4078f2;line-height: 26px;">getBean</span><span style="line-height: 26px;">(Class&lt;T&gt;&nbsp;clazz)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a626a4;line-height: 26px;">return</span>&nbsp;applicationContext.getBean(clazz);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 6、构造MQManager </section></li> </ul> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oexqmcDw0weKQa3AT91gGibCzcn7gTKBF908ibBgmbHfGPVTc2naYj2VoJRcac69ac5FsowsAI4ibYGliatBqP7ibrMzib/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@Configuration</span><br><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span>&nbsp;<span style="color: #c18401;line-height: 26px;">MQManager</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #4078f2;line-height: 26px;">@Bean</span>(<span style="color: #50a14f;line-height: 26px;">"messageModel"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;RingBuffer&lt;MessageModel&gt;&nbsp;<span style="color: #4078f2;line-height: 26px;">messageModelRingBuffer</span><span style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">//定义用于事件处理的线程池,&nbsp;Disruptor通过java.util.concurrent.ExecutorSerivce提供的线程来触发consumer的事件处理</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ExecutorService&nbsp;executor&nbsp;=&nbsp;Executors.newFixedThreadPool(<span style="color: #986801;line-height: 26px;">2</span>);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">//指定事件工厂</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HelloEventFactory&nbsp;factory&nbsp;=&nbsp;<span style="color: #a626a4;line-height: 26px;">new</span>&nbsp;HelloEventFactory();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">//指定ringbuffer字节大小,必须为2的N次方(能将求模运算转为位运算提高效率),否则将影响效率</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a626a4;line-height: 26px;">int</span>&nbsp;bufferSize&nbsp;=&nbsp;<span style="color: #986801;line-height: 26px;">1024</span>&nbsp;*&nbsp;<span style="color: #986801;line-height: 26px;">256</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">//单线程模式,获取额外的性能</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Disruptor&lt;MessageModel&gt;&nbsp;disruptor&nbsp;=&nbsp;<span style="color: #a626a4;line-height: 26px;">new</span>&nbsp;Disruptor&lt;&gt;(factory,&nbsp;bufferSize,&nbsp;executor,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ProducerType.SINGLE,&nbsp;<span style="color: #a626a4;line-height: 26px;">new</span>&nbsp;BlockingWaitStrategy());<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">//设置事件业务处理器---消费者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;disruptor.handleEventsWith(<span style="color: #a626a4;line-height: 26px;">new</span>&nbsp;HelloEventHandler());<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">//&nbsp;启动disruptor线程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;disruptor.start();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">//获取ringbuffer环,用于接取生产者生产的事件</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RingBuffer&lt;MessageModel&gt;&nbsp;ringBuffer&nbsp;=&nbsp;disruptor.getRingBuffer();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a626a4;line-height: 26px;">return</span>&nbsp;ringBuffer;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></code></pre> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 7、构造Mqservice和实现类-生产者 </section></li> </ul> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oexqmcDw0weKQa3AT91gGibCzcn7gTKBF908ibBgmbHfGPVTc2naYj2VoJRcac69ac5FsowsAI4ibYGliatBqP7ibrMzib/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">interface</span>&nbsp;<span style="color: #c18401;line-height: 26px;">DisruptorMqService</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">/**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;消息<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;<span style="color: #a626a4;line-height: 26px;">@param</span>&nbsp;message<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">void</span>&nbsp;<span style="color: #4078f2;line-height: 26px;">sayHelloMq</span><span style="line-height: 26px;">(String&nbsp;message)</span></span>;<br>}<br><br><span style="color: #4078f2;line-height: 26px;">@Slf</span>4j<br><span style="color: #4078f2;line-height: 26px;">@Component</span><br><span style="color: #4078f2;line-height: 26px;">@Service</span><br><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span>&nbsp;<span style="color: #c18401;line-height: 26px;">DisruptorMqServiceImpl</span>&nbsp;<span style="color: #a626a4;line-height: 26px;">implements</span>&nbsp;<span style="color: #c18401;line-height: 26px;">DisruptorMqService</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #4078f2;line-height: 26px;">@Autowired</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a626a4;line-height: 26px;">private</span>&nbsp;RingBuffer&lt;MessageModel&gt;&nbsp;messageModelRingBuffer;<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #4078f2;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="color: #a626a4;line-height: 26px;">void</span>&nbsp;<span style="color: #4078f2;line-height: 26px;">sayHelloMq</span><span style="line-height: 26px;">(String&nbsp;message)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.info(<span style="color: #50a14f;line-height: 26px;">"record&nbsp;the&nbsp;message:&nbsp;{}"</span>,message);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">//获取下一个Event槽的下标</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a626a4;line-height: 26px;">long</span>&nbsp;sequence&nbsp;=&nbsp;messageModelRingBuffer.next();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a626a4;line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">//给Event填充数据</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageModel&nbsp;event&nbsp;=&nbsp;messageModelRingBuffer.get(sequence);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;event.setMessage(message);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.info(<span style="color: #50a14f;line-height: 26px;">"往消息队列中添加消息:{}"</span>,&nbsp;event);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #a626a4;line-height: 26px;">catch</span>&nbsp;(Exception&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.error(<span style="color: #50a14f;line-height: 26px;">"failed&nbsp;to&nbsp;add&nbsp;event&nbsp;to&nbsp;messageModelRingBuffer&nbsp;for&nbsp;:&nbsp;e&nbsp;=&nbsp;{},{}"</span>,e,e.getMessage());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #a626a4;line-height: 26px;">finally</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">//发布Event,激活观察者去消费,将sequence传递给改消费者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">//注意最后的publish方法必须放在finally中以确保必须得到调用;如果某个请求的sequence未被提交将会堵塞后续的发布操作或者其他的producer</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;messageModelRingBuffer.publish(sequence);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 8、构造测试类及方法 </section></li> </ul> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oexqmcDw0weKQa3AT91gGibCzcn7gTKBF908ibBgmbHfGPVTc2naYj2VoJRcac69ac5FsowsAI4ibYGliatBqP7ibrMzib/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@Slf</span>4j<br><span style="color: #4078f2;line-height: 26px;">@RunWith</span>(SpringRunner<span style="line-height: 26px;">.<span style="color: #a626a4;line-height: 26px;">class</span>)<br>@<span style="color: #c18401;line-height: 26px;">SpringBootTest</span>(<span style="color: #c18401;line-height: 26px;">classes</span>&nbsp;</span>=&nbsp;DemoApplication<span style="line-height: 26px;">.<span style="color: #a626a4;line-height: 26px;">class</span>)<br><span style="color: #c18401;line-height: 26px;">public</span>&nbsp;<span style="color: #c18401;line-height: 26px;">class</span>&nbsp;<span style="color: #c18401;line-height: 26px;">DemoApplicationTests</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #4078f2;line-height: 26px;">@Autowired</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a626a4;line-height: 26px;">private</span>&nbsp;DisruptorMqService&nbsp;disruptorMqService;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">/**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;项目内部使用Disruptor做消息队列<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;<span style="color: #a626a4;line-height: 26px;">@throws</span>&nbsp;Exception<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #4078f2;line-height: 26px;">@Test</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="color: #a626a4;line-height: 26px;">void</span>&nbsp;<span style="color: #4078f2;line-height: 26px;">sayHelloMqTest</span><span style="line-height: 26px;">()</span>&nbsp;<span style="color: #a626a4;line-height: 26px;">throws</span>&nbsp;Exception</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;disruptorMqService.sayHelloMq(<span style="color: #50a14f;line-height: 26px;">"消息到了,Hello&nbsp;world!"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.info(<span style="color: #50a14f;line-height: 26px;">"消息队列已发送完毕"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">//这里停止2000ms是为了确定是处理消息是异步的</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(<span style="color: #986801;line-height: 26px;">2000</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">测试运行结果</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oexqmcDw0weKQa3AT91gGibCzcn7gTKBF908ibBgmbHfGPVTc2naYj2VoJRcac69ac5FsowsAI4ibYGliatBqP7ibrMzib/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #986801;line-height: 26px;">2020</span>-<span style="color: #986801;line-height: 26px;">04</span>-<span style="color: #986801;line-height: 26px;">05</span>&nbsp;<span style="color: #986801;line-height: 26px;">14</span>:<span style="color: #986801;line-height: 26px;">31</span>:<span style="color: #986801;line-height: 26px;">18.543</span>&nbsp;&nbsp;INFO&nbsp;<span style="color: #986801;line-height: 26px;">7274</span>&nbsp;---&nbsp;[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;main]&nbsp;c.e.u.d.d.s.Impl.DisruptorMqServiceImpl&nbsp;&nbsp;:&nbsp;record&nbsp;the&nbsp;message:&nbsp;消息到了,Hello&nbsp;world!<br><span style="color: #986801;line-height: 26px;">2020</span>-<span style="color: #986801;line-height: 26px;">04</span>-<span style="color: #986801;line-height: 26px;">05</span>&nbsp;<span style="color: #986801;line-height: 26px;">14</span>:<span style="color: #986801;line-height: 26px;">31</span>:<span style="color: #986801;line-height: 26px;">18.545</span>&nbsp;&nbsp;INFO&nbsp;<span style="color: #986801;line-height: 26px;">7274</span>&nbsp;---&nbsp;[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; main] c.e.u.d.d.s.Impl.DisruptorMqServiceImpl &nbsp;:&nbsp;往消息队列中添加消息:MessageModel(message=消息到了,Hello world!)<br><span style="color: #986801;line-height: 26px;">2020</span>-<span style="color: #986801;line-height: 26px;">04</span>-<span style="color: #986801;line-height: 26px;">05</span>&nbsp;<span style="color: #986801;line-height: 26px;">14</span>:<span style="color: #986801;line-height: 26px;">31</span>:<span style="color: #986801;line-height: 26px;">18.545</span>&nbsp;&nbsp;INFO&nbsp;<span style="color: #986801;line-height: 26px;">7274</span>&nbsp;---&nbsp;[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;main]&nbsp;c.e.utils.demo.DemoApplicationTests&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;消息队列已发送完毕<br><span style="color: #986801;line-height: 26px;">2020</span>-<span style="color: #986801;line-height: 26px;">04</span>-<span style="color: #986801;line-height: 26px;">05</span>&nbsp;<span style="color: #986801;line-height: 26px;">14</span>:<span style="color: #986801;line-height: 26px;">31</span>:<span style="color: #986801;line-height: 26px;">19.547</span>&nbsp;&nbsp;INFO&nbsp;<span style="color: #986801;line-height: 26px;">7274</span>&nbsp;---&nbsp;[pool-<span style="color: #986801;line-height: 26px;">1</span>-thread-<span style="color: #986801;line-height: 26px;">1</span>]&nbsp;c.e.u.d.disrupMq.mq.HelloEventHandler&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;消费者处理消息开始<br><span style="color: #986801;line-height: 26px;">2020</span>-<span style="color: #986801;line-height: 26px;">04</span>-<span style="color: #986801;line-height: 26px;">05</span>&nbsp;<span style="color: #986801;line-height: 26px;">14</span>:<span style="color: #986801;line-height: 26px;">31</span>:<span style="color: #986801;line-height: 26px;">19.547</span>&nbsp;&nbsp;INFO&nbsp;<span style="color: #986801;line-height: 26px;">7274</span>&nbsp;---&nbsp;[pool-<span style="color: #986801;line-height: 26px;">1</span>-thread-<span style="color: #986801;line-height: 26px;">1</span>] c.e.u.d.disrupMq.mq.HelloEventHandler &nbsp;&nbsp;&nbsp;:&nbsp;消费者消费的信息是:MessageModel(message=消息到了,Hello world!)<br><span style="color: #986801;line-height: 26px;">2020</span>-<span style="color: #986801;line-height: 26px;">04</span>-<span style="color: #986801;line-height: 26px;">05</span>&nbsp;<span style="color: #986801;line-height: 26px;">14</span>:<span style="color: #986801;line-height: 26px;">31</span>:<span style="color: #986801;line-height: 26px;">19.547</span>&nbsp;&nbsp;INFO&nbsp;<span style="color: #986801;line-height: 26px;">7274</span>&nbsp;---&nbsp;[pool-<span style="color: #986801;line-height: 26px;">1</span>-thread-<span style="color: #986801;line-height: 26px;">1</span>]&nbsp;c.e.u.d.disrupMq.mq.HelloEventHandler&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;消费者处理消息结束<br></code></pre> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);"><strong style="font-weight: 700;color: rgb(248, 57, 41);">总结</strong></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">其实 生成者 -&gt; 消费者 模式是很常见的,通过一些消息队列也可以轻松做到上述的效果。不同的地方在于,Disruptor 是在内存中以队列的方式去实现的,而且是无锁的。这也是 Disruptor 为什么高效的原因。</p> <hr data-tool="mdnice编辑器" style="margin: 10px 0px;height: 1px;padding: 0px;border-width: initial;border-style: none;border-color: initial;text-align: center;background-image: linear-gradient(to right, rgba(248, 57, 41, 0), rgba(248, 57, 41, 0.75), rgba(248, 57, 41, 0));"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">欢迎加入我的知识星球,<a href="https://mp.weixin.qq.com/s?__biz=MzU3MDAzNDg1MA==&amp;mid=2247512889&amp;idx=1&amp;sn=3f4600c571d97f1311def99c103365c2&amp;chksm=fcf76cf4cb80e5e2034beb924b8ba5dc0c76af6526d24313c978ede2e193ef13b18189edad1d&amp;token=510846437&amp;lang=zh_CN&amp;scene=21#wechat_redirect" style="text-decoration: none;word-wrap: break-word;color: rgb(248, 57, 41);font-weight: 400;border-bottom: 1px solid rgb(248, 57, 41);" data-linktype="2">点击了<span style="display: none;line-height: 0px;">‍</span>解详情</a></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">星球会员可以免费观看陈某所有更新的<span style="color: rgb(255, 41, 65);"><strong>视频教程</strong></span>(几杯咖啡钱)。加入方式,长按下方二维码:</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU3MDAzNDg1MA==&amp;mid=2247512889&amp;idx=1&amp;sn=3f4600c571d97f1311def99c103365c2&amp;chksm=fcf76cf4cb80e5e2034beb924b8ba5dc0c76af6526d24313c978ede2e193ef13b18189edad1d&amp;scene=21#wechat_redirect" textvalue="你已选中了添加链接的内容" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="inset: auto;margin: 20px 0px;"><img class="rich_pages wxw-img" data-ratio="0.5493333333333333" src="/upload/21a4b2197b0e6b273a80b02ced47cfc2.jpg" data-type="jpeg" data-w="1125" style="border-radius: 6px;display: block;margin: 0px;max-width: 95%;object-fit: contain;"></span></a> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">目前已更新视频如下:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: decimal;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <strong style="font-weight: 700;color: rgb(248, 57, 41);">《Spring Cloud Alibaba 实战》</strong>:https://sourl.cn/72X9JV </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <strong style="font-weight: 700;color: rgb(248, 57, 41);">《亿级数据分库分表实战》(正在更新)</strong> :https://sourl.cn/U9pKj4 </section></li> </ol> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <br> </section> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <span style="color: rgb(255, 41, 65);"><strong>后期随着新视频加入价格会逐步上涨,前期加入的免费看~</strong></span> <br> </section> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-role="outer" label="edit by 135editor" mpa-from-tpl="t"> <section data-role="paragraph" mpa-from-tpl="t"> <h1 style="margin-top: 1em;margin-bottom: 1em;outline: 0px;font-size: 28px;color: rgb(34, 34, 34);letter-spacing: 0.544px;line-height: 36px;text-align: center;"><span style="outline: 0px;color: rgb(255, 122, 69);font-size: 17px;">=== 《Spring Cloud Alibaba 实战》===</span></h1> <section data-mpa-template="t" style="margin-bottom: 0em;outline: 0px;color: rgb(34, 34, 34);font-size: 17px;letter-spacing: 0.544px;text-align: justify;" mpa-from-tpl="t"> <section data-mpa-template="t" mpa-from-tpl="t" style="outline: 0px;"> <section data-style-type="5" data-tools="新媒体排版" data-id="1259732" mpa-from-tpl="t" style="outline: 0px;color: rgb(0, 0, 0);font-size: medium;"> <section mpa-from-tpl="t" style="outline: 0px;"> <section mpa-from-tpl="t" style="padding: 10px;outline: 0px;text-align: center;border-width: 1px;border-style: solid;border-color: rgb(240, 240, 240);"> <section mpa-from-tpl="t" style="outline: 0px;display: inline-block;"> <section mpa-from-tpl="t" style="outline: 0px;overflow: hidden scroll;height: 344px;display: inline-block;color: inherit;"> <section mpa-from-tpl="t" style="padding: 3px 2px;outline: 0px;color: inherit;border-color: rgb(252, 180, 43);"> <p style="outline: 0px;clear: both;min-height: 1em;background-color: rgb(240, 240, 240);"><img class="rich_pages wxw-img" data-ratio="10.251040221914009" src="/upload/c4fa108d39460b62296450215a23330c.jpg" data-type="jpeg" data-w="721" style="outline: 0px;vertical-align: top;width: 100%;height: auto !important;visibility: visible !important;"></p> </section> </section> </section> <p style="margin: 15px auto 5px;outline: 0px;clear: both;min-height: 1em;"><strong style="outline: 0px;" mpa-from-tpl="t"><span style="outline: 0px;font-size: 13px;color: rgb(0, 122, 170);">滚动查看更多</span></strong></p> </section> </section> </section> </section> </section> </section> </section> </section> </section> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-role="outer" label="edit by 135editor" mpa-from-tpl="t"> <section data-role="paragraph" mpa-from-tpl="t"> <h1 style="margin-top: 1em;margin-bottom: 1em;outline: 0px;font-size: 28px;color: rgb(34, 34, 34);letter-spacing: 0.544px;line-height: 36px;text-align: center;"><span style="outline: 0px;color: rgb(255, 122, 69);font-size: 17px;">=== 《亿级数据分库分表实战 》===</span></h1> <section data-mpa-template="t" style="margin-bottom: 0em;outline: 0px;color: rgb(34, 34, 34);font-size: 17px;letter-spacing: 0.544px;text-align: justify;" mpa-from-tpl="t"> <section data-mpa-template="t" mpa-from-tpl="t" style="outline: 0px;"> <section data-style-type="5" data-tools="新媒体排版" data-id="1259732" mpa-from-tpl="t" style="outline: 0px;color: rgb(0, 0, 0);font-size: medium;"> <section mpa-from-tpl="t" style="outline: 0px;"> <section mpa-from-tpl="t" style="padding: 10px;outline: 0px;text-align: center;border-width: 1px;border-style: solid;border-color: rgb(240, 240, 240);"> <section mpa-from-tpl="t" style="outline: 0px;display: inline-block;"> <section mpa-from-tpl="t" style="outline: 0px;overflow: hidden scroll;height: 344px;display: inline-block;color: inherit;"> <section mpa-from-tpl="t" style="padding: 3px 2px;outline: 0px;color: inherit;border-color: rgb(252, 180, 43);"> <p style="outline: 0px;clear: both;min-height: 1em;background-color: rgb(240, 240, 240);"><img class="rich_pages wxw-img" data-ratio="6.261772853185596" src="/upload/cf3d00018dfcbf4b83afd26fda262b38.jpg" data-type="jpeg" data-w="722" style="outline: 0px;vertical-align: top;width: 100%;height: auto !important;visibility: visible !important;"></p> </section> </section> </section> <p style="margin: 15px auto 5px;outline: 0px;clear: both;min-height: 1em;"><strong style="outline: 0px;" mpa-from-tpl="t"><span style="outline: 0px;font-size: 13px;color: rgb(0, 122, 170);">滚动查看更多</span></strong></p> </section> </section> </section> </section> </section> </section> </section> </section> </section> <p><br mpa-from-tpl="t"></p> <section class="channels_iframe_wrp"> <mpvideosnap class="js_uneditable custom_select_card channels_iframe videosnap_video_iframe" data-pluginname="videosnap" data-id="export/UzFfAgtgekIEAQAAAAAASqI0zMZdLQAAAAstQy6ubaLX4KHWvLEZgBPE9qNgLkIhE6qDzNPgMIvacPAwfhmQsyDypUSNQO6c" data-url="https://findermp.video.qq.com/251/20304/stodownload?encfilekey=rjD5jyTuFrIpZ2ibE8T7YmwgiahniaXswqzcgibbmslI58cRQsf5kj4StubFX6atfiazhOibDQkeKIlIx0lhs4kI59a6yfcqwm5cWGgicv9DUZ23VNhiaXJqMwDrdg&amp;adaptivelytrans=0&amp;bizid=1023&amp;dotrans=0&amp;hy=SH&amp;idx=1&amp;m=&amp;scene=0&amp;token=x5Y29zUxcibAIvqUibe7BYsuuF7U7R2DbVxUbpKDrTvkPLtE2OSfqzt9cDVVSLicMk5CtV8NciaujVY" data-headimgurl="http://wx.qlogo.cn/finderhead/5Oom8vdOxTI453gNLedGe8Cjh0r6goaCUQdbMlXd4mo/0" data-username="v2_060000231003b20faec8c4e0801fc5d0cd01ed3cb0774a1a71882e00775192f7e6d81d63849e@finder" data-nickname="码猿技术专栏" data-desc="定义3个注解将微服务鉴权下放到下游服务" data-nonceid="8284320766962804662" data-type="video" data-width="1920" data-height="1080"></mpvideosnap> </section> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <br> </section> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: right;font-weight: 500;color: rgb(53, 53, 53);"> <strong style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;letter-spacing: 0.544px;orphans: 2;text-align: right;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);">帅气的人都在点赞、分享、在看三连</strong> <img class="rich_pages wxw-img" data-fileid="100011967" data-ratio="1" data-type="png" data-w="20" src="/upload/1d550a991385b842a21e2b301725407e.png" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;vertical-align: text-bottom;height: auto !important;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-align: right;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;word-spacing: 0.8px;color: rgb(53, 53, 53);font-size: 16px;background-color: rgb(255, 255, 255);display: inline-block;visibility: visible !important;width: 20px !important;"> </section> </section>

基于代价的慢查询优化建议

作者:微信小助手

<p style="padding-right: 0.5em;padding-left: 0.5em;white-space: normal;" data-mpa-powered-by="yiban.io"><img class="rich_pages wxw-img" data-cropselx1="0" data-cropselx2="578" data-cropsely1="0" data-cropsely2="48" data-ratio="0.10078125" data-s="300,640" src="/upload/4dbe1debe51138f467e6ce57d61bcbd7.png" data-type="png" data-w="1280" style="height: 58px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;width: 578px;"><br></p> <p style="padding-right: 0.5em;padding-left: 0.5em;white-space: normal;text-align: center;"><strong style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><span style="color: rgb(136, 136, 136);font-size: 12px;letter-spacing: 1px;">总第503</span></strong><strong style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><span style="color: rgb(136, 136, 136);font-size: 12px;letter-spacing: 1px;">篇</span></strong></p> <p style="padding-right: 0.5em;padding-left: 0.5em;white-space: normal;text-align: center;"><strong><span style="color: rgb(136, 136, 136);font-size: 12px;letter-spacing: 1px;">2022年 第020篇</span></strong></p> <section data-role="outer" label="Powered by 135editor.com" style="margin-right: 0.5em;margin-left: 0.5em;padding-right: 0.5em;padding-left: 0.5em;white-space: normal;"> <section data-role="outer" label="Powered by 135editor.com"> <section data-tools="135编辑器" data-id="127" style="border-width: 0px;border-style: none;border-color: initial;"> <section data-tools="135编辑器" data-id="127" style="border-width: 0px;border-style: none;border-color: initial;"> <section style="margin: 60px 16px 16px;border-width: 1px;border-style: solid;border-color: rgb(235, 234, 225);text-align: center;border-radius: 8px;"> <section style="margin-top: -3.3em;margin-right: 5px;margin-left: 5px;font-weight: inherit;text-decoration: inherit;font-size: 18px;color: inherit;"> <p style="margin-right: auto;margin-bottom: 15px;margin-left: auto;border-width: 2px;border-style: solid;border-color: rgb(235, 234, 225);width: 108px;height: 108px;border-radius: 50%;box-shadow: rgb(201, 201, 201) 0px 2px 2px 2px;background-color: rgb(254, 254, 254);"><img class="rich_pages wxw-img" data-cropselx1="0" data-cropselx2="93" data-cropsely1="0" data-cropsely2="93" data-ratio="0.9966329966329966" src="/upload/c1f9e58a75e676cd4bee8d68e9fe8659.png" data-type="png" data-w="594" data-width="100%" opacity="" style="height: 103px;border-radius: 50%;color: inherit;display: inline-block;width: 103px;visibility: visible !important;" title="undefined" border="0"></p> </section> <section data-brushtype="text" data-style="text-align: left; font-size: 14px; color: inherit;" style="margin: 8px 15px;line-height: 1.4;"> <section style="text-align: justify;"> <span style="font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 14px;color: rgb(136, 136, 136);">对于数据库来说,慢查询往往意味着风险。SQL执行得越慢,消耗的CPU资源或IO资源也会越大。大量的慢查询可直接引发业务故障,关注慢查询即是关注故障本身。本文主要介绍了美团如何利用数据库的代价优化器来优化慢查询,并给出索引建议,评估跟踪建议质量,运营治理慢查询。</span> </section> </section> </section> </section> </section> </section> </section> <ul style="margin: 8px 32px;padding-left: 25px;" class="list-paddingleft-1"> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">1 背景</span></p></li> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">2 基于代价的优化器介绍</span></p></li> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: square;" class="list-paddingleft-1"> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">2.1 SQL执行与优化器</span></p></li> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">2.2 代价模型介绍</span></p></li> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">2.3 基于代价的索引选择</span></p></li> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">2.4 基于代价的索引推荐思路</span></p></li> </ul> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">3 索引推荐实现</span></p></li> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: square;" class="list-paddingleft-1"> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">3.1 前置校验</span></p></li> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">3.2 提取关键列名</span></p></li> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">3.3 生成候选索引</span></p></li> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">3.4 数据采集</span></p></li> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">3.5 统计数据计算</span></p></li> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">3.6 候选索引代价评估</span></p></li> </ul> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">4 推荐质量保证</span></p></li> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: square;" class="list-paddingleft-1"> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">4.1 有效性验证</span></p></li> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">4.2 效果追踪</span></p></li> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">4.3 仿真环境</span></p></li> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">4.4 测试案例库</span></p></li> </ul> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">5 慢查询治理运营</span></p></li> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: square;" class="list-paddingleft-1"> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">5.1 过去-历史慢查询</span></p></li> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">5.2 现在-新增慢查询</span></p></li> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">5.3 未来-潜在慢查询</span></p></li> </ul> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">6 项目运行情况</span></p></li> <li style="font-size: 13px;color: rgb(136, 136, 136);"><p><span style="font-size: 13px;color: rgb(136, 136, 136);">7 未来规划</span></p></li> </ul> <h2 data-tool="mdnice编辑器" style="margin: 30px 0px 15px;font-weight: bold;font-size: 22px;"><span style="font-size: 20px;color: rgb(255, 195, 0);">1 背景</span></h2> <section style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-left: 0px;margin-right: 0px;"> <span style="font-size: 15px;">慢查询是指数据库中查询时间超过指定阈值(</span> <span style="font-size: 15px;color: rgb(136, 136, 136);">美团设置为100ms</span> <span style="font-size: 15px;">)的SQL,它是数据库的性能杀手,也是业务优化数据库访问的重要抓手。随着美团业务的高速增长,日均慢查询量已经过亿条,此前因慢查询导致的故障约占数据库故障总数的10%以上,而且高级别的故障呈日益增长趋势。因此,对慢查询的优化已经变得刻不容缓。</span> </section> <section style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-left: 0px;margin-right: 0px;"> <span style="font-size: 15px;">那么如何优化慢查询呢?最直接有效的方法就是选用一个查询效率高的索引。关于高效率的索引推荐,主要有基于经验规则和代价的两种算法。在日常工作中,基于经验规则的推荐随处可见,对于简单的SQL,如</span> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">select * from sync_test1 where name like 'Bobby%'</span></code> <span style="font-size: 15px;">,直接添加索引IX(</span> <span style="font-size: 15px;color: rgb(136, 136, 136);">name</span> <span style="font-size: 15px;">) 就可以取得不错的效果;但对于稍微复杂点的SQL,如</span> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">select * from sync_test1 where name like 'Bobby%' and dt &gt; '2021-07-06'</span></code> <span style="font-size: 15px;">,到底选择IX(</span> <span style="font-size: 15px;color: rgb(136, 136, 136);">name</span> <span style="font-size: 15px;">)、IX(</span> <span style="font-size: 15px;color: rgb(136, 136, 136);">dt</span> <span style="font-size: 15px;">)、IX(</span> <span style="font-size: 15px;color: rgb(136, 136, 136);">dt,name</span> <span style="font-size: 15px;">) 还是IX(</span> <span style="font-size: 15px;color: rgb(136, 136, 136);">name,dt</span> <span style="font-size: 15px;">),该方法也无法给出准确的回答。更别说像多表Join、子查询这样复杂的场景了。所以采用基于代价的推荐来解决该问题会更加普适,因为基于代价的方法使用了和数据库优化器相同的方式,去量化评估所有的可能性,选出的是执行SQL耗费代价最小的索引。</span> </section> <h2 data-tool="mdnice编辑器" style="margin: 30px 0px 15px;font-weight: bold;font-size: 22px;"><span style="font-size: 20px;color: rgb(255, 195, 0);">2 基于代价的优化器介绍</span></h2> <h3 data-tool="mdnice编辑器" style="margin: 30px 0px 15px;font-weight: bold;font-size: 20px;"><span style="font-size: 18px;">2.1 SQL执行与优化器</span></h3> <section style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-left: 0px;margin-right: 0px;"> <span style="font-size: 15px;">一条SQL在MySQL服务器中执行流程主要包含:SQL解析、基于语法树的准备工作、优化器的逻辑变化、优化器的代价准备工作、基于代价模型的优化、进行额外的优化和运行执行计划等部分。具体如下图所示:</span> </section> <section style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;margin-left: 0px;margin-right: 0px;"> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-cropselx1="0" data-cropselx2="558" data-cropsely1="0" data-cropsely2="439" data-ratio="0.7870646766169154" src="/upload/452b9a9c64691a21edb3a5b4e18dea2b.jpg" data-type="jpeg" data-w="1005" style="display: block;margin-right: auto;margin-left: auto;width: 558px;height: 439px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> <span style="font-size: 12px;">SQL执行与优化器</span> </figcaption> </figure> </section> <h3 data-tool="mdnice编辑器" style="margin: 30px 0px 15px;font-weight: bold;font-size: 20px;"><span style="font-size: 18px;">2.2 代价模型介绍</span></h3> <section style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-left: 0px;margin-right: 0px;"> <span style="font-size: 15px;">而对于优化器来说,执行一条SQL有各种各样的方案可供选择,如表是否用索引、选择哪个索引、是否使用范围扫描、多表Join的连接顺序和子查询的执行方式等。如何从这些可选方案中选出耗时最短的方案呢?这就需要定义一个量化数值指标,这个指标就是代价(</span> <span style="font-size: 15px;color: rgb(136, 136, 136);">Cost</span> <span style="font-size: 15px;">),我们分别计算出可选方案的操作耗时,从中选出最小值。</span> </section> <section style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-left: 0px;margin-right: 0px;"> <span style="font-size: 15px;">代价模型将操作分为Server层和Engine(</span> <span style="font-size: 15px;color: rgb(136, 136, 136);">存储引擎</span> <span style="font-size: 15px;">)层两类,Server层主要是CPU代价,Engine层主要是IO代价,比如MySQL从磁盘读取一个数据页的代价io_block_read_cost为1,计算符合条件的行代价为row_evaluate_cost为0.2。除此之外还有:</span> </section> <ol data-tool="mdnice编辑器" style="margin: 8px 0px;padding-left: 25px;" class="list-paddingleft-1"> <li style="font-size: 14px;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 14px;">memory_temptable_create_cost (</span> <span style="font-size: 14px;color: rgb(136, 136, 136);">default 2.0</span> <span style="font-size: 14px;">) 内存临时表的创建代价。</span> </section></li> <li style="font-size: 14px;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 14px;">memory_temptable_row_cost (</span> <span style="font-size: 14px;color: rgb(136, 136, 136);">default 0.2</span> <span style="font-size: 14px;">) 内存临时表的行代价。</span> </section></li> <li style="font-size: 14px;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 14px;">key_compare_cost (</span> <span style="font-size: 14px;color: rgb(136, 136, 136);">default 0.1</span> <span style="font-size: 14px;">) 键比较的代价,例如排序。</span> </section></li> <li style="font-size: 14px;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 14px;">disk_temptable_create_cost (</span> <span style="font-size: 14px;color: rgb(136, 136, 136);">default 40.0</span> <span style="font-size: 14px;">) 内部myisam或innodb临时表的创建代价。</span> </section></li> <li style="font-size: 14px;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 14px;">disk_temptable_row_cost (</span> <span style="font-size: 14px;color: rgb(136, 136, 136);">default 1.0</span> <span style="font-size: 14px;">) 内部myisam或innodb临时表的行代价。</span> </section></li> </ol> <section style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-left: 0px;margin-right: 0px;"> <span style="font-size: 15px;">在MySQL 5.7中,这些操作代价的默认值都可以进行配置。为了计算出方案的总代价,还需要参考一些统计数据,如表数据量大小、元数据和索引信息等。MySQL的代价优化器模型整体如下图所示:</span> </section> <section style="padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;margin-left: 0px;margin-right: 0px;"> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-cropselx1="0" data-cropselx2="558" data-cropsely1="0" data-cropsely2="259" data-ratio="0.4634146341463415" src="/upload/1fd37163dc0b4f7552e7030e038050af.jpg" data-type="jpeg" data-w="861" style="display: block;margin-right: auto;margin-left: auto;width: 559px;height: 259px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> <span style="font-size: 12px;">代价模型</span> </figcaption> </figure> </section> <h3 data-tool="mdnice编辑器" style="margin: 30px 0px 15px;font-weight: bold;font-size: 20px;"><span style="font-size: 18px;">2.3 基于代价的索引选择</span></h3> <section style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-left: 0px;margin-right: 0px;"> <span style="font-size: 15px;">还是继续拿上述的</span> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">SQL select * from sync_test1 where name like 'Bobby%' and dt &gt; '2021-07-06'</span></code> <span style="font-size: 15px;">为例,我们看看MySQL优化器是如何根据代价模型选择索引的。首先,我们直接在建表时加入四个候选索引。</span> </section> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"> <section style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(56, 58, 66);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(250, 250, 250);border-radius: 5px;margin-left: 0px;margin-right: 0px;"> <span style="font-size: 12px;"><span style="font-size: 12px;color: rgb(166, 38, 164);line-height: 26px;">Create</span>&nbsp;<span style="font-size: 12px;color: rgb(166, 38, 164);line-height: 26px;">Table</span>:&nbsp;<span style="font-size: 12px;color: rgb(166, 38, 164);line-height: 26px;">CREATE</span>&nbsp;<span style="font-size: 12px;color: rgb(166, 38, 164);line-height: 26px;">TABLE</span>&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">`sync_test1`</span>&nbsp;(<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">`id`</span>&nbsp;<span style="font-size: 12px;color: rgb(193, 132, 1);line-height: 26px;">int</span>(<span style="font-size: 12px;color: rgb(152, 104, 1);line-height: 26px;">11</span>)&nbsp;<span style="font-size: 12px;color: rgb(166, 38, 164);line-height: 26px;">NOT</span>&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">NULL</span>&nbsp;AUTO_INCREMENT,<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">`cid`</span>&nbsp;<span style="font-size: 12px;color: rgb(193, 132, 1);line-height: 26px;">int</span>(<span style="font-size: 12px;color: rgb(152, 104, 1);line-height: 26px;">11</span>)&nbsp;<span style="font-size: 12px;color: rgb(166, 38, 164);line-height: 26px;">NOT</span>&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">NULL</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">`phone`</span>&nbsp;<span style="font-size: 12px;color: rgb(193, 132, 1);line-height: 26px;">int</span>(<span style="font-size: 12px;color: rgb(152, 104, 1);line-height: 26px;">11</span>)&nbsp;<span style="font-size: 12px;color: rgb(166, 38, 164);line-height: 26px;">NOT</span>&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">NULL</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">`name`</span>&nbsp;<span style="font-size: 12px;color: rgb(193, 132, 1);line-height: 26px;">varchar</span>(<span style="font-size: 12px;color: rgb(152, 104, 1);line-height: 26px;">10</span>)&nbsp;<span style="font-size: 12px;color: rgb(166, 38, 164);line-height: 26px;">NOT</span>&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">NULL</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">`address`</span>&nbsp;<span style="font-size: 12px;color: rgb(193, 132, 1);line-height: 26px;">varchar</span>(<span style="font-size: 12px;color: rgb(152, 104, 1);line-height: 26px;">255</span>)&nbsp;<span style="font-size: 12px;color: rgb(166, 38, 164);line-height: 26px;">DEFAULT</span>&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">NULL</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">`dt`</span>&nbsp;datetime&nbsp;<span style="font-size: 12px;color: rgb(166, 38, 164);line-height: 26px;">DEFAULT</span>&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">NULL</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;PRIMARY&nbsp;<span style="font-size: 12px;color: rgb(166, 38, 164);line-height: 26px;">KEY</span>&nbsp;(<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">`id`</span>),<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(166, 38, 164);line-height: 26px;">KEY</span>&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">`IX_name`</span>&nbsp;(<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">`name`</span>),<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(166, 38, 164);line-height: 26px;">KEY</span>&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">`IX_dt`</span>&nbsp;(<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">`dt`</span>),<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(166, 38, 164);line-height: 26px;">KEY</span>&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">`IX_dt_name`</span>&nbsp;(<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">`dt`</span>,<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">`name`</span>),<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(166, 38, 164);line-height: 26px;">KEY</span>&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">`IX_name_dt`</span>&nbsp;(<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">`name`</span>,<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">`dt`</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;)&nbsp;<span style="font-size: 12px;color: rgb(166, 38, 164);line-height: 26px;">ENGINE</span>=<span style="font-size: 12px;color: rgb(166, 38, 164);line-height: 26px;">InnoDB</span></span> </section></pre> <section style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-left: 0px;margin-right: 0px;"> <span style="font-size: 15px;">通过执行explain看出MySQL最终选择了IX_name索引。</span> </section> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"> <section style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(56, 58, 66);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(250, 250, 250);border-radius: 5px;margin-left: 0px;margin-right: 0px;"> <span style="font-size: 12px;">mysql&gt;&nbsp;explain&nbsp;&nbsp;select&nbsp;*&nbsp;from&nbsp;sync_test1&nbsp;<span style="font-size: 12px;color: rgb(193, 132, 1);line-height: 26px;">where</span>&nbsp;name&nbsp;like&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">'Bobby%'</span>&nbsp;and&nbsp;dt&nbsp;&gt;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">'2021-07-06'</span>;<br>+----+-------------+------------+------------+-------+-------------------------------------+---------+---------+------+------+----------+------------------------------------+<br>|&nbsp;id&nbsp;|&nbsp;select_type&nbsp;|&nbsp;table&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;partitions&nbsp;|&nbsp;<span style="font-size: 12px;color: rgb(193, 132, 1);line-height: 26px;">type</span>&nbsp;&nbsp;|&nbsp;possible_keys&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;key&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;key_len&nbsp;|&nbsp;ref&nbsp;&nbsp;|&nbsp;rows&nbsp;|&nbsp;filtered&nbsp;|&nbsp;Extra&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>+----+-------------+------------+------------+-------+-------------------------------------+---------+---------+------+------+----------+------------------------------------+<br>|&nbsp;&nbsp;1&nbsp;|&nbsp;SIMPLE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;sync_test1&nbsp;|&nbsp;NULL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;range&nbsp;|&nbsp;IX_name,IX_dt,IX_dt_name,IX_name_dt&nbsp;|&nbsp;IX_name&nbsp;|&nbsp;12&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;NULL&nbsp;|&nbsp;&nbsp;572&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;36.83&nbsp;|&nbsp;Using&nbsp;index&nbsp;condition;&nbsp;Using&nbsp;<span style="font-size: 12px;color: rgb(193, 132, 1);line-height: 26px;">where</span>&nbsp;|<br>+----+-------------+------------+------------+-------+-------------------------------------+---------+---------+------+------+----------+------------------------------------+</span> </section></pre> <section style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-left: 0px;margin-right: 0px;"> <span style="font-size: 15px;">然后再打开MySQL追踪优化器Trace功能。可以看出,没有选择其他三个索引的原因均是因为在其他三个索引上使用range scan的代价均&gt;= IX_name。</span> </section> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"> <section style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(56, 58, 66);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(250, 250, 250);border-radius: 5px;margin-left: 0px;margin-right: 0px;"> <span style="font-size: 12px;">mysql&gt;&nbsp;select&nbsp;*&nbsp;from&nbsp;INFORMATION_SCHEMA.OPTIMIZER_TRACE\G;<br>***************************&nbsp;1.&nbsp;row&nbsp;***************************<br><br>TRACE:&nbsp;{<br>...<br><span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"rows_estimation"</span>:&nbsp;[<br>{<br><span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"table"</span>:&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"`sync_test1`"</span>,<br><span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"range_analysis"</span>:&nbsp;{<br><span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"table_scan"</span>:&nbsp;{<br>&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"rows"</span>:&nbsp;105084,<br>&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"cost"</span>:&nbsp;21628<br>},<br>...<br><span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"analyzing_range_alternatives"</span>:&nbsp;{<br>&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"range_scan_alternatives"</span>:&nbsp;[<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"index"</span>:&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"IX_name"</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"ranges"</span>:&nbsp;[<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"Bobby\u0000\u0000\u0000\u0000\u0000&nbsp;&lt;=&nbsp;name&nbsp;&lt;=&nbsp;Bobbyÿÿÿÿÿ"</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;],<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"index_dives_for_eq_ranges"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">true</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"rowid_ordered"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">false</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"using_mrr"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">false</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"index_only"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">false</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"rows"</span>:&nbsp;572,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"cost"</span>:&nbsp;687.41,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"chosen"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">true</span><br>&nbsp;&nbsp;&nbsp;&nbsp;},<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"index"</span>:&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"IX_dt"</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"ranges"</span>:&nbsp;[<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"0x99aa0c0000&nbsp;&lt;&nbsp;dt"</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;],<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"index_dives_for_eq_ranges"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">true</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"rowid_ordered"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">false</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"using_mrr"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">false</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"index_only"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">false</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"rows"</span>:&nbsp;38698,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"cost"</span>:&nbsp;46439,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"chosen"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">false</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"cause"</span>:&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"cost"</span><br>&nbsp;&nbsp;&nbsp;&nbsp;},<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"index"</span>:&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"IX_dt_name"</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"ranges"</span>:&nbsp;[<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"0x99aa0c0000&nbsp;&lt;&nbsp;dt"</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;],<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"index_dives_for_eq_ranges"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">true</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"rowid_ordered"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">false</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"using_mrr"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">false</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"index_only"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">false</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"rows"</span>:&nbsp;38292,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"cost"</span>:&nbsp;45951,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"chosen"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">false</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"cause"</span>:&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"cost"</span><br>&nbsp;&nbsp;&nbsp;&nbsp;},<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"index"</span>:&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"IX_name_dt"</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"ranges"</span>:&nbsp;[<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"Bobby\u0000\u0000\u0000\u0000\u0000&nbsp;&lt;=&nbsp;name&nbsp;&lt;=&nbsp;Bobbyÿÿÿÿÿ"</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;],<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"index_dives_for_eq_ranges"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">true</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"rowid_ordered"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">false</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"using_mrr"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">false</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"index_only"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">false</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"rows"</span>:&nbsp;572,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"cost"</span>:&nbsp;687.41,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"chosen"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">false</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"cause"</span>:&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"cost"</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;],<br>&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"analyzing_roworder_intersect"</span>:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"usable"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">false</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"cause"</span>:&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"too_few_roworder_scans"</span><br>&nbsp;&nbsp;}<br>},<br><span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"chosen_range_access_summary"</span>:&nbsp;{<br>&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"range_access_plan"</span>:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"type"</span>:&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"range_scan"</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"index"</span>:&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"IX_name"</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"rows"</span>:&nbsp;572,<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"ranges"</span>:&nbsp;[<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"Bobby\u0000\u0000\u0000\u0000\u0000&nbsp;&lt;=&nbsp;name&nbsp;&lt;=&nbsp;Bobbyÿÿÿÿÿ"</span><br>&nbsp;&nbsp;&nbsp;&nbsp;]<br>&nbsp;&nbsp;},<br>&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"rows_for_plan"</span>:&nbsp;572,<br>&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"cost_for_plan"</span>:&nbsp;687.41,<br>&nbsp;&nbsp;<span style="font-size: 12px;color: rgb(80, 161, 79);line-height: 26px;">"chosen"</span>:&nbsp;<span style="font-size: 12px;color: rgb(1, 132, 187);line-height: 26px;">true</span><br>}<br>...<br>}</span> </section></pre> <section style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-left: 0px;margin-right: 0px;"> <span style="font-size: 15px;">下面我们根据代价模型来推演一下代价的计算过程:</span> </section> <ol data-tool="mdnice编辑器" style="margin: 8px 0px;padding-left: 25px;" class="list-paddingleft-1"> <li style="font-size: 14px;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 14px;">走全表扫描的代价:io_cost + cpu_cost = (数据页个数 * io_block_read_cost)+ (数据行数 * row_evaluate_cost + 1.1) &nbsp;= (data_length / block_size + 1)+ (rows * 0.2 + 1.1) = &nbsp;(9977856 / 16384 + 1) + (105084 * 0.2 + 1.1) = &nbsp;21627.9。</span> </section></li> <li style="font-size: 14px;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 14px;">走二级索引IX_name的代价:io_cost + cpu_cost = (预估范围行数 * io_block_read_cost + 1) + (数据行数 * row_evaluate_cost + 0.01) = &nbsp;(572 * 1 + &nbsp;1) + (572*0.2 + 0.01) = 687.41。</span> </section></li> <li style="font-size: 14px;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 14px;">走二级索引IX_dt的代价:io_cost + cpu_cost = (预估范围行数 * io_block_read_cost + 1) + (数据行数 * row_evaluate_cost + 0.01) &nbsp;= (38698 * 1 + 1) + (38698*0.2 + 0.01) = 46438.61。</span> </section></li> <li style="font-size: 14px;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px

舒服了,踩到一个关于分布式锁的非比寻常的BUG!

作者:微信小助手

<section style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn"> <br> </section> <p style="white-space: normal;"><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;text-align: left;"></span></strong></span><span style="background-color: rgb(255, 255, 255);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 16px;text-align: left;">提到分布式锁,大家一般都会想到 Redis。</span></p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 16px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 26px;visibility: visible;">想到 Redis,一部分同学会说到 Redisson。</p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 16px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 26px;visibility: visible;">那么说到 Redisson,就不得不掰扯掰扯一下它的“看门狗”机制了。</p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 16px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 26px;visibility: visible;">所以你以为这篇文章我要给你讲“看门狗”吗?</p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 16px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 26px;visibility: visible;">不是,我主要是想给你汇报一下我最近研究的由于引入“看门狗”之后,给 Redisson 带来的两个看起来就心里一紧的 bug :</p> <ul data-tool="mdnice编辑器" class="list-paddingleft-1" style="margin: 8px 0px;padding: 0px 0px 0px 25px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 16px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;visibility: visible;"> <li style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"> <section style="margin: 5px 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;line-height: 26px;color: rgb(1, 1, 1);visibility: visible;"> 看门狗不生效的 BUG。 </section></li> <li style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"> <section style="margin: 5px 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;line-height: 26px;color: rgb(1, 1, 1);visibility: visible;"> 看门狗导致死锁的 BUG。 </section></li> </ul> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 16px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 26px;visibility: visible;">为了能让你丝滑入戏,我还是先简单的给你铺垫一下,Redisson 的看门狗到底是个啥东西。</p> <p style="margin: 0px 0px 0em;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 16px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: center;visibility: visible;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="1.0449897750511248" data-s="300,640" src="/upload/d5cbd1171e44db40fe2f39de080c776e.png" data-type="png" data-w="978" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;vertical-align: bottom;height: auto !important;visibility: visible !important;width: 249px !important;"></p> <h2 data-tool="mdnice编辑器"><span style="color: rgb(123, 12, 0);font-size: 18px;"><strong>看门狗描述</strong></span><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"></h2> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 16px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 26px;visibility: visible;">你去看 Redisson 的 wiki 文档,在锁的这一部分,开篇就提到了一个单词:watchdog</p> <blockquote data-tool="mdnice编辑器" style="margin: 20px 0px;padding: 10px 10px 10px 20px;outline: 0px;border-left: 3px solid rgb(239, 112, 96);color: rgb(106, 115, 125);font-size: 0.9em;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;border-top: none;border-right: none;border-bottom: none;overflow: auto;background: rgb(255, 249, 249);visibility: visible;"> <p style="margin: 0px;padding: 8px 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;font-size: 16px;color: black;line-height: 26px;visibility: visible;">https://github.com/redisson/redisson/wiki/8.-distributed-locks-and-synchronizer<span style="color: rgb(51, 51, 51);font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;text-align: justify;"></span></p> </blockquote> <figure data-tool="mdnice编辑器" style="margin: 10px 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 16px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;display: flex;flex-direction: column;justify-content: center;align-items: center;visibility: visible;"> <img class="rich_pages wxw-img" data-rat

这个队列的思路真的好,现在它是我简历上的亮点了。

作者:微信小助手

<section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">前几天在一个开源项目的 github 里面看到这样的一个 pr:</span> </section> <section style="font-size: 16px;color: black;padding: 0px 10px;word-spacing: 0px;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.12454479242534595" src="/upload/d6cda315b351563981c14d9731a5743.png" data-type="png" data-w="1373" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">光是看这个名字,里面有个 MemorySafe,我就有点陷进去了。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">我先给你看看这个东西:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.28969359331476324" src="/upload/cc473917e17f350930c16b7b32757810.png" data-type="png" data-w="718" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">这个肯定很眼熟吧?我是从阿里巴巴开发规范中截的图。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">为什么不建议使用 &nbsp;FixedThreadPool 和 SingleThreadPool 呢?</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">因为队列太长了,请求会堆积,请求一堆积,容易造成 OOM。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">那么问题又来了:前面提到的线程池用的队列是什么队列呢?</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.4984984984984985" src="/upload/f19d9c9b34d95801087b769b7b9df2d3.png" data-type="png" data-w="666" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">用的是没有指定长度的 LinkedBlockingQueue。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">没有指定长度,默认长度是 Integer.MAX_VALUE,可以理解为无界队列了:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.2611464968152866" src="/upload/db821b8f1faf37f7ec78a9d9003db3b5.png" data-type="png" data-w="471" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">所以,在我的认知里面,使用 LinkedBlockingQueue 是可能会导致 OOM 的。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">如果想避免这个 OOM 就需要在初始化的时候指定一个合理的值。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">“合理的值”,听起来轻描淡写的四个字,但是这个值到底是多少呢,你说的准吗?</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">基本上说不准。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">所以,当我看到 pr 上的 MemorySafeLinkedBlockingQueue 这个名字的时候,我就陷进去了。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">在 LinkedBlockingQueue 前面加上了 MemorySafe 这个限定词。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">表示这是一个内存安全的 LinkedBlockingQueue。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">于是,我想要研究一下到底是怎么样来实现“安全”的,所以啪的一下就点进去了,很快啊。</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" src="/upload/8ab34ee4600d01c8deb41472218ac1ba.png" data-cropx1="0" data-cropx2="435" data-cropy1="0" data-cropy2="417" data-ratio="0.9586206896551724" src="https://mmbiz.qpic.cn/mmbiz_jpg/ELQw2WCMgt3Kbp8TCq9H2KVCy5hCT6yB2y4wzICpMRrrU2TDMgcH2Av1eIyyyYsXMANJ0epEhB9NATmobn6dCQ/640?wx_fmt=jpeg" data-type="jpeg" data-w="435" style="display: block;margin: 0px auto;max-width: 100%;width: 242px;height: 232px;"> </figure> </section> <h2 data-tool="mdnice编辑器" style="margin: 30px 0px 15px;padding: 0px;font-weight: bold;font-size: 1.3em;max-width: 100%;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;letter-spacing: normal;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;border-bottom: 2px solid rgb(239, 112, 96);line-height: 1.75em;overflow-wrap: break-word !important;box-sizing: border-box !important;"><span style="margin: 0px 3px 0px 0px;padding: 3px 10px 1px;max-width: 100%;display: inline-block;font-weight: bold;background: rgb(239, 112, 96);color: rgb(255, 255, 255);border-top-right-radius: 3px;border-top-left-radius: 3px;font-size: 15px;overflow-wrap: break-word !important;box-sizing: border-box !important;">MemorySafeLBQ</span></h2> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">在这个 pr 里面我们看一下它主要是想干个什么事儿:</span> </section> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;border-left: 3px solid rgb(239, 112, 96);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;background: rgb(255, 249, 249);"> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">https://github.com/apache/dubbo/pull/10021</span> </section> </blockquote> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.3674052894924946" src="/upload/ee26c4c8542abf98e24d391dace2fa71.png" data-type="png" data-w="1399" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">提供代码的哥们是这样描述它的功能的:</span> </section> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;border-left: 3px solid rgb(239, 112, 96);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;background: rgb(255, 249, 249);"> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">可以完全解决因为 LinkedBlockingQueue 造成的 OOM 问题,而且不依赖 instrumentation,比 MemoryLimitedLinkedBlockingQueue 更好用。</span> </section> </blockquote> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">然后可以看到这次提交涉及到 7 个文件。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">实际上真正核心的代码是这两个:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.9017857142857143" src="/upload/f206c9c0deb88ee3c18b21675e08a0c7.png" data-type="png" data-w="1008" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">但是不要慌,先眼熟一下这两个类,然后我先按下不表。先追根溯源,从源头上讲。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">这两个类的名字太长了,所以先约定一下,在本文中,<strong style="font-weight: bold;color: black;">我用 MemoryLimitedLBQ 来代替 MemoryLimitedLinkedBlockingQueue。用 MemorySafeLBQ 来代替 MemorySafeLinkedBlockingQueue。</strong></span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.9384965831435079" src="/upload/5774b932136bb2468b1ed2c22c81d412.png" data-type="png" data-w="439" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">可以看到,在 pr 里面它还提到了“比 MemoryLimitedLBQ 更好用”。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">也就是说,它是用来替代 MemoryLimitedLBQ 这个类的。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">这个类从命名上也看得出来,也是一个 LinkedBlockingQueue,但是它的限定词是 MemoryLimited,可以限制内存的。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">我找了一下,这个类对应的 pr 是这个:</span> </section> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;border-left: 3px solid rgb(239, 112, 96);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;background: rgb(255, 249, 249);"> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">https://github.com/apache/dubbo/pull/9722</span> </section> </blockquote> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.41395623891188643" src="/upload/c9f92420c2406311ac79e96a65630e6f.png" data-type="png" data-w="1691" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">在这个 pr 里面,有大佬问他:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.1488138030194105" src="/upload/4f5650cd2ea14a213db7826541cb969e.png" data-type="png" data-w="1391" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">你这个新队列实现的意义或目的是什么?你能不能说出当前版本库中需要被这个队列取代的队列?这样我们才好决定是否使用这个队列。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">也就是说他只是提交了一个新的队列,但是并没有说到应用场景是什么,导致官方不知道该不该接受这个 pr。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">于是,他补充了一个回复:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5082555635319455" src="/upload/416a15fe96351c011da31a72393fbf11.png" data-type="png" data-w="1393" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">就是拿的 FixedThreadPool 做的示例。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">在这个里面,就使用了无参的 LinkedBlockingQueue,所以会有 OOM 的风险。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">那么就可以使用 MemoryLimitedLBQ 来代替这个队列。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">比如,我可以限制这个队列可以使用的最大内存为 100M,通过限制内存的方式来达到避免 OOM 的目的。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">好,到这里我先给你梳理一下。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">首先应该是有一个叫 MemoryLimitedLBQ 的队列,它可以限制这个队列最大可以占用的内存。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">然后,由于某些原因,又出现了一个叫做 MemorySafeLBQ 的队列,宣称比它更好用,所以来取代它。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">所以,接下来我就要梳理清楚三个问题:</span> </section> <ul data-tool="mdnice编辑器" style="margin: 8px 0px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"> <li style="font-size: 15px;"> <section style="margin-top: 5px;margin-bottom: 5px;text-align: left;color: rgb(1, 1, 1);font-weight: 500;line-height: 1.75em;"> <span style="font-size: 15px;">MemoryLimitedLBQ 的实现原理是什么?</span> </section></li> <li style="font-size: 15px;"> <section style="margin-top: 5px;margin-bottom: 5px;text-align: left;color: rgb(1, 1, 1);font-weight: 500;line-height: 1.75em;"> <span style="font-size: 15px;">MemorySafeLBQ 的实现原理是什么?</span> </section></li> <li style="font-size: 15px;"> <section style="margin-top: 5px;margin-bottom: 5px;text-align: left;color: rgb(1, 1, 1);font-weight: 500;line-height: 1.75em;"> <span style="font-size: 15px;">MemorySafeLBQ 为什么比 MemoryLimitedLBQ 更好用?</span> </section></li> </ul> <h2 data-tool="mdnice编辑器" style="margin: 30px 0px 15px;padding: 0px;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;line-height: 1.75em;"><span style="font-size: 15px;"><span style="display: inline-block;font-weight: bold;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">MemoryLimitedLBQ</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid rgb(239, 235, 233);border-right: 20px solid transparent;"> </span></span></h2> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">别看这个玩意我是在 Dubbo 的 pr 里面看到的,但是它本质上是一个队列的实现方式。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">所以,完全可以脱离于框架而存在。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">也就是说,你打开下面这个链接,然后直接把相关的两个类粘出来,就可以跑起来,为你所用:</span> </section> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;border-left: 3px solid rgb(239, 112, 96);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;background: rgb(255, 249, 249);"> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">https://github.com/apache/dubbo/pull/9722/files</span> </section> </blockquote> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">我先给你看看 MemoryLimitedLBQ 这个类,它就是继承自 LinkedBlockingQueue,然后重写了它的几个核心方法。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">只是自定义了一个 memoryLimiter 的对象,然后每个核心方法里面都操作了 memoryLimiter 对象:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="1.3580470162748643" src="/upload/bb5766f46c84ba6bda240e717d8bde50.png" data-type="png" data-w="1106" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">所以真正的秘密就藏在 memoryLimiter 对象里面。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">比如,我带你看看这个 put 方法:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.3020030816640986" src="/upload/f3da8a4e2a95c5237da4927511265c34.png" data-type="png" data-w="649" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">这里面调用了 memoryLimiter 对象的 acquireInterruptibly 方法。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">在解读 acquireInterruptibly 方法之前,我们先关注一下它的几个成员变量:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.878619153674833" src="/upload/6a0827fc1c65cb2b16362919759f5ee7.png" data-type="png" data-w="898" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <ul data-tool="mdnice编辑器" style="margin: 8px 0px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"> <li style="font-size: 15px;"> <section style="margin-top: 5px;margin-bottom: 5px;text-align: left;color: rgb(1, 1, 1);font-weight: 500;line-height: 1.75em;"> <span style="font-size: 15px;">memoryLimit 就是表示这个队列最大所能容纳的大小。</span> </section></li> <li style="font-size: 15px;"> <section style="margin-top: 5px;margin-bottom: 5px;text-align: left;color: rgb(1, 1, 1);font-weight: 500;line-height: 1.75em;"> <span style="font-size: 15px;">memory 是 LongAdder 类型,表示的是当前已经使用的大小。</span> </section></li> <li style="font-size: 15px;"> <section style="margin-top: 5px;margin-bottom: 5px;text-align: left;color: rgb(1, 1, 1);font-weight: 500;line-height: 1.75em;"> <span style="font-size: 15px;">acquireLock、notLimited、releaseLock、notEmpty 是锁相关的参数,从名字上可以知道,往队列里面放元素和释放队列里面的元素都需要获取对应的锁。</span> </section></li> <li style="font-size: 15px;"> <section style="margin-top: 5px;margin-bottom: 5px;text-align: left;color: rgb(1, 1, 1);font-weight: 500;line-height: 1.75em;"> <span style="font-size: 15px;">inst 这个参数是 Instrumentation 类型的。</span> </section></li> </ul> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">前面几个参数至少我还很眼熟的,但是这个 inst 就有点奇怪了。</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="1.0943396226415094" src="/upload/359508af5277467fe239c99e98650eca.png" data-type="png" data-w="318" style="display: block;margin: 0px auto;max-width: 100%;width: 193px;height: 211px;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">这玩意日常开发中基本上用不上,但是用好了,这就是个黑科技了。很多工具都是基于这个玩意来实现的,比如大名鼎鼎的 Arthas。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">它可以更加方便的做字节码增强操作,允许我们对已经加载甚至还没有被加载的类进行修改的操作,实现类似于性能监控的功能。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">可以说 Instrumentation 就是 memoryLimiter 的关键点:</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">比如在 memoryLimiter 的 acquireInterruptibly 方法里面,它是这样的用的:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.46136618141097424" src="/upload/327d3d364e22b46c5c3db95a8720bb36.png" data-type="png" data-w="893" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">看方法名称你也知道了,get 这个 object 的 size,这个 object 就是方法的入参,也就是要放入到队列里面的元素。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">为了证明我没有乱说,我带你看看这个方法上的注释:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.2089116143170197" src="/upload/e3a0e62585cf2103433986e015c81d10.png" data-type="png" data-w="1369" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;border-left: 3px solid rgb(239, 112, 96);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;background: rgb(255, 249, 249);"> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">an implementation-specific approximation of the amount of storage consumed by the specified object</span> </section> </blockquote> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">注意这个单词:approximation.</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">这可是正儿八经的四级词汇,还是 a 开头的,你要是不眼熟的话可是要挨板子的。</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.39232053422370616" src="/upload/12cc5c3bc65094f83e5589574c12184b.png" data-type="png" data-w="599" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">整句话翻译过来就是:返回指定对象所消耗的存储量的一个特定实现的近似值。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">再说的直白点就是你传进来的这个对象,在内存里面到底占用了多长的长度,这个长度不是一个非常精确的值。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">所以,理解了 inst.getObjectSize(e) 这行代码,我们再仔细看看 acquireInterruptibly 是怎么样的:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.8314977973568282" src="/upload/46094ade098026cb789fcf7c80742971.png" data-type="png" data-w="908" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">首先,两个标号为 ① 的地方,表示操作这个方法是要上锁的,整个 try 里面的方法是线程安全的。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">然后标号为 ② 的里面干了什么事儿?</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">就是计算 memory 这个 LongAdder 类型的 sum 值加上当前这个对象的值之后,是不是大于或者等于 memoryLimit。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">如果计算后的值真的超过了 memoryLimit,那么说明需要阻塞一下下了,调用 notLimited.await() 方法。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">如果没有超过 memoryLimit,说明还能往队列里面放东西,那么就更新 memory 的值。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">接着到了标号为 ③ 的地方。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">来到这里,再次判断一下当前已经使用的值是否没有超过 memoryLimit,如果是的话,就调用 notLimited.signal() 方法,唤醒一下之前由于 memoryLimit 参数限制导致不能放入的对象。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">整个逻辑非常的清晰。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">而整个逻辑里面的核心逻辑就是调用 Instrumentation 类型的 getObjectSize 方法获得当前放入对象的一个 size,并判断当前已经使用的值加上这个 size 之后,是否大于了我们设置的最大值。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">所以,你用脚趾头猜也能猜到了,在 release 方法里面,肯定也是计算当前对象的 size,然后再从 memory 里面减出去:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.7993311036789298" src="/upload/7a3d7357ce6cfeef7449dd7aabb33ff4.png" data-type="png" data-w="897" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">说穿了,也就这么屁大点事儿。</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="1.0870786516853932" src="/upload/21594fd61daaabd60b0e4b34ec2e4c8f.png" data-type="png" data-w="356" style="display: block;margin: 0px auto;max-width: 100%;width: 216px;height: 235px;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">然后,你再次审视一下这个 acquireInterruptibly 方法的 try 代码块里面的逻辑,你有没有发现什么 BUG:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.6444929116684842" src="/upload/3d20d116d8cf94c63a5524809a586f32.png" data-type="png" data-w="917" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">如果你没反应过来,那我再提个醒:你认真的分析一下 sum 这个局部变量是不是有点不妥?</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">你要是还没反应过来,那我直接给你上个代码。后面有一次提交,是把 sum 修改为了 memory.sum() :</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5328185328185329" src="/upload/8a4250a0ed24664f6e542c5a7406cab0.png" data-type="png" data-w="1554" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">为什么这样改呢?</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">我给你说个场景,假设我们的 memoryLimit 是 1000,当前已经使用的 memory 是 800,也就是 sum 是 800。这个时候我要放的元素计算出来的 size 是 300,也就是 objectSize 是 300。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">sum+objectSize=1100,比 memoryLimit 的值大,是不是在这个 while 判断的时候被拦截住了:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.600656455142232" src="/upload/8f84e7ea3272a8dc65c32ec851072658.png" data-type="png" data-w="914" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">之后,假设队列里面又释放了一个 size 为 600 的对象。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">这个时候执行 memory.add(-objectSize) 方法,memory 变为 200:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.7064116985376828" src="/upload/b96828634b501b231ee45f34e3563004.png" data-type="png" data-w="889" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">那么会调用 signalNotLimited 方法,唤醒这个被拦截的这个哥们:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.6446469248291572" src="/upload/7324a205b65229c059a84cec1e4f185f.png" data-type="png" data-w="439" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">这个哥们一被唤醒,一看代码:</span> </section> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"> <section style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(51, 51, 51);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(248, 248, 248);border-radius: 5px;margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <span style="font-size: 15px;"><span style="color: rgb(51, 51, 51);font-weight: bold;line-height: 26px;">while</span>&nbsp;(sum&nbsp;+&nbsp;objectSize&nbsp;&gt;=&nbsp;memoryLimit)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;notLimited.await();<br>}<br></span> </section></pre> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">心里想:我这里的 sum 是 800,objectSize 是 300,还是大于 memoryLimit 啊,把我唤醒干啥玩意,傻逼吗?</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">那么你说,它骂的是谁?</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.8873239436619719" src="/upload/50f2b3b64bb2c90fe2bab1597b88f072.png" data-type="png" data-w="426" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">这个地方的代码肯定得这样,每次都查看最新的 memory 值才行:</span> </section> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"> <section style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(51, 51, 51);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(248, 248, 248);border-radius: 5px;margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <span style="font-size: 15px;"><span style="color: rgb(51, 51, 51);font-weight: bold;line-height: 26px;">while</span>&nbsp;(memory.sum()&nbsp;+&nbsp;objectSize&nbsp;&gt;=&nbsp;memoryLimit)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;notLimited.await();<br>}<br></span> </section></pre> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">所以,这个地方是个 BUG,还是个死循环的 BUG。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">前面代码截图中还出现了一个链接,就是说的这个 BUG:</span> </section> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;border-left: 3px solid rgb(239, 112, 96);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;background: rgb(255, 249, 249);"> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">https://github.com/apache/incubator-shenyu/pull/3335</span> </section> </blockquote> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.41785714285714287" src="/upload/31c37c382f39e1e7f82c5a7f215e5352.png" data-type="png" data-w="1400" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">另外,你可以看到链接中的项目名称是 incubator-shenyu,这是一个开源的 API 网关:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5833333333333334" src="/upload/7ca00d34472e7992226185526213778d.png" data-type="png" data-w="1152" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;"><strong style="font-weight: bold;color: black;">本文中的 MemoryLimitedLBQ 和 MemorySafeLBQ 最先都是出自这个开源项目。</strong></span> </section> <h2 data-tool="mdnice编辑器" style="margin: 30px 0px 15px;padding: 0px;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;line-height: 1.75em;"><span style="font-size: 15px;"><span style="display: inline-block;font-weight: bold;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">MemorySafeLBQ</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid rgb(239, 235, 233);border-right: 20px solid transparent;"> </span></span></h2> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">前面了解了 MemoryLimitedLBQ 的基本原理。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">接下来我带你看看 MemorySafeLBQ 这个玩意。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">它的源码可以通过这个链接直接获取到:</span> </section> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;border-left: 3px solid rgb(239, 112, 96);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;background: rgb(255, 249, 249);"> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">https://github.com/apache/dubbo/pull/10021/files</span> </section> </blockquote> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">也是拿出来就可以放到自己的项目跑,把文件作者修改为自己的名字的那种。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">让我们回到最开始的地方:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.3674052894924946" src="/upload/ee26c4c8542abf98e24d391dace2fa71.png" data-type="png" data-w="1399" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">这个 pr 里面说了,我搞 MemorySafeLBQ 出来,就是为了替代 MemoryLimitedLBQ 的,因为我比它好用,而且我还不依赖于 Instrumentation。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">但是看了源码之后,会发现其实思路都是差不多的。只不过 MemorySafeLBQ 属于是反其道而行之。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">怎么个“反其道”法呢?</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">看一下源码:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.6656314699792961" src="/upload/54bf22d47dfee1ec5d29a5b6c8aa11d8.png" data-type="png" data-w="966" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">MemorySafeLBQ 还是继承自 LinkedBlockingQueue,只是多了一个自定义的成员变量,叫做 maxFreeMemory,初始值是 256 * 1024 * 1024。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">这个变量的名字就非常值得注意,你再细细品品。maxFreeMemory,最大的剩余内存,默认是 256M。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">前面一节讲的 MemoryLimitedLBQ 限制的是这个队列最多能使用多少空间,是站在队列的角度。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">而 MemorySafeLBQ 限制的是 JVM 里面的剩余空间。比如默认就是当整个 JVM 只剩下 256M 可用内存的时候,再往队列里面加元素我就不让你加了。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">因为整个内存都比较吃紧了,队列就不能无限制的继续添加了,从这个角度来规避了 OOM 的风险。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">这样的一个反其道而行之。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">另外,它说它不依赖 Instrumentation 了,那么它怎么检测内存的使用情况呢?</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.17695852534562212" src="/upload/c4d95c9b9124de12c2afc2fbe597304b.png" data-type="png" data-w="1085" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">使用的是 ManagementFactory 里面的 MemoryMXBean。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">这个 MemoryMXBean 其实你一点也不陌生。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">JConsole 你用过吧?</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">下面这个界面进去过吧?</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">这些信息就是从 ManagementFactory 里面拿出来的:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.6791383219954649" src="/upload/3771e93172a8d1ebaed346734d7510d6.png" data-type="png" data-w="882" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">所以,确实它没有使用 Instrumentation,但是它使用了 ManagementFactory。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">目的都是为了获取内存的运行状态。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">那么怎么看出来它比 MemoryLimitedLBQ 更好用呢?</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">我看了,关键方法就是这个 hasRemainedMemory,在调用 put、offer 方法之前就要先调用这个方法:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.49922360248447206" src="/upload/2e1737cdea29e698000d1067213162fd.png" data-type="png" data-w="1288" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">而且你看 MemorySafeLBQ 只是重写了放入元素的 put、offer 方法,并不关注移除元素。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">为什么呢?</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">因为它的设计理念是只关心添加元素时候的剩余空间大小,它甚至都不会去关注当前这个元素的大小。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">而还记得前面讲的 MemoryLimitedLBQ 吗?它里面还计算了每个元素的大小,然后搞了一个变量来累加。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">MemoryLimitedLBQ 的 hasRemainedMemory 方法里面也只有一行代码,其中 maxFreeMemory 是类初始化的时候就指定好了。那么关键的代码就是 MemoryLimitCalculator.maxAvailable()。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">所以我们看看 MemoryLimitCalculator 的源码。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">这个类的源码写的非常的简单,我全部截完都只有这么一点内容,全部加起来也就是 20 多行代码:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.9286666666666666" src="/upload/45d5e6f4f2886ca3cd31c4e7ccca70f0.png" data-type="png" data-w="1500" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">而整个方法的核心就是我框起来的 static 代码块,里面一共有三行代码。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">第一行是调用 refresh 方法,也就是对 maxAvilable 这个参数进行重新赋值,这个参数代表的意思是当前还可以使用的 JVM 内存。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">第二行是注入了一个每 50ms 运行一次的定时任务。到点了,就触发一下 refresh 方法,保证 maxAvilable 参数的准实时性。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">第三行是加入了 JVM 的 ShutdownHook,停服务的时候需要把这个定时任务给停了,达到优雅停机的目的。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">核心逻辑就这么点。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">从我的角度来说,确实是比 MemoryLimitedLBQ 使用起来更简单,更好用。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">最后,再看看作者提供的 MemorySafeLBQ 测试用例,我补充了一点注释,很好理解,自己去品,不再多说:</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5469988674971688" src="/upload/b2df38380e0a50e1cae148e1645bb49c.png" data-type="png" data-w="883" style="display: block;margin: 0 auto;max-width: 100%;"> </figure> </section> <h2 data-tool="mdnice编辑器" style="margin: 30px 0px 15px;padding: 0px;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;line-height: 1.75em;"><span style="font-size: 15px;"><span style="display: inline-block;font-weight: bold;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">它是你的了</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid rgb(239, 235, 233);border-right: 20px solid transparent;"> </span></span></h2> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">文章里面提到的 MemoryLimitedLBQ 和 MemorySafeLBQ,我说了,这两个玩意是完全独立于框架的,代码直接粘过来就可以用。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">代码也没几行,不管是用 Instrumentation 还是 ManagementFactory,核心思想都是限制内存。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">思路扩展一下,比如我们有的项目里面用 Map 来做本地缓存,就会放很多元素进去,也会有 OOM 的风险,那么通过前面说的思路,是不是就找到了一个问题的解决方案?</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">所以,思路是很重要的,掌握到了这个思路,面试的时候也能多掰扯几句嘛。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">再比如,我看到这个玩意的时候,联想到了之前写过的线程池参数动态调整。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">就拿 MemorySafeLBQ 这个队列来说,它里面的 maxFreeMemory 这个参数,可不可以做成动态调整的?</span> </section> <section style="margin-left: 0px;margin-right: 0px;line-height: 1.75em;"> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.8240963855421687" src="/upload/836d14512bfadc44ebf70ccc114f1467.png" data-type="png" data-w="415" style="display: block;margin: 0px auto;max-width: 100%;width: 340px;height: 280px;"> </figure> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">不外乎就是把之前的队列长度可调整修改为了队列占用的内存空间可调整。一个参数的变化而已,实现方案可以直接套用。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">这些都是我从开源项目里面看到的,但是在我看到的那一刻,它就是我的。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">现在,我把它写出来,分享给你,它就是你的了。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">不客气,来个三连就行。</span> </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 1.75em;"> <span style="font-size: 15px;">好了,那本文的技术部分就到这里啦。</span> </section>

别再写代码测试并发了,太 Low!模拟并发的 4 种方法,还有谁不会??

作者:微信小助手

<section data-class="_mbEditor" data-id="132148" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;visibility: visible;"> <section data-tools-id="46476" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: Arial;border-width: 0px 0px 2px;border-style: none none solid;color: rgb(49, 147, 105);border-color: rgb(239, 112, 96);visibility: visible;"> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 32px;display: inline-block;font-weight: bold;font-size: 16px;line-height: 28px;color: rgb(239, 112, 96);visibility: visible;">Postman</p> </section> </section> </section> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;visibility: visible;"><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"></p> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;visibility: visible;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;visibility: visible;">Postman是一个款http请求模拟工具</span></p> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;visibility: visible;"><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"></p> <p style="margin: 0px 0px 0em;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: center;visibility: visible;"><img class="rich_pages wxw-img" data-ratio="1.156596794081381" src="/upload/9f245ef679505212cf69d188ebf66960.png" data-type="png" data-w="811" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;vertical-align: bottom;height: auto !important;float: none;visibility: visible !important;width: 556px !important;"></p> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;visibility: visible;"><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"></p> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;visibility: visible;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;visibility: visible;">首先演示一下postman最基本的使用</span></p> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;visibility: visible;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;visibility: visible;">创建一个Springboot项目,测试的代码如下:</span></p> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;visibility: visible;"><br mpa-from-tpl="t" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"></p> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;visibility: visible;"> <pre style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;background: none;visibility: visible;"><code style="margin: 0px 0.15em;padding: 5.95px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;border-radius: 4px;font-size: 0.85em;background: rgb(35, 36, 31);color: rgb(248, 248, 242);display: block;overflow-x: auto;white-space: nowrap;visibility: visible;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 12px;font-family: CourierNewPS-ItalicMT;visibility: visible;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(249, 38, 114);background: rgba(0, 0, 0, 0);display: inline;width: 39px;text-decoration: none solid rgb(249, 38, 114);font-weight: 400;font-style: normal;visibility: visible;">import</span>&nbsp;<span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(249, 38, 114);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(249, 38, 114);font-weight: 400;font-style: normal;visibility: visible;">org</span><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(166, 226, 46);background: rgba(0, 0, 0, 0);display: inline;width: 105px;text-decoration: none solid rgb(166, 226, 46);font-weight: 400;font-style: normal;visibility: visible;">.springframework</span><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(166, 226, 46);background: rgba(0, 0, 0, 0);display: inline;width: 27px;text-decoration: none solid rgb(166, 226, 46);font-weight: 400;font-style: normal;visibility: visible;">.web</span><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(166, 226, 46);background: rgba(0, 0, 0, 0);display: inline;width: 33px;text-decoration: none solid rgb(166, 226, 46);font-weight: 400;font-style: normal;visibility: visible;">.bind</span><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(166, 226, 46);background: rgba(0, 0, 0, 0);display: inline;width: 72px;text-decoration: none solid rgb(166, 226, 46);font-weight: 400;font-style: normal;visibility: visible;">.annotation</span><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(166, 226, 46);background: rgba(0, 0, 0, 0);display: inline;width: 73px;text-decoration: none solid rgb(166, 226, 46);font-weight: 400;font-style: normal;visibility: visible;">.GetMapping</span>;<br mpa-from-tpl="t" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(249, 38, 114);background: rgba(0, 0, 0, 0);display: inline;width: 39px;text-decoration: none solid rgb(249, 38, 114);font-weight: 400;font-style: normal;visibility: visible;">import</span>&nbsp;<span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(249, 38, 114);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(249, 38, 114);font-weight: 400;font-style: normal;visibility: visible;">org</span><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(166, 226, 46);background: rgba(0, 0, 0, 0);display: inline;width: 105px;text-decoration: none solid rgb(166, 226, 46);font-weight: 400;font-style: normal;visibility: visible;">.springframework</span><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(166, 226, 46);background: rgba(0, 0, 0, 0);display: inline;width: 27px;text-decoration: none solid rgb(166, 226, 46);font-weight: 400;font-style: normal;visibility: visible;">.web</span><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(166, 226, 46);background: rgba(0, 0, 0, 0);display: inline;width: 33px;text-decoration: none solid rgb(166, 226, 46);font-weight: 400;font-style: normal;visibility: visible;">.bind</span><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(166, 226, 46);background: rgba(0, 0, 0, 0);display: inline;width: 72px;text-decoration: none solid rgb(166, 226, 46);font-weight: 400;font-style: normal;visibility: visible;">.annotation</span><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(166, 226, 46);background: rgba(0, 0, 0, 0);display: inline;width: 99px;text-decoration: none solid rgb(166, 226, 46);font-weight: 400;font-style: normal;visibility: visible;">.RequestMapping</span>;<br mpa-from-tpl="t" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(249, 38, 114);background: rgba(0, 0, 0, 0);display: inline;width: 39px;text-decoration: none solid rgb(249, 38, 114);font-weight: 400;font-style: normal;visibility: visible;">import</span>&nbsp;<span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(249, 38, 114);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(249, 38, 114);font-weight: 400;font-style: normal;visibility: visible;">org</span><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(166, 226, 46);background: rgba(0, 0, 0, 0);display: inline;width: 105px;text-decoration: none solid rgb(166, 226, 46);font-weight: 400;font-style: normal;visibility: visible;">.springframework</span><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(166, 226, 46);background: rgba(0, 0, 0, 0);display: inline;width: 27px;text-decoration: none solid rgb(166, 226, 46);font-weight: 400;font-style: normal;visibility: visible;">.web</span><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(166, 226, 46);background: rgba(0, 0, 0, 0);display: inline;width: 33px;text-decoration: none solid rgb(166, 226, 46);font-weight: 400;font-style: normal;visibility: visible;">.bind</span><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(166, 226, 46);background: rgba(0, 0, 0, 0);display: inline;width: 72px;text-decoration: none solid rgb(166, 226, 46);font-weight: 400;font-style: normal;visibility: visible;">.annotation</span><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(166, 226, 46);background: rgba(0, 0, 0, 0);display: inline;width: 99px;text-decoration: none solid rgb(166, 226, 46);font-weight: 400;font-style: normal;visibility: visible;">.RestController</span>;<br mpa-from-tpl="t" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"><br mpa-from-tpl="t" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;">@<span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(249, 38, 114);background: rgba(0, 0, 0, 0);display: inline;width: 93px;text-decoration: none solid rgb(249, 38, 114);font-weight: 400;font-style: normal;visibility: visible;">RestController</span><br mpa-from-tpl="t" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;">@RequestMapping(<span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(230, 219, 116);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(230, 219, 116);font-weight: 400;font-style: normal;visibility: visible;">"test"</span>)<br mpa-from-tpl="t" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;">public class TestConrtoller {<br mpa-from-tpl="t" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"><br mpa-from-tpl="t" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;">&nbsp;&nbsp;&nbsp;&nbsp;@<span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(249, 38, 114);background: rgba(0, 0, 0, 0);display: inline;width: 66px;text-decoration: none solid rgb(249, 38, 114);font-weight: 400;font-style: normal;">GetMapping</span>("<span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(249, 38, 114);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(249, 38, 114);font-weight: 400;font-style: normal;">demo</span>")<br mpa-from-tpl="t" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">&nbsp;&nbsp;&nbsp;&nbsp;public String testDemo() {<br mpa-from-tpl="t" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(249, 38, 114);background: rgba(0, 0, 0, 0);display: inline;width: 39px;text-decoration: none solid rgb(249, 38, 114);font-weight: 400;font-style: normal;">return</span>&nbsp;"<span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: CourierNewPS-ItalicMT;font-size: 12px;color: rgb(249, 38, 114);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(249, 38, 114);font-weight: 400;font-style: normal;">result</span>~";<br mpa-from-tpl="t" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">&nbsp;&nbsp;&nbsp;&nbsp;}<br mpa-from-tpl="t" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">}</span></code></pre> </section> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;"><br></p> <p style="margin: 0px 0px 0em;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.24633991088478677" src="/upload/5507a24662f85e55c11fe14b872d6412.png" data-type="png" data-w="1571" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;border-width: 1px;border-style: solid;border-color: rgb(238, 237, 235);vertical-align: bottom;float: none;box-sizing: border-box !important;overflow-wrap: break-word !important;background-color: rgb(238, 237, 235) !important;background-size: 22px !important;background-position: center center !important;background-repeat: no-repeat !important;height: 138.472px !important;width: 556px !important;"></p> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;"><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;">为了便于操作,一般会将</span></p> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;"><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;">http://127.0.0.1:8080 是经常使用的地址+端口号,可以设置为环境</span></p> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;"><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;">点击右上角的设置图标</span></p> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;"><br></p> <p style="margin: 0px 0px 0em;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.4130019120458891" data-type="png" data-w="523" src="/upload/c24aa9b48cf658d4f3a324781c063cbf.png" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;border-width: 1px;border-style: solid;border-color: rgb(238, 237, 235);vertical-align: bottom;float: none;box-sizing: border-box !important;overflow-wrap: break-word !important;background-color: rgb(238, 237, 235) !important;background-size: 22px !important;background-position: center center !important;background-repeat: no-repeat !important;height: 217.174px !important;width: 523px !important;"></p> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;"><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;">选择global</span></p> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;"><br></p> <p style="margin: 0px 0px 0em;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.8853591160220995" data-type="png" data-w="724" src="/upload/cb8cd1740c959f4b2b36451f869f5479.png" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;border-width: 1px;border-style: solid;border-color: rgb(238, 237, 235);vertical-align: bottom;float: none;box-sizing: border-box !important;overflow-wrap: break-word !important;background-color: rgb(238, 237, 235) !important;background-size: 22px !important;background-position: center center !important;background-repeat: no-repeat !important;height: 492.489px !important;width: 556px !important;"></p> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;"><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;text-align: left;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;">输入信息</span></p> <p style="margin: 0px;padding: 0px;outl

老大说:谁要再用double定义商品金额,就自己收拾东西走

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="white-space: normal;text-size-adjust: auto;padding-right: 10px;padding-left: 10px;line-height: 1.6;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;margin-top: -10px;font-size: 15px;letter-spacing: 0.05em;color: rgb(89, 89, 89);"> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.42592592592592593" src="/upload/23679f8d3726c8bc6fb84a35a2ff74c7.png" data-type="png" data-w="3240" style="display: block;margin-right: auto;margin-left: auto;width: auto;"> </figure> <hr data-tool="mdnice编辑器" style="height: 1px;border-style: solid;border-color: rgb(53, 179, 120);margin: 1.5em auto;"> <h2 data-tool="mdnice编辑器" style="line-height: 32px;color: rgb(53, 179, 120);display: inline-block;border-bottom: 0px solid rgb(53, 179, 120);border-top-color: rgb(53, 179, 120);border-right-color: rgb(53, 179, 120);border-left-color: rgb(53, 179, 120);font-size: 23px;margin-top: 1em;margin-bottom: 0rem;padding-top: 0.5em;padding-bottom: 0.5em;font-weight: bold;">先看现象</h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">涉及诸如<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">float</code>或者<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">double</code>这两种浮点型数据的处理时,偶尔总会有一些<strong style="color: rgb(53, 179, 120);">怪怪的现象</strong>,不知道大家注意过没,举几个常见的栗子:</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">典型现象(一):条件判断超预期</strong></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background-color: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-top-left-radius: 0px;border-top-right-radius: 0px;border-bottom-right-radius: 0px;border-bottom-left-radius: 0px;font-size: 12px;background-position: initial initial;background-repeat: initial initial;">System.out.println(&nbsp;<span style="line-height: 26px;">1f</span>&nbsp;==&nbsp;<span style="line-height: 26px;">0.9999999f</span>&nbsp;);&nbsp;&nbsp;&nbsp;<span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;打印:false</span><br>System.out.println(&nbsp;<span style="line-height: 26px;">1f</span>&nbsp;==&nbsp;<span style="line-height: 26px;">0.99999999f</span>&nbsp;);&nbsp;&nbsp;<span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;打印:true &nbsp;&nbsp;&nbsp;纳尼?</span><br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">典型现象(二):数据转换超预期</strong></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background-color: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-top-left-radius: 0px;border-top-right-radius: 0px;border-bottom-right-radius: 0px;border-bottom-left-radius: 0px;font-size: 12px;background-position: initial initial;background-repeat: initial initial;"><span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">float</span>&nbsp;f&nbsp;=&nbsp;<span style="line-height: 26px;">1.1f</span>;<br><span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">double</span>&nbsp;d&nbsp;=&nbsp;(<span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">double</span>)&nbsp;f;<br>System.out.println(f);&nbsp;&nbsp;<span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;打印:1.1</span><br>System.out.println(d);&nbsp;&nbsp;<span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;打印:1.100000023841858 &nbsp;纳尼?</span><br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">典型现象(三):基本运算超预期</strong></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background-color: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-top-left-radius: 0px;border-top-right-radius: 0px;border-bottom-right-radius: 0px;border-bottom-left-radius: 0px;font-size: 12px;background-position: initial initial;background-repeat: initial initial;">System.out.println(&nbsp;<span style="line-height: 26px;">0.2</span>&nbsp;+&nbsp;<span style="line-height: 26px;">0.7</span>&nbsp;);&nbsp;&nbsp;<br><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;打印:0.8999999999999999 &nbsp;&nbsp;纳尼?</span><br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">典型现象(四):数据自增超预期</strong></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background-color: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-top-left-radius: 0px;border-top-right-radius: 0px;border-bottom-right-radius: 0px;border-bottom-left-radius: 0px;font-size: 12px;background-position: initial initial;background-repeat: initial initial;"><span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">float</span>&nbsp;f1&nbsp;=&nbsp;<span style="line-height: 26px;">8455263f</span>;<br><span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">for</span>&nbsp;(<span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">int</span>&nbsp;i&nbsp;=&nbsp;<span style="line-height: 26px;">0</span>;&nbsp;i&nbsp;&lt;&nbsp;<span style="line-height: 26px;">10</span>;&nbsp;i++)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(f1);<br>&nbsp;&nbsp;&nbsp;&nbsp;f1++;<br>}<br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;打印:8455263.0</span><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;打印:8455264.0</span><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;打印:8455265.0</span><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;打印:8455266.0</span><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;打印:8455267.0</span><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;打印:8455268.0</span><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;打印:8455269.0</span><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;打印:8455270.0</span><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;打印:8455271.0</span><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;打印:8455272.0</span><br><br><span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">float</span>&nbsp;f2&nbsp;=&nbsp;<span style="line-height: 26px;">84552631f</span>;<br><span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">for</span>&nbsp;(<span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">int</span>&nbsp;i&nbsp;=&nbsp;<span style="line-height: 26px;">0</span>;&nbsp;i&nbsp;&lt;&nbsp;<span style="line-height: 26px;">10</span>;&nbsp;i++)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(f2);<br>&nbsp;&nbsp;&nbsp;&nbsp;f2++;<br>}<br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;打印:8.4552632E7 &nbsp;&nbsp;纳尼?不是&nbsp;+1了吗?</span><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;打印:8.4552632E7 &nbsp;&nbsp;纳尼?不是&nbsp;+1了吗?</span><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;打印:8.4552632E7 &nbsp;&nbsp;纳尼?不是&nbsp;+1了吗?</span><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;打印:8.4552632E7 &nbsp;&nbsp;纳尼?不是&nbsp;+1了吗?</span><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;打印:8.4552632E7 &nbsp;&nbsp;纳尼?不是&nbsp;+1了吗?</span><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;打印:8.4552632E7 &nbsp;&nbsp;纳尼?不是&nbsp;+1了吗?</span><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;打印:8.4552632E7 &nbsp;&nbsp;纳尼?不是&nbsp;+1了吗?</span><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;打印:8.4552632E7 &nbsp;&nbsp;纳尼?不是&nbsp;+1了吗?</span><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;打印:8.4552632E7 &nbsp;&nbsp;纳尼?不是&nbsp;+1了吗?</span><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;打印:8.4552632E7 &nbsp;&nbsp;纳尼?不是&nbsp;+1了吗?</span><br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">看到没,这些简单场景下的使用情况都很难满足我们的需求,所以说用浮点数(包括<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">double</code>和<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">float</code>)处理问题有非常多<strong style="color: rgb(53, 179, 120);">隐晦的坑</strong>在等着咱们!</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">怪不得技术总监发狠话:谁要是敢在处理诸如&nbsp;<strong style="color: rgb(53, 179, 120);">商品金额</strong>、<strong style="color: rgb(53, 179, 120);">订单交易</strong>、以及<strong style="color: rgb(53, 179, 120);">货币计算</strong>时用浮点型数据(<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">double</code>/<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">float</code>),直接让我们走人!</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.8888888888888888" src="/upload/b4965d91e207b6a1c00ff7b79488a39d.png" data-type="png" data-w="198" style="display: block;margin-right: auto;margin-left: auto;width: auto;"> </figure> <hr data-tool="mdnice编辑器" style="height: 1px;border-style: solid;border-color: rgb(53, 179, 120);margin: 1.5em auto;"> <h2 data-tool="mdnice编辑器" style="line-height: 32px;color: rgb(53, 179, 120);display: inline-block;border-bottom: 0px solid rgb(53, 179, 120);border-top-color: rgb(53, 179, 120);border-right-color: rgb(53, 179, 120);border-left-color: rgb(53, 179, 120);font-size: 23px;margin-top: 1em;margin-bottom: 0rem;padding-top: 0.5em;padding-bottom: 0.5em;font-weight: bold;">原因出在哪里?</h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">我们就以第一个典型现象为例来分析一下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background-color: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-top-left-radius: 0px;border-top-right-radius: 0px;border-bottom-right-radius: 0px;border-bottom-left-radius: 0px;font-size: 12px;background-position: initial initial;background-repeat: initial initial;">System.out.println(&nbsp;<span style="line-height: 26px;">1f</span>&nbsp;==&nbsp;<span style="line-height: 26px;">0.99999999f</span>&nbsp;);<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">直接用代码去比较<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">1</code>和<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">0.99999999</code>,居然打印出<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">true</code>!</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="1.10625" src="/upload/8c38edbb78444c2f72f83e4a6ed72a04.png" data-type="png" data-w="160" style="display: block;margin-right: auto;margin-left: auto;width: auto;"> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">这说明了什么?这说明了计算机压根<strong style="color: rgb(53, 179, 120);">区分不出来</strong>这两个数。这是为什么呢?</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">我们不妨来简单思考一下:</p> <blockquote data-tool="mdnice编辑器" style="font-size: 0.9em;overflow: auto;padding: 10px 10px 10px 20px;margin: 10px 5px;border-left-color: rgb(53, 179, 120);border-right: 0px solid rgb(53, 179, 120);color: rgb(97, 97, 97);quotes: none;background-color: rgb(251, 249, 253);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">我们知道输入的这两个浮点数只是我们人类肉眼所看到的具体数值,是我们通常所理解的十进制数,但是计算机底层在计算时可不是按照十进制来计算的,学过基本计组原理的都知道,计算机底层最终都是基于像<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">010100100100110011011</code>这种<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">0</code>、<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">1</code>二进制来完成的。</p> </blockquote> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">所以为了搞懂实际情况,我们应该将这两个十进制浮点数<strong style="color: rgb(53, 179, 120);">转化到二进制空间</strong>来看一看。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">十进制浮点数转二进制</strong>&nbsp;怎么转、怎么计算,我想这应该属于基础计算机进制转换常识,在&nbsp;<strong style="color: rgb(53, 179, 120);">《计算机组成原理》</strong>&nbsp;类似的课上肯定学过了,咱就不在此赘述了,直接给出结果(把它转换到<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">IEEE 754 Single precision 32-bit</code>,也就<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">float</code>类型对应的精度)</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background-color: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-top-left-radius: 0px;border-top-right-radius: 0px;border-bottom-right-radius: 0px;border-bottom-left-radius: 0px;font-size: 12px;background-position: initial initial;background-repeat: initial initial;">1.0(十进制)<br> ↓<br>00111111 10000000 00000000 00000000(二进制)<br> ↓<br>0x3F800000(十六进制)<br></code></pre> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background-color: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-top-left-radius: 0px;border-top-right-radius: 0px;border-bottom-right-radius: 0px;border-bottom-left-radius: 0px;font-size: 12px;background-position: initial initial;background-repeat: initial initial;">0.99999999(十进制)<br> ↓<br>00111111 10000000 00000000 00000000(二进制)<br> ↓<br>0x3F800000(十六进制)<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">果不其然</strong>,这两个十进制浮点数的底层二进制表示是一毛一样的,怪不得<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">==</code>的判断结果返回<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">true</code>!</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">但是<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">1f == 0.9999999f</code>返回的结果是符合预期的,打印<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">false</code>,我们也把它们转换到二进制模式下看看情况:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background-color: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-top-left-radius: 0px;border-top-right-radius: 0px;border-bottom-right-radius: 0px;border-bottom-left-radius: 0px;font-size: 12px;background-position: initial initial;background-repeat: initial initial;">1.0(十进制)<br> ↓<br>00111111 10000000 00000000 00000000(二进制)<br> ↓<br>0x3F800000(十六进制)<br></code></pre> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background-color: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-top-left-radius: 0px;border-top-right-radius: 0px;border-bottom-right-radius: 0px;border-bottom-left-radius: 0px;font-size: 12px;background-position: initial initial;background-repeat: initial initial;">0.9999999(十进制)<br> ↓<br>00111111 01111111 11111111 11111110(二进制)<br> ↓<br>0x3F7FFFFE(十六进制)<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">哦,很明显,它俩的二进制数字表示确实不一样,这是理所应当的结果。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">那么为什么<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">0.99999999</code>的底层二进制表示竟然是:<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">00111111 10000000 00000000 00000000</code>呢?</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">这不明明是浮点数<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">1.0</code>的二进制表示吗?</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">这就要谈一下浮点数的精度问题了。</p> <hr data-tool="mdnice编辑器" style="height: 1px;border-style: solid;border-color: rgb(53, 179, 120);margin: 1.5em auto;"> <h2 data-tool="mdnice编辑器" style="line-height: 32px;color: rgb(53, 179, 120);display: inline-block;border-bottom: 0px solid rgb(53, 179, 120);border-top-color: rgb(53, 179, 120);border-right-color: rgb(53, 179, 120);border-left-color: rgb(53, 179, 120);font-size: 23px;margin-top: 1em;margin-bottom: 0rem;padding-top: 0.5em;padding-bottom: 0.5em;font-weight: bold;">浮点数的精度问题!</h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">学过&nbsp;<strong style="color: rgb(53, 179, 120);">《计算机组成原理》</strong>&nbsp;这门课的小伙伴应该都知道,浮点数在计算机中的存储方式遵循<strong style="color: rgb(53, 179, 120);">IEEE 754 浮点数计数标准</strong>,可以用科学计数法表示为:</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><span style="cursor: pointer;"><span role="presentation" data-formula=" 1.M...×2^E " data-formula-type="inline-equation" style=""> <svg xmlns="http://www.w3.org/2000/svg" role="img" focusable="false" viewbox="0 -843.8 5031.2 843.8" aria-hidden="true" style="vertical-align: 0px;width: 11.383ex;height: 1.909ex;"> <g stroke="currentColor" fill="currentColor" stroke-width="0" transform="matrix(1 0 0 -1 0 0)"> <g data-mml-node="math"> <g data-mml-node="mn"> <path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path> <path data-c="2E" d="M78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z" transform="translate(500, 0)"></path> </g> <g data-mml-node="mi" transform="translate(778, 0)"> <path data-c="4D" d="M289 629Q289 635 232 637Q208 637 201 638T194 648Q194 649 196 659Q197 662 198 666T199 671T201 676T203 679T207 681T212 683T220 683T232 684Q238 684 262 684T307 683Q386 683 398 683T414 678Q415 674 451 396L487 117L510 154Q534 190 574 254T662 394Q837 673 839 675Q840 676 842 678T846 681L852 683H948Q965 683 988 683T1017 684Q1051 684 1051 673Q1051 668 1048 656T1045 643Q1041 637 1008 637Q968 636 957 634T939 623Q936 618 867 340T797 59Q797 55 798 54T805 50T822 48T855 46H886Q892 37 892 35Q892 19 885 5Q880 0 869 0Q864 0 828 1T736 2Q675 2 644 2T609 1Q592 1 592 11Q592 13 594 25Q598 41 602 43T625 46Q652 46 685 49Q699 52 704 61Q706 65 742 207T813 490T848 631L654 322Q458 10 453 5Q451 4 449 3Q444 0 433 0Q418 0 415 7Q413 11 374 317L335 624L267 354Q200 88 200 79Q206 46 272 46H282Q288 41 289 37T286 19Q282 3 278 1Q274 0 267 0Q265 0 255 0T221 1T157 2Q127 2 95 1T58 0Q43 0 39 2T35 11Q35 13 38 25T43 40Q45 46 65 46Q135 46 154 86Q158 92 223 354T289 629Z"></path> </g> <g data-mml-node="mo" transform="translate(1829, 0)"> <path data-c="2E" d="M78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z"></path> </g> <g data-mml-node="mo" transform="translate(2273.7, 0)"> <path data-c="2E" d="M78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z"></path> </g> <g data-mml-node="mo" transform="translate(2718.3, 0)"> <path data-c="2E" d="M78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z"></path> </g> <g data-mml-node="mo" transform="translate(3163, 0)"> <path data-c="D7" d="M630 29Q630 9 609 9Q604 9 587 25T493 118L389 222L284 117Q178 13 175 11Q171 9 168 9Q160 9 154 15T147 29Q147 36 161 51T255 146L359 250L255 354Q174 435 161 449T147 471Q147 480 153 485T168 490Q173 490 175 489Q178 487 284 383L389 278L493 382Q570 459 587 475T609 491Q630 491 630 471Q630 464 620 453T522 355L418 250L522 145Q606 61 618 48T630 29Z"></path> </g> <g data-mml-node="msup" transform="translate(3941, 0)"> <g data-mml-node="mn"> <path data-c="32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z"></path> </g> <g data-mml-node="mi" transform="translate(500, 363) scale(0.707)"> <path data-c="45" d="M492 213Q472 213 472 226Q472 230 477 250T482 285Q482 316 461 323T364 330H312Q311 328 277 192T243 52Q243 48 254 48T334 46Q428 46 458 48T518 61Q567 77 599 117T670 248Q680 270 683 272Q690 274 698 274Q718 274 718 261Q613 7 608 2Q605 0 322 0H133Q31 0 31 11Q31 13 34 25Q38 41 42 43T65 46Q92 46 125 49Q139 52 144 61Q146 66 215 342T285 622Q285 629 281 629Q273 632 228 634H197Q191 640 191 642T193 659Q197 676 203 680H757Q764 676 764 669Q764 664 751 557T737 447Q735 440 717 440H705Q698 445 698 453L701 476Q704 500 704 528Q704 558 697 578T678 609T643 625T596 632T532 634H485Q397 633 392 631Q388 629 386 622Q385 619 355 499T324 377Q347 376 372 376H398Q464 376 489 391T534 472Q538 488 540 490T557 493Q562 493 565 493T570 492T572 491T574 487T577 483L544 351Q511 218 508 216Q505 213 492 213Z"></path> </g> </g> </g> </g> </svg></span></span></p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">只要给出:<strong style="color: rgb(53, 179, 120);">符号(S)</strong>、<strong style="color: rgb(53, 179, 120);">阶码部分(E)</strong>、<strong style="color: rgb(53, 179, 120);">尾数部分(M)</strong>&nbsp;这三个维度的信息,一个浮点数的表示就完全确定下来了,所以<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">float</code>和<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">double</code>这两种浮点数在内存中的存储结构如下所示:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.23749119097956306" src="/upload/522d133b6772da5b3ca27c7aea4d4a71.png" data-type="png" data-w="2838" style="display: block;margin-right: auto;margin-left: auto;width: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.21780821917808219" src="/upload/94b6ff3972030d6030d08406b44d2434.png" data-type="png" data-w="2920" style="display: block;margin-right: auto;margin-left: auto;width: auto;"> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">1、符号部分(S)</strong></p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">0</code>-正&nbsp;&nbsp;<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">1</code>-负</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">2、阶码部分(E)(指数部分)</strong>:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 对于 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">float</code>型浮点数,指数部分 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">8</code>位,考虑可正可负,因此可以表示的指数范围为 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">-127 ~ 128</code> </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 对于 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">double</code>型浮点数,指数部分 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">11</code>位,考虑可正可负,因此可以表示的指数范围为 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">-1023 ~ 1024</code> </section></li> </ul> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">3、尾数部分(M)</strong>:</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">浮点数的精度是由尾数的位数来决定的:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 对于 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">float</code>型浮点数,尾数部分 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">23</code>位,换算成十进制就是&nbsp; <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">2^23=8388608</code>,所以十进制精度只有 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">6 ~ 7</code>位; </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 对于 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">double</code>型浮点数,尾数部分 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">52</code>位,换算成十进制就是&nbsp; <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">2^52 = 4503599627370496</code>,所以十进制精度只有 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">15 ~ 16</code>位 </section></li> </ul> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">所以对于上面的数值<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">0.99999999f</code>,很明显已经超过了<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">float</code>型浮点数据的精度范围,出问题也是在所难免的。</p> <hr data-tool="mdnice编辑器" style="height: 1px;border-style: solid;border-color: rgb(53, 179, 120);margin: 1.5em auto;"> <h2 data-tool="mdnice编辑器" style="line-height: 32px;color: rgb(53, 179, 120);display: inline-block;border-bottom: 0px solid rgb(53, 179, 120);border-top-color: rgb(53, 179, 120);border-right-color: rgb(53, 179, 120);border-left-color: rgb(53, 179, 120);font-size: 23px;margin-top: 1em;margin-bottom: 0rem;padding-top: 0.5em;padding-bottom: 0.5em;font-weight: bold;">精度问题如何解决</h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">所以如果涉及<strong style="color: rgb(53, 179, 120);">商品金额</strong>、<strong style="color: rgb(53, 179, 120);">交易值</strong>、<strong style="color: rgb(53, 179, 120);">货币计算</strong>等这种对精度要求很高的场景该怎么办呢?</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">方法一:用字符串或者数组解决多位数问题</strong></p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">校招刷过算法题的小伙伴们应该都知道,用字符串或者数组表示大数是一个典型的解题思路。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">比如经典面试题:<strong style="color: rgb(53, 179, 120);">编写两个任意位数大数的加法、减法、乘法等运算</strong>。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">这时候我们我们可以用字符串或者数组来表示这种大数,然后按照四则运算的规则来手动模拟出具体计算过程,中间还需要考虑各种诸如:<strong style="color: rgb(53, 179, 120);">进位</strong>、<strong style="color: rgb(53, 179, 120);">借位</strong>、<strong style="color: rgb(53, 179, 120);">符号</strong>等等问题的处理,确实十分复杂,本文不做赘述。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">方法二:Java的大数类是个好东西</strong></p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">JDK早已为我们考虑到了浮点数的计算精度问题,因此提供了专用于高精度数值计算的<strong style="color: rgb(53, 179, 120);">大数类</strong>来方便我们使用。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">在前文<a href="https://mp.weixin.qq.com/s?__biz=MzU4ODI1MjA3NQ==&amp;mid=2247485421&amp;idx=1&amp;sn=c4543020e3d347267dbd8491ee48d2d5&amp;scene=21#wechat_redirect" style="overflow-wrap: break-word;font-weight: bold;color: rgb(53, 179, 120);border-bottom: 1px solid rgb(53, 179, 120);" data-linktype="2">《不瞒你说,我最近跟Java源码杠上了》</a>中说过,Java的大数类位于<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">java.math</code>包下:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="1.1428571428571428" src="/upload/c05ba76399906763d03f5f84479bf2d.png" data-type="png" data-w="826" style="display: block;margin-right: auto;margin-left: auto;width: auto;"> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">可以看到,常用的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">BigInteger</code>&nbsp;和&nbsp;<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">BigDecimal</code>就是处理高精度数值计算的利器。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background-color: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-top-left-radius: 0px;border-top-right-radius: 0px;border-bottom-right-radius: 0px;border-bottom-left-radius: 0px;font-size: 12px;background-position: initial initial;background-repeat: initial initial;">BigDecimal&nbsp;num3&nbsp;=&nbsp;<span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">new</span>&nbsp;BigDecimal(&nbsp;Double.toString(&nbsp;<span style="line-height: 26px;">1.0f</span>&nbsp;)&nbsp;);<br>BigDecimal&nbsp;num4&nbsp;=&nbsp;<span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">new</span>&nbsp;BigDecimal(&nbsp;Double.toString(&nbsp;<span style="line-height: 26px;">0.99999999f</span>&nbsp;)&nbsp;);<br>System.out.println(&nbsp;num3&nbsp;==&nbsp;num4&nbsp;);&nbsp;&nbsp;<span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;打印&nbsp;false</span><br><br>BigDecimal&nbsp;num1&nbsp;=&nbsp;<span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">new</span>&nbsp;BigDecimal(&nbsp;Double.toString(&nbsp;<span style="line-height: 26px;">0.2</span>&nbsp;)&nbsp;);<br>BigDecimal&nbsp;num2&nbsp;=&nbsp;<span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">new</span>&nbsp;BigDecimal(&nbsp;Double.toString(&nbsp;<span style="line-height: 26px;">0.7</span>&nbsp;)&nbsp;);<br><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;加</span><br>System.out.println(&nbsp;num1.add(&nbsp;num2&nbsp;)&nbsp;);&nbsp;&nbsp;<span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;打印:0.9</span><br><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;减</span><br>System.out.println(&nbsp;num2.subtract(&nbsp;num1&nbsp;)&nbsp;);&nbsp;&nbsp;<span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;打印:0.5</span><br><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;乘</span><br>System.out.println(&nbsp;num1.multiply(&nbsp;num2&nbsp;)&nbsp;);&nbsp;&nbsp;<span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;打印:0.14</span><br><br><span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;除</span><br>System.out.println(&nbsp;num2.divide(&nbsp;num1&nbsp;)&nbsp;);&nbsp;&nbsp;<span style="color: rgb(117, 113, 94);line-height: 26px;">//&nbsp;打印:3.5</span><br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">当然了,像<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">BigInteger</code>&nbsp;和&nbsp;<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">BigDecimal</code>这种大数类的运算效率肯定是不如原生类型效率高,代价还是比较昂贵的,是否选用需要根据实际场景来评估。</p> <hr data-tool="mdnice编辑器" style="height: 1px;border-style: solid;border-color: rgb(53, 179, 120);margin: 1.5em auto;"> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;margin: 1em 4px;line-height: normal;"> 每天进步一点点,Peace!&nbsp; </section> <section style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;margin: 1em 4px;line-height: normal;"> 2020.04.09晚 </section> <section class="js_darkmode__84" data-tools="135编辑器" data-id="94301" data-darkmode-color="rgb(168, 168, 168)" data-style="max-width: 100%; letter-spacing: 0.544px; color: rgb(62, 62, 62); font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif; font-size: 16px; widows: 1; text-align: right; box-sizing: border-box !important; overflow-wrap: break-word !important;" style="max-width: 100%;color: rgb(62, 62, 62);letter-spacing: 0.544px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 16px;widows: 1;text-align: right;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section data-darkmode-color="rgb(168, 168, 168)" style="max-width: 100%;display: inline-block;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section powered-by="xiumi.us" data-darkmode-color="rgb(168, 168, 168)" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section data-darkmode-color="rgb(168, 168, 168)" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section data-darkmode-color="rgb(168, 168, 168)" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <pre data-darkmode-color="rgb(168, 168, 168)" style="max-width: 100%;font-size: inherit;color: inherit;line-height: inherit;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section data-tools="135编辑器" data-id="94250" data-darkmode-color="rgb(168, 168, 168)" style="max-width: 100%;letter-spacing: 0.544px;white-space: normal;border-width: 0px;border-style: none;border-color: initial;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section data-tools="135编辑器" data-id="91842" data-darkmode-color="rgb(168, 168, 168)" style="max-width: 100%;border-width: 0px;border-style: none;border-color: initial;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section data-darkmode-color="rgb(168, 168, 168)" style="max-width: 100%;width: auto;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section data-darkmode-color="rgb(168, 168, 168)" style="max-width: 100%;display: inline-block;clear: both;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section class="js_darkmode__85" data-brushtype="text" data-darkmode-color="rgb(168, 168, 168)" data-darkmode-bgimage="1" data-style="padding: 18px 15px 20px 10px; max-width: 100%; color: rgb(86, 146, 214); text-align: center; letter-spacing: 1.5px; background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/Pn4Sm0RsAuhpplm16ibb8iaib7RoGQ5iaHEdy66AHd7QqL7A2s5icSBE0aw4iaKOKPnXGYxQPhG7VMpbbYV6VJprSh7w/640?wx_fmt=png&quot;); background-size: 100% 100%; background-repeat: no-repeat; box-sizing: border-box !important; overflow-wrap: break-word !important;" style="padding: 18px 15px 20px 10px;max-width: 100%;background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/Pn4Sm0RsAuhpplm16ibb8iaib7RoGQ5iaHEdy66AHd7QqL7A2s5icSBE0aw4iaKOKPnXGYxQPhG7VMpbbYV6VJprSh7w/640?wx_fmt=png&quot;);color: rgb(86, 146, 214);text-align: center;letter-spacing: 1.5px;background-size: 100% 100%;background-repeat: no-repeat;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section data-darkmode-color="rgb(168, 168, 168)" data-darkmode-bgimage="1" style="max-width: 100%;display: flex;justify-content: center;align-items: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section data-darkmode-color="rgb(168, 168, 168)" data-darkmode-bgimage="1" style="margin-left: 2px;max-width: 100%;width: 20px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <img data-ratio="0.8936170212765957" data-type="png" data-w="47" src="/upload/737696b8734b6f688ae95660c4411917.png" style="margin-bottom: -6px;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 20px !important;"> </section> <section data-brushtype="text" data-darkmode-color="rgb(168, 168, 168)" data-darkmode-bgimage="1" style="max-width: 100%;font-size: 14px;color: rgb(51, 51, 51);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span data-darkmode-color="rgb(168, 168, 168)" data-darkmode-bgimage="1" style="max-width: 100%;font-family: 楷体, 楷体_GB2312, SimKai;box-sizing: border-box !important;overflow-wrap: break-word !important;">给个[<strong data-darkmode-color="rgb(168, 168, 168)" data-darkmode-bgimage="1" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">在看</strong>],是对程序羊最大的支持</span> </section> <p data-darkmode-color="rgb(168, 168, 168)" data-darkmode-bgimage="1" style="max-width: 100%;min-height: 1em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br data-darkmode-color="rgb(168, 168, 168)" data-darkmode-bgimage="1" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> </section> </section> </section> </section> </section> </section></pre> </section> </section> </section> </section> </section> </section>

监控神器Prometheus,开箱即用!

作者:微信小助手

<section class="mp_profile_iframe_wrp" data-mpa-powered-by="yiban.io"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzkyNTI5NTQ1NQ==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/wxcY9TH8dPsYAnrjaZktBe0iahF8ic9QkF26cAw8pK6HPR1bfFEImdyJspvkQvQwmnYxP4eEVW60ewVVickcWXnrQ/0?wx_fmt=png" data-nickname="架构文摘" data-alias="ArchDigest" data-signature="每天一篇架构领域重磅好文,涉及一线互联网公司应用架构(高可用、高性能、高稳定)、大数据、机器学习、Java架构等各个热门领域。" data-from="0"></mpprofile> </section> <section powered-by="xmyeditor.com" style="box-sizing: border-box;max-width: 100% !important;"> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="box-sizing: border-box;max-width: 100% !important;"> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" powered-by="xmyeditor.com"> <p style="outline: 0px;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);font-size: 16px;font-weight: bold;text-align: right;visibility: visible;line-height: 1.5em;margin-top: 1em;"><span style="outline: 0px;font-size: 13px;color: rgb(136, 136, 136);visibility: visible;font-family: Optima-Regular, PingFangTC-light;">文章来源:【公众号:云加社区】</span><span style="font-family: Optima-Regular, PingFangTC-light;outline: 0px;font-size: 13px;color: rgb(136, 136, 136);visibility: visible;display: none;line-height: 0px;">‍<br></span></p> <section style="outline: 0px;color: rgb(34, 34, 34);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);font-size: 16px;font-weight: bold;text-align: right;visibility: visible;line-height: 1.5em;box-sizing: border-box;max-width: 100% !important;"> <br> </section> <section style="text-align: left;margin-bottom: 20px;box-sizing: border-box;max-width: 100% !important;"> <span style="font-family: Optima-Regular, PingFangTC-light;"><strong><span style="outline: 0px;font-size: 15px;visibility: visible;">目录</span></strong></span> </section> <ul class="list-paddingleft-1"> <li><p><span style="font-size: 15px;letter-spacing: 2px;font-family: Optima-Regular, PingFangTC-light;">简介</span></p></li> <li><p><span style="outline: 0px;letter-spacing: 2px;visibility: visible;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;">整体生态</span></p></li> <li><p><span style="outline: 0px;letter-spacing: 2px;visibility: visible;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;">工作原理</span></p></li> <li><p><span style="outline: 0px;letter-spacing: 2px;visibility: visible;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;">Metric 指标</span></p></li> <li><p><span style="outline: 0px;letter-spacing: 2px;visibility: visible;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;">PromQL</span></p></li> <li><p><span style="outline: 0px;letter-spacing: 2px;visibility: visible;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;">Grafana 可视化</span></p></li> <li><p><span style="outline: 0px;letter-spacing: 2px;visibility: visible;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;">监控告警</span></p></li> </ul> <p><br></p> <section style="box-sizing: border-box;font-style: normal;font-weight: 400;text-align: justify;font-size: 16px;"> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="text-align: center;justify-content: center;margin: 10px 0%;display: flex;flex-flow: row nowrap;box-sizing: border-box;"> <section style="display: inline-block;width: auto;vertical-align: top;min-width: 10%;max-width: 100%;flex: 0 0 auto;height: auto;border-bottom: 1px solid rgb(126, 169, 195);border-bottom-right-radius: 0px;line-height: 0;align-self: flex-start;box-sizing: border-box;"> <section style="margin: 0px 0%;box-sizing: border-box;" powered-by="xiumi.us"> <section style="letter-spacing: 0px;line-height: 1.8;font-size: 15px;color: rgb(126, 169, 195);padding: 0px 5px;box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong style="box-sizing: border-box;">简介</strong></p> </section> </section> <section style="margin: 0px 0% -3px;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;width: 5px;height: 5px;vertical-align: top;overflow: hidden;border-width: 0px;border-radius: 202px;border-style: none;border-color: rgb(62, 62, 62);background-color: rgb(126, 169, 195);box-sizing: border-box;"> <section> <svg viewbox="0 0 1 1" style="float:left;line-height:0;width:0;vertical-align:top;"></svg> </section> </section> </section> </section> </section> </section> </section> <p><br></p> <p style="margin: 0px;padding: 0px;clear: both;min-height: 1em;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(51, 51, 51);font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: justify;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;"><span style="margin: 0px;padding: 0px;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;font-size: 15px;">Prometheus 是一个开源的完整监控解决方案,本文将从指标抓取到查询及可视化展示,以及最后的监控告警,对 Prometheus 做一个基本的认识。</span></p> <p style="margin: 0px;padding: 0px;clear: both;min-height: 1em;max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;color: rgb(51, 51, 51);font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: justify;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;"><br></p> <p style="margin: 0px 0px 8px;padding: 0px;clear: both;min-height: 1em;max-width: 100%;color: rgb(51, 51, 51);font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: justify;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;overflow-wrap: break-word !important;box-sizing: bor

Spring Boot+gRPC构建微服务并部署到Istio(详细教程)

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style=""> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">作为Service Mesh和云原生技术的忠实拥护者,我却一直没有开发过Service Mesh的应用。正好最近受够了Spring Cloud的“折磨”,对Kubernetes也可以熟练使用了,而且网上几乎没有Spring Boot微服务部署到Istio的案例,我就开始考虑用Spring Boot写个微服务的Demo并且部署到Istio。项目本身不复杂,就是发送一个字符串并且返回一个字符串的最简单的Demo。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;border-left: 3px solid rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 26px;">题外话:我本来是想用Spring MVC写的——因为周围有的同学不相信Spring MVC也可以开发微服务,但是Spring MVC的各种配置和依赖问题把我整的想吐,为了少掉几根头发,还是用了方便好用的Spring Boot。</p> </blockquote> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span>为什么要用Istio?<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">目前,对于Java技术栈来说,构建微服务的最佳选择是Spring Boot而Spring Boot一般搭配目前落地案例很多的微服务框架Spring Cloud来使用。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">Spring Cloud看似很完美,但是在实际上手开发后,很容易就会发现Spring Cloud存在以下比较严重的问题:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 服务治理相关的逻辑存在于Spring Cloud Netflix等SDK中,与业务代码紧密耦合。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> SDK对业务代码侵入太大,SDK发生升级且无法向下兼容时,业务代码必须做出改变以适配SDK的升级——即使业务逻辑并没有发生任何变化。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 各种组件令人眼花缭乱,质量也参差不齐,学习成本太高,且组件之间代码很难完全复用,仅仅为了实现治理逻辑而学习SDK也并不是很好的选择。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 绑定于Java技术栈,虽然可以接入其他语言但要手动实现服务治理相关的逻辑,不符合微服务“可以用多种语言进行开发”的原则。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> Spring Cloud仅仅是一个开发框架,没有实现微服务所必须的服务调度、资源分配等功能,这些需求要借助Kubernetes等平台来完成。但Spring Cloud与Kubernetes功能上有重合,且部分功能也存在冲突,二者很难完美配合。 </section></li> </ul> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;">替代Spring Cloud的选择有没有呢?有!它就是Istio。</strong></p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">Istio彻底把治理逻辑从业务代码中剥离出来,成为了独立的进程(Sidecar)。部署时两者部署在一起,在一个Pod里共同运行,业务代码完全感知不到Sidecar的存在。这就实现了治理逻辑对业务代码的零侵入——实际上不仅是代码没有侵入,在运行时两者也没有任何的耦合。这使得不同的微服务完全可以使用不同语言、不同技术栈来开发,也不用担心服务治理问题,可以说这是一种很优雅的解决方案了。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">所以,“为什么要使用Istio”这个问题也就迎刃而解了——因为Istio解决了传统微服务诸如业务逻辑与服务治理逻辑耦合、不能很好地实现跨语言等痛点,而且非常容易使用。只要会用Kubernetes,学习Istio的使用一点都不困难。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span>为什么要使用gRPC作为通信框架?<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">在微服务架构中,服务之间的通信是一个比较大的问题,一般采用RPC或者RESTful API来实现。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">Spring Boot可以使用RestTemplate调用远程服务,但这种方式不直观,代码也比较复杂,进行跨语言通信也是个比较大的问题;而gRPC相比Dubbo等常见的Java RPC框架更加轻量,使用起来也很方便,代码可读性高,并且与Istio和Kubernetes可以很好地进行整合,在Protobuf和HTTP2的加持下性能也还不错,所以这次选择了gRPC来解决Spring Boot微服务间通信的问题。并且,虽然gRPC没有服务发现、负载均衡等能力,但是Istio在这方面就非常强大,两者形成了完美的互补关系。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">由于考虑到各种<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;color: #1e6bb8;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;">grpc-spring-boot-starter</code>可能会对Spring Boot与Istio的整合产生不可知的副作用,所以这一次我没有用任何的<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;color: #1e6bb8;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;">grpc-spring-boot-starter</code>,而是直接手写了gRPC与Spring Boot的整合。不想借助第三方框架整合gRPC和Spring Boot的可以简单参考一下我的实现。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span>编写业务代码<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">首先使用<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;color: #1e6bb8;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;">Spring Initializr</code>建立父级项目<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;color: #1e6bb8;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;">spring-boot-istio</code>,并引入gRPC的依赖。pom文件如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;">&lt;?xml&nbsp;version="1.0"&nbsp;encoding="UTF-8"?&gt;</span><br><span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">project</span>&nbsp;<span style="line-height: 26px;">xmlns</span>=<span style="color: #a6e22e;line-height: 26px;">"http://maven.apache.org/POM/4.0.0"</span>&nbsp;<span style="line-height: 26px;">xmlns:xsi</span>=<span style="color: #a6e22e;line-height: 26px;">"http://www.w3.org/2001/XMLSchema-instance"</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;">xsi:schemaLocation</span>=<span style="color: #a6e22e;line-height: 26px;">"http://maven.apache.org/POM/4.0.0&nbsp;https://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">modelVersion</span>&gt;</span>4.0.0<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">modelVersion</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">modules</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">module</span>&gt;</span>spring-boot-istio-api<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">module</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">module</span>&gt;</span>spring-boot-istio-server<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">module</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">module</span>&gt;</span>spring-boot-istio-client<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">module</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">modules</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">parent</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">groupId</span>&gt;</span>org.springframework.boot<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">groupId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">artifactId</span>&gt;</span>spring-boot-starter-parent<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">artifactId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">version</span>&gt;</span>2.2.6.RELEASE<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">version</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">relativePath</span>/&gt;</span>&nbsp;<span style="color: #75715e;line-height: 26px;">&lt;!--&nbsp;lookup&nbsp;parent&nbsp;from&nbsp;repository&nbsp;--&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">parent</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">groupId</span>&gt;</span>site.wendev<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">groupId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">artifactId</span>&gt;</span>spring-boot-istio<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">artifactId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">version</span>&gt;</span>0.0.1-SNAPSHOT<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">version</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">name</span>&gt;</span>spring-boot-istio<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">name</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">description</span>&gt;</span>Demo&nbsp;project&nbsp;for&nbsp;Spring&nbsp;Boot&nbsp;With&nbsp;Istio.<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">description</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">packaging</span>&gt;</span>pom<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">packaging</span>&gt;</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">properties</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">java.version</span>&gt;</span>1.8<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">java.version</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">properties</span>&gt;</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">dependencyManagement</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">dependencies</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">dependency</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">groupId</span>&gt;</span>io.grpc<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">groupId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">artifactId</span>&gt;</span>grpc-all<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">artifactId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">version</span>&gt;</span>1.28.1<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">version</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">dependency</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">dependencies</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">dependencyManagement</span>&gt;</span><br><span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">project</span>&gt;</span><br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">然后建立公共依赖模块<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;color: #1e6bb8;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;">spring-boot-istio-api</code>,pom文件如下,主要就是gRPC的一些依赖:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;">&lt;?xml&nbsp;version="1.0"&nbsp;encoding="UTF-8"?&gt;</span><br><span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">project</span>&nbsp;<span style="line-height: 26px;">xmlns</span>=<span style="color: #a6e22e;line-height: 26px;">"http://maven.apache.org/POM/4.0.0"</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;">xmlns:xsi</span>=<span style="color: #a6e22e;line-height: 26px;">"http://www.w3.org/2001/XMLSchema-instance"</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;">xsi:schemaLocation</span>=<span style="color: #a6e22e;line-height: 26px;">"http://maven.apache.org/POM/4.0.0&nbsp;http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">parent</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">artifactId</span>&gt;</span>spring-boot-istio<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">artifactId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">groupId</span>&gt;</span>site.wendev<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">groupId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">version</span>&gt;</span>0.0.1-SNAPSHOT<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">version</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">parent</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">modelVersion</span>&gt;</span>4.0.0<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">modelVersion</span>&gt;</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">artifactId</span>&gt;</span>spring-boot-istio-api<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">artifactId</span>&gt;</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">dependencies</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">dependency</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">groupId</span>&gt;</span>io.grpc<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">groupId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">artifactId</span>&gt;</span>grpc-all<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">artifactId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">dependency</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">dependency</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">groupId</span>&gt;</span>javax.annotation<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">groupId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">artifactId</span>&gt;</span>javax.annotation-api<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">artifactId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">version</span>&gt;</span>1.3.2<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">version</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">dependency</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">dependencies</span>&gt;</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">build</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">extensions</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">extension</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">groupId</span>&gt;</span>kr.motd.maven<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">groupId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">artifactId</span>&gt;</span>os-maven-plugin<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">artifactId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">version</span>&gt;</span>1.6.2<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">version</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">extension</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">extensions</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">plugins</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">plugin</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">groupId</span>&gt;</span>org.xolstice.maven.plugins<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">groupId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">artifactId</span>&gt;</span>protobuf-maven-plugin<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">artifactId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">version</span>&gt;</span>0.6.1<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">version</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">configuration</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">protocArtifact</span>&gt;</span>com.google.protobuf:protoc:3.11.3:exe:${os.detected.classifier}<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">protocArtifact</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">pluginId</span>&gt;</span>grpc-java<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">pluginId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">pluginArtifact</span>&gt;</span>io.grpc:protoc-gen-grpc-java:1.28.1:exe:${os.detected.classifier}<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">pluginArtifact</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">protocExecutable</span>&gt;</span>/Users/jiangwen/tools/protoc-3.11.3/bin/protoc<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">protocExecutable</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">configuration</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">executions</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">execution</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">goals</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">goal</span>&gt;</span>compile<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">goal</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="color: #f92672;line-height: 26px;">goal</span>&gt;</span>compile-custom<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">goal</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">goals</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">execution</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">executions</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">plugin</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">plugins</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">build</span>&gt;</span><br><span style="color: #f92672;line-height: 26px;">&lt;/<span style="color: #f92672;line-height: 26px;">project</span>&gt;</span><br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">建立<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;color: #1e6bb8;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;">src/main/proto</code>文件夹,在此文件夹下建立<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;color: #1e6bb8;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;">hello.proto</code>,定义服务间的接口如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;">syntax&nbsp;=&nbsp;<span style="color: #a6e22e;line-height: 26px;">"proto3"</span>;<br><br>option&nbsp;java_package&nbsp;=&nbsp;<span style="color: #a6e22e;line-height: 26px;">"site.wendev.spring.boot.istio.api"</span>;<br>option&nbsp;java_outer_classname&nbsp;=&nbsp;<span style="color: #a6e22e;line-height: 26px;">"HelloWorldService"</span>;<br><br>package&nbsp;helloworld;<br><br>service&nbsp;HelloWorld&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;rpc&nbsp;SayHello&nbsp;(HelloRequest)&nbsp;returns&nbsp;(HelloResponse)&nbsp;{}<br>}<br><br>message&nbsp;HelloRequest&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;name&nbsp;=&nbsp;1;<br>}<br><br>message&nbsp;HelloResponse&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;message&nbsp;=&nbsp;1;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">很简单,就是发送一个name返回一个带name的message。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">然后生成服务端和客户端的代码,并且放到java文件夹下。这部分内容可以参考gRPC的官方文档。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">有了API模块之后,就可以编写服务提供者(服务端)和服务消费者(客户端)了。这里我们重点看一下如何整合gRPC和Spring Boot。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span>服务端<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">业务代码非常简单:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;">/**<br>&nbsp;*&nbsp;服务端业务逻辑实现<br>&nbsp;*<br>&nbsp;*&nbsp;<span style="font-weight: bold;line-height: 26px;">@author</span>&nbsp;江文<br>&nbsp;*/</span><br><span style="color: #75715e;line-height: 26px;">@Slf</span>4j<br><span style="color: #75715e;line-height: 26px;">@Component</span><br><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">class</span>&nbsp;<span style="font-weight: bold;color: white;line-height: 26px;">HelloServiceImpl</span>&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">extends</span>&nbsp;<span style="font-weight: bold;color: white;line-height: 26px;">HelloWorldGrpc</span>.<span style="font-weight: bold;color: white;line-height: 26px;">HelloWorldImplBase</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #75715e;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span>&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">void</span>&nbsp;<span style="color: #a6e22e;font-weight: bold;line-height: 26px;">sayHello</span><span style="line-height: 26px;">(HelloWorldService.HelloRequest&nbsp;request,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StreamObserver&lt;HelloWorldService.HelloResponse&gt;&nbsp;responseObserver)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #75715e;line-height: 26px;">//&nbsp;根据请求对象建立响应对象,返回响应信息</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HelloWorldService.HelloResponse&nbsp;response&nbsp;=&nbsp;HelloWorldService.HelloResponse<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.newBuilder()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.setMessage(String.format(<span style="color: #a6e22e;line-height: 26px;">"Hello,&nbsp;%s.&nbsp;This&nbsp;message&nbsp;comes&nbsp;from&nbsp;gRPC."</span>,&nbsp;request.getName()))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.build();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;responseObserver.onNext(response);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;responseObserver.onCompleted();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.info(<span style="color: #a6e22e;line-height: 26px;">"Client Message Received:[{}]"</span>,&nbsp;request.getName());<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;">光有业务代码还不行,我们还需要在应用启动时把<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;color: #1e6bb8;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;">gRPC Server</code>也给一起启动起来。首先写一下Server端的启动、关闭等逻辑:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;">/**<br>&nbsp;*&nbsp;gRPC&nbsp;Server的配置——启动、关闭等<br>&nbsp;*&nbsp;需要使用&lt;code&gt;<span style="font-weight: bold;line-height: 26px;">@Component</span>&lt;/code&gt;注解注册为一个Spring&nbsp;Bean<br>&nbsp;*<br>&nbsp;*&nbsp;<span style="font-weight: bold;line-height: 26px;">@author</span>&nbsp;江文<br>&nbsp;*/</span><br><span style="color: #75715e;line-height: 26px;">@Slf</span>4j<br><span style="color: #75715e;line-height: 26px;">@Component</span><br><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">class</span>&nbsp;<span style="font-weight: bold;color: white;line-height: 26px;">GrpcServerConfiguration</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #75715e;line-height: 26px;">@Autowired</span><br>&nbsp;&nbsp;&nbsp;&nbsp;HelloServiceImpl&nbsp;service;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #75715e;line-height: 26px;">/**&nbsp;注入配置文件中的端口信息&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #75715e;line-height: 26px;">@Value</span>(<span style="color: #a6e22e;line-height: 26px;">"${grpc.server-port}"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">private</span>&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">int</span>&nbsp;port;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">private</span>&nbsp;Server&nbsp;server;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span>&nbsp;<span style="color: #f92672;font-weight: bold;line-heigh

聊聊 分布式配置中心 Apollo

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.6;word-break: break-word;overflow-wrap: break-word;text-align: left;padding: 5px;font-size: 16px;color: rgb(53, 53, 53);word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">今天深入聊一聊携程开源的一款分布式配置中心Apollo,在功能上和Nacos不相上下。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);"><a href="https://mp.weixin.qq.com/s?__biz=MzU3MDAzNDg1MA==&amp;mid=2247511213&amp;idx=1&amp;sn=9635f9d57390269edef798fa66a03e3b&amp;chksm=fcf77360cb80fa76fe5c9abee83c54fb4e06038d555663c07f5c1be7838bc7abdee5417a8e18&amp;token=1267466554&amp;lang=zh_CN&amp;scene=21#wechat_redirect" style="text-decoration: none;word-wrap: break-word;color: rgb(248, 57, 41);font-weight: 400;border-bottom: 1px solid rgb(248, 57, 41);" data-linktype="2">1. 基本概念</a></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">由于 Apollo 概念比较多,刚开始使用比较复杂,最好先过一遍概念再动手实践尝试使用。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;"><a href="https://mp.weixin.qq.com/s?__biz=MzU3MDAzNDg1MA==&amp;mid=2247511213&amp;idx=1&amp;sn=9635f9d57390269edef798fa66a03e3b&amp;chksm=fcf77360cb80fa76fe5c9abee83c54fb4e06038d555663c07f5c1be7838bc7abdee5417a8e18&amp;token=1267466554&amp;lang=zh_CN&amp;scene=21#wechat_redirect" style="text-decoration: none;word-wrap: break-word;color: rgb(248, 57, 41);font-weight: 400;border-bottom: 1px solid rgb(248, 57, 41);" data-linktype="2">1、背景</a></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">随着程序功能的日益复杂,程序的配置日益增多,各种功能的开关、参数的配置、服务器的地址……对程序配置的期望值也越来越高,配置修改后实时生效,灰度发布,分环境、分集群管理配置,完善的权限、审核机制…… 在这样的大环境下,传统的通过配置文件、数据库等方式已经越来越无法满足开发人员对配置管理的需求。因此 Apollo 配置中心应运而生!</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">2、简介</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">Apollo(阿波罗)是携程框架部门研发的开源配置管理中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">3、特点</span><span style="display: none;"></span></h3> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 部署简单 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 灰度发布 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 版本发布管理 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 提供开放平台API </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 客户端配置信息监控 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 提供Java和.Net原生客户端 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 配置修改实时生效(热发布) </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 权限管理、发布审核、操作审计 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 统一管理不同环境、不同集群的配置 </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">4、基础模型</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">如下即是 Apollo 的基础模型:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> (1)、用户在配置中心对配置进行修改并发布 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> (2)、配置中心通知Apollo客户端有配置更新 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> (3)、Apollo客户端从配置中心拉取最新的配置、更新本地配置并通知到应用 </section></li> </ul> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.6329896907216495" src="/upload/8b60dc52f2c172b1f891a1412829827.png" data-type="png" data-w="970" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">5、Apollo 的四个维度</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">Apollo支持4个维度管理Key-Value格式的配置:</strong></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> application (应用) </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> environment (环境) </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> cluster (集群) </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> namespace (命名空间) </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">(1)、application</strong></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> Apollo 客户端在运行时需要知道当前应用是谁,从而可以根据不同的应用来获取对应应用的配置。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 每个应用都需要有唯一的身份标识,可以在代码中配置 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">app.id</code> 参数来标识当前应用,Apollo 会根据此指来辨别当前应用。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">(2)、environment</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">在实际开发中,我们的应用经常要部署在不同的环境中,一般情况下分为开发、测试、生产等等不同环境,不同环境中的配置也是不同的,在 Apollo 中默认提供了四种环境:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> FAT(Feature Acceptance Test):功能测试环境 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> UAT(User Acceptance Test):集成测试环境 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> DEV(Develop):开发环境 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> PRO(Produce):生产环境 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">在程序中如果想指定使用哪个环境,可以配置变量 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">env</code> 的值为对应环境名称即可。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">(3)、cluster</strong></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 一个应用下不同实例的分组,比如典型的可以按照数据中心分,把上海机房的应用实例分为一个集群,把北京机房的应用实例分为另一个集群。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 对不同的集群,同一个配置可以有不一样的值,比如说上面所指的两个北京、上海两个机房设置两个集群,两个集群中都有 mysql 配置参数,其中参数中配置的地址是不一样的。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">(4)、namespace</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">一个应用中不同配置的分组,可以简单地把 namespace 类比为不同的配置文件,不同类型的配置存放在不同的文件中,如数据库配置文件,RPC 配置文件,应用自身的配置文件等。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">熟悉 SpringBoot 的都知道,SpringBoot 项目都有一个默认配置文件 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">application.yml</code>,如果还想用多个配置,可以创建多个配置文件来存放不同的配置信息,通过指定 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">spring.profiles.active</code> 参数指定应用不同的配置文件。这里的 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">namespace</code> 概念与其类似,将不同的配置放到不同的配置 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">namespace</code> 中。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">Namespace 分为两种权限,分别为:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <strong style="font-weight: 700;color: rgb(248, 57, 41);">public(公共的):</strong> public权限的 Namespace,能被任何应用获取。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <strong style="font-weight: 700;color: rgb(248, 57, 41);">private(私有的):</strong> 只能被所属的应用获取到。一个应用尝试获取其它应用 private 的 Namespace,Apollo 会报 "404" 异常。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">Namespace 分为三种类型,分别为:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <strong style="font-weight: 700;color: rgb(248, 57, 41);">私有类型:</strong> 私有类型的 Namespace 具有 private 权限。例如 application Namespace 为私有类型。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <strong style="font-weight: 700;color: rgb(248, 57, 41);">公共类型:</strong> 公共类型的 Namespace 具有 public 权限。公共类型的N amespace 相当于游离于应用之外的配置,且通过 Namespace 的名称去标识公共 Namespace,所以公共的 Namespace 的名称必须全局唯一。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <strong style="font-weight: 700;color: rgb(248, 57, 41);">关联类型(继承类型):</strong> 关联类型又可称为继承类型,关联类型具有 private 权限。关联类型的 Namespace 继承于公共类型的 Namespace,将里面的配置全部继承,并且可以用于覆盖公共 Namespace 的某些配置。 </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">6、本地缓存</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">Apollo客户端会把从服务端获取到的配置在本地文件系统缓存一份,用于在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置,不影响应用正常运行。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">本地缓存路径默认位于以下路径,所以请确保/opt/data或C:\opt\data\目录存在,且应用有读写权限。</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <strong style="font-weight: 700;color: rgb(248, 57, 41);">Mac/Linux:</strong> /opt/data/{appId}/config-cache </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <strong style="font-weight: 700;color: rgb(248, 57, 41);">Windows:</strong> C:\opt\data{appId}\config-cache </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">本地配置文件会以下面的文件名格式放置于本地缓存路径下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oexqmcDw0weKQfou5D5p14lh1GbM2X2JzcqPRQrNwmL00gjsQyVRHD9L7u5zwiaPTRaaB7sdrvwJxF4GL6UU92icqC/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;">{appId}+{cluster}+{namespace}.properties<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">7、客户端设计</span><span style="display: none;"></span></h3> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img src="/upload/35581efe6d32946e8ee178c3985bd28a.jpg" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;" data-type="jpeg" data-ratio="0.48556701030927835" data-w="970"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">上图简要描述了Apollo客户端的实现原理</strong></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">客户端还会定时从 Apollo 配置中心服务端拉取应用的最新配置。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <br> </section></li> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: #f83929;font-size: 16px;list-style-type: square;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 这是一个 fallback 机制,为了防止推送机制失效导致配置不更新 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 客户端定时拉取会上报本地版本,所以一般情况下,对于定时拉取的操作,服务端都会返回 304 - Not Modified </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 定时频率默认为每 5 分钟拉取一次,客户端也可以通过在运行时指定 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(271, 93, 108);">apollo.refreshInterval</code> 来覆盖,单位为分钟。 </section></li> </ul> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">客户端从 Apollo 配置中心服务端获取到应用的最新配置后,会保存在内存中。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">客户端会把从服务端获取到的配置在本地文件系统缓存一份 在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">应用程序从 Apollo 客户端获取最新的配置、订阅配置更新通知。</p> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><a href="https://mp.weixin.qq.com/s?__biz=MzU3MDAzNDg1MA==&amp;mid=2247511213&amp;idx=1&amp;sn=9635f9d57390269edef798fa66a03e3b&amp;chksm=fcf77360cb80fa76fe5c9abee83c54fb4e06038d555663c07f5c1be7838bc7abdee5417a8e18&amp;token=1267466554&amp;lang=zh_CN&amp;scene=21#wechat_redirect" style="text-decoration: none;word-wrap: break-word;color: rgb(248, 57, 41);font-weight: 400;border-bottom: 1px solid rgb(248, 57, 41);" data-linktype="2"><strong style="font-weight: 700;color: rgb(248, 57, 41);">配置更新推送实现</strong></a></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">前面提到了 Apollo 客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。长连接实际上我们是通过 Http Long Polling 实现的,具体而言:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">客户端发起一个 Http 请求到服务端</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">服务端会保持住这个连接 60 秒</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <br> </section></li> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: #f83929;font-size: 16px;list-style-type: square;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 如果在 60 秒内有客户端关心的配置变化,被保持住的客户端请求会立即返回,并告知客户端有配置变化的 namespace 信息,客户端会据此拉取对应 namespace 的最新配置 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 如果在 60 秒内没有客户端关心的配置变化,那么会返回 Http 状态码 304 给客户端 </section></li> </ul> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">客户端在收到服务端请求后会立即重新发起连接,回到第一步</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">考虑到会有数万客户端向服务端发起长连,在服务端我们使用了 async servlet(Spring DeferredResult) 来服务 Http Long Polling 请求。</p> </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">8、总体设计</span><span style="display: none;"></span></h3> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img src="/upload/15d94e37340367e957d93bc1f8554ab7.jpg" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;" data-type="jpeg" data-ratio="0.865979381443299" data-w="970"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">上图简要描述了Apollo的总体设计,我们可以从下往上看:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> Config Service 提供配置的读取、推送等功能,服务对象是 Apollo 客户端 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> Admin Service 提供配置的修改、发布等功能,服务对象是 Apollo Portal(管理界面) </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> Config Service 和 Admin Service 都是多实例、无状态部署,所以需要将自己注册到 Eureka 中并保持心跳 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 在 Eureka 之上我们架了一层 Meta Server 用于封装Eureka的服务发现接口 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> Client 通过域名访问 Meta Server 获取Config Service服务列表(IP+Port),而后直接通过 IP+Port 访问服务,同时在 Client 侧会做 load balance 错误重试 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> Portal 通过域名访问 Meta Server 获取 Admin Service 服务列表(IP+Port),而后直接通过 IP+Port 访问服务,同时在 Portal 侧会做 load balance、错误重试 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 为了简化部署,我们实际上会把 Config Service、Eureka 和 Meta Server 三个逻辑角色部署在同一个 JVM 进程中 </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">9、可用性考虑</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">配置中心作为基础服务,可用性要求非常高,下面的表格描述了不同场景下Apollo的可用性:</p> <section data-tool="mdnice编辑器" style="overflow-x: auto;"> <table> <thead> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <th style="border-width: 1px;borde