文章列表

Linux Device eth0 does not seem to be present 解决办法

作者:じ☆ve宝贝

用ifconfig查看发现缺少eth0,只有lo;用ifconfig -a查看发现多出了eth1的信息。 ## 解决办法1: ``` mv /etc/sysconfig/network-scripts/ifcfg-eth0 /etcsysconfig/network-scripts/ifcfg-eth1 将eth0的mac地址改为eth1的mac地址,同时改变其DEVICE名称为eth1,再重启网络即可。 ``` ## 解决办法2: ``` 查看 /etc/udev/rules.d/70-persistent-net.rules中绑定的 ATTR{address}地址和NAME修改对应的/etc/sysconfig/network-scripts/ifcfg-eth*的HWADDR地址和DEVICE值即可 ``` ## 重启网卡 ``` service network restart ```

互联网架构“高并发”到底怎么玩?

作者:微信小助手

<blockquote class="js_blockquote_wrap" data-type="2" data-source-title="" data-content-utf8-length="46" data-author-name="" data-url=""> <section class="js_blockquote_digest"> <section> <span style="letter-spacing: 1px;">最近留言问“高并发”的朋友颇多,公众号又不支持历史文章检索,故重新优化发布,希望大家有收获。&nbsp;</span> </section> </section> </blockquote> <p><br></p> <p><strong><span style="letter-spacing: 1px;font-size: 15px;">什么是高并发?</span></strong></p> <p><span style="letter-spacing: 1px;font-size: 15px;">高并发</span><span style="letter-spacing: 1px;font-size: 12px;">(High Concurrency)</span><span style="letter-spacing: 1px;font-size: 15px;">是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">通过设计保证系统能够同时并行处理很多请求</span><span style="letter-spacing: 1px;font-size: 15px;">。</span></p> <p><br></p> <p><strong><span style="letter-spacing: 1px;font-size: 15px;">高并发相关的常见指标有哪些?</span></strong></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="letter-spacing: 1px;font-size: 15px;">响应时间</span><span style="letter-spacing: 1px;font-size: 12px;">(Response Time)</span></p></li> <li><p><span style="letter-spacing: 1px;font-size: 15px;">吞吐量</span><span style="letter-spacing: 1px;font-size: 12px;">(Throughput)</span></p></li> <li><p><span style="letter-spacing: 1px;font-size: 15px;">每秒查询率QPS</span><span style="letter-spacing: 1px;font-size: 12px;">(Query Per Second)</span></p></li> <li><p><span style="letter-spacing: 1px;font-size: 15px;">并发用户数</span></p></li> </ul> <p><br></p> <p><strong><span style="letter-spacing: 1px;font-size: 15px;">什么是响应时间?</span></strong></p> <p><span style="letter-spacing: 1px;font-size: 15px;">系统对请求做出响应的时间。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">例如:系统处理一个HTTP请求需要200ms,这个200ms就是系统的响应时间。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><strong><span style="letter-spacing: 1px;font-size: 15px;">什么是吞吐量?</span></strong></p> <p><span style="letter-spacing: 1px;font-size: 15px;">单位时间内处理的请求数量。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><strong><span style="letter-spacing: 1px;font-size: 15px;">什么是QPS?</span></strong></p> <p><span style="letter-spacing: 1px;font-size: 15px;">每秒响应请求数。在互联网领域,这个指标和吞吐量区分的没有这么明显。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><strong><span style="letter-spacing: 1px;font-size: 15px;">什么是并发用户数?</span></strong></p> <p><span style="letter-spacing: 1px;font-size: 15px;">同时承载正常使用系统功能的用户数量。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">例如:一个即时通讯系统,同时在线量一定程度上代表了系统的并发用户数。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">&nbsp;</span></p> <p><strong><span style="letter-spacing: 1px;font-size: 15px;">如何提升系统的并发能力?</span></strong></p> <p><span style="letter-spacing: 1px;font-size: 15px;">互联网分布式架构设计,提高系统并发能力的方式,方法论上主要有两种:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="letter-spacing: 1px;font-size: 15px;">垂直扩展</span><span style="letter-spacing: 1px;font-size: 12px;">(Scale Up)</span></p></li> <li><p><span style="letter-spacing: 1px;font-size: 15px;">水平扩展</span><span style="letter-spacing: 1px;font-size: 12px;">(Scale Out)</span></p></li> </ul> <p><strong><span style="letter-spacing: 1px;font-size: 15px;"><br></span></strong></p> <p><strong><span style="letter-spacing: 1px;font-size: 15px;">什么是垂直扩展?</span></strong></p> <p><span style="letter-spacing: 1px;font-size: 15px;">垂直扩展是指,</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">提升单机处理能力</span><span style="letter-spacing: 1px;font-size: 15px;">,垂直扩展的方式又有两种:</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(1)<strong>增强单机硬件性能</strong>,例如:增加CPU核数如32核,升级更好的网卡如万兆,升级更好的硬盘如SSD,扩充硬盘容量如2T,扩充系统内存如128G;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(2)<strong>提升单机架构性能</strong>,例如:使用Cache来减少IO次数,使用异步来增加单服务吞吐量,使用无锁数据结构来减少响应时间;</span></p> <p><span style="color: rgb(0, 82, 255);"><em><span style="letter-spacing: 1px;font-size: 15px;">画外音:在互联网业务发展非常迅猛的早期,如果预算不是问题,强烈建议使用“增强单机硬件性能”的方式提升系统并发能力,因为这个阶段,公司的战略往往是发展业务抢时间,而“增强单机硬件性能”往往是最快的方法。</span></em></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">&nbsp;</span></p> <p><strong><span style="letter-spacing: 1px;font-size: 15px;">垂直扩展有什么瓶颈?</span></strong></p> <p><span style="letter-spacing: 1px;font-size: 15px;">不管是提升单机硬件性能,还是提升单机架构性能,都有一个致命的<strong>不足</strong>:</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">单机性能总是有极限的</span><span style="letter-spacing: 1px;font-size: 15px;">。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><strong><span style="letter-spacing: 1px;font-size: 15px;">如何突破单机的极限?</span></strong></p> <p><span style="letter-spacing: 1px;font-size: 15px;">互联网分布式架构设计,高并发终极解决方案还是水平扩展。</span></p> <p><br></p> <p><strong><span style="letter-spacing: 1px;font-size: 15px;">什么是水平扩展?</span></strong></p> <p><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">只要增加服务器数量,就能线性扩充系统性能</span><span style="letter-spacing: 1px;font-size: 15px;">。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">&nbsp;</span></p> <p><strong><span style="letter-spacing: 1px;font-size: 15px;">常见的互联网分层架构如何?</span></strong></p> <p><strong><span style="letter-spacing: 1px;font-size: 15px;">各层该如何落地水平扩展?</span></strong><br></p> <p><span style="font-size:3px;"></span><img data-ratio="0.8803827751196173" data-type="png" data-w="418" data-s="300,640" src="/upload/b8b8c82b5fd8014127be532f7974cbb0.png"><br><span style="letter-spacing: 1px;font-size: 15px;">常见互联网分布式架构如上,分为:</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(1)<strong>客户端层</strong>:典型调用方是浏览器browser或者手机应用APP;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(2)<strong>反向代理层</strong>:系统入口,反向代理;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(3)<strong>站点应用层</strong>:实现核心应用逻辑,返回html或者json;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(4)<strong>服务层</strong>:如果实现了服务化,就有这一层;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(5)<strong>数据-缓存层</strong>:缓存加速访问存储;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(6)<strong>数据-数据库层</strong>:数据库固化数据存储;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">要想真个系统支持水平扩展,就必须每一层都支持水平扩展。</span></p> <p><br></p> <p><strong><span style="letter-spacing: 1px;font-size: 15px;">反向代理层如何进行水平扩展?</span></strong></p> <p><img data-ratio="0.265748031496063" data-type="png" data-w="508" data-s="300,640" src="/upload/e8430ab37eb63a1be4e6ff9e47faa96d.png"><br><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">反向代理层的水平扩展,是通过“DNS轮询”实现的</span><span style="letter-spacing: 1px;font-size: 15px;">:dns-server对于一个域名配置了多个解析ip,每次DNS解析请求来访问dns-server,会轮询返回这些ip。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">当nginx成为瓶颈的时候,只要增加服务器数量,新增nginx服务的部署,增加一个外网ip,就能扩展反向代理层的性能,做到理论上的无限高并发。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">&nbsp;</span></p> <p><strong><span style="letter-spacing: 1px;font-size: 15px;">站点层如何进行水平扩展?</span></strong></p> <p><img data-ratio="0.26129032258064516" data-type="png" data-w="310" data-s="300,640" src="/upload/26c64d62173a0e157071c7139ff755db.png"><br><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">站点层的水平扩展,是通过“nginx”实现的</span><span style="letter-spacing: 1px;font-size: 15px;">,通过修改nginx.conf,可以设置多个web后端。</span></p> <p><span style="color: rgb(0, 82, 255);"><em><span style="letter-spacing: 1px;font-size: 15px;">画外音:nginx是个例子,有可能是LVS或者F5等反向代理。</span></em></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">当web后端成为瓶颈的时候,只要增加服务器数量,新增web服务的部署,在nginx配置中配置上新的web后端,就能扩展站点层的性能,做到理论上的无限高并发。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">&nbsp;</span></p> <p><strong><span style="letter-spacing: 1px;font-size: 15px;">服务层如何进行水平扩展?</span></strong></p> <p><img data-ratio="0.6303501945525292" data-type="png" data-w="257" data-s="300,640" src="/upload/8d8678d55e3ea181cb0cfc5c989ec5bc.png"><br><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">服务层的水平扩展,是通过“服务连接池”实现的</span><span style="letter-spacing: 1px;font-size: 15px;">。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">站点层通过RPC-client调用下游的服务层RPC-server时,RPC-client中的连接池会建立与下游服务多个连接,当服务成为瓶颈的时候,只要增加服务器数量,新增服务部署,在RPC-client处建立新的下游服务连接,就能扩展服务层性能,做到理论上的无限高并发。</span></p> <p><span style="color: rgb(0, 82, 255);"><em><span style="letter-spacing: 1px;font-size: 15px;">画外音:如果需要优雅的进行服务层自动扩容,这里可能需要配置中心里服务自动发现功能的支持。</span></em></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">&nbsp;</span></p> <p><strong><span style="letter-spacing: 1px;font-size: 15px;">数据层如何进行水平扩展?</span></strong></p> <p><span style="letter-spacing: 1px;font-size: 15px;">在数据量很大的情况下,数据层(缓存,数据库)涉及数据的水平扩展,将原本存储在一台服务器上的数据(缓存,数据库)水平拆分到不同服务器上去,以达到扩充系统性能的目的。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">&nbsp;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">互联网数据层常见的水平拆分方式有这么几种,以数据库为例:</span></p> <p><strong><span style="letter-spacing: 1px;font-size: 15px;">一、按照范围水平拆分</span></strong></p> <p><img data-ratio="0.3938356164383562" data-type="png" data-w="292" data-s="300,640" src="/upload/9c9fde55df36e91d4b913b4bca48dddf.png"><br><span style="letter-spacing: 1px;font-size: 15px;">每一个数据服务,存储一定范围的数据,上图为例:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="letter-spacing: 1px;font-size: 15px;">user0库,存储uid范围1-1kw</span></p></li> <li><p><span style="letter-spacing: 1px;font-size: 15px;">user1库,存储uid范围1kw-2kw</span></p></li> </ul> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">这个方案的好处是:</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(1)规则简单,service只需判断一下uid范围就能路由到对应的存储服务;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(2)数据均衡性较好;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(3)比较容易扩展,可以随时加一个uid[2kw,3kw]的数据服务;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">不足是:</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(1)请求的负载不一定均衡,一般来说,新注册的用户会比老用户更活跃,大range的服务请求压力会更大;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">&nbsp;</span></p> <p><strong><span style="letter-spacing: 1px;font-size: 15px;">二、按照哈希水平拆分</span></strong></p> <p><img data-ratio="0.4233576642335766" data-type="png" data-w="274" data-s="300,640" src="/upload/6816dc28a5ce59deed81633f724ef61e.png"><br><span style="letter-spacing: 1px;font-size: 15px;">每一个数据库,存储某个key值hash后的部分数据,上图为例:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="letter-spacing: 1px;font-size: 15px;">user0库,存储偶数uid数据</span></p></li> <li><p><span style="letter-spacing: 1px;font-size: 15px;">user1库,存储奇数uid数据</span></p></li> </ul> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">这个方案的好处是:</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(1)规则简单,service只需对uid进行hash能路由到对应的存储服务;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(2)数据均衡性较好;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(3)请求均匀性较好;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">不足是:</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(1)不容易扩展,扩展一个数据服务,hash方法改变时候,可能需要进行数据迁移;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">&nbsp;</span></p> <p><strong><span style="letter-spacing: 1px;font-size: 15px;">通过水平拆分来扩充系统性能,与主从同步读写分离来扩充数据库性能,有什么本质的不同?</span></strong></p> <p><em><span style="color: rgb(0, 82, 255);letter-spacing: 1px;font-size: 15px;">画外音:这两个方案千万别搞混。</span></em></p> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">通过<strong>水平拆分扩展数据库性能</strong>:</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(1)</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">每个服务器上存储的数据量是总量的1/n</span><span style="letter-spacing: 1px;font-size: 15px;">,所以单机的性能也会有提升;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(2)</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">n个服务器上的数据没有交集</span><span style="letter-spacing: 1px;font-size: 15px;">,那个服务器上数据的并集是数据的全集;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(3)数据水平拆分到了n个服务器上,理论上</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">读性能扩充了n倍,写性能也扩充了n倍</span><span style="letter-spacing: 1px;font-size: 15px;">(其实远不止n倍,因为单机的数据量变为了原来的1/n);</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">通过<strong>主从同步读写分离扩展数据库性能</strong>:</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(1)每个服务器上存储的</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">数据量是和总量相同</span><span style="letter-spacing: 1px;font-size: 15px;">;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(2)</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">n个服务器上的数</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">据都一样</span><span style="letter-spacing: 1px;font-size: 15px;">,都是全集;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(3)理论上</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">读性能扩充了n倍,写仍然是单点</span><span style="letter-spacing: 1px;font-size: 15px;">,写性能不变;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">&nbsp;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">缓存层的水平拆分和数据库层的水平拆分类似,也是以范围拆分和哈希拆分的方式居多,就不再展开。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">&nbsp;</span></p> <p><strong><span style="letter-spacing: 1px;font-size: 15px;">总结</span></strong></p> <p><span style="letter-spacing: 1px;font-size: 15px;">高并发</span><span style="letter-spacing: 1px;font-size: 12px;">(High Concurrency)</span><span style="letter-spacing: 1px;font-size: 15px;">是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">提高系统并发能力的方法主要有两种:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="letter-spacing: 1px;font-size: 15px;">垂直扩展</span><span style="letter-spacing: 1px;font-size: 12px;">(Scale Up)</span></p></li> <li><p><span style="letter-spacing: 1px;font-size: 15px;">水平扩展</span><span style="letter-spacing: 1px;font-size: 12px;">(Scale Out)</span></p></li> </ul> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">前者垂直扩展可以通过提升单机硬件性能,或者提升单机架构性能,来提高并发性,但单机性能总是有极限的,互联网分布式架构设计高并发终极解决方案还是后者:水平扩展。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">互联网分层架构中,各层次水平扩展的实践又有所不同:</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(1)<strong>反向代理层</strong>可以通过“DNS轮询”的方式来进行水平扩展;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(2)<strong>站点层</strong>可以通过nginx来进行水平扩展;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(3)<strong>服务层</strong>可以通过服务连接池来进行水平扩展;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(4)<strong>数据库</strong>可以按照数据范围,或者数据哈希的方式来进行水平扩展;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">各层实施水平扩展后,能够</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">通过增加服务器数量的方式来提升系统的性能</span><span style="letter-spacing: 1px;font-size: 15px;">,做到理论上的性能无限。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">思路</span><span style="letter-spacing: 1px;font-size: 15px;">比结论重要。<br></span></p> <p style="text-align: center;color: rgb(51, 51, 51);line-height: normal;clear: both;box-sizing: border-box;background-color: rgb(255, 255, 255);"><span style="box-sizing: border-box;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="box-sizing: border-box;font-size: 12px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><img width="auto" style="box-sizing: border-box;height: 130px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 677px;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;visibility: visible;width: 130px;word-wrap: break-word;" data-ratio="1" data-type="jpeg" data-w="250" data-s="300,640" src="/upload/7ddc9700032e2c5cee163f1f1a37b46c.jpg"></span></strong></span></p> <p style="text-align: center;color: rgb(51, 51, 51);line-height: normal;clear: both;box-sizing: border-box;background-color: rgb(255, 255, 255);"><span style="box-sizing: border-box;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="box-sizing: border-box;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="box-sizing: border-box;font-size: 12px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;">架构师之路</span></strong><span style="box-sizing: border-box;font-size: 12px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;">-分享技术</span></span><span style="box-sizing: border-box;color: rgb(255, 76, 0);font-size: 12px;letter-spacing: 1px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;">思路</span></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span><span style="letter-spacing: 1px;font-size: 15px;">相关文章:</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">《<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&amp;mid=2651962050&amp;idx=1&amp;sn=f60b8bb833fe3425f5227da42e3b3adf&amp;chksm=bd2d0f1e8a5a8608f81d42a16eea476d0bd4763f84f9a008ed616d1cfa050a4015780f898eb1&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2" data-itemshowtype="0">互联网架构“高可用”到底怎么玩?</a>》</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">《<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&amp;mid=2651961829&amp;idx=1&amp;sn=6992d6e36abf3c2918fc3e743b96abed&amp;chksm=bd2d0c398a5a852f97a4fd6cd078486a27311e71d825073003d06969440337f32ef6a856c9f0&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2" data-itemshowtype="0">“反向代理层”绝不能替代“DNS轮询”</a>》</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">《<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&amp;mid=2651961985&amp;idx=1&amp;sn=6f757843f5c159eab00d847e9c2cc995&amp;chksm=bd2d0f5d8a5a864b05fada6919204378134e174f1105a0716dd879845b0d365c913ef8e94a12&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2" data-itemshowtype="0">离不开的微服务架构,脱不开的RPC细节</a>》</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">《<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&amp;mid=2651962060&amp;idx=1&amp;sn=ec16ae5a34302f5d7f93043590fd266d&amp;chksm=bd2d0f108a5a860649f30b575a81b5af29630d96a047a7899a1db6d1a3e764d15ae21a2e912c&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2" data-itemshowtype="0">KA,连接池居然这么简单?</a>》</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">给你一台机器,你的架构能提高并发么?</span></p>

没那么简单的线程池

作者:微信小助手

<p style="text-align: center;"><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;text-align: center;max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">(给</span><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;text-align: center;max-width: 100%;line-height: 22.4px;color: rgb(0, 128, 255);">ImportNew</span><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;text-align: center;max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">加星标,提高Java技能)</span></p> <blockquote> <p style="letter-spacing: 0.5440000295639038px;white-space: normal;background-color: rgb(255, 255, 255);max-width: 100%;min-height: 1em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">转自:crossoverJie</span></p> </blockquote> <p><br></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">前言</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="2.1237113402061856" data-s="300,640" src="/upload/1c8a1d321cb6f2cbac44d32cbfb91719.jpg" data-type="jpeg" data-w="194" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">原以为线程池还挺简单的(平时常用,也分析过原理),这次是想自己动手写一个线程池来更加深入的了解它;但在动手写的过程中落地到细节时发现并没想的那么容易。结合源码对比后确实不得不佩服 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">DougLea </span><span style="font-size: 15px;">。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">我觉得大部分人直接去看 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">java.util.concurrent.ThreadPoolExecutor</span><span style="font-size: 15px;"> 的源码时都是看一个大概,因为其中涉及到了许多细节处理,还有部分 AQS 的内容,所以想要理清楚具体细节并不是那么容易。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">与其挨个分析源码不如自己实现一个简版,当然简版并不意味着功能缺失,需要保证核心逻辑一致。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">所以也是本篇文章的目的:</span></p> <p><span style="font-size: 15px;"><br></span></p> <blockquote class="js_blockquote_wrap" data-type="2" data-url="" data-author-name="" data-content-utf8-length="48" data-source-title=""> <section class="js_blockquote_digest"> <p>自己动手写一个五脏俱全的线程池,同时会了解到线程池的工作原理,以及如何在工作中合理的利用线程池。</p> </section> </blockquote> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">再开始之前建议对线程池不是很熟悉的朋友看看这几篇:</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">这里我截取了部分内容,也许可以埋个伏笔(坑)。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.4992867332382311" data-s="300,640" src="/upload/228722b0120d0c0e908fc2f498bd1401.jpg" data-type="jpeg" data-w="701" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.8321377331420373" data-s="300,640" src="/upload/625d32d76d5f0d418a7c72e60b81e1b5.jpg" data-type="jpeg" data-w="697" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">创建线程池</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">现在进入正题,新建了一个</span><span style="font-size: 15px;color: rgb(171, 25, 66);"> CustomThreadPool </span><span style="font-size: 15px;">类,它的工作原理如下:</span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.25167336010709507" data-s="300,640" src="/upload/279a4353b8097bc89b7c023e21cffb30.jpg" data-type="jpeg" data-w="747" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">简单来说就是往线程池里边丢任务,丢的任务会缓冲到队列里;线程池里存储的其实就是一个个的 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">Thread </span><span style="font-size: 15px;">,他们会一直不停的从刚才缓冲的队列里获取任务执行。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">流程还是挺简单。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">先来看看我们这个自创的线程池的效果如何吧:</span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.38809034907597534" data-s="300,640" src="/upload/e689158b862936d7678d35135608e4d0.jpg" data-type="jpeg" data-w="974" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.4666666666666667" data-s="300,640" src="/upload/ef1bba1b4576a0e4a4bcd25822f6105f.gif" data-type="gif" data-w="600" style=""></p> <p><span style="font-size: 15px;"></span><br></p> <p><span style="font-size: 15px;">初始化了一个核心为3、最大线程数为5、队列大小为 4 的线程池。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">先往其中丢了 10 个任务,由于阻塞队列的大小为 4 ,最大线程数为 5 ,所以由于队列里缓冲不了最终会创建 5 个线程(上限)。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">过段时间没有任务提交后( </span><span style="font-size: 15px;color: rgb(171, 25, 66);">sleep</span><span style="font-size: 15px;">)则会自动缩容到三个线程(保证不会小于核心线程数)。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">构造函数</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">来看看具体是如何实现的。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">下面则是这个线程池的构造函数:</span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.336231884057971" data-s="300,640" src="/upload/9a632be932e95414643add00cdf0c088.jpg" data-type="jpeg" data-w="1035" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">会有以下几个核心参数:</span></p> <p><span style="font-size: 15px;"><br></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;color: rgb(171, 25, 66);">miniSize </span><span style="font-size: 15px;">最小线程数,等效于 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">ThreadPool </span><span style="font-size: 15px;">中的核心线程数。</span></p></li> <li><p><span style="font-size: 15px;color: rgb(171, 25, 66);">maxSize </span><span style="font-size: 15px;">最大线程数。</span></p></li> <li><p><span style="font-size: 15px;color: rgb(171, 25, 66);">keepAliveTime </span><span style="font-size: 15px;">线程保活时间。</span></p></li> <li><p><span style="font-size: 15px;color: rgb(171, 25, 66);">workQueue</span><span style="font-size: 15px;"> 阻塞队列。</span></p></li> <li><p><span style="font-size: 15px;color: rgb(171, 25, 66);">notify </span><span style="font-size: 15px;">通知接口。</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">大致上都和 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">ThreadPool </span><span style="font-size: 15px;">中的参数相同,并且作用也是类似的。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">需要注意的是其中初始化了一个</span><span style="font-size: 15px;color: rgb(171, 25, 66);"> workers </span><span style="font-size: 15px;">成员变量:</span></p> <p><span style="font-size: 15px;"><br></span></p> <pre style="overflow-x:auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;display: block !important;min-width: 400px;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);font-weight: 400;" class="c hljs cpp"><span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">/**<br>* 存放线程池<br>*/</span><br><span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">private</span> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">volatile</span> Set&lt;Worker&gt; workers;<br> <br><span class="hljs-function" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> <span class="hljs-title" style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">CustomThreadPool</span><span class="hljs-params" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;">(<span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">int</span> miniSize, <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">int</span> maxSize, <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">long</span> keepAliveTime,<br> TimeUnit unit, BlockingQueue&lt;Runnable&gt; workQueue, Notify notify) </span></span>{<br><br> workers = <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">new</span> ConcurrentHashSet&lt;&gt;();<br>}</code></pre> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;color: rgb(171, 25, 66);">workers </span><span style="font-size: 15px;">是最终存放线程池中运行的线程,在</span><span style="font-size: 15px;color: rgb(171, 25, 66);"> j.u.c</span><span style="font-size: 15px;"> 源码中是一个</span><span style="font-size: 15px;color: rgb(171, 25, 66);"> HashSet </span><span style="font-size: 15px;">所以对他所有的操作都是需要加锁。</span></p> <p><br></p> <p><span style="font-size: 15px;">我这里为了简便起见就自己定义了一个线程安全的 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">Set</span><span style="font-size: 15px;"> 称为 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">ConcurrentHashSet</span><span style="font-size: 15px;">。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.595213319458897" data-s="300,640" src="/upload/fdf81f5153a8aa03c290902e95edcbd7.jpg" data-type="jpeg" data-w="961" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">其实原理也非常简单,和 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">HashSet </span><span style="font-size: 15px;">类似也是借助于 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">HashMap</span><span style="font-size: 15px;"> 来存放数据,利用其 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">key</span><span style="font-size: 15px;"> 不可重复的特性来实现 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">set</span><span style="font-size: 15px;"> ,只是这里的 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">HashMap</span><span style="font-size: 15px;"> 是用并发安全的 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">ConcurrentHashMap</span><span style="font-size: 15px;"> 来实现的。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">这样就能保证对它的写入、删除都是线程安全的。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">不过由于 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">ConcurrentHashMap</span><span style="font-size: 15px;"> 的</span><span style="font-size: 15px;color: rgb(171, 25, 66);"> size() </span><span style="font-size: 15px;">函数并不准确,所以我这里单独利用了一个 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">AtomicInteger </span><span style="font-size: 15px;">来统计容器大小。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">创建核心线程</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">往线程池中丢一个任务的时候其实要做的事情还蛮多的,最重要的事情莫过于创建线程存放到线程池中了。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">当然我们不能无限制的创建线程,不然拿线程池来就没任何意义了。于是 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">miniSize maxSize </span><span style="font-size: 15px;">这两个参数就有了它的意义。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">但这两个参数再哪一步的时候才起到作用呢?这就是首先需要明确的。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.530758226037196" data-s="300,640" src="/upload/87a0d1e34138fb426afa4be86152ba87.jpg" data-type="jpeg" data-w="699" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">从这个流程图可以看出第一步是需要判断是否大于核心线程数,如果没有则创建。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.3580613254203759" data-s="300,640" src="/upload/8b42ac7614752545b3840c5117eb3057.jpg" data-type="jpeg" data-w="1011" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.15814850530376084" data-s="300,640" src="/upload/f3319b7d475f41136a58e4b760b2d96d.jpg" data-type="jpeg" data-w="1037" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">结合代码可以发现在执行任务的时候会判断是否大于核心线程数,从而创建线程。</span></p> <p><span style="font-size: 15px;"><br></span></p> <blockquote class="js_blockquote_wrap" data-type="2" data-url="" data-author-name="" data-content-utf8-length="31" data-source-title=""> <section class="js_blockquote_digest"> <p><span style="color: rgb(171, 25, 66);">worker.startTask()&nbsp;</span>执行任务部分放到后面分析。</p> </section> </blockquote> <p><br></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.07191448007774538" data-s="300,640" src="/upload/ee450f3a1f4886f58a4140d911557089.jpg" data-type="jpeg" data-w="1029" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">这里的 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">miniSize</span><span style="font-size: 15px;"> 由于会在多线程场景下使用,所以也用 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">volatile</span><span style="font-size: 15px;"> 关键字来保证可见性。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">队列缓冲</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.530758226037196" data-s="300,640" src="/upload/87a0d1e34138fb426afa4be86152ba87.jpg" data-type="jpeg" data-w="699" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">结合上面的流程图,第二步自然是要判断队列是否可以存放任务(是否已满)。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.32976653696498054" data-s="300,640" src="/upload/1ba2866c056038b98a9f1a938b3b15b.jpg" data-type="jpeg" data-w="1028" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">优先会往队列里存放。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">上至封顶</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.35135135135135137" data-s="300,640" src="/upload/7b268626a00c3903dadc5e8e3ebcfc0d.jpg" data-type="jpeg" data-w="1036" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">一旦写入失败则会判断当前线程池的大小是否大于最大线程数,如果没有则继续创建线程执行。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">不然则执行会尝试阻塞写入队列( </span><span style="font-size: 15px;color: rgb(171, 25, 66);">j.u.c</span><span style="font-size: 15px;"> 会在这里执行拒绝策略)</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">以上的步骤和刚才那张流程图是一样的,这样大家是否有看出什么坑嘛?</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">时刻小心</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.533715925394548" data-s="300,640" src="/upload/5ff0e23bcf82722bc7fc6eac12889aa9.jpg" data-type="jpeg" data-w="697" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">从上面流程图的这两步可以看出会直接</span><span style="font-size: 15px;color: rgb(171, 25, 66);">创建新的线程</span><span style="font-size: 15px;">。</span></p> <p><br></p> <p><span style="font-size: 15px;">这个过程相对于中间</span><span style="font-size: 15px;color: rgb(171, 25, 66);">直接写入阻塞队列</span><span style="font-size: 15px;">的开销是非常大的,主要有以下两个原因:</span></p> <p><span style="font-size: 15px;"><br></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;">创建线程会加锁,虽说最终用的是 ConcurrentHashMap 的写入函数,但依然存在加锁的可能。</span></p></li> <li><p><span style="font-size: 15px;">会创建新的线程,创建线程还需要调用操作系统的 API 开销较大。</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <blockquote class="js_blockquote_wrap" data-type="2" data-url="" data-author-name="" data-content-utf8-length="37" data-source-title=""> <section class="js_blockquote_digest"> <p>所以理想情况下我们应该避免这两步,尽量让丢入线程池中的任务进入阻塞队列中。</p> </section> </blockquote> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">执行任务</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">任务是添加进来了,那是如何执行的?</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">在创建任务的时候提到过 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">worker.startTask() </span><span style="font-size: 15px;">函数:</span></p> <p><span style="font-size: 15px;"><br></span></p> <pre style="overflow-x:auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;display: block !important;min-width: 400px;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);font-weight: 400;" class="c hljs cpp"><span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">/**<br>* 添加任务,需要加锁<br>* @param runnable 任务<br>*/</span> <br> <span class="hljs-function" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">private</span> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">void</span> <span class="hljs-title" style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">addWorker</span><span class="hljs-params" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;">(Runnable runnable)</span> </span>{<br> <br> Worker worker = <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">new</span> Worker(runnable, <span class="hljs-literal" style="color: rgb(86, 182, 194);font-weight: 400;font-style: normal;">true</span>);<br> worker.startTask();<br> workers.add(worker);<br> <br>}</code></pre> <p><br></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">也就是在创建线程执行任务的时候会创建 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">Worker</span><span style="font-size: 15px;"> 对象,利用它的 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">startTask() </span><span style="font-size: 15px;">方法来执行任务。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">所以先来看看 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">Worker </span><span style="font-size: 15px;">对象是长啥样的:</span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.4634858812074002" data-s="300,640" src="/upload/6107d4d7ee63d64b3f885ec73064f52f.jpg" data-type="jpeg" data-w="1027" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">其实他本身也是一个线程,将接收到需要执行的任务存放到成员变量 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">task </span><span style="font-size: 15px;">处。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">而其中最为关键的则是执行任务 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">worker.startTask() </span><span style="font-size: 15px;">这一步骤。</span></p> <p><span style="font-size: 15px;"><br></span></p> <pre style="overflow-x:auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;display: block !important;min-width: 400px;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);font-weight: 400;" class="c hljs cpp"><span class="hljs-function" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">void</span> <span class="hljs-title" style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">startTask</span><span class="hljs-params" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;">()</span> </span>{<br> thread.start();<br>}</code></pre> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">其实就是运行了</span><span style="font-size: 15px;color: rgb(171, 25, 66);"> worker </span><span style="font-size: 15px;">线程自己,下面来看 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">run </span><span style="font-size: 15px;">方法。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.5463320463320464" data-s="300,640" src="/upload/4cbb2601d3dd7c18272797084849844.jpg" data-type="jpeg" data-w="1036" style=""></p> <p><br></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;">第一步是将创建线程时传过来的任务执行( </span><span style="font-size: 15px;color: rgb(171, 25, 66);">task.run</span><span style="font-size: 15px;">),接着会一直不停的从队列里获取任务执行,直到获取不到新任务了。</span></p></li> <li><p><span style="font-size: 15px;">任务执行完毕后将内置的计数器 -1 ,方便后面任务全部执行完毕进行通知。</span></p></li> <li><p><span style="font-size: 15px;">worker 线程获取不到任务后退出,需要将自己从线程池中释放掉( </span><span style="font-size: 15px;color: rgb(171, 25, 66);">workers.remove(this)</span><span style="font-size: 15px;">)。</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">从队列里获取任务</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">其实 getTask 也是非常关键的一个方法,它封装了从队列中获取任务,同时对不需要保活的线程进行回收。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.5606207565470417" data-s="300,640" src="/upload/3c1895d8094514f5a25a5bf7d6558001.jpg" data-type="jpeg" data-w="1031" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">很明显,核心作用就是从队列里获取任务;但有两个地方需要注意:</span></p> <p><span style="font-size: 15px;"><br></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;">当线程数超过核心线程数时,在获取任务的时候需要通过保活时间从队列里获取任务;一旦获取不到任务则队列肯定是空的,这样返回 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">null </span><span style="font-size: 15px;">之后在上文的 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">run() </span><span style="font-size: 15px;">中就会退出这个线程;从而达到了回收线程的目的,也就是我们之前演示的效果</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.246875" data-s="300,640" src="/upload/ca64f5b4a91b1d2ad78475a5554ce3af.jpg" data-type="jpeg" data-w="960" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">这里需要加锁,加锁的原因是这里肯定会出现并发情况,不加锁会导致 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">workers.size()&gt;miniSize</span><span style="font-size: 15px;"> 条件多次执行,从而导致线程被全部回收完毕。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">关闭线程池</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">最后来谈谈线程关闭的事;</span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.3505859375" data-s="300,640" src="/upload/dc4cc937cee3fe5e8e39674920e663c.jpg" data-type="jpeg" data-w="1024" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">还是以刚才那段测试代码为例,如果提交任务后我们没有关闭线程,会发现即便是任务执行完毕后程序也不会退出。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">从刚才的源码里其实也很容易看出来,不退出的原因是 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">Worker</span><span style="font-size: 15px;"> 线程一定还会一直阻塞在 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">task=workQueue.take()</span><span style="font-size: 15px;">; 处,即便是线程缩容了也不会小于核心线程数。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">通过堆栈也能证明:</span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.379978471474704" data-s="300,640" src="/upload/51264be9e2cbe58e8e9cd0ce1638c94a.jpg" data-type="jpeg" data-w="929" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">恰好剩下三个线程阻塞于此处。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">而关闭线程通常又有以下两种:</span></p> <p><span style="font-size: 15px;"><br></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;">立即关闭:执行关闭方法后不管现在线程池的运行状况,直接一刀切全部停掉,这样会导致任务丢失。</span></p></li> <li><p><span style="font-size: 15px;">不接受新的任务,同时等待现有任务执行完毕后退出线程池。</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">立即关闭</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">我们先来看第一种</span><span style="font-size: 15px;color: rgb(171, 25, 66);">立即关闭</span><span style="font-size: 15px;">:</span></p> <p><span style="font-size: 15px;"><br></span></p> <pre style="overflow-x:auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;display: block !important;min-width: 400px;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);font-weight: 400;" class="c hljs cpp"><span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">/**<br>* 立即关闭线程池,会造成任务丢失<br>*/</span><br> <br> <span class="hljs-function" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">void</span> <span class="hljs-title" style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">shutDownNow</span><span class="hljs-params" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;">()</span> </span>{<br> isShutDown.<span class="hljs-built_in" style="color: rgb(230, 192, 123);font-weight: 400;font-style: normal;">set</span>(<span class="hljs-literal" style="color: rgb(86, 182, 194);font-weight: 400;font-style: normal;">true</span>);<br> tryClose(<span class="hljs-literal" style="color: rgb(86, 182, 194);font-weight: 400;font-style: normal;">false</span>);<br> <br>}<br> <br><span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">/**<br>* 关闭线程池<br>*<br>* @param isTry true 尝试关闭 --&gt; 会等待所有任务执行完毕<br>* false 立即关闭线程池--&gt; 任务有丢失的可能<br>*/</span><br> <br> <span class="hljs-function" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">private</span> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">void</span> <span class="hljs-title" style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">tryClose</span><span class="hljs-params" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;">(boolean isTry)</span> </span>{<br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">if</span> (!isTry) {<br> closeAllTask();<br> } <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">else</span> {<br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">if</span> (isShutDown.get() &amp;&amp; totalTask.get() == <span class="hljs-number" style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">0</span>) {<br> closeAllTask();<br> <br> }<br> }<br>}<br> <br><span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">/**<br>* 关闭所有任务<br>*/</span><br> <br> <span class="hljs-function" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">private</span> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">void</span> <span class="hljs-title" style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">closeAllTask</span><span class="hljs-params" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;">()</span> </span>{<br> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">for</span> (Worker worker : workers) {<br> <span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//LOGGER.info("开始关闭");</span><br> worker.close();<br> <br> }<br>}<br> <br> <span class="hljs-function" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">void</span> <span class="hljs-title" style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">close</span><span class="hljs-params" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;">()</span> </span>{<br> thread.interrupt();<br> <br>}</code></pre> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">很容易看出,最终就是遍历线程池里所有的 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">worker </span><span style="font-size: 15px;">线程挨个执行他们的中断函数。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">我们来测试一下:</span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.3904109589041096" data-s="300,640" src="/upload/18208b778ef91153743d7ae7b6012605.jpg" data-type="jpeg" data-w="1022" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.25164835164835164" data-s="300,640" src="/upload/7c1321fb82b04e5b2d148b6dd574a3cc.jpg" data-type="jpeg" data-w="910" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">可以发现后面丢进去的三个任务其实是没有被执行的。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">完事后关闭</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">而</span><span style="font-size: 15px;color: rgb(171, 25, 66);">正常关闭</span><span style="font-size: 15px;">则不一样:</span></p> <p><span style="font-size: 15px;"><br></span></p> <pre style="overflow-x:auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;display: block !important;min-width: 400px;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);font-weight: 400;" class="c hljs cpp"><span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">/**<br>* 任务执行完毕后关闭线程池<br>*/</span> <br> <span class="hljs-function" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">void</span> <span class="hljs-title" style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">shutdown</span><span class="hljs-params" style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;">()</span> </span>{<br> isShutDown.<span class="hljs-built_in" style="color: rgb(230, 192, 123);font-weight: 400;font-style: normal;">set</span>(<span class="hljs-literal" style="color: rgb(86, 182, 194);font-weight: 400;font-style: normal;">true</span>);<br> tryClose(<span class="hljs-literal" style="color: rgb(86, 182, 194);font-weight: 400;font-style: normal;">true</span>);<br> <br>}</code></pre> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.27053140096618356" data-s="300,640" src="/upload/bc077df1e37f0ab3fc434e39e01f11cf.jpg" data-type="jpeg" data-w="1035" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">他会在这里多了一个判断,需要所有任务都执行完毕之后才会去中断线程。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">同时在线程需要回收时都会尝试关闭线程:</span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.0842911877394636" data-s="300,640" src="/upload/5c8b2d6b74539168cc93bbad7cc33eab.jpg" data-type="jpeg" data-w="1044" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.0935390549662488" data-s="300,640" src="/upload/97afda6aee5c0f319784f1ad481f389.jpg" data-type="jpeg" data-w="1037" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">来看看实际效果:</span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.5009259259259259" data-s="300,640" src="/upload/c304ae8bbbbb164d6b1643ce03f8750d.jpg" data-type="jpeg" data-w="1080" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">回收线程</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">上文或多或少提到了线程回收的事情,其实总结就是以下两点:</span></p> <p><span style="font-size: 15px;"><br></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;">一旦执行了 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">shutdown/shutdownNow</span><span style="font-size: 15px;"> 方法都会将线程池的状态置为关闭状态,这样只要</span><span style="font-size: 15px;color: rgb(171, 25, 66);"> worker </span><span style="font-size: 15px;">线程尝试从队列里获取任务时就会直接返回空,导致 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">worker</span><span style="font-size: 15px;"> 线程被回收。</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.0842911877394636" data-s="300,640" src="/upload/5c8b2d6b74539168cc93bbad7cc33eab.jpg" data-type="jpeg" data-w="1044" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;">一旦线程池大小超过了核心线程数就会使用保活时间来从队列里获取任务,所以一旦获取不到返回 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">null </span><span style="font-size: 15px;">时就会触发回收</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.0935390549662488" data-s="300,640" src="/upload/97afda6aee5c0f319784f1ad481f389.jpg" data-type="jpeg" data-w="1037" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">但如果我们的队列足够大,导致线程数都不会超过核心线程数,这样是不会触发回收的。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.7171814671814671" data-s="300,640" src="/upload/8b933bf2a3ba0322f6b9280e5104b8c7.jpg" data-type="jpeg" data-w="1036" style=""></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">比如这里我将队列大小调为 10 ,这样任务就会累计在队列里,不会创建五个 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">worker</span><span style="font-size: 15px;"> 线程。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">所以一直都是 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">Thread-1~3</span><span style="font-size: 15px;"> 这三个线程在反复调度任务。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">总结</span></strong></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">本次实现了线程池里大部分核心功能,我相信只要看完并动手敲一遍一定会对线程池有不一样的理解。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">结合目前的内容来总结下:</span></p> <p><span style="font-size: 15px;"><br></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;">线程池、队列大小要设计的合理,尽量的让任务从队列中获取执行。</span></p></li> <li><p><span style="font-size: 15px;">慎用</span><span style="font-size: 15px;color: rgb(171, 25, 66);"> shutdownNow() </span><span style="font-size: 15px;">方法关闭线程池,会导致任务丢失(除非业务允许)。</span></p></li> <li><p><span style="font-size: 15px;">如果任务多,线程执行时间短可以调大 </span><span style="font-size: 15px;color: rgb(171, 25, 66);">keepalive</span><span style="font-size: 15px;"> 值,使得线程尽量不被回收从而可以复用线程。</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">同时下次会分享一些线程池的新特性,如:</span></p> <p><span style="font-size: 15px;"><br></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;">执行带有返回值的线程。</span></p></li> <li><p><span style="font-size: 15px;">异常处理怎么办?</span></p></li> <li><p><span style="font-size: 15px;">所有任务执行完怎么通知我?</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">本文所有源码:</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">https://github.com/crossoverJie/JCSprout/blob/master/src/main/java/com/crossoverjie/concurrent/CustomThreadPool.java</span></p> <p><span style="font-size: 15px;"><br></span></p> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;max-width: 100%;box-sizing: border-box;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <section class="" powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <section style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <section style="padding-top: 1.1em;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;display: inline-block;vertical-align: top;overflow-wrap: break-word !important;"> <section style="padding: 0.2em 0.4em;max-width: 100%;box-sizing: border-box;border-top-left-radius: 0px;border-top-right-radius: 0.5em;border-bottom-right-radius: 0.5em;border-bottom-left-radius: 0px;background-color: rgb(249, 110, 87);color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <p style="max-width: 100%;box-sizing: border-box;min-height: 1em;overflow-wrap: break-word !important;"><strong style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;">推荐阅读</strong></p> </section> <section style="max-width: 100%;box-sizing: border-box;width: 0px;border-right-width: 4px;border-right-style: solid;border-right-color: rgb(249, 110, 87);border-top-width: 4px;border-top-style: solid;border-top-color: rgb(249, 110, 87);overflow-wrap: break-word !important;border-left-width: 4px !important;border-left-style: solid !important;border-left-color: transparent !important;border-bottom-width: 4px !important;border-bottom-style: solid !important;border-bottom-color: transparent !important;"></section> </section> <section style="padding-left: 10px;max-width: 100%;box-sizing: border-box;display: inline-block;vertical-align: top;color: rgb(160, 160, 160);font-size: 14px;overflow-wrap: break-word !important;"> <p style="max-width: 100%;box-sizing: border-box;min-height: 1em;overflow-wrap: break-word !important;">(点击标题可跳转阅读)</p> </section> <section style="margin-top: -3.5em;margin-left: 8px;padding: 3.5em 10px 10px;max-width: 100%;box-sizing: border-box;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);overflow-wrap: break-word !important;"> <section class="" powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;font-size: 14px;line-height: 2.6;overflow-wrap: break-word !important;"> <p><a href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&amp;mid=2651479349&amp;idx=3&amp;sn=b2da977392044265898b93e8b1cbd46e&amp;chksm=bd25314a8a52b85cbe7440e27b1133718a8ad66f902514d2bb0d12576d84ea3cf40af6ea7624&amp;scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" style="font-size: 12px;text-decoration: underline;" data-linktype="2"><span style="font-size: 12px;">从串行线程封闭到对象池、线程池</span></a><br></p> <p><a href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&amp;mid=2651478324&amp;idx=1&amp;sn=d387a2db34cda989fbf4b3282f5585d5&amp;chksm=bd25354b8a52bc5d7d18f77f290cfd4de2c69215ac5625819da036579f168d69716fea420888&amp;scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" style="font-size: 12px;text-decoration: underline;" data-linktype="2"><span style="font-size: 12px;">更好的使用 JAVA 线程池</span></a><br></p> <p><a href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&amp;mid=2651478541&amp;idx=1&amp;sn=6ad7f19084ff68ab7c7d23bc35c6eccc&amp;chksm=bd2536728a52bf6410354735f6b120b6a4898a17f36a39caac2f03c5703441db1f0281c0e602&amp;scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" style="font-size: 12px;text-decoration: underline;" data-linktype="2"><span style="font-size: 12px;">线程池调整真的很重要</span></a><br></p> <p><span style="font-size: 12px;"></span></p> </section> </section> </section> </section> </section> </section> </section> </section> <p style="white-space: normal;"><br></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;color: rgb(255, 169, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;">看完本文有收获?请转发分享给更多人</span></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><strong style="max-width: 100%;color: rgb(255, 169, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;">关注「ImportNew」,提升Java技能</strong></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="" data-ratio="0.9166666666666666" data-s="300,640" data-type="png" data-w="600" width="auto" src="/upload/899866149276fa5fddb73c61ae04be64.png" style="box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 600px !important;"></p> <p style="text-align: right;"><span style="font-size: 14px;text-align: right;">好文章,我</span><span style="font-size: 14px;text-align: right;color: rgb(255, 41, 65);">在看</span><span style="font-size: 14px;text-align: right;">❤️</span></p>

Maven eclipse设置maven

作者:じ☆ve宝贝

1.下载maven http://maven.apache.org/download.cgi 2.解压,并修改conf下的settings.xml ``` <?xml version="1.0" encoding="UTF-8"?> <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"> <!-- 修改maven下载的jar包位置 --> <localRepository>D:/Develop/Tool/mavenjar/</localRepository> <pluginGroups></pluginGroups> <proxies></proxies> <servers></servers> <mirrors> <!-- 修改maven下载的仓库地址 --> <mirror> <id>mirrorId</id> <mirrorOf>repositoryId</mirrorOf> <name>oschina</name> <url> http://maven.oschina.net/content/groups/public/</url> </mirror> </mirrors> <profiles></profiles> </settings> ``` 3.在eclipse中配置maven ![eclipse设置maven](/upload/QQ201603241038291.png "eclipse设置maven") ![eclipse设置maven](/upload/QQ20160324104106.png "eclipse设置maven") 这样maven就设置完成了,赶紧去创建一个maven项目吧

Spring 面试问题 TOP 50

作者:微信小助手

<blockquote> <p style="text-align: left;"><span style="font-size: 12px;color: rgb(136, 136, 136);">来自:静默虚空</span></p> <p style="text-align: left;"><span style="font-size: 12px;color: rgb(136, 136, 136);">链接:https://www.cnblogs.com/jingmoxukong/p/9408037.html</span></p> <p style="text-align: left;"><span style="font-size: 12px;color: rgb(136, 136, 136);">原文:https://www.edureka.co/blog/interview-questions/spring-interview-questions/</span></p> </blockquote> <section class="output_wrapper" style="font-size: 15px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;"> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">Spring Framework 现在几乎已成为 Java Web 开发的标配框架。那么,作为 Java 程序员,你对 Spring 的主要技术点又掌握了多少呢?不妨用本文的问题来检测一下。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">本文内容主要翻译自 Top 50 Spring Interview Questions You Must Prepare In 2018</p> <h3 style="line-height: inherit;margin-top: 1.5em;font-weight: bold;font-size: 1.3em;margin-bottom: 2em;margin-right: 5px;padding: 8px 15px;letter-spacing: 2px;background-image: linear-gradient(to right bottom, rgb(0, 188, 212), rgb(63, 81, 181));background-color: rgb(63, 81, 181);color: rgb(255, 255, 255);border-left: 10px solid rgb(51, 51, 51);border-radius: 5px;text-shadow: rgb(102, 102, 102) 1px 1px 1px;box-shadow: rgb(102, 102, 102) 1px 1px 2px;"><span style="font-size: inherit;color: inherit;line-height: inherit;">1. 一般问题</span></h3> <h4 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">1.1. 不同版本的 Spring Framework 有哪些主要功能?</span></h4> <table> <thead style="font-size: inherit;color: inherit;line-height: inherit;"> <tr style="font-size: inherit;color: inherit;line-height: inherit;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="color: inherit;line-height: inherit;font-size: 1em;border-top-width: 1px;border-color: rgb(204, 204, 204);padding: 0.5em 1em;text-align: left;background-color: rgb(240, 240, 240);">Version</th> <th style="color: inherit;line-height: inherit;font-size: 1em;border-top-width: 1px;border-color: rgb(204, 204, 204);padding: 0.5em 1em;text-align: left;background-color: rgb(240, 240, 240);" width="267">Feature</th> </tr> </thead> <tbody style="font-size: inherit;color: inherit;line-height: inherit;border-width: 0px;border-style: initial;border-color: initial;"> <tr style="font-size: inherit;color: inherit;line-height: inherit;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;"> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;">Spring 2.5</td> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;" width="277">发布于 2007 年。这是第一个支持注解的版本。</td> </tr> <tr style="font-size: inherit;color: inherit;line-height: inherit;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;"> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;">Spring 3.0</td> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;" width="317">发布于 2009 年。它完全利用了 Java5 中的改进,并为 JEE6 提供了支持。</td> </tr> <tr style="font-size: inherit;color: inherit;line-height: inherit;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;"> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;">Spring 4.0</td> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;" width="297">发布于 2013 年。这是第一个完全支持 JAVA8 的版本。</td> </tr> </tbody> </table> <h4 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">1.2. 什么是 Spring Framework?</span></h4> <ul style="" class=" list-paddingleft-2"> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">Spring 是一个开源应用框架,旨在降低应用程序开发的复杂度。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">它是轻量级、松散耦合的。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">它具有分层体系结构,允许用户选择组件,同时还为 J2EE 应用程序开发提供了一个有凝聚力的框架。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">它可以集成其他框架,如 Structs、Hibernate、EJB 等,所以又称为框架的框架。</span></p></li> </ul> <h4 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">1.3. 列举 Spring Framework 的优点。</span></h4> <ul style="" class=" list-paddingleft-2"> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">由于 Spring Frameworks 的分层架构,用户可以自由选择自己需要的组件。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">Spring Framework 支持 POJO(Plain Old Java Object) 编程,从而具备持续集成和可测试性。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">由于依赖注入和控制反转,JDBC 得以简化。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">它是开源免费的。</span></p></li> </ul> <h4 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">1.4. Spring Framework 有哪些不同的功能?</span></h4> <ul style="" class=" list-paddingleft-2"> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">轻量级 - Spring 在代码量和透明度方面都很轻便。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">IOC - 控制反转</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">AOP - 面向切面编程可以将应用业务逻辑和系统服务分离,以实现高内聚。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">容器 - Spring 负责创建和管理对象(Bean)的生命周期和配置。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">MVC - 对 web 应用提供了高度可配置性,其他框架的集成也十分方便。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">事务管理 - 提供了用于事务管理的通用抽象层。Spring 的事务支持也可用于容器较少的环境。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">JDBC 异常 - Spring 的 JDBC 抽象层提供了一个异常层次结构,简化了错误处理策略。</span></p></li> </ul> <h4 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">1.5. Spring Framework 中有多少个模块,它们分别是什么?</span></h4> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><img src="/upload/c2b44e9f16fd04d39c3e619644f0c03c.null" class="" data-ratio="0.75" data-w="672"></p> <ul style="" class=" list-paddingleft-2"> <li><p>Spring 核心容器 – 该层基本上是 Spring Framework 的核心。它包含以下模块:<br>Spring Core<br>Spring Bean<br>SpEL (Spring Expression Language)<br>Spring Context</p></li> <li><p>数据访问/集成 – 该层提供与数据库交互的支持。它包含以下模块:<br>JDBC (Java DataBase Connectivity)<br>ORM (Object Relational Mapping)<br>OXM (Object XML Mappers)<br>JMS (Java Messaging Service)<br>Transaction</p></li> <li><p>Web – 该层提供了创建 Web 应用程序的支持。它包含以下模块:<br>Web<br>Web – Servlet<br>Web – Socket<br>Web – Portlet</p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">AOP – 该层支持面向切面编程</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">Instrumentation – 该层为类检测和类加载器实现提供支持。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">Test – 该层为使用 JUnit 和 TestNG 进行测试提供支持。</span></p></li> <li><p>几个杂项模块:<br>Messaging – 该模块为 STOMP 提供支持。它还支持注解编程模型,该模型用于从 WebSocket 客户端路由和处理 STOMP 消息。<br>Aspects – 该模块为与 AspectJ 的集成提供支持。</p></li> </ul> <h4 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">1.6. 什么是 Spring 配置文件?</span></h4> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">Spring 配置文件是 XML 文件。该文件主要包含类信息。它描述了这些类是如何配置以及相互引入的。但是,XML 配置文件冗长且更加干净。如果没有正确规划和编写,那么在大项目中管理变得非常困难。</p> <h4 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">1.7. Spring 应用程序有哪些不同组件?</span></h4> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">Spring 应用一般有以下组件:</p> <ul style="" class=" list-paddingleft-2"> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">接口 - 定义功能。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">Bean 类 - 它包含属性,setter 和 getter 方法,函数等。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">Spring 面向切面编程(AOP) - 提供面向切面编程的功能。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">Bean 配置文件 - 包含类的信息以及如何配置它们。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">用户程序 - 它使用接口。</span></p></li> </ul> <h4 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">1.8 使用 Spring 有哪些方式?</span></h4> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">使用 Spring 有以下方式:</p> <ul style="" class=" list-paddingleft-2"> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">作为一个成熟的 Spring Web 应用程序。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">作为第三方 Web 框架,使用 Spring Frameworks 中间层。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">用于远程使用。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">作为企业级 Java Bean,它可以包装现有的 POJO(Plain Old Java Objects)。</span></p></li> </ul> <h3 style="line-height: inherit;margin-top: 1.5em;font-weight: bold;font-size: 1.3em;margin-bottom: 2em;margin-right: 5px;padding: 8px 15px;letter-spacing: 2px;background-image: linear-gradient(to right bottom, rgb(0, 188, 212), rgb(63, 81, 181));background-color: rgb(63, 81, 181);color: rgb(255, 255, 255);border-left: 10px solid rgb(51, 51, 51);border-radius: 5px;text-shadow: rgb(102, 102, 102) 1px 1px 1px;box-shadow: rgb(102, 102, 102) 1px 1px 2px;"><span style="font-size: inherit;color: inherit;line-height: inherit;">2. 依赖注入(Ioc)</span></h3> <h4 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">2.1. 什么是 Spring IOC 容器?</span></h4> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">Spring 框架的核心是 Spring 容器。容器创建对象,将它们装配在一起,配置它们并管理它们的完整生命周期。Spring 容器使用依赖注入来管理组成应用程序的组件。容器通过读取提供的配置元数据来接收对象进行实例化,配置和组装的指令。该元数据可以通过 XML,Java 注解或 Java 代码提供。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;text-align: center;"><img src="/upload/c0459ee7af9f2aa8b6f1cb39b765107a.null" class="" data-ratio="0.84" data-w="300"></p> <h4 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">2.2. 什么是依赖注入?</span></h4> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">在依赖注入中,您不必创建对象,但必须描述如何创建它们。您不是直接在代码中将组件和服务连接在一起,而是描述配置文件中哪些组件需要哪些服务。由 IoC 容器将它们装配在一起。</p> <h4 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">2.3. 可以通过多少种方式完成依赖注入?</span></h4> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">通常,依赖注入可以通过三种方式完成,即:</p> <ul style="" class=" list-paddingleft-2"> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">构造函数注入</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">setter 注入</span></p></li> <li><p>接口注入<br>在 Spring Framework 中,仅使用构造函数和 setter 注入。</p></li> </ul> <h4 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">2.4. 区分构造函数注入和 setter 注入。</span></h4> <table> <thead style="font-size: inherit;color: inherit;line-height: inherit;"> <tr style="font-size: inherit;color: inherit;line-height: inherit;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="color: inherit;line-height: inherit;font-size: 1em;border-top-width: 1px;border-color: rgb(204, 204, 204);padding: 0.5em 1em;text-align: left;background-color: rgb(240, 240, 240);">构造函数注入</th> <th style="color: inherit;line-height: inherit;font-size: 1em;border-top-width: 1px;border-color: rgb(204, 204, 204);padding: 0.5em 1em;text-align: left;background-color: rgb(240, 240, 240);" width="189">setter 注入</th> </tr> </thead> <tbody style="font-size: inherit;color: inherit;line-height: inherit;border-width: 0px;border-style: initial;border-color: initial;"> <tr style="font-size: inherit;color: inherit;line-height: inherit;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;"> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;">没有部分注入</td> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;" width="199">有部分注入</td> </tr> <tr style="font-size: inherit;color: inherit;line-height: inherit;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;"> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;">不会覆盖 setter 属性</td> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;" width="209">会覆盖 setter 属性</td> </tr> <tr style="font-size: inherit;color: inherit;line-height: inherit;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;"> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;">任意修改都会创建一个新实例</td> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;" width="219">任意修改不会创建一个新实例</td> </tr> <tr style="font-size: inherit;color: inherit;line-height: inherit;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;"> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;">适用于设置很多属性</td> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;" width="229">适用于设置少量属性</td> </tr> </tbody> </table> <h4 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">2.5. spring 中有多少种 IOC 容器?</span></h4> <ul style="" class=" list-paddingleft-2"> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">BeanFactory - BeanFactory 就像一个包含 bean 集合的工厂类。它会在客户端要求时实例化 bean。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">ApplicationContext - ApplicationContext 接口扩展了 BeanFactory 接口。它在 BeanFactory 基础上提供了一些额外的功能。</span></p></li> </ul> <h4 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">2.6. 区分 BeanFactory 和 ApplicationContext。</span></h4> <table> <thead style="font-size: inherit;color: inherit;line-height: inherit;"> <tr style="font-size: inherit;color: inherit;line-height: inherit;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="color: inherit;line-height: inherit;font-size: 1em;border-top-width: 1px;border-color: rgb(204, 204, 204);padding: 0.5em 1em;text-align: left;background-color: rgb(240, 240, 240);">BeanFactory</th> <th style="color: inherit;line-height: inherit;font-size: 1em;border-top-width: 1px;border-color: rgb(204, 204, 204);padding: 0.5em 1em;text-align: left;background-color: rgb(240, 240, 240);" width="171">ApplicationContext</th> </tr> </thead> <tbody style="font-size: inherit;color: inherit;line-height: inherit;border-width: 0px;border-style: initial;border-color: initial;"> <tr style="font-size: inherit;color: inherit;line-height: inherit;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;"> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;">它使用懒加载</td> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;" width="181">它使用即时加载</td> </tr> <tr style="font-size: inherit;color: inherit;line-height: inherit;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;"> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;">它使用语法显式提供资源对象</td> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;" width="221">它自己创建和管理资源对象</td> </tr> <tr style="font-size: inherit;color: inherit;line-height: inherit;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;"> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;">不支持国际化</td> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;" width="201">支持国际化</td> </tr> <tr style="font-size: inherit;color: inherit;line-height: inherit;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;"> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;">不支持基于依赖的注解</td> <td style="color: inherit;line-height: inherit;font-size: 1em;border-color: rgb(204, 204, 204);padding: 0.5em 1em;" width="211">支持基于依赖的注解</td> </tr> </tbody> </table> <h4 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">2.7. 列举 IoC 的一些好处。</span></h4> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">IoC 的一些好处是:</p> <ul style="" class=" list-paddingleft-2"> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">它将最小化应用程序中的代码量。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">它将使您的应用程序易于测试,因为它不需要单元测试用例中的任何单例或 JNDI 查找机制。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">它以最小的影响和最少的侵入机制促进松耦合。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">它支持即时的实例化和延迟加载服务。</span></p></li> </ul> <h4 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">2.8. Spring IoC 的实现机制。</span></h4> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">Spring 中的 IoC 的实现原理就是工厂模式加反射机制。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">示例:</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs java" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 12px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;"><span class="hljs-class" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">interface</span>&nbsp;<span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">Fruit</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">public</span>&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">abstract</span>&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">void</span>&nbsp;<span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">eat</span><span class="hljs-params" style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">()</span></span>;<br>}<br><span class="hljs-class" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">class</span>&nbsp;<span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">Apple</span>&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">implements</span>&nbsp;<span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">Fruit</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">public</span>&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">void</span>&nbsp;<span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">eat</span><span class="hljs-params" style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">()</span></span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"Apple"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br><span class="hljs-class" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">class</span>&nbsp;<span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">Orange</span>&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">implements</span>&nbsp;<span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">Fruit</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">public</span>&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">void</span>&nbsp;<span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">eat</span><span class="hljs-params" style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">()</span></span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"Orange"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br><span class="hljs-class" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">class</span>&nbsp;<span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">Factory</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">public</span>&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">static</span>&nbsp;Fruit&nbsp;<span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">getInstance</span><span class="hljs-params" style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">(String&nbsp;ClassName)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Fruit&nbsp;f=<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">null</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f=(Fruit)Class.forName(ClassName).newInstance();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">catch</span>&nbsp;(Exception&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">return</span>&nbsp;f;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br><span class="hljs-class" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">class</span>&nbsp;<span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">Client</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">public</span>&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">static</span>&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">void</span>&nbsp;<span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">main</span><span class="hljs-params" style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">(String[]&nbsp;a)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Fruit&nbsp;f=Factory.getInstance(<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"io.github.dunwu.spring.Apple"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">if</span>(f!=<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">null</span>){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f.eat();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <h3 style="line-height: inherit;margin-top: 1.5em;font-weight: bold;font-size: 1.3em;margin-bottom: 2em;margin-right: 5px;padding: 8px 15px;letter-spacing: 2px;background-image: linear-gradient(to right bottom, rgb(0, 188, 212), rgb(63, 81, 181));background-color: rgb(63, 81, 181);color: rgb(255, 255, 255);border-left: 10px solid rgb(51, 51, 51);border-radius: 5px;text-shadow: rgb(102, 102, 102) 1px 1px 1px;box-shadow: rgb(102, 102, 102) 1px 1px 2px;"><span style="font-size: inherit;color: inherit;line-height: inherit;">3. Beans</span></h3> <h4 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">3.1. 什么是 spring bean?</span></h4> <ul style="" class=" list-paddingleft-2"> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">它们是构成用户应用程序主干的对象。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">Bean 由 Spring IoC 容器管理。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">它们由 Spring IoC 容器实例化,配置,装配和管理。</span></p></li> <li><p><span style="font-size: inherit;color: inherit;line-height: inherit;">Bean 是基于用户提供给容器的配置元数据创建。</span></p></li> </ul> <h4 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;color: rgb(204, 0, 0);"><span style="font-size: inherit;color: inherit;line-height: inherit;">3.2. spring 提供了哪些配置方式?</span></h4> <ul style="" class=" list-paddingleft-2"> <li><p>基于 xml 配置<br>bean 所需的依赖项和服务在 XML 格式的配置文件中指定。这些配置文件通常包含许多 bean 定义和特定于应用程序的配置选项。它们通常以 bean 标签开头。例如:</p></li> </ul> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs xml" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 12px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;"><span class="hljs-tag" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">&lt;<span class="hljs-name" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">bean</span>&nbsp;<span class="hljs-attr" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">id</span>=<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"studentbean"</span>&nbsp;<span class="hljs-attr" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">class</span>=<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"org.edureka.firstSpring.StudentBean"</span>&gt;</span><br>&nbsp;<span class="hljs-tag" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">&lt;<span class="hljs-name" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">property</span>&nbsp;<span class="hljs-attr" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">name</span>=<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"name"</span>&nbsp;<span class="hljs-attr" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">value</span>=<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"Edureka"</span>&gt;&lt;/<span class="hljs-name" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">property</span>&gt;</span><br><span class="hljs-tag" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">&lt;/<span class="hljs-name" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">bean</span>&gt;</span><br></code></pre> <ul style="" class=" list-paddingleft-2"> <li><p>基于注解配置<br>您可以通过在相关的类,方法或字段声明上使用注解,将 bean 配置为组件类本身,而不是使用 XML 来描述 bean 装配。默认情况下,Spring 容器中未打开注解装配。因此,您需要在使用它之前在 Spring 配置文件中启用它。例如:</p></li> </ul> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs xml" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 12px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;"><span class="hljs-tag" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">&lt;<span class="hljs-name" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">beans</span>&gt;</span><br><span class="hljs-tag" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">&lt;<span class="hljs-name" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">context:annotation-config</span>/&gt;</span><br><span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">&lt;!--&nbsp;bean&nbsp;definitions&nbsp;go&nbsp;here&nbsp;--&gt;</span><br><span class="hljs-tag" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">&lt;/<span class="hljs-name" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">beans</span>&gt;</span><br></code></pre> <ul style="" class=" list-paddingleft-2"> <li><p>基于 Java API 配置<br>Spring 的 Java 配置是通过使用 @Bean 和 @Configuration 来实现。</p></li> </ul> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">@Bean 注解扮演与 <bean style="font-size: inherit;color: inherit;line-height: inherit;"></bean>元素相同的角色。<br>@Configuration 类允许通过简单地调用同一个类中的其他 @Bean 方法来定义 bean 间依赖关系。<br>例如:</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs java" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 12px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;"><span class="hljs-meta" style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);word-wrap: inherit !important;word-break: inherit !important;">@Configuration</span><br><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">public</span>&nbsp;<span class="hljs-class" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">class</span>&nbsp;<span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">StudentConfig</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);word-wrap: inherit !important;word-break: inherit !important;">@Bean</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">public</span>&nbsp;StudentBean&nbsp;<span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">myStudent</span><span class="hljs-params" style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">return</span>&nbsp;<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">new</span>&nbsp;StudentBean();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <h4 style="line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.2em;color: rgb(204,

Spring Cloud微服务Sentinel+Apollo限流、熔断实战

作者:微信小助手

<p><img class="currentImg" data-backh="292" data-backw="574" data-before-oversubscription-url="https://mmbiz.qlogo.cn/mmbiz_jpg/l89kosVutolqolMOHjxgib2AZOEb1HkrOPphapyC1BPe5FkIicsQOXwP5bkkc6Px4uib7cjc4G2z55BqGCvq7h6og/0?wx_fmt=jpeg" data-oversubscription-url="http://mmbiz.qpic.cn/mmbiz_jpg/l89kosVutolqolMOHjxgib2AZOEb1HkrOeGYsialPCT2wZ6w5HcEeItIiaSstQsywA7zdDPnJtERKia5UY9mK2MoIg/0?wx_fmt=jpeg" data-ratio="0.5083333333333333" src="/upload/842b7e8ee6f078dc77bdb6d6d9b9a978.jpg" data-type="jpeg" data-w="600" style="top: 101px;left: 202px;width: 100%;height: auto;cursor: pointer;" title="点击查看源网页" width="600" height="305"><br></p> <section class="" style="margin-top: 40px;margin-right: 8px;margin-left: 8px;padding: 19px;max-width: 100%;box-sizing: border-box;font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;letter-spacing: 0.544px;white-space: normal;font-size: 15px;line-height: 27px;color: rgb(89, 89, 89);background-color: rgb(239, 239, 239);overflow-wrap: break-word !important;"> <span style="font-size: 15px;">在Spring Cloud微服务体系中,由于限流熔断组件Hystrix开源版本不在维护,因此国内不少有类似需求的公司已经将眼光转向阿里开源的Sentinel框架。而以下要介绍的正是作者最近两个月的真实项目实践过程,这中间被不少网络Demo示例级别水文误导过,为了以正视听特将实践过程加以总结,希望能够帮到有类似需要的朋友!(PS:此文有点长,看下概念部分后可以点击在看+收藏,以备需要)</span> <br> </section> <section class="" style="max-width: 100%;box-sizing: border-box;color: rgb(63, 63, 63);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;font-size: 16px;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;overflow-wrap: break-word !important;"> <strong><span style="margin-top: 30px;margin-bottom: 10px;padding-left: 20px;max-width: 100%;display: inline-block;height: 31px;line-height: 35px;color: rgb(60, 112, 198);background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/FE4VibF0SjfObryL1Crvw0EnBbYnPyn9oeGoiaEGzeLV4t7vK9Kqp7bBUSzaYIkyxJQf32zcoFkG1V37m5LtKzAQ/640?wx_fmt=png&quot;);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 30px;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 17px;">一、Sentinel概述<br><br></span></strong> </section> <p style="margin: 10px 0px 0px;padding: 0px;color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-size: 14px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 2em;"><span style="font-size: 16px;">在基于Spring Cloud构建的微服务体系中,服务之间的调用链路会随着系统的演进变得越来越长,这无疑会增加了整个系统的不可靠因素。在并发流量比较高的情况下,由于网络调用之间存在一定的超时时间,链路中的某个服务出现宕机都会大大增加整个调用链路的响应时间,而瞬间的流量洪峰则会导致这条链路上所有服务的可用线程资源被打满,从而造成整体服务的不可用,这也就是我们常说的“<strong>雪崩效应</strong>”。<br><br></span></p> <p style="margin: 10px 0px 0px;padding: 0px;color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-size: 14px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 2em;"><span style="font-size: 16px;">而在微服务系统设计的过程中,为了应对这样的糟糕情况,最常用的手段就是进行<strong>”流量控制“</strong>以及对网络服务的调用实现<strong>“熔断降级”</strong>。所谓流量控制就是根据服务的承载能力指定一个策略,对一定时间窗口内的网络调用次数进行限制,例如1s内某个服务最多只能处理10个请求,那么1s内的第11+的请求会被被限制丢弃;而熔断降级的概念则是说在A服务→B服务调用过程中,按照一定的规则A服务发现调用B服务经常失败或响应时间过长,如果触发了A服务对B服务调用的熔断降级规则,那么在一定时间窗口内,A服务在处理请求的过程中对于B服务的调用将会直接在A服务的逻辑中被熔断降级,请求则不会通过网络打到B服务,从而避免A服务由于过长的超时时间导致自身资源被耗尽的情况发生。<br><br></span></p> <section style="margin: 10px 0px 0px;padding: 0px;color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-size: 14px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 2em;"> <span style="font-size: 16px;">虽然我们知道以上两种手段非常有用,但若没有合适的技术来支持,就好像一句话说的“虽然明白很多道理,但是依然过不好这一生”一样。而Sentinel就是这样一种技术,它是阿里巴巴开源的一款客户端限流组件,可以与Spring Cloud微服务体系无缝地集成;而与之对应的是另外一款Netflix公司推出的知名度也比较高的Hystrix组件,Hystrix也是Spring Cloud官方集成熔断限流组件,只不过相对于Sentinel来说,Hystrix所提供的功能和灵活度比较低,并且它目前已经处于开源版本暂停维护的状态,因此目前国内很多基于Spring Cloud搞微服务的公司都转向了Sentinel。关于二者的对比由于不是本文的重点,这里就不再赘述,大家搜索下就好(ps:可能网上也没几篇能说明白的文章,关键还在于大家实际使用对比)。</span> </section> <p style="margin: 10px 0px 0px;padding: 0px;text-align: center;"><strong><span style="margin-top: 30px;margin-bottom: 10px;padding-left: 20px;max-width: 100%;display: inline-block;height: 31px;line-height: 35px;color: rgb(60, 112, 198);background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/FE4VibF0SjfObryL1Crvw0EnBbYnPyn9oeGoiaEGzeLV4t7vK9Kqp7bBUSzaYIkyxJQf32zcoFkG1V37m5LtKzAQ/640?wx_fmt=png&quot;);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 30px;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 17px;">二、Sentinel+Apollo架构说明</span></strong><br style="color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-size: 14px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: start;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;"></p> <h3 style="margin: 30px 0px 0px;padding: 0px;color: rgb(51, 51, 51);font-size: 16px;font-weight: bold;line-height: 1.5;text-transform: none;"><span style="font-size: 17px;">Sentinel开源版本架构</span></h3> <p><br></p> <section style="line-height: 2em;"> <span style="font-size: 16px;">在Github Sentinel官方Wiki说明以及网上一大堆的水文中,关于Sentinel的资料已经很多了,但是大多数属于Demo级别,所以本文不想过多的耗费大家的精力(因为在学习过程中,作者也被误导过)。以下将从实际生产的使用方式上来阐述如何构建Sentinel的使用架构。</span> </section> <section style="line-height: 2em;"> <br> </section> <section style="margin: 10px 0px 0px;padding: 0px;color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-size: 14px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 2em;"> <span style="font-size: 16px;">从本质上说Sentinel与Hystrix是一类性质的熔断限流组件,之所以说它们只是组件就在于它们都需要内嵌于微服务应用本身的主进程之中,所有的限流、熔断策略及指标信息的收集等逻辑都是基于客户端的(这里不要对客户端有所误会,它指的是处于调用端上游的微服务本身)。而这一点是明显区别于Service Mesh(服务网格)架构中将熔断、限流等逻辑抽象在SideCar(边车)而不是微服务应用本身的。<br><br></span> </section> <p style="margin: 10px 0px 0px;padding: 0px;color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-size: 14px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 2em;"><span style="font-size: 16px;">因此从这种意义上说,Sentinel的使用应该是并不复杂的,它应该与Hystrix一样,在Spring Cloud微服务应用中引入相关依赖即可。事实上从某种程度来说的确如此,只不过Sentinel提供了比Hystrix要强一点的规则配置能力,提供了可以进行限流、熔断降级以及热点、授权等其他规则统一配置和管理的控制台服务-&gt;sentinel-dashboard。<br></span></p> <p style="margin: 10px 0px 0px;padding: 0px;color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-size: 14px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 2em;"><br></p> <p style="margin: 10px 0px 0px;padding: 0px;color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-size: 14px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 2em;"><span style="font-size: 16px;">虽然如此,但这也并没有改变Sentinel作为客户端限流组件性质,通过控制台配置的规则依然要推送到微服务应用Sentinel客户端本身才能生效,而微服务之间的调用链路等指标信息也需要推送给Sentinel控制台,才能比较方便地使用Sentinel提供的一些能力,因此在开源的架构版本中需要微服务应用本身开启独立端口与sentinel-dashboard进行通信,从而获取配置规则以及上送微服务应用各类指标信息。而这一点,显然也会占用微服务额外的资源,并且由于sentinel-dashboard在此条件下并不具备集群部署能力,因此也会形成一个单节点问题,但是有一套控制台总好过于没有,如果希望比较方便快速地应用Sentinel这也是一种代价。此时的Sentinel架构如下图所示:<br></span></p> <p style="margin: 10px 0px 0px;padding: 0px;color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-size: 14px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 2em;"><span style="font-size: 15px;"><img class="" data-ratio="0.6399345335515548" src="/upload/750abb2d49f185b1341385ea6e98d172.png" data-type="png" data-w="1222" style="width: 488px;height: 312px;"></span><span style="font-size: 17px;"><strong>Sentinel+Apollo架构</strong></span><span style="font-size: 16px;"><strong><span style="color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;"><br></span></strong></span></p> <p style="margin: 10px 0px 0px;padding: 0px;color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-size: 14px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 2em;"><span style="font-size: 16px;"><strong><span style="color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;"><br></span></strong></span></p> <p style="margin: 10px 0px 0px;padding: 0px;color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-size: 14px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: left;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 2em;"><span style="font-size: 16px;"><strong><span style="color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">在开源版本架构中,通过sentinel-dashboard控制台配置的限流、熔断降级等规则都是存储于Sentinel控制台服务内存之中的,如果控制台服务重启或者微服务应用重启都会导致规则丢失。</span></strong><strong><span style="color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">而这在生产环境下是不可接受的,因此Sentinel在官方的生产架构指导中也是推荐使用第三方数据源(如本文的Apollo)作为永久存储中心,这样各个微服务的限流、降级规则都可以永久存储。虽然Sentinel官方推荐使用第三方数据源作为规则存储中心,目前也提供了针对Apollo、Nacos、Zookeeper、Redis、Consul、Spring Cloud Config等多种存储源的依赖集成Jar,但是却并没有针对这些数据源提供一个可以实际使用的sentinel-dashboard第三方数据源存储版本,所以当你选择了一种数据源那么就需要你自己对sentinel-dashboard项目进行改造,这里作者针对Sentinel 1.7.0(成文时最新版本)使用Apollo数据源改造了一个版本,所有规则基本可用,但可能会有细节的Bug需要自行Fix。具体代码改造点见Github链接:</span></strong></span></p> <p><br></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;word-spacing: 0px;letter-spacing: 0px;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin: 0px;padding: 0px;"><code class="hljs cpp" style="overflow-wrap: break-word;margin: 0px 2px;line-height: 18px;font-size: 14px;font-weight: normal;word-spacing: 0px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46) none repeat scroll 0% 0%;overflow-x: auto;padding: 0.5em;white-space: pre !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">https:<span class="hljs-comment" style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">//github.com/manongwudi/Sentinel/commit/f3a27adb6fdbf13d9eaa4510e317c1b55c206e89</span><br></code></pre> </section> <p style="margin: 10px 0px 0px;padding: 0px;"><br></p> <p style="margin: 10px 0px 0px;padding: 0px;color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-size: 14px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 2em;"><span style="font-size: 16px;">关于以上sentinel-dashboard接入Apollo数据源的代码改造情况,大家可以详细参考上述链接,这里作者只说以下几个重点:<br></span></p> <p style="margin: 10px 0px 0px;padding: 0px;color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-size: 14px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 2em;"><span style="font-size: 16px;"><br></span></p> <p style="margin: 10px 0px 0px;padding: 0px;color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-size: 14px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 2em;"><span style="font-size: 16px;">目前官方推荐的方式是通过Apollo的开放平台授权的方式进行写入,因此我们需要在sentinel-dashboard项目pom.xml文件引入以下依赖:</span></p> <p style="margin: 10px 0px 0px;padding: 0px;color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-size: 14px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 2em;"><br></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;word-spacing: 0px;letter-spacing: 0px;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin: 0px;padding: 0px;"><code class="java language-java hljs" style="overflow-wrap: break-word;margin: 0px 2px;line-height: 18px;font-size: 14px;font-weight: normal;word-spacing: 0px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46) none repeat scroll 0% 0%;overflow-x: auto;padding: 0.5em;white-space: pre !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">&lt;!--&nbsp;Apollo配置依赖&nbsp;--&gt;<br>&lt;dependency&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;groupId&gt;com.ctrip.framework.apollo&lt;/groupId&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;artifactId&gt;apollo-openapi&lt;/artifactId&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;version&gt;<span class="hljs-number" style="font-size: inherit;line-height: inherit;margin: 0px;padding: 0px;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">1.5</span>.0&lt;/version&gt;<br>&lt;/dependency&gt;<br></code></pre> </section> <p style="margin: 10px 0px 0px;padding: 0px;color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-size: 14px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 2em;"><span style="color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;font-size: 15px;"><br></span></p> <p style="margin: 10px 0px 0px;padding: 0px;color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-size: 14px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 2em;"><span style="color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;font-size: 16px;">之后我们需要在Apollo Portal创建一个针对sentinel-dashboard的应用,具体创建方法如下图所示:</span></p> <p style="margin: 10px 0px 0px;padding: 0px;color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-size: 14px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;line-height: 2em;"><span style="color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.39707927677329624" data-s="300,640" src="/upload/978e02b87110a34732ce469e500baed3.png" data-type="png" data-w="2876" style=""><br></p> <p style="text-align: left;"><br></p> <p style="margin: 10px 0px 0px;padding: 0px;line-height: 2em;"><span style="font-size: 16px;">以上我们创建了一个针对Sentinel控制台的应用(这里的应用是Apollo配置中心的基本概念,具体微服务接入Apollo的方法,大家可以自行搜索)。<br></span></p> <p style="margin: 10px 0px 0px;padding: 0px;line-height: 2em;"><span style="font-size: 16px;"><br></span></p> <p style="margin: 10px 0px 0px;padding: 0px;line-height: 2em;"><span style="color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;font-size: 16px;">创建应用后,未来Sentinel控制台在启动是需要指定Apollo应用ID才能接入Apollo,而接入Apollo之后Sentinel的规则需要写入该应用下的namespace空间,因此还需要创建针对该应用的namespace空间,具体创建方式如下图所示:</span></p> <p style="margin: 10px 0px 0px;padding: 0px;line-height: 2em;"><span style="font-size: 15px;color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;"><br></span></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.5413481584433635" data-s="300,640" src="/upload/a0ca0a1bb2e017f629e57418311371d4.png" data-type="png" data-w="2878" style=""><br></p> <p style="text-align: left;"><br></p> <section style="margin: 10px 0px 0px;padding: 0px;line-height: 2em;"> <span style="color: rgb(51, 51, 51);font-family: Arial, sans-serif;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;text-align: start;text-indent: 0px;text-transform: none;white-space: normal;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;font-size: 16px;">点击进入应用,然后点击“添加Namespace",创建一个具体存储Sentinel各种限流、熔断降级等规则的Apollo存储空间,这里需要注意的是所创建的空间类型一定要是"public"公共空间,因为最终这些规则是需要具体的微服务应用去获取的,而在Apollo中应用下只有公共Namecspace才能被其他应用继承。<br><br>最后我们在Apollo控制台选择“管理员工具-&gt;开放平台授权管理”�

JFinal使用了BaseController定义公共返回结果方法怎么避免路径映射

作者:じ☆ve宝贝

> 在使用JFinal的过程中,因为返回成功的json中包含code,msg和data但是有一些成功没有data返回,故在baseController中重载了buildSuccessJsonResult方法,导致JFinal出现The action "xx.xx.xx.EventController.buildSuccessJsonResult()" can not be mapped, actionKey "/v1/event/buildSuccessJsonResult" is already in use. 可能查找的姿势不对,没有找到相关解决文档,故寻找源码打算自己重写对应类实现。没想到源码中已经实现。在这里记录一下寻找过程。 ## BaseController的方法 ![](/upload/ef90a08f62b8454db415ceefe79e2d43.png) ## 查找所有引用Controller的位置 ![](/upload/398e4b3799ae419ea134021b636f84bc.png) **路径的映射肯定是在core里面,然后看到ActionMapping(见名视意)直接找到得来全不费工夫。** ## 找到buildActionMapping ![](/upload/4fe8df51e05f4bfb8ded1c3965e200c0.png) **当时第一反应看到buildExcludedMethodName方法以为找到了组织,增加一个注解,扫到注解添加到这个里面就打工告成。但是感觉波总大神不应该没有想到这种需求的思路又往下看了一下,发现了新大陆。@NotAction 注解可以直接排除掉。** **JFinal 排除路径映射的注解是 @NotAction**

面试题:InnoDB中一棵B+树能存多少行数据?

作者:微信小助手

<section class="xmteditor" style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn"></section> <section class="xmteditor" style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn"></section> <section data-role="outer" label="Powered by 135editor.com" style="font-size: 16px;padding-left: 0.5em;padding-right: 0.5em;" data-mpa-powered-by="yiban.io"> <section data-role="outer" label="Powered by 135editor.com"> <section data-role="outer" label="Powered by 135editor.com" style="font-size:16px;"> <section data-role="outer" label="Powered by 135editor.com"> <section class="" data-tools="135编辑器" data-id="91525" style="white-space: normal;"> <section style="margin: 8px;padding: 10px;max-width: 100%;box-sizing: border-box;line-height: 25.6px;border-radius: 10px;height: auto;box-shadow: rgb(221, 221, 221) 2px 2px 8px;display: -webkit-flex;word-wrap: break-word !important;"> <section style="max-width: 100%;flex: 0 0 2cm;height: 78px;width: 75px;box-sizing: border-box !important;word-wrap: break-word !important;"> <p style="max-width: 100%;min-height: 1em;text-align: center;box-sizing: border-box !important;word-wrap: break-word !important;"><a href="https://mp.weixin.qq.com/s?__biz=Mzg5MzAyNDI4Mw==&amp;mid=2247483912&amp;idx=3&amp;sn=4e16e34c2a6d9acb72683fcaeeb98631&amp;scene=21#wechat_redirect" target="_blank" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 0px;right: auto;bottom: auto;"><img class="" data-copyright="0" data-cropselx1="0" data-cropselx2="75" data-cropsely1="0" data-cropsely2="75" data-ratio="1" src="/upload/461fbe1cf2f8abc8c9ca19590be382d2.jpg" data-type="jpeg" data-w="425" style="height: 75px;letter-spacing: 0.544px;text-align: justify;color: rgb(62, 62, 62);border-radius: 37px;width: 75px;box-shadow: rgb(170, 170, 170) 0px 0px 14px 0px;margin: 0px;box-sizing: border-box !important;word-wrap: break-word !important;visibility: visible !important;top: auto;left: auto;right: auto;bottom: auto;"></span></a></p> </section> <section style="padding-right: 10px;padding-left: 10px;max-width: 100%;box-sizing: border-box;flex: 1 1 auto;height: 55px;word-wrap: break-word !important;"> <section style="max-width: 100%;line-height: 35px;white-space: nowrap;box-sizing: border-box !important;word-wrap: break-word !important;"> <a href="https://mp.weixin.qq.com/s?__biz=Mzg5MzAyNDI4Mw==&amp;mid=2247483912&amp;idx=3&amp;sn=4e16e34c2a6d9acb72683fcaeeb98631&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2"><span style="color:#1f497d;font-family:宋体, SimSun;"><span style="letter-spacing: 0.544px;"><strong>黑客技术</strong></span></span></a> </section> <section style="max-width: 100%;font-size: 13px;line-height: 20px;color: rgb(127, 127, 127);box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="max-width: 100%;letter-spacing: 0.544px;color: rgb(165, 165, 165);font-family: 黑体, SimHei;box-sizing: border-box !important;word-wrap: break-word !important;">点击右侧关注,了解黑客的世界!</span> </section> </section> <section style="max-width: 100%;flex: 0 0 1.5cm;font-size: 15px;color: rgb(86, 187, 55);letter-spacing: 0px;text-align: center;line-height: 6;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="margin-top: 25px;max-width: 100px;vertical-align: middle;overflow: hidden;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="max-width: 100%;width: 55px;height: 30px;background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_gif/S7SAuLQzeTVj0YaoibbZqxicYkQrLnR3WtTQlHpHouqUmibjUCY9F5wpG0DmyMetZy9pjDdiabWo4XXdtCib3VcnI7w/640?wx_fmt=gif&quot;);background-size: 100% 100%;background-repeat: no-repeat;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="max-width: 100%;opacity: 0;box-sizing: border-box !important;word-wrap: break-word !important;"> <a href="https://mp.weixin.qq.com/s?__biz=Mzg5MzAyNDI4Mw==&amp;mid=2247483912&amp;idx=3&amp;sn=4e16e34c2a6d9acb72683fcaeeb98631&amp;scene=21#wechat_redirect" target="_blank" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 0px;right: auto;bottom: auto;"><img class="" data-copyright="0" data-cropselx1="0" data-cropselx2="56" data-cropsely1="0" data-cropsely2="56" data-ratio="1" src="/upload/f92c6b5f891800a8e8a41e6dad1950e4.jpg" data-type="jpeg" data-w="258" style="height: 56px;top: auto;left: auto;right: auto;bottom: auto;width: 56px;margin: 0px;box-sizing: border-box !important;word-wrap: break-word !important;visibility: visible !important;" title="1081255447.jpg"></span></a> </section> </section> </section> </section> </section> </section> <section class="xmteditor" style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn"></section> <section class="xmteditor" style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn"></section> <section class="" data-tools="135编辑器" data-id="91525" style="white-space: normal;"> <section style="margin: 8px;padding: 10px;max-width: 100%;box-sizing: border-box;line-height: 25.6px;border-radius: 10px;height: auto;box-shadow: rgb(221, 221, 221) 2px 2px 8px;display: -webkit-flex;word-wrap: break-word !important;"> <section style="max-width: 100%;flex: 0 0 2cm;height: 78px;width: 75px;box-sizing: border-box !important;word-wrap: break-word !important;"> <p style="max-width: 100%;min-height: 1em;text-align: center;box-sizing: border-box !important;word-wrap: break-word !important;"><a href="https://mp.weixin.qq.com/s?__biz=MzU2MzcxNzgwMg==&amp;mid=2247483650&amp;idx=3&amp;sn=7d8d32410010e2f58a8b0e9b15fa23ec&amp;scene=21#wechat_redirect" target="_blank" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 0px;right: auto;bottom: auto;"><img class="" data-copyright="0" data-cropselx1="0" data-cropselx2="76" data-cropsely1="0" data-cropsely2="74" data-ratio="1" src="/upload/c978be494b76a0bf054d19dfd16ca72d.jpg" data-type="jpeg" data-w="456" style="height: 76px;letter-spacing: 0.544px;text-align: justify;color: rgb(62, 62, 62);border-radius: 37px;width: 76px;box-shadow: rgb(170, 170, 170) 0px 0px 14px 0px;box-sizing: border-box !important;word-wrap: break-word !important;visibility: visible !important;margin: 0px;"></span></a></p> </section> <section style="padding-right: 10px;padding-left: 10px;max-width: 100%;box-sizing: border-box;flex: 1 1 auto;height: 55px;word-wrap: break-word !important;"> <section style="max-width: 100%;line-height: 35px;white-space: nowrap;box-sizing: border-box !important;word-wrap: break-word !important;"> <a href="https://mp.weixin.qq.com/s?__biz=MzU2MzcxNzgwMg==&amp;mid=2247483650&amp;idx=3&amp;sn=7d8d32410010e2f58a8b0e9b15fa23ec&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2"><span style="max-width: 100%;font-family: 宋体, SimSun;box-sizing: border-box !important;word-wrap: break-word !important;"><strong style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;letter-spacing: 0.544px;color: rgb(31, 73, 125);box-sizing: border-box !important;word-wrap: break-word !important;"><strong>Linux编程</strong></span></strong></span></a> </section> <section style="max-width: 100%;font-size: 13px;line-height: 20px;color: rgb(127, 127, 127);box-sizing: border-box !important;word-wrap: break-word !important;"> <a href="https://mp.weixin.qq.com/s?__biz=MzU2MzcxNzgwMg==&amp;mid=2247483650&amp;idx=3&amp;sn=7d8d32410010e2f58a8b0e9b15fa23ec&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2"><span style="max-width: 100%;letter-spacing: 0.544px;color: rgb(165, 165, 165);font-family: 黑体, SimHei;box-sizing: border-box !important;word-wrap: break-word !important;">点击右侧关注,免费入门到精通!</span></a> </section> </section> <section style="max-width: 100%;flex: 0 0 1.5cm;font-size: 15px;color: rgb(86, 187, 55);letter-spacing: 0px;text-align: center;line-height: 6;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="margin-top: 25px;max-width: 100px;vertical-align: middle;overflow: hidden;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="max-width: 100%;width: 55px;height: 30px;background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_gif/S7SAuLQzeTVj0YaoibbZqxicYkQrLnR3WtTQlHpHouqUmibjUCY9F5wpG0DmyMetZy9pjDdiabWo4XXdtCib3VcnI7w/640?wx_fmt=gif&quot;);background-size: 100% 100%;background-repeat: no-repeat;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="max-width: 100%;opacity: 0;box-sizing: border-box !important;word-wrap: break-word !important;"> <a href="https://mp.weixin.qq.com/s?__biz=MzU2MzcxNzgwMg==&amp;mid=2247483650&amp;idx=3&amp;sn=7d8d32410010e2f58a8b0e9b15fa23ec&amp;scene=21#wechat_redirect" target="_blank" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 0px;right: auto;bottom: auto;"><img class="" data-copyright="0" data-cropselx1="0" data-cropselx2="56" data-cropsely1="0" data-cropsely2="56" data-ratio="1" src="/upload/f92c6b5f891800a8e8a41e6dad1950e4.jpg" data-type="jpeg" data-w="258" style="height: 56px;top: auto;left: auto;right: auto;bottom: auto;width: 56px;margin: 0px;box-sizing: border-box !important;word-wrap: break-word !important;visibility: visible !important;" title="1081255447.jpg"></span></a> </section> </section> </section> </section> </section> </section> <section class="" data-tools="135编辑器" data-id="91525" style="white-space: normal;"> <section style="margin: 8px;padding: 10px;max-width: 100%;box-sizing: border-box;line-height: 25.6px;border-radius: 10px;height: auto;box-shadow: rgb(221, 221, 221) 2px 2px 8px;display: -webkit-flex;word-wrap: break-word !important;"> <section style="max-width: 100%;flex: 0 0 2cm;height: 78px;width: 75px;box-sizing: border-box !important;word-wrap: break-word !important;"> <p style="max-width: 100%;min-height: 1em;text-align: center;box-sizing: border-box !important;word-wrap: break-word !important;"><a href="https://mp.weixin.qq.com/s?__biz=MzU0MTg4NzYyMA==&amp;mid=2247483752&amp;idx=7&amp;sn=1fa48fb58dd6c18fccb8f0ed611352f7&amp;scene=21#wechat_redirect" target="_blank" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 0px;right: auto;bottom: auto;"><img class="" data-copyright="0" data-cropselx1="0" data-cropselx2="75" data-cropsely1="0" data-cropsely2="75" data-ratio="1" src="/upload/360f4cbf71e007952f8d91cb4b2f51b3.jpg" data-type="jpeg" data-w="640" style="height: 75px;letter-spacing: 0.544px;text-align: justify;color: rgb(62, 62, 62);border-radius: 37px;width: 75px;box-shadow: rgb(170, 170, 170) 0px 0px 14px 0px;margin: 0px;top: auto;left: auto;right: auto;bottom: auto;box-sizing: border-box !important;word-wrap: break-word !important;visibility: visible !important;"></span></a></p> </section> <section style="padding-right: 10px;padding-left: 10px;max-width: 100%;box-sizing: border-box;flex: 1 1 auto;height: 55px;word-wrap: break-word !important;"> <section style="max-width: 100%;line-height: 35px;white-space: nowrap;box-sizing: border-box !important;word-wrap: break-word !important;"> <a href="https://mp.weixin.qq.com/s?__biz=MzU0MTg4NzYyMA==&amp;mid=2247483752&amp;idx=7&amp;sn=1fa48fb58dd6c18fccb8f0ed611352f7&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2"><span style="color:#1f497d;font-family:宋体, SimSun;"><span style="letter-spacing: 0.544px;"><strong>程序员严选</strong></span></span></a> </section> <section style="max-width: 100%;font-size: 13px;line-height: 20px;color: rgb(127, 127, 127);box-sizing: border-box !important;word-wrap: break-word !important;"> <a href="https://mp.weixin.qq.com/s?__biz=MzU0MTg4NzYyMA==&amp;mid=2247483752&amp;idx=7&amp;sn=1fa48fb58dd6c18fccb8f0ed611352f7&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2"><span style="max-width: 100%;letter-spacing: 0.544px;color: rgb(165, 165, 165);font-family: 黑体, SimHei;box-sizing: border-box !important;word-wrap: break-word !important;">甄选正品好物,程序员生活指南!</span></a> </section> </section> <section style="max-width: 100%;flex: 0 0 1.5cm;font-size: 15px;color: rgb(86, 187, 55);letter-spacing: 0px;text-align: center;line-height: 6;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="margin-top: 25px;max-width: 100px;vertical-align: middle;overflow: hidden;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="max-width: 100%;width: 55px;height: 30px;background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_gif/S7SAuLQzeTVj0YaoibbZqxicYkQrLnR3WtTQlHpHouqUmibjUCY9F5wpG0DmyMetZy9pjDdiabWo4XXdtCib3VcnI7w/640?wx_fmt=gif&quot;);background-size: 100% 100%;background-repeat: no-repeat;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="max-width: 100%;opacity: 0;box-sizing: border-box !important;word-wrap: break-word !important;"> <a href="https://mp.weixin.qq.com/s?__biz=MzU0MTg4NzYyMA==&amp;mid=2247483752&amp;idx=7&amp;sn=1fa48fb58dd6c18fccb8f0ed611352f7&amp;scene=21#wechat_redirect" target="_blank" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 0px;right: auto;bottom: auto;"><img class="" data-copyright="0" data-cropselx1="0" data-cropselx2="56" data-cropsely1="0" data-cropsely2="56" data-ratio="1" src="/upload/f92c6b5f891800a8e8a41e6dad1950e4.jpg" data-type="jpeg" data-w="258" style="height: 56px;top: auto;left: auto;right: auto;bottom: auto;width: 56px;margin: 0px;box-sizing: border-box !important;word-wrap: break-word !important;visibility: visible !important;" title="1081255447.jpg"></span></a> </section> </section> </section> </section> </section> </section> <p style="letter-spacing: 1.5px;"><span style="font-size: 14px;color: rgb(136, 136, 136);"><br></span></p> <p style="letter-spacing: 1.5px;"><span style="font-size: 14px;color: rgb(136, 136, 136);">作者丨李平</span><br></p> <p style="letter-spacing: 1.5px;"><span style="font-size: 14px;color: rgb(136, 136, 136);">www.cnblogs.com/leefreeman/p/8315844.html</span></p> <p style="margin-top: 0em;margin-bottom: 0em;padding-right: 0.5em;padding-left: 0.5em;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;line-height: 1.5em;letter-spacing: 1.5px;"><br></p> </section> </section> <h3 style="margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;"><strong><span style="font-size: 15px;color: rgb(255, 76, 65);">一、InnoDB一棵B+树可以存放多少行数据?</span></strong></span></h3> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">InnoDB一棵B+树可以存放多少行数据?这个问题的简单回答是:约2千万。为什么是这么多呢?因为这是可以算出来的,要搞清楚这个问题,我们先从InnoDB索引数据结构、数据组织方式说起。</span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">我们都知道计算机在存储数据的时候,有最小存储单元,这就好比我们今天进行现金的流通最小单位是一毛。在计算机中磁盘存储数据最小单元是扇区,一个扇区的大小是512字节,而文件系统(例如XFS/EXT4)他的最小单元是块,一个块的大小是4k,而对于我们的InnoDB存储引擎也有自己的最小储存单元——页(Page),一个页的大小是16K。</span></p> <p style="margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="color: rgb(255, 76, 65);font-size: 15px;"><strong>二、下面几张图可以帮你理解最小存储单元:</strong></span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">文件系统中一个文件大小只有1个字节,但不得不占磁盘上4KB的空间。</span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><img class="" data-ratio="0.6979695431472082" src="/upload/f86118811200fbb18db4bba06425f97b.png" data-type="png" data-w="788" style="border-width: 0px;border-style: initial;border-color: initial;border-radius: 6px;height: auto !important;"></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">innodb的所有数据文件(后缀为ibd的文件),他的大小始终都是16384(16k)的整数倍。</span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><img class="" data-ratio="0.6312741312741312" src="/upload/a7977142b82510e7cc2eab7f13a3a785.png" data-type="png" data-w="1036" style="border-width: 0px;border-style: initial;border-color: initial;border-radius: 6px;height: auto !important;"></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">磁盘扇区、文件系统、InnoDB存储引擎都有各自的最小存储单元。</span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><img class="" data-ratio="0.3797709923664122" src="/upload/49477c911e10feba38c3662e8e841eb0.png" data-type="png" data-w="1048" style="border-width: 0px;border-style: initial;border-color: initial;border-radius: 6px;height: auto !important;"></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">在MySQL中我们的InnoDB页的大小默认是16k,当然也可以通过参数设置:</span></p> <p style="text-align: left;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><img class="rich_pages" data-ratio="0.4196642685851319" data-s="300,640" src="/upload/927efa03c2d8a9f6da560c012633d5b4.png" data-type="png" data-w="417" style=""></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">数据表中的数据都是存储在页中的,所以一个页中能存储多少行数据呢?假设一行数据的大小是1k,那么一个页可以存放16行这样的数据。</span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">如果数据库只按这样的方式存储,那么如何查找数据就成为一个问题,因为我们不知道要查找的数据存在哪个页中,也不可能把所有的页遍历一遍,那样太慢了。所以人们想了一个办法,用B+树的方式组织这些数据。如图所示:</span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><img class="" data-ratio="0.4521640091116173" src="/upload/12a8fc7fd7beae8d0cfa426fff375ca6.png" data-type="png" data-w="878" style="border-width: 0px;border-style: initial;border-color: initial;border-radius: 6px;height: auto !important;"></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">我们先将数据记录按主键进行排序,分别存放在不同的页中(为了便于理解我们这里一个页中只存放3条记录,实际情况可以存放很多),除了存放数据的页以外,还有存放键值+指针的页,如图中page number=3的页,该页存放键值和指向数据页的指针,这样的页由N个键值+指针组成。当然它也是排好序的。这样的数据组织形式,我们称为索引组织表。现在来看下,要查找一条数据,怎么查?</span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">如<strong>select * from user where id=5</strong>;</span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">这里id是主键,我们通过这棵B+树来查找,首先找到根页,你怎么知道user表的根页在哪呢?其实每张表的根页位置在表空间文件中是固定的,即page number=3的页(这点我们下文还会进一步证明),找到根页后通过二分查找法,定位到id=5的数据应该在指针P5指向的页中,那么进一步去page number=5的页中查找,同样通过二分查询法即可找到id=5的记录:</span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;"><strong style="color: rgb(0, 0, 0);">| 5 | zhao2 | 27 |</strong></span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">现在我们清楚了InnoDB中主键索引B+树是如何组织数据、查询数据的,我们总结一下:</span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">1、InnoDB存储引擎的最小存储单元是页,页可以用于存放数据也可以用于存放键值+指针,在B+树中叶子节点存放数据,非叶子节点存放键值+指针。</span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">2、索引组织表通过非叶子节点的二分查找法以及指针确定数据在哪个页中,进而在去数据页中查找到需要的数据;</span></p> <h3 style="margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;"><strong><span style="font-size: 15px;color: rgb(255, 76, 65);">三、那么回到我们开始的问题,通常一棵B+树可以存放多少行数据?</span></strong></span></h3> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">这里我们先假设B+树高为2,即存在一个根节点和若干个叶子节点,那么这棵B+树的存放总记录数为:<strong style="color: rgb(0, 0, 0);">根节点指针数*单个叶子节点记录行数</strong>。</span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">上文我们已经说明单个叶子节点(页)中的记录数=16K/1K=16。(这里假设一行记录的数据大小为1k,实际上现在很多互联网业务数据记录大小通常就是1K左右)。</span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;"><strong style="color: rgb(0, 0, 0);">那么现在我们需要计算出非叶子节点能存放多少指针?</strong></span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">其实这也很好算,我们假设主键ID为bigint类型,长度为8字节,而指针大小在InnoDB源码中设置为6字节,这样一共14字节,我们一个页中能存放多少这样的单元,其实就代表有多少指针,即<strong style="color: rgb(0, 0, 0);">16384/14=1170</strong>。那么可以算出一棵高度为2的B+树,能存放<strong style="color: rgb(0, 0, 0);">1170*16=18720</strong>条这样的数据记录。</span></p> <p style="margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;"><strong><span style="font-size: 15px;color: rgb(74, 74, 74);">根据同样的原理我们可以算出一个高度为3的B+树可以存放</span></strong><span style="font-size: 15px;color: rgb(74, 74, 74);">:</span><strong style="color: rgb(74, 74, 74);font-size: 14px;">1170*1170*16=21902400</strong><span style="font-size: 15px;color: rgb(74, 74, 74);">条这样的记录。</span></span></p> <p style="margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="color: rgb(74, 74, 74);font-size: 15px;">所以在InnoDB中B+树高度一般为1-3层,它就能满足千万级的数据存储。在查找数据时一次页的查找代表一次IO,所以通过主键索引查询通常只需要1-3次IO操作即可查找到数据。</span></p> <h3 style="margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="color: rgb(255, 76, 65);font-size: 15px;"><strong>四、怎么得到InnoDB主键索引B+树的高度?</strong></span></h3> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">上面我们通过推断得出B+树的高度通常是1-3,下面我们从另外一个侧面证明这个结论。在InnoDB的表空间文件中,约定<strong style="color: rgb(0, 0, 0);">page number为3</strong>的代表主键索引的根页,而在根页偏移量为<strong style="color: rgb(0, 0, 0);">64</strong>的地方存放了该B+树的page level。如果page level为1,树高为2,page level为2,则树高为3。即B+树的高度=page level+1;下面我们将从实际环境中尝试找到这个page level。</span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">在实际操作之前,你可以通过InnoDB元数据表确认主键索引根页的page number为3,你也可以从《InnoDB存储引擎》这本书中得到确认。</span></p> <p style="text-align: left;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><img class="rich_pages" data-ratio="0.38146551724137934" data-s="300,640" src="/upload/a37c70734975770e9556e715dee96dd2.png" data-type="png" data-w="464" style=""></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">执行结果:</span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><img class="" data-ratio="0.15526315789473685" src="/upload/bc0040b110d74ace8ed7d2d29037f8fb.jpg" data-type="jpeg" data-w="1520" style="border-width: 0px;border-style: initial;border-color: initial;border-radius: 6px;height: auto !important;"></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">可以看出数据库dbt3下的customer表、lineitem表主键索引根页的page number均为3,而其他的二级索引page number为4。关于二级索引与主键索引的区别请参考MySQL相关书籍,本文不在此介绍。</span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;"><strong style="color: rgb(0, 0, 0);">下面我们对数据库表空间文件做想相关的解析:</strong></span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><img class="" data-ratio="0.3019538188277087" src="/upload/6e8a03fd83d5001420edd73ac5068b63.png" data-type="png" data-w="1126" style="border-width: 0px;border-style: initial;border-color: initial;border-radius: 0px;height: auto !important;"></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">因为主键索引B+树的根页在整个表空间文件中的第3个页开始,所以可以算出它在文件中的偏移量:<strong style="color: rgb(0, 0, 0);">16384*3=49152(16384为页大小)</strong>。</span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">另外根据《InnoDB存储引擎》中描述在根页的64偏移量位置前2个字节,保存了page level的值,因此我们想要的page level的值在整个文件中的偏移量为:<strong style="color: rgb(0, 0, 0);">16384*3+64=49152+64=49216</strong>,前2个字节中。</span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;"><strong style="color: rgb(0, 0, 0);">接下来我们用hexdump工具,查看表空间文件指定偏移量上的数据:</strong></span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><img class="" data-ratio="0.3431151241534989" src="/upload/7e0216c91fefb3d19965e1825bd0b43b.png" data-type="png" data-w="886" style="border-width: 0px;border-style: initial;border-color: initial;border-radius: 6px;height: auto !important;"></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;"><strong style="color: rgb(0, 0, 0);">linetem表的page level为2,B+树高度为page level+1=3;</strong><strong style="color: rgb(0, 0, 0);">region表的page level为0,B+树高度为page level+1=1;</strong><strong style="color: rgb(0, 0, 0);">customer表的page level为2,B+树高度为page level+1=3;</strong></span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;"><strong style="color: rgb(0, 0, 0);">这三张表的数据量如下:</strong></span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><img class="" data-ratio="0.9975062344139651" src="/upload/61452431f5fff50307b056f589e26350.png" data-type="png" data-w="802" style="border-width: 0px;border-style: initial;border-color: initial;border-radius: 6px;height: 435px;width: 436px;"></p> <p style="margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;"><strong><span style="font-size: 15px;color: rgb(255, 76, 65);">五、小结</span></strong></span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">lineitem表的数据行数为600多万,B+树高度为3,customer表数据行数只有15万,B+树高度也为3。可以看出尽管数据量差异较大,这两个表树的高度都是3,换句话说这两个表通过索引查询效率并没有太大差异,因为都只需要做3次IO。那么如果有一张表行数是一千万,那么他的B+树高度依旧是3,查询效率仍然不会相差太大。</span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">region表只有5行数据,当然他的B+树高度为1。</span></p> <h3 style="margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><strong><span style="color: rgb(255, 76, 65);font-size: 15px;">六、最后回顾一道面试题</span></strong></h3> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">有一道MySQL的面试题,为什么MySQL的索引要使用B+树而不是其它树形结构?比如B树?</span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">现在这个问题的复杂版本可以参考本文;</span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;"><strong style="color: rgb(0, 0, 0);">他的简单版本回答是:</strong></span></p> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">因为B树不管叶子节点还是非叶子节点,都会保存数据,这样导致在非叶子节点中能保存的指针数量变少(有些资料也称为扇出),指针少的情况下要保存大量数据,只能增加树的高度,导致IO操作变多,查询性能变低;</span></p> <h3 style="margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="color: rgb(255, 76, 65);font-size: 15px;"><strong>七、总结</strong></span></h3> <p style="font-size: 14px;white-space: pre-line;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">本文从一个问题出发,逐步介绍了InnoDB索引组织表的原理、查询方式,并结合已有知识,回答该问题,结合实践来证明。当然为了表述简单易懂,文中忽略了一些细枝末节,比如一个页中不可能所有空间都用于存放数据,它还会存放一些少量的其他字段比如page level,index number等等,另外还有页的填充因子也导致一个页不可能全部用于保存数据。关于二级索引数据存取方式可以参考MySQL相关书籍,他的要点是结合主键索引进行回表查询。</span></p> <p style="margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;color: rgb(255, 76, 65);">参考资料:</span></p> <p style="margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">1、《MySQL技术内幕:InnoDB存储引擎》</span></p> <p style="margin-top: 15px;margin-bottom: 15px;line-height: 2em;letter-spacing: 1.5px;"><span style="font-size: 15px;">2、http://www.innomysql.com/查看-innodb表中每个的索引高度/</span></p> <p><br></p> <section class="xmteditor" style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn"></section> <section class="xmteditor" style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn"></section> <p style="text-align: center;letter-spacing: 1.5px;"><span style="background-color: rgb(0, 213, 255);color: rgb(255, 255, 255);"><strong><span style="background-color: rgb(0, 213, 255);font-size: 20px;">&nbsp;推荐↓↓↓&nbsp;</span></strong></span></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin: 80px 0% 10px;text-align: center;box-sizing: border-box;"> <section style="display: inline-block;vertical-align: top;width: 100%;padding-right: 10px;padding-bottom: 10px;padding-left: 10px;border-radius: 12px;border-width: 0px;border-style: none;border-color: rgb(62, 62, 62);box-sizing: border-box;background-color: rgba(205, 234, 242, 0.96);"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: -70px;margin-right: 0%;margin-left: 0%;box-sizing: border-box;"> <section style="max-width: 100%;vertical-align: middle;display: inline-block;width: 60%;border-width: 5px;border-style: solid;border-color: rgba(100, 217, 251, 0.96);border-radius: 0px;box-sizing: border-box;"> <img class="" data-copyright="0" data-cropselx1="0" data-cropselx2="313" data-cropsely1="0" data-cropsely2="313" data-ratio="1" src="/upload/acfa151057cb2f415480e4b024cfca07.jpg" data-type="jpeg" data-w="1280" style="vertical-align: middle;width: 313px;box-sizing: border-box;height: 313px;"> </section> </section> </section> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="box-sizing: border-box;"> <section style="display: inline-block;vertical-align: top;width: 16%;box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-right: 0%;margin-left: 0%;font-size: 10px;box-sizing: border-box;"> <section style="padding: 4px;display: inline-block;border-radius: 100%;box-sizing: border-box;background-color: rgba(0, 0, 0, 0);"> <section style="border-radius: 100%;border-width: 2px;border-style: solid;border-color: rgb(0, 0, 0);width: 2em;height: 2em;font-size: 13px;line-height: 1.8em;box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong style="box-sizing: border-box;">长</strong></p> </section> </section> </section> </section> </section> <section style="display: inline-block;vertical-align: top;width: 16%;box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-right: 0%;margin-left: 0%;font-size: 10px;box-sizing: border-box;"> <section style="padding: 4px;display: inline-block;border-radius: 100%;box-sizing: border-box;background-color: rgba(0, 0, 0, 0);"> <section style="border-radius: 100%;border-width: 2px;border-style: solid;border-color: rgb(0, 0, 0);width: 2em;height: 2em;font-size: 13px;line-height: 1.8em;box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong style="box-sizing: border-box;">按</strong></p> </section> </section> </section> </section> </section> <section style="display: inline-block;vertical-align: top;width: 16%;box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-right: 0%;margin-left: 0%;font-size: 10px;box-sizing: border-box;"> <section style="padding: 4px;display: inline-block;border-radius: 100%;box-sizing: border-box;background-color: rgba(0, 0, 0, 0);"> <section style="border-radius: 100%;border-width: 2px;border-style: solid;border-color: rgb(0, 0, 0);width: 2em;height: 2em;font-size: 13px;line-height: 1.8em;box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong style="box-sizing: border-box;">关</strong></p> </section> </section> </section> </section> </section> <section style="display: inline-block;vertical-align: top;width: 16%;box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-right: 0%;margin-left: 0%;font-size: 10px;box-sizing: border-box;"> <section style="padding: 4px;display: inline-block;border-radius: 100%;box-sizing: border-box;background-color: rgba(0, 0, 0, 0);"> <section style="border-radius: 100%;border-width: 2px;border-style: solid;border-color: rgb(0, 0, 0);width: 2em;height: 2em;font-size: 13px;line-height: 1.8em;box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong style="box-sizing: border-box;">注</strong></p> </section> </section> </section> </section> </section> </section> </section> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-right: 0%;margin-left: 0%;box-sizing: border-box;"> <section style="font-size: 14px;color: rgb(68, 68, 68);padding-right: 15px;padding-left: 15px;box-sizing: border-box;"> <p style="letter-spacing: 0px;"><strong><span style="font-size: 18px;">👉</span></strong><span style="font-size: 17px;"><strong>【</strong></span><a href="https://mp.weixin.qq.com/s?__biz=MzUzMDc0NzU4Nw==&amp;mid=2247483768&amp;idx=1&amp;sn=4ef4f1510616baa395c507e32bb439d7&amp;scene=21#wechat_redirect" target="_blank" style="text-decoration: underline;color: rgb(255, 79, 121);font-size: 17px;" data-linktype="2"><span style="color: rgb(255, 79, 121);font-size: 17px;"><strong>16个技术公众号</strong></span></a><span style="font-size: 17px;"><strong>】都在这里!</strong></span></p> </section> </section> </section> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin: 8px 0% 15px;box-sizing: border-box;"> <section style="height: 1px;box-sizing: border-box;background-color: rgb(0, 0, 0);"></section> </section> </section> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="box-sizing: border-box;"> <section style="text-align: left;box-sizing: border-box;"> <p style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(136, 136, 136);font-size: 15px;">涵盖:程序员大咖、源码共读、程序员共读、数据结构与算法、黑客技术和网络安全、大数据科技、编程前端、Java、Python、Web编程开发、Android、iOS开发、Linux、数据库研发、幽默程序员等。</span></p> </section> </section> </section> </section> </section> </section> </section> <section data-role="outer" label="Powered by 135editor.com" style="max-width: 100%;font-size: 16px;font-family: 微软雅黑;box-sizing: border-box !important;word-wrap: break-word !important;"> <section class="" data-tools="135编辑器" data-id="94250" style="max-width: 100%;box-sizing: border-box;border-width: 0px;border-style: none;border-color: initial;word-wrap: break-word !important;"> <section class="" data-tools="135编辑器" data-id="91842" style="max-width: 100%;box-sizing: border-box;border-width: 0px;border-style: none;border-color: initial;word-wrap: break-word !important;"> <section style="max-width: 100%;text-align: right;width: auto;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="max-width: 100%;display: inline-block;clear: both;box-sizing: border-box !important;word-wrap: break-word !important;"> <section class="" data-brushtype="text" style="padding: 18px 15px 20px 10px;max-width: 100%;box-sizing: border-box;color: rgb(86, 146, 214);background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/ol72Wnba7fLkfGhCjKwHfZOmHMkVTIomF2Oicxr71sYib6EI8Hyhwtntt683HCer9AfzlYzWQ12A8LiaTuUutwvtg/640?wx_fmt=png&quot;);background-repeat: no-repeat;text-align: center;background-size: 100% 100%;letter-spacing: 1.5px;word-wrap: break-word !important;"> <section style="max-width: 100%;display: flex;justify-content: center;align-items: center;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="margin-left: 2px;max-width: 100%;width: 20px;box-sizing: border-box !important;word-wrap: break-word !important;"> <img class="" data-ratio="0.8936170212765957" src="/upload/737696b8734b6f688ae95660c4411917.png" data-type="png" data-w="47" style="margin-bottom: -6px;box-sizing: border-box !important;word-wrap: break-word !important;width: 20px !important;visibility: visible !important;"> </section> <section class="" data-brushtype="text" style="max-width: 100%;font-size: 14px;color: rgb(51, 51, 51);box-sizing: border-box !important;word-wrap: break-word !important;"> 万水千山总是情,点个 “ <strong style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(0, 112, 192);box-sizing: border-box !important;word-wrap: break-word !important;">在看</span></strong>” 行不行 </section> </section> </section> </section> </section> </section> </section> </section> </section> </section>

千万级并发下,淘宝服务端架构如何演进?

作者:微信小助手

<section style="box-sizing: border-box;font-size: 16px;"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;" powered-by="xiumi.us"> <section style="padding-top: 10px;padding-right: 10px;padding-left: 10px;box-sizing: border-box;background-color: rgb(239, 239, 239);"> <span style="display: inline-block;width: 5%;line-height: 0.8;font-weight: bolder;font-size: 48px;box-sizing: border-box;" title="" opera-tn-ra-cell="_$.pages:0.layers:0.comps:0.txt1"> <section style="box-sizing: border-box;"> “ </section></span> <section style="display: inline-block;vertical-align: top;float: right;width: 90%;line-height: 1.5;font-size: 15px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><span style="letter-spacing: 1px;">本文以淘宝为例,介绍从一百个并发到千万级并发下服务端架构的演进过程,同时列举出每个演进阶段遇到的相关技术,让大家对架构的演进有一个整体的认知,文章最后汇总了一些架构设计的原则。</span></p> </section> <section style="clear: both;box-sizing: border-box;"></section> </section> </section> </section> <p style="line-height: 1.75em;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 27.2px;white-space: normal;text-align: center;margin-bottom: 5px;"><img class="rich_pages" data-copyright="0" data-ratio="0.4824961948249619" data-s="300,640" src="/upload/5b42ea024918c0566ef2be2813540edd.png" data-type="png" data-w="657"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">在介绍架构之前,为了避免部分读者对架构设计中的一些概念不了解,下面对几个最基础的概念进行介绍:</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">①分布式:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">系统中的多个模块在不同服务器上部署,即可称为分布式系统,如 Tomcat 和数据库分别部署在不同的服务器上,或两个相同功能的 Tomcat 分别部署在不同服务器上。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">②高可用:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">系统中部分节点失效时,其他节点能够接替它继续提供服务,则可认为系统具有高可用性。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">③集群:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">一个特定领域的软件部署在多台服务器上并作为一个整体提供一类服务,这个整体称为集群。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如 Zookeeper 中的 Master 和 Slave 分别部署在多台服务器上,共同组成一个整体提供集中配置服务。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在常见的集群中,客户端往往能够连接任意一个节点获得服务,并且当集群中一个节点掉线时,其他节点往往能够自动的接替它继续提供服务,这时候说明集群具有高可用性。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">④负载均衡:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">请求发送到系统时,通过某些方式把请求均匀分发到多个节点上,使系统中每个节点能够均匀的处理请求负载,则可认为系统是负载均衡的。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">⑤正向代理和反向代理:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">系统内部要访问外部网络时,统一通过一个代理服务器把请求转发出去,在外部网络看来就是代理服务器发起的访问,此时代理服务器实现的是正向代理。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">当外部请求进入系统时,代理服务器把该请求转发到系统中的某台服务器上,对外部请求来说,与之交互的只有代理服务器,此时代理服务器实现的是反向代理。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">简单来说,正向代理是代理服务器代替系统内部来访问外部网络的过程,反向代理是外部请求访问系统时通过代理服务器转发到内部服务器的过程。</span></p> <h1 style="white-space: normal;line-height: normal;"><br></h1> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="margin-top: 0.5em;margin-bottom: 0.5em;border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;line-height: 1.2;box-sizing: border-box;"> <section style="margin-bottom: -1px;display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">架构演进</p> </section> </section> </section> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">单机架构</span></strong></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="" data-type="png" title="clipboard.png" data-ratio="0.3626943005181347" data-w="579" src="/upload/b8d823769b9cdb661b8922208ddf7243.png" style="cursor: pointer;display: inline;box-sizing: border-box !important;word-wrap: break-word !important;width: 579px !important;visibility: visible !important;"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">以淘宝作为例子:在网站最初时,应用数量与用户数都较少,可以把 Tomcat 和数据库部署在同一台服务器上。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">浏览器往 www.taobao.com 发起请求时,首先经过 DNS 服务器(域名系统)把域名转换为实际 IP 地址 10.102.4.1,浏览器转而访问该 IP 对应的 Tomcat。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">随着用户数的增长,Tomcat 和数据库之间竞争资源,单机性能不足以支撑业务。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <h2 style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第一次演进:Tomcat 与数据库分开部署</span></strong></h2> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="" data-type="png" title="clipboard.png" data-ratio="0.35628227194492257" data-w="581" src="/upload/29f4c7009a3062d9feeeab2abc55f50d.png" style="cursor: pointer;display: inline;box-sizing: border-box !important;word-wrap: break-word !important;width: 581px !important;visibility: visible !important;"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Tomcat 和数据库分别独占服务器资源,显著提高两者各自性能。</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">随着用户数的增长,并发读写数据库成为瓶颈。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <h2 style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第二次演进:引入本地缓存和分布式缓存</span></strong></h2> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="" data-type="png" title="clipboard.png" data-ratio="0.5524956970740104" data-w="581" src="/upload/133926563349a45899a93b995fae6d06.png" style="cursor: pointer;display: inline;box-sizing: border-box !important;word-wrap: break-word !important;width: 581px !important;visibility: visible !important;"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在 Tomcat 同服务器上或同 JVM 中增加本地缓存,并在外部增加分布式缓存,缓存热门商品信息或热门商品的 HTML 页面等。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">通过缓存能把绝大多数请求在读写数据库前拦截掉,大大降低数据库压力。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">其中涉及的技术包括:使用 Memcached 作为本地缓存,使用 Redis 作为分布式缓存,还会涉及缓存一致性、缓存穿透/击穿、缓存雪崩、热点数据集中失效等问题。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">缓存抗住了大部分的访问请求,随着用户数的增长,并发压力主要落在单机的 Tomcat 上,响应逐渐变慢。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <h2 style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第三次演进:引入反向代理实现负载均衡</span></strong></h2> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="" data-type="png" title="clipboard.png" data-ratio="0.8229426433915212" data-w="401" src="/upload/ecda9be97c64bf2f483eeb51324e1004.png" style="cursor: pointer;display: inline;box-sizing: border-box !important;word-wrap: break-word !important;width: 401px !important;visibility: visible !important;"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在多台服务器上分别部署 Tomcat,使用反向代理软件(Nginx)把请求均匀分发到每个 Tomcat 中。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">此处假设 Tomcat 最多支持 100 个并发,Nginx 最多支持 50000 个并发,那么理论上 Nginx 把请求分发到 500 个 Tomcat 上,就能抗住 50000 个并发。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">其中涉及的技术包括:Nginx、HAProxy,两者都是工作在网络第七层的反向代理软件,主要支持 HTTP 协议,还会涉及 Session 共享、文件上传下载的问题。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">反向代理使应用服务器可支持的并发量大大增加,但并发量的增长也意味着更多请求穿透到数据库,单机的数据库最终成为瓶颈。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <h2 style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第四次演进:数据库读写分离</span></strong></h2> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="" data-type="png" title="clipboard.png" data-ratio="0.6861826697892272" data-w="427" src="/upload/8924548f309eb01e1adfdc6ada752dca.png" style="cursor: pointer;display: inline;box-sizing: border-box !important;word-wrap: break-word !important;width: 427px !important;visibility: visible !important;"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">把数据库划分为读库和写库,读库可以有多个,通过同步机制把写库的数据同步到读库,对于需要查询最新写入数据场景,可通过在缓存中多写一份,通过缓存获得最新数据。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">其中涉及的技术包括:Mycat,它是数据库中间件,可通过它来组织数据库的分离读写和分库分表,客户端通过它来访问下层数据库,还会涉及数据同步,数据一致性的问题。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">业务逐渐变多,不同业务之间的访问量差距较大,不同业务直接竞争数据库,相互影响性能。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <h2 style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第五次演进:数据库按业务分库</span></strong></h2> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="" data-type="png" title="clipboard.png" data-ratio="0.8230912476722533" data-w="537" src="/upload/1abebdecb82aac5f22915c27c85feeef.png" style="cursor: pointer;display: inline;box-sizing: border-box !important;word-wrap: break-word !important;width: 537px !important;visibility: visible !important;"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">把不同业务的数据保存到不同的数据库中,使业务之间的资源竞争降低,对于访问量大的业务,可以部署更多的服务器来支撑。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这样同时导致跨业务的表无法直接做关联分析,需要通过其他途径来解决,但这不是本文讨论的重点,有兴趣的可以自行搜索解决方案。</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">随着用户数的增长,单机的写库会逐渐达到性能瓶颈。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <h2 style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第六次演进:把大表拆分为小表</span></strong></h2> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="" data-type="png" title="clipboard.png" data-ratio="0.7568493150684932" data-w="584" src="/upload/5e4ecd693a65b39632ad22d0f34d191b.png" style="cursor: pointer;display: inline;box-sizing: border-box !important;word-wrap: break-word !important;width: 584px !important;visibility: visible !important;"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">比如针对评论数据,可按照商品 ID 进行 Hash,路由到对应的表中存储;针对支付记录,可按照小时创建表,每个小时表继续拆分为小表,使用用户 ID 或记录编号来路由数据。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">只要实时操作的表数据量足够小,请求能够足够均匀的分发到多台服务器上的小表,那数据库就能通过水平扩展的方式来提高性能。其中前面提到的 Mycat 也支持在大表拆分为小表情况下的访问控制。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这种做法显著的增加了数据库运维的难度,对 DBA 的要求较高。数据库设计到这种结构时,已经可以称为分布式数据库。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">但是这只是一个逻辑的数据库整体,数据库里不同的组成部分是由不同的组件单独来实现的。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如分库分表的管理和请求分发,由 Mycat 实现,SQL 的解析由单机的数据库实现,读写分离可能由网关和消息队列来实现,查询结果的汇总可能由数据库接口层来实现等等,这种架构其实是 MPP(大规模并行处理)架构的一类实现。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">目前开源和商用都已经有不少 MPP 数据库,开源中比较流行的有 Greenplum、TiDB、Postgresql XC、HAWQ 等,商用的如南大通用的 GBase、睿帆科技的雪球 DB、华为的 LibrA 等等。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">不同的 MPP 数据库的侧重点也不一样,如 TiDB 更侧重于分布式 OLTP 场景,Greenplum 更侧重于分布式 OLAP 场景。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这些 MPP 数据库基本都提供了类似 Postgresql、Oracle、MySQL 那样的 SQL 标准支持能力,能把一个查询解析为分布式的执行计划分发到每台机器上并行执行,最终由数据库本身汇总数据进行返回。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">也提供了诸如权限管理、分库分表、事务、数据副本等能力,并且大多能够支持 100 个节点以上的集群,大大降低了数据库运维的成本,并且使数据库也能够实现水平扩展。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">数据库和 Tomcat 都能够水平扩展,可支撑的并发大幅提高,随着用户数的增长,最终单机的 Nginx 会成为瓶颈。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <h2 style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第七次演进:使用 LVS 或 F5 来使多个 Nginx 负载均衡</span></strong></h2> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="" data-type="png" title="clipboard.png" data-ratio="0.8743633276740238" data-w="589" src="/upload/e1a649f549f0c630ff48ffc7e52d8435.png" style="cursor: pointer;display: inline;box-sizing: border-box !important;word-wrap: break-word !important;width: 589px !important;visibility: visible !important;"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">由于瓶颈在 Nginx,因此无法通过两层的 Nginx 来实现多个 Nginx 的负载均衡。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">图中的 LVS 和 F5 是工作在网络第四层的负载均衡解决方案,其中 LVS 是软件,运行在操作系统内核态,可对 TCP 请求或更高层级的网络协议进行转发。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">因此支持的协议更丰富,并且性能也远高于 Nginx,可假设单机的 LVS 可支持几十万个并发的请求转发;F5 是一种负载均衡硬件,与 LVS 提供的能力类似,性能比 LVS 更高,但价格昂贵。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">由于 LVS 是单机版的软件,若 LVS 所在服务器宕机则会导致整个后端系统都无法访问,因此需要有备用节点。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">可使用 Keepalived 软件模拟出虚拟 IP,然后把虚拟 IP 绑定到多台 LVS 服务器上,浏览器访问虚拟 IP 时,会被路由器重定向到真实的 LVS 服务器。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">当主 LVS 服务器宕机时,Keepalived 软件会自动更新路由器中的路由表,把虚拟 IP 重定向到另外一台正常的 LVS 服务器,从而达到 LVS 服务器高可用的效果。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">此处需要注意的是,上图中从 Nginx 层到 Tomcat 层这样画并不代表全部 Nginx 都转发请求到全部的 Tomcat。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在实际使用时,可能会是几个 Nginx 下面接一部分的 Tomcat,这些 Nginx 之间通过 Keepalived 实现高可用,其他的 Nginx 接另外的 Tomcat,这样可接入的 Tomcat 数量就能成倍的增加。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">由于 LVS 也是单机的,随着并发数增长到几十万时,LVS 服务器最终会达到瓶颈,此时用户数达到千万甚至上亿级别,用户分布在不同的地区,与服务器机房距离不同,导致了访问的延迟会明显不同。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <h2 style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第八次演进:通过 DNS 轮询实现机房间的负载均衡</span></strong></h2> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="" data-type="png" title="clipboard.png" data-ratio="0.711436170212766" data-w="752" src="/upload/a6149ca7099ee7f976b4530991215c34.png" style="cursor: pointer;display: inline;box-sizing: border-box !important;word-wrap: break-word !important;width: 677px !important;visibility: visible !important;"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在 DNS 服务器中可配置一个域名对应多个 IP 地址,每个 IP 地址对应到不同的机房里的虚拟 IP。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">当用户访问 www.taobao.com 时,DNS 服务器会使用轮询策略或其他策略,来选择某个 IP 供用户访问。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">此方式能实现机房间的负载均衡,至此,系统可做到机房级别的水平扩展,千万级到亿级的并发量都可通过增加机房来解决,系统入口处的请求并发量不再是问题。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">随着数据的丰富程度和业务的发展,检索、分析等需求越来越丰富,单单依靠数据库无法解决如此丰富的需求。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <h2 style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第九次演进:引入 NoSQL 数据库和搜索引擎等技术</span></strong></h2> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="" data-type="png" title="clipboard.png" data-ratio="0.6467153284671533" data-w="685" src="/upload/559b610c515377ba06d466e5a74acfba.png" style="cursor: pointer;display: inline;box-sizing: border-box !important;word-wrap: break-word !important;width: 677px !important;visibility: visible !important;"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">当数据库中的数据多到一定规模时,数据库就不适用于复杂的查询了,往往只能满足普通查询的场景。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">对于统计报表场景,在数据量大时不一定能跑出结果,而且在跑复杂查询时会导致其他查询变慢,对于全文检索、可变数据结构等场景,数据库天生不适用。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">因此需要针对特定的场景,引入合适的解决方案。如对于海量文件存储,可通过分布式文件系统 HDFS 解决。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">对于 Key Value 类型的数据,可通过 HBase 和 Redis 等方案解决,对于全文检索场景,可通过搜索引擎如 ElasticSearch 解决,对于多维分析场景,可通过 Kylin 或 Druid 等方案解决。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">当然,引入更多组件同时会提高系统的复杂度,不同的组件保存的数据需要同步,需要考虑一致性的问题,需要有更多的运维手段来管理这些组件等。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">引入更多组件解决了丰富的需求,业务维度能够极大扩充,随之而来的是一个应用中包含了太多的业务代码,业务的升级迭代变得困难。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <h2 style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第十次演进:大应用拆分为小应用</span></strong></h2> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="" data-type="png" title="clipboard.png" data-ratio="0.6622998544395924" data-w="687" src="/upload/c2f7a4e9233501a4848f6f6054567a56.png" style="cursor: pointer;display: inline;box-sizing: border-box !important;word-wrap: break-word !important;width: 677px !important;visibility: visible !important;"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">按照业务板块来划分应用代码,使单个应用的职责更清晰,相互之间可以做到独立升级迭代。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这时候应用之间可能会涉及到一些公共配置,可以通过分布式配置中心 Zookeeper 来解决。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">不同应用之间存在共用的模块,由应用单独管理会导致相同代码存在多份,导致公共功能升级时全部应用代码都要跟着升级。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <h2 style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第十一次演进:复用的功能抽离成微服务</span></strong></h2> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="" data-type="png" title="clipboard.png" data-ratio="0.7859237536656891" data-w="682" src="/upload/98f9dad8c5b98aba22865095ea2ea032.png" style="cursor: pointer;display: inline;box-sizing: border-box !important;word-wrap: break-word !important;width: 677px !important;visibility: visible !important;"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如用户管理、订单、支付、鉴权等功能在多个应用中都存在,那么可以把这些功能的代码单独抽取出来形成一个单独的服务来管理。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这样的服务就是所谓的微服务,应用和服务之间通过 HTTP、TCP 或 RPC 请求等多种方式来访问公共服务,每个单独的服务都可以由单独的团队来管理。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">此外,可以通过 Dubbo、Spring Cloud 等框架实现服务治理、限流、熔断、降级等功能,提高服务的稳定性和可用性。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">不同服务的接口访问方式不同,应用代码需要适配多种访问方式才能使用服务。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">此外,应用访问服务,服务之间也可能相互访问,调用链将会变得非常复杂,逻辑变得混乱。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <h2 style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第十二次演进:引入企业服务总线 ESB 屏蔽服务接口的访问差异</span></strong></h2> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="" data-type="png" title="clipboard.png" data-ratio="0.8556998556998557" data-w="693" src="/upload/f4375f65e0efd4e4add3582316d5ca6b.png" style="cursor: pointer;display: inline;box-sizing: border-box !important;word-wrap: break-word !important;width: 677px !important;visibility: visible !important;"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">通过 ESB 统一进行访问协议转换,应用统一通过 ESB 来访问后端服务,服务与服务之间也通过 ESB 来相互调用,以此降低系统的耦合程度。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这种单个应用拆分为多个应用,公共服务单独抽取出来来管理,并使用企业消息总线来解除服务之间耦合问题的架构,就是所谓的 SOA(面向服务)架构,这种架构与微服务架构容易混淆,因为表现形式十分相似。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">个人理解,微服务架构更多是指把系统里的公共服务抽取出来单独运维管理的思想,而 SOA 架构则是指一种拆分服务并使服务接口访问变得统一的架构思想,SOA 架构中包含了微服务的思想。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">业务不断发展,应用和服务都会不断变多,应用和服务的部署变得复杂,同一台服务器上部署多个服务还要解决运行环境冲突的问题。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">此外,对于如大促这类需要动态扩缩容的场景,需要水平扩展服务的性能,就需要在新增的服务上准备运行环境,部署服务等,运维将变得十分困难。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <h2 style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第十三次演进:引入容器化技术实现运行环境隔离与动态服务管理</span></strong></h2> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="" data-type="png" title="clipboard.png" data-ratio="0.951937984496124" data-w="645" src="/upload/58e7caf953fad9e924478fde2b52865.png" style="cursor: pointer;display: inline;box-sizing: border-box !important;word-wrap: break-word !important;width: 645px !important;visibility: visible !important;"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">目前最流行的容器化技术是 Docker,最流行的容器管理服务是 Kubernetes(K8S),应用/服务可以打包为 Docker 镜像,通过 K8S 来动态分发和部署镜像。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Docker 镜像可理解为一个能运行你的应用/服务的最小的操作系统,里面放着应用/服务的运行代码,运行环境根据实际的需要设置好。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">把整个“操作系统”打包为一个镜像后,就可以分发到需要部署相关服务的机器上,直接启动 Docker 镜像就可以把服务起来,使服务的部署和运维变得简单。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在大促之前,可以在现有的机器集群上划分出服务器来启动 Docker 镜像,增强服务的性能,大促过后就可以关闭镜像,对机器上的其他服务不造成影响(在之前,服务运行在新增机器上需要修改系统配置来适配服务,这会导致机器上其他服务需要的运行环境被破坏)。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">使用容器化技术后,服务动态扩缩容问题得以解决,但是机器还是需要公司自身来管理,在非大促的时候,还是需要闲置着大量的机器资源来应对大促,机器自身成本和运维成本都极高,资源利用率低。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <h2 style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第十四次演进:以云平台承载系统</span></strong></h2> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="" data-type="png" title="clipboard.png" data-ratio="0.59625" data-w="800" src="/upload/2575102bcc964096fe42806ff02afadb.png" style="cursor: pointer;display: inline;box-sizing: border-box !important;word-wrap: break-word !important;width: 677px !important;visibility: visible !important;"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">系统可部署到公有云上,利用公有云的海量机器资源,解决动态硬件资源的问题。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在大促的时间段里,在云平台中临时申请更多的资源,结合 Docker 和 K8S 来快速部署服务,在大促结束后释放资源,真正做到按需付费,资源利用率大大提高,同时大大降低了运维成本。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">所谓的云平台,就是把海量机器资源,通过统一的资源管理,抽象为一个资源整体,在之上可按需动态申请硬件资源(如 CPU、内存、网络等),并且之上提供通用的操作系统。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">提供常用的技术组件(如 Hadoop 技术栈,MPP 数据库等)供用户使用,甚至提供开发好的应用,用户不需要关系应用内部使用了什么技术,就能够解决需求(如音视频转码服务、邮件服务、个人博客等)。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">在云平台中会涉及如下几个概念:</span></p> <ul class=" list-paddingleft-2" style=""> <li><p style="line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">IaaS:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">基础设施即服务。对应于上面所说的机器资源统一为资源整体,可动态申请硬件资源的层面。</span></p></li> <li><p style="line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">PaaS:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">平台即服务。对应于上面所说的提供常用的技术组件方便系统的开发和维护。</span></p></li> <li><p style="line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">SaaS:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">软件即服务。对应于上面所说的提供开发好的应用或服务,按功能或性能要求付费。</span></p></li> </ul> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">至此,以上所提到的从高并发访问问题,到服务的架构和系统实施的层面都有了各自的解决方案。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">但同时也应该意识到,在上面的介绍中,其实是有意忽略了诸如跨机房数据同步、分布式事务实现等等的实际问题,这些问题以后有机会再拿出来单独讨论。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="margin-top: 0.5em;margin-bottom: 0.5em;border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;line-height: 1.2;box-sizing: border-box;"> <section style="margin-bottom: -1px;display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">架构设计总结</p> </section> </section> </section> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">①架构的调整是否必须按照上述演变路径进行?</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">不是的,以上所说的架构演变顺序只是针对某个侧面进行单独的改进,在实际场景中,可能同一时间会有几个问题需要解决,或者可能先达到瓶颈的是另外的方面,这时候就应该按照实际问题实际解决。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如在政府类的网站并发量可能不大,但业务可能很丰富的场景,高并发就不是重点解决的问题,此时优先需要的可能会是丰富需求的解决方案。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">②对于将要实施的系统,架构应该设计到什么程度?</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">对于单次实施并且性能指标明确的系统,架构设计到能够支持系统的性能指标要求就足够了,但要留有扩展架构的接口以便不备之需。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">对于不断发展的系统,如电商平台,应设计到能满足下一阶段用户量和性能指标要求的程度,并根据业务的增长不断的迭代升级架构,以支持更高的并发和更丰富的业务。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">③服务端架构和大数据架构有什么区别?</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">所谓的“大数据”其实是海量数据采集清洗转换、数据存储、数据分析、数据服务等场景解决方案的一个统称,在每一个场景都包含了多种可选的技术。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如数据采集有 Flume、Sqoop、Kettle 等,数据存储有分布式文件系统 HDFS、FastDFS,NoSQL 数据库 HBase、MongoDB 等,数据分析有 Spark 技术栈、机器学习算法等。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">总的来说大数据架构就是根据业务的需求,整合各种大数据组件组合而成的架构,一般会提供分布式存储、分布式计算、多维分析、数据仓库、机器学习算法等能力。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">而服务端架构更多指的是应用组织层面的架构,底层能力往往是由大数据架构来提供。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">④有没有一些架构设计的原则?</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">设计原则如下:</span></p> <ul class=" list-paddingleft-2" style=""> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">N+1 设计。</span></strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">系统中的每个组件都应做到没有单点故障。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">回滚设计。</span></strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">确保系统可以向前兼容,在系统升级时应能有办法回滚版本。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">禁用设计。</span></strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">应该提供控制具体功能是否可用的配置,在系统出现故障时能够快速下线功能。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">监控设计。</span></strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">在设计阶段就要考虑监控的手段。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">多活数据中心设计。</span></strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">若系统需要极高的高可用,应考虑在多地实施数据中心进行多活,至少在一个机房断电的情况下系统依然可用。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">采用成熟的技术。</span></strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">刚开发的或开源的技术往往存在很多隐藏的 Bug,出了问题没有商业支持可能会是一个灾难。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">资源隔离设计。</span></strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">应避免单一业务占用全部资源。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">架构应能水平扩展。</span></strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">系统只有做到能水平扩展,才能有效避免瓶颈问题。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">非核心则购买。</span></strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">非核心功能若需要占用大量的研发资源才能解决,则考虑购买成熟的产品。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">使用商用硬件。</span></strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">商用硬件能有效降低硬件故障的机率。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">快速迭代。</span></strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">系统应该快速开发小功能模块,尽快上线进行验证,早日发现问题大大降低系统交付的风险。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">无状态设计。</span></strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">服务接口应该做成无状态的,当前接口的访问不依赖于接口上次访问的状态。</span></p></li> </ul> <p style="white-space: normal;line-height: normal;"><br></p> <p style="white-space: normal;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);letter-spacing: 1px;"><em><span style="font-size: 14px;">作者:欧仕华</span></em></span></p> <p style="white-space: normal;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);letter-spacing: 1px;"><em><span style="font-size: 14px;">简介:在大数据领域具有多年的开发经验,对常用大数据技术都有所了解,在架构设计、高并发、分布式等方面具有一定经验。喜欢学习新技术,乐于分享,欢迎关注我的 SegmentFault 博客:huashiou。</span></em></span></p> <p style="white-space: normal;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);letter-spacing: 1px;"><em><span style="font-size: 14px;">编辑:陶家龙、孙淑娟</span></em></span><br></p> <p style="white-space: normal;line-height: 1.75em;"><span style="font-size: 14px;letter-spacing: 1px;color: rgb(89, 89, 89);"><em>出处:https://segmentfault.com/a/1190000018626163</em></span></p> <p style="line-height: 27.2px;white-space: normal;text-align: center;"><img class="rich_pages" data-copyright="0" data-ratio="0.3939393939393939" src="/upload/58a14061a632a0fe87d40beb73c1aa.gif" data-type="gif" data-w="660"></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="margin-top: 0.5em;margin-bottom: 0.5em;box-sizing: border-box;"> <section style="font-size: 15px;border-style: solid;border-width: 0px 0px 1px;color: rgb(89, 89, 89);border-bottom-color: rgba(215, 215, 215, 0.960784);box-sizing: border-box;"> <p style="box-sizing: border-box;"><span style="letter-spacing: 1px;"><strong>精彩文章推荐:</strong></span></p> </section> </section> </section> <p style="white-space: normal;line-height: 2em;"><a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&amp;mid=2655824599&amp;idx=1&amp;sn=411c40ebaa3446f3b7bb16da1becd12e&amp;chksm=bd74e7008a036e1654dac15658f7adc53b4dfec3484c64805b9dc27b0b5ab2419a9d193e308c&amp;scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2" style="color: rgb(89, 89, 89);font-size: 14px;letter-spacing: 1px;">吃透这篇,你也能搭建出一个高并发和高性能的系统</a><br></p> <p style="white-space: normal;line-height: 2em;"><a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&amp;mid=2655824683&amp;idx=1&amp;sn=adc98729e0e7ee6a5a4ddc3105e51ca6&amp;chksm=bd74e6fc8a036fea81049bfd987b1e9bab9912d6477fa204558988056158a9ab785f8f046735&amp;scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2" style="color: rgb(89, 89, 89);font-size: 14px;letter-spacing: 1px;">PB级数据实时查询,滴滴Elasticsearch多集群架构实践</a><br></p> <p style="white-space: normal;line-height: 2em;"><a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&amp;mid=2655820468&amp;idx=1&amp;sn=7b79ed5d1b7acc411a24c3b074d4ca8a&amp;chksm=bd74d7638a035e754c88cc4a2bf0414319072c78713617792256c810ac0ca817fc40bf16da9e&amp;scene=21#wechat_redirect" target="_blank" data-itemshowtype="11" data-linktype="2" style="color: rgb(89, 89, 89);font-size: 14px;letter-spacing: 1px;">百万并发下的Nginx优化,看这一篇就够了!</a></p>

Zookeeper怎么实现分布式锁?

作者:微信小助手

<section class="xmteditor" style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn"></section> <section data-role="outer" label="Powered by 135editor.com" data-mpa-powered-by="yiban.io" style="padding-left: 0.5em;padding-right: 0.5em;"> <section data-role="outer" label="Powered by 135editor.com"> <section data-id="2860" class="v3editor"> <section data-width="100%"> <section data-width="98%"> <section> <section class="" data-tools="135编辑器" data-id="91525" style="white-space: normal;"> <section style="margin: 8px;padding: 10px;max-width: 100%;box-sizing: border-box;line-height: 25.6px;border-radius: 10px;height: auto;box-shadow: rgb(221, 221, 221) 2px 2px 8px;display: -webkit-flex;word-wrap: break-word !important;"> <section style="max-width: 100%;flex: 0 0 2cm;height: 78px;width: 75px;box-sizing: border-box !important;word-wrap: break-word !important;"> <p style="max-width: 100%;min-height: 1em;text-align: center;box-sizing: border-box !important;word-wrap: break-word !important;"><a href="https://mp.weixin.qq.com/s?__biz=Mzg5MzAyNDI4Mw==&amp;mid=2247483912&amp;idx=3&amp;sn=4e16e34c2a6d9acb72683fcaeeb98631&amp;scene=21#wechat_redirect" target="_blank" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 0px;right: auto;bottom: auto;"><img class="" data-copyright="0" data-cropselx1="0" data-cropselx2="75" data-cropsely1="0" data-cropsely2="75" data-ratio="1" src="/upload/461fbe1cf2f8abc8c9ca19590be382d2.jpg" data-type="jpeg" data-w="425" style="height: 75px;letter-spacing: 0.544px;text-align: justify;color: rgb(62, 62, 62);border-radius: 37px;width: 75px;box-shadow: rgb(170, 170, 170) 0px 0px 14px 0px;margin: 0px;box-sizing: border-box !important;word-wrap: break-word !important;visibility: visible !important;top: auto;left: auto;right: auto;bottom: auto;"></span></a></p> </section> <section style="padding-right: 10px;padding-left: 10px;max-width: 100%;box-sizing: border-box;flex: 1 1 auto;height: 55px;word-wrap: break-word !important;"> <section style="max-width: 100%;line-height: 35px;white-space: nowrap;box-sizing: border-box !important;word-wrap: break-word !important;"> <a href="https://mp.weixin.qq.com/s?__biz=Mzg5MzAyNDI4Mw==&amp;mid=2247483912&amp;idx=3&amp;sn=4e16e34c2a6d9acb72683fcaeeb98631&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2"><span style="color:#1f497d;font-family:宋体, SimSun;"><span style="letter-spacing: 0.544px;"><strong>黑客技术</strong></span></span></a> </section> <section style="max-width: 100%;font-size: 13px;line-height: 20px;color: rgb(127, 127, 127);box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="max-width: 100%;letter-spacing: 0.544px;color: rgb(165, 165, 165);font-family: 黑体, SimHei;box-sizing: border-box !important;word-wrap: break-word !important;">点击右侧关注,了解黑客的世界!</span> </section> </section> <section style="max-width: 100%;flex: 0 0 1.5cm;font-size: 15px;color: rgb(86, 187, 55);letter-spacing: 0px;text-align: center;line-height: 6;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="margin-top: 25px;max-width: 100px;vertical-align: middle;overflow: hidden;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="max-width: 100%;width: 55px;height: 30px;background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_gif/S7SAuLQzeTVj0YaoibbZqxicYkQrLnR3WtTQlHpHouqUmibjUCY9F5wpG0DmyMetZy9pjDdiabWo4XXdtCib3VcnI7w/640?wx_fmt=gif&quot;);background-size: 100% 100%;background-repeat: no-repeat;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="max-width: 100%;opacity: 0;box-sizing: border-box !important;word-wrap: break-word !important;"> <a href="https://mp.weixin.qq.com/s?__biz=Mzg5MzAyNDI4Mw==&amp;mid=2247483912&amp;idx=3&amp;sn=4e16e34c2a6d9acb72683fcaeeb98631&amp;scene=21#wechat_redirect" target="_blank" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 0px;right: auto;bottom: auto;"><img class="" data-copyright="0" data-cropselx1="0" data-cropselx2="56" data-cropsely1="0" data-cropsely2="56" data-ratio="1" src="/upload/f92c6b5f891800a8e8a41e6dad1950e4.jpg" data-type="jpeg" data-w="258" style="height: 56px;top: auto;left: auto;right: auto;bottom: auto;width: 56px;margin: 0px;box-sizing: border-box !important;word-wrap: break-word !important;visibility: visible !important;" title="1081255447.jpg"></span></a> </section> </section> </section> </section> </section> </section> <section class="xmteditor" style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn"></section> <section class="xmteditor" style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn"></section> <section class="" data-tools="135编辑器" data-id="91525" style="white-space: normal;"> <section style="margin: 8px;padding: 10px;max-width: 100%;box-sizing: border-box;line-height: 25.6px;border-radius: 10px;height: auto;box-shadow: rgb(221, 221, 221) 2px 2px 8px;display: -webkit-flex;word-wrap: break-word !important;"> <section style="max-width: 100%;flex: 0 0 2cm;height: 78px;width: 75px;box-sizing: border-box !important;word-wrap: break-word !important;"> <p style="max-width: 100%;min-height: 1em;text-align: center;box-sizing: border-box !important;word-wrap: break-word !important;"><a href="https://mp.weixin.qq.com/s?__biz=MzU2MzcxNzgwMg==&amp;mid=2247483650&amp;idx=3&amp;sn=7d8d32410010e2f58a8b0e9b15fa23ec&amp;scene=21#wechat_redirect" target="_blank" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 0px;right: auto;bottom: auto;"><img class="" data-copyright="0" data-cropselx1="0" data-cropselx2="76" data-cropsely1="0" data-cropsely2="74" data-ratio="1" src="/upload/c978be494b76a0bf054d19dfd16ca72d.jpg" data-type="jpeg" data-w="456" style="height: 76px;letter-spacing: 0.544px;text-align: justify;color: rgb(62, 62, 62);border-radius: 37px;width: 76px;box-shadow: rgb(170, 170, 170) 0px 0px 14px 0px;box-sizing: border-box !important;word-wrap: break-word !important;visibility: visible !important;margin: 0px;"></span></a></p> </section> <section style="padding-right: 10px;padding-left: 10px;max-width: 100%;box-sizing: border-box;flex: 1 1 auto;height: 55px;word-wrap: break-word !important;"> <section style="max-width: 100%;line-height: 35px;white-space: nowrap;box-sizing: border-box !important;word-wrap: break-word !important;"> <a href="https://mp.weixin.qq.com/s?__biz=MzU2MzcxNzgwMg==&amp;mid=2247483650&amp;idx=3&amp;sn=7d8d32410010e2f58a8b0e9b15fa23ec&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2"><span style="max-width: 100%;font-family: 宋体, SimSun;box-sizing: border-box !important;word-wrap: break-word !important;"><strong style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;letter-spacing: 0.544px;color: rgb(31, 73, 125);box-sizing: border-box !important;word-wrap: break-word !important;"><strong>Linux编程</strong></span></strong></span></a> </section> <section style="max-width: 100%;font-size: 13px;line-height: 20px;color: rgb(127, 127, 127);box-sizing: border-box !important;word-wrap: break-word !important;"> <a href="https://mp.weixin.qq.com/s?__biz=MzU2MzcxNzgwMg==&amp;mid=2247483650&amp;idx=3&amp;sn=7d8d32410010e2f58a8b0e9b15fa23ec&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2"><span style="max-width: 100%;letter-spacing: 0.544px;color: rgb(165, 165, 165);font-family: 黑体, SimHei;box-sizing: border-box !important;word-wrap: break-word !important;">点击右侧关注,免费入门到精通!</span></a> </section> </section> <section style="max-width: 100%;flex: 0 0 1.5cm;font-size: 15px;color: rgb(86, 187, 55);letter-spacing: 0px;text-align: center;line-height: 6;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="margin-top: 25px;max-width: 100px;vertical-align: middle;overflow: hidden;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="max-width: 100%;width: 55px;height: 30px;background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_gif/S7SAuLQzeTVj0YaoibbZqxicYkQrLnR3WtTQlHpHouqUmibjUCY9F5wpG0DmyMetZy9pjDdiabWo4XXdtCib3VcnI7w/640?wx_fmt=gif&quot;);background-size: 100% 100%;background-repeat: no-repeat;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="max-width: 100%;opacity: 0;box-sizing: border-box !important;word-wrap: break-word !important;"> <a href="https://mp.weixin.qq.com/s?__biz=MzU2MzcxNzgwMg==&amp;mid=2247483650&amp;idx=3&amp;sn=7d8d32410010e2f58a8b0e9b15fa23ec&amp;scene=21#wechat_redirect" target="_blank" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 0px;right: auto;bottom: auto;"><img class="" data-copyright="0" data-cropselx1="0" data-cropselx2="56" data-cropsely1="0" data-cropsely2="56" data-ratio="1" src="/upload/f92c6b5f891800a8e8a41e6dad1950e4.jpg" data-type="jpeg" data-w="258" style="height: 56px;top: auto;left: auto;right: auto;bottom: auto;width: 56px;margin: 0px;box-sizing: border-box !important;word-wrap: break-word !important;visibility: visible !important;" title="1081255447.jpg"></span></a> </section> </section> </section> </section> </section> </section> <section class="" data-tools="135编辑器" data-id="91525" style="white-space: normal;"> <section style="margin: 8px;padding: 10px;max-width: 100%;box-sizing: border-box;line-height: 25.6px;border-radius: 10px;height: auto;box-shadow: rgb(221, 221, 221) 2px 2px 8px;display: -webkit-flex;word-wrap: break-word !important;"> <section style="max-width: 100%;flex: 0 0 2cm;height: 78px;width: 75px;box-sizing: border-box !important;word-wrap: break-word !important;"> <p style="max-width: 100%;min-height: 1em;text-align: center;box-sizing: border-box !important;word-wrap: break-word !important;"><a href="https://mp.weixin.qq.com/s?__biz=MzU0MTg4NzYyMA==&amp;mid=2247483752&amp;idx=7&amp;sn=1fa48fb58dd6c18fccb8f0ed611352f7&amp;scene=21#wechat_redirect" target="_blank" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 0px;right: auto;bottom: auto;"><img class="" data-copyright="0" data-cropselx1="0" data-cropselx2="75" data-cropsely1="0" data-cropsely2="75" data-ratio="1" src="/upload/360f4cbf71e007952f8d91cb4b2f51b3.jpg" data-type="jpeg" data-w="640" style="height: 75px;letter-spacing: 0.544px;text-align: justify;color: rgb(62, 62, 62);border-radius: 37px;width: 75px;box-shadow: rgb(170, 170, 170) 0px 0px 14px 0px;margin: 0px;top: auto;left: auto;right: auto;bottom: auto;box-sizing: border-box !important;word-wrap: break-word !important;visibility: visible !important;"></span></a></p> </section> <section style="padding-right: 10px;padding-left: 10px;max-width: 100%;box-sizing: border-box;flex: 1 1 auto;height: 55px;word-wrap: break-word !important;"> <section style="max-width: 100%;line-height: 35px;white-space: nowrap;box-sizing: border-box !important;word-wrap: break-word !important;"> <a href="https://mp.weixin.qq.com/s?__biz=MzU0MTg4NzYyMA==&amp;mid=2247483752&amp;idx=7&amp;sn=1fa48fb58dd6c18fccb8f0ed611352f7&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2"><span style="color:#1f497d;font-family:宋体, SimSun;"><span style="letter-spacing: 0.544px;"><strong>程序员严选</strong></span></span></a> </section> <section style="max-width: 100%;font-size: 13px;line-height: 20px;color: rgb(127, 127, 127);box-sizing: border-box !important;word-wrap: break-word !important;"> <a href="https://mp.weixin.qq.com/s?__biz=MzU0MTg4NzYyMA==&amp;mid=2247483752&amp;idx=7&amp;sn=1fa48fb58dd6c18fccb8f0ed611352f7&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2"><span style="max-width: 100%;letter-spacing: 0.544px;color: rgb(165, 165, 165);font-family: 黑体, SimHei;box-sizing: border-box !important;word-wrap: break-word !important;">甄选正品好物,程序员生活指南!</span></a> </section> </section> <section style="max-width: 100%;flex: 0 0 1.5cm;font-size: 15px;color: rgb(86, 187, 55);letter-spacing: 0px;text-align: center;line-height: 6;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="margin-top: 25px;max-width: 100px;vertical-align: middle;overflow: hidden;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="max-width: 100%;width: 55px;height: 30px;background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_gif/S7SAuLQzeTVj0YaoibbZqxicYkQrLnR3WtTQlHpHouqUmibjUCY9F5wpG0DmyMetZy9pjDdiabWo4XXdtCib3VcnI7w/640?wx_fmt=gif&quot;);background-size: 100% 100%;background-repeat: no-repeat;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="max-width: 100%;opacity: 0;box-sizing: border-box !important;word-wrap: break-word !important;"> <a href="https://mp.weixin.qq.com/s?__biz=MzU0MTg4NzYyMA==&amp;mid=2247483752&amp;idx=7&amp;sn=1fa48fb58dd6c18fccb8f0ed611352f7&amp;scene=21#wechat_redirect" target="_blank" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 0px;right: auto;bottom: auto;"><img class="" data-copyright="0" data-cropselx1="0" data-cropselx2="56" data-cropsely1="0" data-cropsely2="56" data-ratio="1" src="/upload/f92c6b5f891800a8e8a41e6dad1950e4.jpg" data-type="jpeg" data-w="258" style="height: 56px;top: auto;left: auto;right: auto;bottom: auto;width: 56px;margin: 0px;box-sizing: border-box !important;word-wrap: break-word !important;visibility: visible !important;" title="1081255447.jpg"></span></a> </section> </section> </section> </section> </section> </section> <p style="letter-spacing: 1.5px;"><span style="font-size: 14px;color: rgb(136, 136, 136);"><br></span></p> <p style="letter-spacing: 1.5px;"><span style="font-size: 14px;color: rgb(136, 136, 136);">作者丨coolblog</span><br></p> <p style="letter-spacing: 1.5px;"><span style="font-size: 14px;color: rgb(136, 136, 136);">https://segmentfault.com/a/1190000010895869</span></p> <h2 style="margin-top: 1.5rem;margin-bottom: 1rem;font-size: 20px;white-space: normal;color: rgb(21, 153, 87);line-height: 1.35;text-align: left;letter-spacing: 1.5px;font-family: Menlo, Monaco, &quot;Source Code Pro&quot;, Consolas, Inconsolata, &quot;Ubuntu Mono&quot;, &quot;DejaVu Sans Mono&quot;, &quot;Courier New&quot;, &quot;Droid Sans Mono&quot;, &quot;Hiragino Sans GB&quot;, 微软雅黑, monospace !important;"><span style="letter-spacing: 0.5px;font-size: 16px;">1. 背景</span></h2> </section> </section> </section> </section> <p style="text-align: left;letter-spacing: 1.5px;"><span style="color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;white-space: pre-line;letter-spacing: 0.5px;font-size: 16px;">最近在学习 Zookeeper,在刚开始接触 Zookeeper 的时候,完全不知道 Zookeeper 有什么用。且很多资料都是将 Zookeeper 描述成一个“类 Unix/Linux 文件系统”的中间件,导致我很难将类 Unix/Linux 文件系统的 Zookeeper 和分布式应用联系在一起。</span></p> <p style="text-align: left;letter-spacing: 1.5px;"><span style="color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;white-space: pre-line;letter-spacing: 0.5px;font-size: 16px;"><br></span></p> <p style="text-align: left;letter-spacing: 1.5px;"><span style="color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;white-space: pre-line;letter-spacing: 0.5px;font-size: 16px;">后来在粗读了《ZooKeeper 分布式过程协同技术详解》和《从Paxos到Zookeeper 分布式一致性原理与实践》两本书,并动手写了一些 CURD demo 后,初步对 Zookeeper 有了一定的了解。</span></p> <p style="text-align: left;letter-spacing: 1.5px;"><span style="color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;white-space: pre-line;letter-spacing: 0.5px;font-size: 16px;"><br></span></p> <p style="text-align: left;letter-spacing: 1.5px;"><span style="font-size: 16px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;white-space: pre-line;letter-spacing: 0.5px;">不过比较肤浅,为了进一步加深对 Zookeeper 的认识,我利用空闲时间编写了本篇文章对应的 demo – 基于 Zookeeper 的分布式锁实现。通过编写这个分布式锁 demo,使我对</span><span style="font-size: 16px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;white-space: pre-line;letter-spacing: 0.5px;">Zookeeper</span><span style="font-size: 16px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;white-space: pre-line;letter-spacing: 0.5px;">的 watcher 机制、Zookeeper 的用途等有了更进一步的认识。</span></p> <p style="text-align: left;letter-spacing: 1.5px;"><span style="color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;white-space: pre-line;letter-spacing: 0.5px;font-size: 16px;"><br></span></p> <p style="text-align: left;letter-spacing: 1.5px;"><span style="color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;white-space: pre-line;letter-spacing: 0.5px;font-size: 16px;">不过我所编写的分布式锁还是比较简陋的,实现的也不够优美,仅仅是个练习,仅供参考使用。好了,题外话就说到这里,接下来我们就来聊聊基于 Zookeeper 的分布式锁实现。<br></span></p> <h2 style="margin-top: 1.5rem;margin-bottom: 1rem;color: rgb(21, 153, 87);line-height: 1.35;font-size: 20px;text-align: left;white-space: normal;letter-spacing: 1.5px;font-family: Menlo, Monaco, &quot;Source Code Pro&quot;, Consolas, Inconsolata, &quot;Ubuntu Mono&quot;, &quot;DejaVu Sans Mono&quot;, &quot;Courier New&quot;, &quot;Droid Sans Mono&quot;, &quot;Hiragino Sans GB&quot;, 微软雅黑, monospace !important;"><span style="letter-spacing: 0.5px;font-size: 16px;">2. 独占锁和读写锁的实现</span></h2> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">在本章,我将分别说明独占锁和读写锁详细的实现过程,并配以相应的流程图帮助大家了解实现的过程。</span></p> <h3 style="margin-top: 1.5rem;margin-bottom: 1rem;color: rgb(21, 153, 87);line-height: 1.35;font-size: 18px;text-align: left;white-space: normal;letter-spacing: 1.5px;font-family: Menlo, Monaco, &quot;Source Code Pro&quot;, Consolas, Inconsolata, &quot;Ubuntu Mono&quot;, &quot;DejaVu Sans Mono&quot;, &quot;Courier New&quot;, &quot;Droid Sans Mono&quot;, &quot;Hiragino Sans GB&quot;, 微软雅黑, monospace !important;"><span style="letter-spacing: 0.5px;font-size: 16px;">2.1 独占锁的实现</span></h3> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">独占锁又称排它锁,从字面意思上很容易理解他们的用途。即如果某个操作 O<span style="font-size: 16px;letter-spacing: 1px;line-height: 0;bottom: -0.25em;vertical-align: baseline;">1</span>&nbsp;对访问资源 R<span style="font-size: 16px;letter-spacing: 1px;line-height: 0;bottom: -0.25em;vertical-align: baseline;">1</span>&nbsp;的过程加锁,在操作 O<span style="font-size: 16px;letter-spacing: 1px;line-height: 0;bottom: -0.25em;vertical-align: baseline;">1</span>&nbsp;结束对资源 R<span style="font-size: 16px;letter-spacing: 1px;line-height: 0;bottom: -0.25em;vertical-align: baseline;">1</span>&nbsp;访问前,其他操作不允许访问资源 R<span style="font-size: 16px;letter-spacing: 1px;line-height: 0;bottom: -0.25em;vertical-align: baseline;">1</span>。以上算是对独占锁的简单定义了,那么这段定义在 Zookeeper 的“类 Unix/Linux 文件系统”的结构中是怎样实现的呢?在锁答案前,我们先看张图:</span></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><img class="" data-ratio="0.5371024734982333" src="/upload/5bacf63580770edad03e626c9f308899.png" data-type="png" data-w="283" style="border-width: 0px;border-style: initial;border-color: initial;border-radius: 6px;margin: 0px;top: 0px;left: 0px;right: 0px;bottom: 0px;"></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">图1 独占锁的 Zookeeper 节点结构</span></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">如上图,对于独占锁,我们可以将资源 R<span style="font-size: 16px;letter-spacing: 1px;line-height: 0;bottom: -0.25em;vertical-align: baseline;">1</span>&nbsp;看做是 lock 节点,操作 O<span style="font-size: 16px;letter-spacing: 1px;line-height: 0;bottom: -0.25em;vertical-align: baseline;">1</span>&nbsp;访问资源 R<span style="font-size: 16px;letter-spacing: 1px;line-height: 0;bottom: -0.25em;vertical-align: baseline;">1</span>&nbsp;看做创建 lock 节点,释放资源 R<span style="font-size: 16px;letter-spacing: 1px;line-height: 0;bottom: -0.25em;vertical-align: baseline;">1</span> 看做删除 lock 节点。这样我们就将独占锁的定义对应于具体的 Zookeeper 节点结构,通过创建 lock 节点获取锁,删除节点释放锁。详细的过程如下:</span></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">1、多个客户端竞争创建 lock 临时节点2、其中某个客户端成功创建 lock 节点,其他客户端对 lock 节点设置 watcher3、持有锁的客户端删除 lock 节点或该客户端崩溃,由 Zookeeper 删除 lock 节点4、其他客户端获得 lock 节点被删除的通知5、重复上述4个步骤,直至无客户端在等待获取锁了</span></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">上面即独占锁具体的实现步骤,理解起来并不复杂,这里不再赘述。</span></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><img class="" data-ratio="0.9533799533799534" src="/upload/bf2a8a88f1d857ed8ae277e8f33722cb.png" data-type="png" data-w="429" style="border-width: 0px;border-style: initial;border-color: initial;border-radius: 6px;margin: 0px;top: 0px;left: 0px;right: 0px;bottom: 0px;"></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">图2 获取独占锁流程图</span></p> <h3 style="margin-top: 1.5rem;margin-bottom: 1rem;color: rgb(21, 153, 87);line-height: 1.35;font-size: 18px;text-align: left;white-space: normal;letter-spacing: 1.5px;font-family: Menlo, Monaco, &quot;Source Code Pro&quot;, Consolas, Inconsolata, &quot;Ubuntu Mono&quot;, &quot;DejaVu Sans Mono&quot;, &quot;Courier New&quot;, &quot;Droid Sans Mono&quot;, &quot;Hiragino Sans GB&quot;, 微软雅黑, monospace !important;"><span style="letter-spacing: 0.5px;font-size: 16px;">2.2 读写锁的实现</span></h3> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">说完独占锁的实现,这节来说说读写锁的实现。读写锁包含一个读锁和写锁,操作 O<span style="font-size: 16px;letter-spacing: 1px;line-height: 0;bottom: -0.25em;vertical-align: baseline;">1</span>&nbsp;对资源 R<span style="font-size: 16px;letter-spacing: 1px;line-height: 0;bottom: -0.25em;vertical-align: baseline;">1</span>&nbsp;加读锁,且获得了锁,其他操作可同时对资源 R<span style="font-size: 16px;letter-spacing: 1px;line-height: 0;bottom: -0.25em;vertical-align: baseline;">1</span> 设置读锁,进行共享读操作。</span></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">如果操作 O<span style="font-size: 16px;letter-spacing: 1px;line-height: 0;bottom: -0.25em;vertical-align: baseline;">1</span>&nbsp;对资源 R<span style="font-size: 16px;letter-spacing: 1px;line-height: 0;bottom: -0.25em;vertical-align: baseline;">1</span>&nbsp;加写锁,且获得了锁,其他操作再对资源 R<span style="font-size: 16px;letter-spacing: 1px;line-height: 0;bottom: -0.25em;vertical-align: baseline;">1</span> 设置不同类型的锁都会被阻塞。总结来说,读锁具有共享性,而写锁具有排他性。那么在 Zookeeper 中,我们可以用怎样的节点结构实现上面的操作呢?</span></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><img class="" data-ratio="0.5852417302798982" src="/upload/8778762acc914ccac4e0f0d11bc28c8e.png" data-type="png" data-w="393" style="border-width: 0px;border-style: initial;border-color: initial;border-radius: 6px;margin: 0px;top: 0px;left: 0px;right: 0px;bottom: 0px;"></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">图3 读写锁的 Zookeeper 节点结构</span></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">在 Zookeeper 中,由于读写锁和独占锁的节点结构不同,读写锁的客户端不用再去竞争创建 lock 节点。所以在一开始,所有的客户端都会创建自己的锁节点。如果不出意外,所有的锁节点都能被创建成功,此时锁节点结构如图3所示。之后,客户端从 Zookeeper 端获取 /share_lock 下所有的子节点,并判断自己能否获取锁。如果客户端创建的是读锁节点,获取锁的条件(满足其中一个即可)如下:</span></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">1、自己创建的节点序号排在所有其他子节点前面2、自己创建的节点前面无写锁节点</span></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">如果客户端创建的是写锁节点,由于写锁具有排他性。所以获取锁的条件要简单一些,只需确定自己创建的锁节点是否排在其他子节点前面即可。</span></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">不同于独占锁,读写锁的实现稍微复杂一下。读写锁有两种实现方式,各有异同,接下来就来说说这两种实现方式。</span></p> <h4 style="margin-top: 1.5rem;margin-bottom: 1rem;color: rgb(21, 153, 87);line-height: 1.35;font-size: 18px;text-align: left;white-space: normal;letter-spacing: 1.5px;font-family: Menlo, Monaco, &quot;Source Code Pro&quot;, Consolas, Inconsolata, &quot;Ubuntu Mono&quot;, &quot;DejaVu Sans Mono&quot;, &quot;Courier New&quot;, &quot;Droid Sans Mono&quot;, &quot;Hiragino Sans GB&quot;, 微软雅黑, monospace !important;"><span style="letter-spacing: 0.5px;font-size: 16px;">读写锁的第一种实现</span></h4> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">第一种实现是对 /sharelock 节点设置 watcher,当 /sharelock 下的子节点被删除时,未获取锁的客户端收到 /share_lock 子节点变动的通知。在收到通知后,客户端重新判断自己创建的子节点是否可以获取锁,如果失败,再次等待通知。详细流程如下:</span></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">1、所有客户端创建自己的锁节点2、从 Zookeeper 端获取 /sharelock 下所有的子节点,并对 /sharelock 节点设置 watcher3、判断自己创建的锁节点是否可以获取锁,如果可以,持有锁。否则继续等待4、持有锁的客户端删除自己的锁节点,其他客户端收到 /share_lock 子节点变动的通知5、重复步骤2、3、4,直至无客户端在等待获取锁了</span></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">上述步骤对于的流程图如下:</span></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><img class="" data-ratio="1.132387706855792" src="/upload/bde6afcaccc2d2681b12e531230e8c61.png" data-type="png" data-w="423" style="border-width: 0px;border-style: initial;border-color: initial;border-radius: 6px;margin: 0px;top: 0px;left: 0px;right: 0px;bottom: 0px;"></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">图4 获取读写锁实现1流程图</span></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">上面获取读写锁流程并不复杂,但却存在性能问题。以图3所示锁节点结构为例,第一个锁节点 host1-W-0000000001 被移除后,Zookeeper 会将 /share_lock 子节点变动的通知分发给所有的客户端。</span></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">但实际上,该子节点变动通知除了能影响 host2-R-0000000002 节点对应的客户端外,分发给其他客户端则是在做无用功,因为其他客户端即使获取了通知也无法获取锁。所以这里需要做一些优化,优化措施是让客户端只在自己关心的节点被删除时,再去获取锁。</span></p> <h4 style="margin-top: 1.5rem;margin-bottom: 1rem;color: rgb(21, 153, 87);line-height: 1.35;font-size: 18px;text-align: left;white-space: normal;letter-spacing: 1.5px;font-family: Menlo, Monaco, &quot;Source Code Pro&quot;, Consolas, Inconsolata, &quot;Ubuntu Mono&quot;, &quot;DejaVu Sans Mono&quot;, &quot;Courier New&quot;, &quot;Droid Sans Mono&quot;, &quot;Hiragino Sans GB&quot;, 微软雅黑, monospace !important;"><span style="letter-spacing: 0.5px;font-size: 16px;">读写锁的第二种实现</span></h4> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">在了解读写锁第一种实现的弊端后,我们针对这一实现进行优化。这里客户端不再对 /share_lock 节点进行监视,而只对自己关心的节点进行监视。还是以图3的锁节点结构进行举例说明,host2-R-0000000002 对应的客户端 C<span style="font-size: 16px;letter-spacing: 1px;line-height: 0;bottom: -0.25em;vertical-align: baseline;">2</span> 只需监视 host1-W-0000000001 节点是否被删除即可。</span></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">而 host3-W-0000000003 对应的客户端 C<span style="font-size: 16px;letter-spacing: 1px;line-height: 0;bottom: -0.25em;vertical-align: baseline;">3</span>&nbsp;只需监视 host2-R-0000000002 节点是否被删除即可,只有 host2-R-0000000002 节点被删除,客户端 C<span style="font-size: 16px;letter-spacing: 1px;line-height: 0;bottom: -0.25em;vertical-align: baseline;">3</span> 才能获取锁。而 host1-W-0000000001 节点被删除时,产生的通知对于客户端 C<span style="font-size: 16px;letter-spacing: 1px;line-height: 0;bottom: -0.25em;vertical-align: baseline;">3</span>&nbsp;来说是无用的,即使客户端 C<span style="font-size: 16px;letter-spacing: 1px;line-height: 0;bottom: -0.25em;vertical-align: baseline;">3</span> 响应了通知也没法获取锁。</span></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">这里总结一下,不同客户端关心的锁节点是不同的。如果客户端创建的是读锁节点,那么客户端只需找出比读锁节点序号小的最后一个的写锁节点,并设置 watcher 即可。而如果是写锁节点,则更简单,客户端仅需对该节点的上一个节点设置 watcher 即可。详细的流程如下:</span></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">1、所有客户端创建自己的锁节点2、从 Zookeeper 端获取 /share_lock 下所有的子节点3、判断自己创建的锁节点是否可以获取锁,如果可以,持有锁。否则对自己关心的锁节点设置 watcher4、持有锁的客户端删除自己的锁节点,某个客户端收到该节点被删除的通知,并获取锁5、重复步骤4,直至无客户端在等待获取锁了</span></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">上述步骤对于的流程图如下:</span></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><img class="" data-ratio="1.1551724137931034" src="/upload/135f7b874376d9a28b24242ea6849d99.png" data-type="png" data-w="406" style="border-width: 0px;border-style: initial;border-color: initial;border-radius: 6px;margin: 0px;top: 0px;left: 0px;right: 0px;bottom: 0px;"></p> <p style="margin-top: 15px;margin-bottom: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">图5 获取读写锁实现2流程图</span></p> <h2 style="margin-top: 1.5rem;margin-bottom: 1rem;color: rgb(21, 153, 87);line-height: 1.35;font-size: 20px;text-align: left;white-space: normal;letter-spacing: 1.5px;font-family: Menlo, Monaco, &quot;Source Code Pro&quot;, Consolas, Inconsolata, &quot;Ubuntu Mono&quot;, &quot;DejaVu Sans Mono&quot;, &quot;Courier New&quot;, &quot;Droid Sans Mono&quot;, &quot;Hiragino Sans GB&quot;, 微软雅黑, monospace !important;"><span style="letter-spacing: 0.5px;font-size: 16px;">3. 写在最后</span></h2> <p style="margin-top: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;margin-bottom: 5px;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">本文较为详细的描述了基于 Zookeeper 分布式锁的实现过程,并根据上面描述的两种锁原理实现了较为简单的分布式锁 demo,代码放在了 github 上,需要的朋友自取。</span></p> <p style="margin-top: 15px;font-size: 14px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;margin-bottom: 5px;letter-spacing: 1.5px;"><span style="letter-spacing: 0.5px;font-size: 16px;">因为这只是一个简单的 demo,代码实现的并不优美,仅供参考。最后,如果你觉得文章还不错的话,欢迎点赞。如果有不妥的地方,也请提出来,我会虚心改之。</span></p> <p><br></p> <p style="text-align: left;letter-spacing: 1.5px;"><span style="color: rgb(136, 136, 136);font-size: 16px;">参考</span></p> <p style="text-align: left;letter-spacing: 1.5px;"><span style="color: rgb(136, 136, 136);font-size: 16px;">《ZooKeeper 分布式过程协同技术详解》&nbsp;</span></p> <p style="text-align: left;letter-spacing: 1.5px;"><span style="color: rgb(136, 136, 136);font-size: 16px;">《从Paxos到Zookeeper 分布式一致性原理与实践》</span></p> <p style="text-align: left;letter-spacing: 1.5px;"><span style="color: rgb(136, 136, 136);font-size: 16px;"><br></span></p> <p style="text-align: left;letter-spacing: 1.5px;"><span style="font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;white-space: pre-line;color: rgb(136, 136, 136);font-size: 16px;">GitHub 代码:</span></p> <p style="text-align: left;letter-spacing: 1.5px;"><span style="font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;white-space: pre-line;color: rgb(136, 136, 136);font-size: 16px;">https://github.com/code4wt/distributed_lock</span></p> <p style="text-align: left;letter-spacing: 1.5px;"><span style="font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;white-space: pre-line;color: rgb(136, 136, 136);font-size: 16px;"><br></span></p> <section class="xmteditor" style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn"></section> <section class="xmteditor" style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn"></section> <p style="text-align: center;letter-spacing: 1.5px;"><span style="background-color: rgb(0, 213, 255);color: rgb(255, 255, 255);"><strong><span style="background-color: rgb(0, 213, 255);font-size: 20px;">&nbsp;推荐↓↓↓&nbsp;</span></strong></span></p> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin: 80px 0% 10px;text-align: center;box-sizing: border-box;"> <section style="display: inline-block;vertical-align: top;width: 100%;padding-right: 10px;padding-bottom: 10px;padding-left: 10px;border-radius: 12px;border-width: 0px;border-style: none;border-color: rgb(62, 62, 62);box-sizing: border-box;background-color: rgba(205, 234, 242, 0.96);"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: -70px;margin-right: 0%;margin-left: 0%;box-sizing: border-box;"> <section style="max-width: 100%;vertical-align: middle;display: inline-block;width: 60%;border-width: 5px;border-style: solid;border-color: rgba(100, 217, 251, 0.96);border-radius: 0px;box-sizing: border-box;"> <img class="" data-copyright="0" data-cropselx1="0" data-cropselx2="313" data-cropsely1="0" data-cropsely2="313" data-ratio="1" src="/upload/17d8e00bb9865c0932bfd9de1886ac40.jpg" data-type="jpeg" data-w="1280" style="vertical-align: middle;width: 313px;box-sizing: border-box;height: 313px;"> </section> </section> </section> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="box-sizing: border-box;"> <section style="display: inline-block;vertical-align: top;width: 16%;box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-right: 0%;margin-left: 0%;font-size: 10px;box-sizing: border-box;"> <section style="padding: 4px;display: inline-block;border-radius: 100%;box-sizing: border-box;background-color: rgba(0, 0, 0, 0);"> <section style="border-radius: 100%;border-width: 2px;border-style: solid;border-color: rgb(0, 0, 0);width: 2em;height: 2em;font-size: 13px;line-height: 1.8em;box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong style="box-sizing: border-box;">长</strong></p> </section> </section> </section> </section> </section> <section style="display: inline-block;vertical-align: top;width: 16%;box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-right: 0%;margin-left: 0%;font-size: 10px;box-sizing: border-box;"> <section style="padding: 4px;display: inline-block;border-radius: 100%;box-sizing: border-box;background-color: rgba(0, 0, 0, 0);"> <section style="border-radius: 100%;border-width: 2px;border-style: solid;border-color: rgb(0, 0, 0);width: 2em;height: 2em;font-size: 13px;line-height: 1.8em;box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong style="box-sizing: border-box;">按</strong></p> </section> </section> </section> </section> </section> <section style="display: inline-block;vertical-align: top;width: 16%;box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-right: 0%;margin-left: 0%;font-size: 10px;box-sizing: border-box;"> <section style="padding: 4px;display: inline-block;border-radius: 100%;box-sizing: border-box;background-color: rgba(0, 0, 0, 0);"> <section style="border-radius: 100%;border-width: 2px;border-style: solid;border-color: rgb(0, 0, 0);width: 2em;height: 2em;font-size: 13px;line-height: 1.8em;box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong style="box-sizing: border-box;">关</strong></p> </section> </section> </section> </section> </section> <section style="display: inline-block;vertical-align: top;width: 16%;box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-right: 0%;margin-left: 0%;font-size: 10px;box-sizing: border-box;"> <section style="padding: 4px;display: inline-block;border-radius: 100%;box-sizing: border-box;background-color: rgba(0, 0, 0, 0);"> <section style="border-radius: 100%;border-width: 2px;border-style: solid;border-color: rgb(0, 0, 0);width: 2em;height: 2em;font-size: 13px;line-height: 1.8em;box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong style="box-sizing: border-box;">注</strong></p> </section> </section> </section> </section> </section> </section> </section> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-right: 0%;margin-left: 0%;box-sizing: border-box;"> <section style="font-size: 14px;color: rgb(68, 68, 68);padding-right: 15px;padding-left: 15px;box-sizing: border-box;"> <p style="letter-spacing: 0px;"><strong><span style="font-size: 18px;">👉</span></strong><span style="font-size: 17px;"><strong>【</strong></span><a href="https://mp.weixin.qq.com/s?__biz=MzUzMDc0NzU4Nw==&amp;mid=2247483768&amp;idx=1&amp;sn=4ef4f1510616baa395c507e32bb439d7&amp;scene=21#wechat_redirect" target="_blank" style="text-decoration: underline;color: rgb(255, 79, 121);font-size: 17px;" data-linktype="2"><span style="color: rgb(255, 79, 121);font-size: 17px;"><strong>16个技术公众号</strong></span></a><span style="font-size: 17px;"><strong>】都在这里!</strong></span></p> </section> </section> </section> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin: 8px 0% 15px;box-sizing: border-box;"> <section style="height: 1px;box-sizing: border-box;background-color: rgb(0, 0, 0);"></section> </section> </section> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="box-sizing: border-box;"> <section style="text-align: left;box-sizing: border-box;"> <p style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(136, 136, 136);font-size: 15px;">涵盖:程序员大咖、源码共读、程序员共读、数据结构与算法、黑客技术和网络安全、大数据科技、编程前端、Java、Python、Web编程开发、Android、iOS开发、Linux、数据库研发、幽默程序员等。</span></p> </section> </section> </section> </section> </section> </section> </section> <section data-role="outer" label="Powered by 135editor.com" style="max-width: 100%;font-size: 16px;font-family: 微软雅黑;box-sizing: border-box !important;word-wrap: break-word !important;"> <section class="" data-tools="135编辑器" data-id="94250" style="max-width: 100%;box-sizing: border-box;border-width: 0px;border-style: none;border-color: initial;word-wrap: break-word !important;"> <section class="" data-tools="135编辑器" data-id="91842" style="max-width: 100%;box-sizing: border-box;border-width: 0px;border-style: none;border-color: initial;word-wrap: break-word !important;"> <section style="max-width: 100%;text-align: right;width: auto;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="max-width: 100%;display: inline-block;clear: both;box-sizing: border-box !important;word-wrap: break-word !important;"> <section class="" data-brushtype="text" style="padding: 18px 15px 20px 10px;max-width: 100%;box-sizing: border-box;color: rgb(86, 146, 214);background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/ol72Wnba7fLkfGhCjKwHfZOmHMkVTIomF2Oicxr71sYib6EI8Hyhwtntt683HCer9AfzlYzWQ12A8LiaTuUutwvtg/640?wx_fmt=png&quot;);background-repeat: no-repeat;text-align: center;background-size: 100% 100%;letter-spacing: 1.5px;word-wrap: break-word !important;"> <section style="max-width: 100%;display: flex;justify-content: center;align-items: center;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="margin-left: 2px;max-width: 100%;width: 20px;box-sizing: border-box !important;word-wrap: break-word !important;"> <img class="" data-ratio="0.8936170212765957" src="/upload/737696b8734b6f688ae95660c4411917.png" data-type="png" data-w="47" style="margin-bottom: -6px;box-sizing: border-box !important;word-wrap: break-word !important;width: 20px !important;visibility: visible !important;"> </section> <section class="" data-brushtype="text" style="max-width: 100%;font-size: 14px;color: rgb(51, 51, 51);box-sizing: border-box !important;word-wrap: break-word !important;"> 万水千山总是情,点个 “ <strong style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(0, 112, 192);box-sizing: border-box !important;word-wrap: break-word !important;">在看</span></strong>” 行不行 </section> </section> </section> </section> </section> </section> </section> </section> </section> </section>