文章列表

Elasticsearch如何做到亿级数据查询毫秒级返回?

作者:微信小助手

<section style="box-sizing: border-box;font-size: 16px;"> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <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;"> <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;">如果面试的时候碰到这样一个面试题:ES 在数据量很大的情况下(数十亿级别)如何提高查询效率?</span></p> </section> <section style="clear: both;box-sizing: border-box;"></section> </section> </section> </section> </section> <p style="line-height: 1.75em;"><br></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;"><img class="rich_pages" data-copyright="0" data-ratio="0.48205928237129486" data-s="300,640" src="/upload/413de884be844596fd2c43f3193fb1e3.png" data-type="png" data-w="641" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这个问题说白了,就是看你有没有实际用过 ES,因为啥?</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">其实 ES 性能并没有你想象中那么好的。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">很多时候数据量大了,特别是有几亿条数据的时候,可能你会懵逼的发现,跑个搜索怎么一下 5~10s,坑爹了。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第一次搜索的时候,是 5~10s,后面反而就快了,可能就几百毫秒。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">你就很懵,每个用户第一次访问都会比较慢,比较卡么?所以你要是没玩儿过 ES,或者就是自己玩玩儿 Demo,被问到这个问题容易懵逼,显示出你对 ES 确实玩的不怎么样?</span></p> <section class="" data-tools="135编辑器" data-id="39" data-color="#138bde"> <section class="" data-tools="135编辑器" data-id="39" data-color="#138bde" data-custom="#1e9be8"> <section> <p style="line-height: normal;"><br></p> </section> </section> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">说实话,ES 性能优化是没有银弹的。</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">啥意思呢?就是不要期待着随手调一个参数,就可以万能的应对所有的性能慢的场景。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">也许有的场景是你换个参数,或者调整一下语法,就可以搞定,但是绝对不是所有场景都可以这样。</span></p> <p style="line-height: normal;"><br></p> <section class="" data-tools="135编辑器" data-id="86122" data-color="#138bde"> <section> <section> <section> <section style="box-sizing: border-box;font-size: 16px;"> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">性能优化的杀手锏:Filesystem Cache</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> </section> </section> </section> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">你往 ES 里写的数据,实际上都写到磁盘文件里去了,查询的时候,操作系统会将磁盘文件里的数据自动缓存到 Filesystem Cache 里面去。</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="rich_pages " data-croporisrc="/upload/37bc9c8439d751a982ba02711a24aa93.jpg" data-cropx1="0" data-cropx2="515" data-cropy1="44" data-cropy2="484" data-ratio="0.8543689320388349" data-s="300,640" data-type="jpeg" data-w="515" src="https://mmbiz.qpic.cn/mmbiz_jpg/tibrg3AoIJTtmGR5Eb1iaOWMib3auLw3FWr3vOYdA7POCribHibIDQia3SbdPcaJDrAYQdMz7gqHuMeRicRrr5AiablonA/640?wx_fmt=jpeg" style="box-sizing: border-box !important;word-wrap: break-word !important;width: 515px !important;visibility: visible !important;"></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">ES 的搜索引擎严重依赖于底层的 Filesystem Cache,你如果给 Filesystem Cache 更多的内存,尽量让内存可以容纳所有的 IDX Segment File 索引数据文件,那么你搜索的时候就基本都是走内存的,性能会非常高。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">性能差距究竟可以有多大?</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">我们之前很多的测试和压测,如果走磁盘一般肯定上秒,搜索性能绝对是秒级别的,1 秒、5 秒、10 秒。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">但如果是走 Filesystem Cache,是走纯内存的,那么一般来说性能比走磁盘要高一个数量级,基本上就是毫秒级的,从几毫秒到几百毫秒不等。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这里有个真实的案例:</span></strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">某个公司 ES 节点有 3 台机器,每台机器看起来内存很多 64G,总内存就是 64 * 3 = 192G。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">每台机器给 ES JVM Heap 是 32G,那么剩下来留给 Filesystem Cache 的就是每台机器才 32G,总共集群里给 Filesystem Cache 的就是 32 * 3 = 96G 内存。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">而此时,整个磁盘上索引数据文件,在 3 台机器上一共占用了 1T 的磁盘容量,ES 数据量是 1T,那么每台机器的数据量是 300G。这样性能好吗?&nbsp;</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Filesystem Cache 的内存才 100G,十分之一的数据可以放内存,其他的都在磁盘,然后你执行搜索操作,大部分操作都是走磁盘,性能肯定差。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">归根结底,你要让 ES 性能好,最佳的情况下,就是你的机器的内存,至少可以容纳你的总数据量的一半。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">根据我们自己的生产环境实践经验,最佳的情况下,是仅仅在 ES 中就存少量的数据,就是你要用来搜索的那些索引,如果内存留给 Filesystem Cache 的是 100G,那么你就将索引数据控制在 100G 以内。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这样的话,你的数据几乎全部走内存来搜索,性能非常之高,一般可以在1秒以内。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">比如说你现在有一行数据:id,name,age .... 30 个字段。但是你现在搜索,只需要根据 id,name,age 三个字段来搜索。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果你傻乎乎往 ES 里写入一行数据所有的字段,就会导致说 90% 的数据是不用来搜索的。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">结果硬是占据了 ES 机器上的 Filesystem Cache 的空间,单条数据的数据量越大,就会导致 Filesystem Cahce 能缓存的数据就越少。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">其实,仅仅写入 ES 中要用来检索的少数几个字段就可以了,比如说就写入 es id,name,age 三个字段。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">然后你可以把其他的字段数据存在 MySQL/HBase 里,我们一般是建议用 ES + HBase 这么一个架构。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">HBase 的特点是适用于海量数据的在线存储,就是对 HBase 可以写入海量数据,但是不要做复杂的搜索,做很简单的一些根据 id 或者范围进行查询的这么一个操作就可以了。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">从 ES 中根据 name 和 age 去搜索,拿到的结果可能就 20 个 doc id,然后根据 doc id 到 HBase 里去查询每个 doc id 对应的完整的数据,给查出来,再返回给前端。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">写入 ES 的数据最好小于等于,或者是略微大于 ES 的 Filesystem Cache 的内存容量。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">然后你从 ES 检索可能就花费 20ms,然后再根据 ES 返回的 id 去 HBase 里查询,查 20 条数据,可能也就耗费个 30ms。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">可能你原来那么玩儿,1T 数据都放 ES,会每次查询都是 5~10s,现在可能性能就会很高,每次查询就是 50ms。</span></p> <p style="line-height: normal;"><br></p> <section class="" data-tools="135编辑器" data-id="86122" data-color="#138bde"> <section> <section> <section> <section style="box-sizing: border-box;font-size: 16px;"> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">数据预热</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> </section> </section> </section> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">假如说,哪怕是你就按照上述的方案去做了,ES 集群中每个机器写入的数据量还是超过了 Filesystem Cache 一倍。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">比如说你写入一台机器 60G 数据,结果 Filesystem Cache 就 30G,还是有 30G 数据留在了磁盘上。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;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;">举个例子,拿微博来说,你可以把一些大 V,平时看的人很多的数据,提前在后台搞个系统。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">每隔一会儿,自己的后台系统去搜索一下热数据,刷到 Filesystem Cache 里去,后面用户实际上来看这个热数据的时候,他们就是直接从内存里搜索了,很快。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">或者是电商,你可以将平时查看最多的一些商品,比如说 iPhone 8,热数据提前后台搞个程序,每隔 1 分钟自己主动访问一次,刷到 Filesystem Cache 里去。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">对于那些你觉得比较热的、经常会有人访问的数据,最好做一个专门的缓存预热子系统。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">就是对热数据每隔一段时间,就提前访问一下,让数据进入 Filesystem Cache 里面去。这样下次别人访问的时候,性能一定会好很多。</span></p> <p style="line-height: normal;"><br></p> <section class="" data-tools="135编辑器" data-id="86122" data-color="#138bde"> <section> <section> <section> <section style="box-sizing: border-box;font-size: 16px;"> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">冷热分离</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> </section> </section> </section> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">ES 可以做类似于 MySQL 的水平拆分,就是说将大量的访问很少、频率很低的数据,单独写一个索引,然后将访问很频繁的热数据单独写一个索引。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">最好是将冷数据写入一个索引中,然后热数据写入另外一个索引中,这样可以确保热数据在被预热之后,尽量都让他们留在 Filesystem OS Cache 里,别让冷数据给冲刷掉。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">你看,假设你有 6 台机器,2 个索引,一个放冷数据,一个放热数据,每个索引 3 个 Shard。3 台机器放热数据 Index,另外 3 台机器放冷数据 Index。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这样的话,你大量的时间是在访问热数据 Index,热数据可能就占总数据量的 10%,此时数据量很少,几乎全都保留在 Filesystem Cache 里面了,就可以确保热数据的访问性能是很高的。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">但是对于冷数据而言,是在别的 Index 里的,跟热数据 Index 不在相同的机器上,大家互相之间都没什么联系了。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果有人访问冷数据,可能大量数据是在磁盘上的,此时性能差点,就 10% 的人去访问冷数据,90% 的人在访问热数据,也无所谓了。</span></p> <p style="line-height: normal;"><br></p> <section class="" data-tools="135编辑器" data-id="86122" data-color="#138bde"> <section> <section> <section> <section style="box-sizing: border-box;font-size: 16px;"> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">Document 模型设计</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> </section> </section> </section> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">对于 MySQL,我们经常有一些复杂的关联查询。在 ES 里该怎么玩儿,ES 里面的复杂的关联查询尽量别用,一旦用了性能一般都不太好。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">最好是先在 Java 系统里就完成关联,将关联好的数据直接写入 ES 中。搜索的时候,就不需要利用 ES 的搜索语法来完成 Join 之类的关联搜索了。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Document 模型设计是非常重要的,很多操作,不要在搜索的时候才想去执行各种复杂的乱七八糟的操作。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">ES 能支持的操作就那么多,不要考虑用 ES 做一些它不好操作的事情。如果真的有那种操作,尽量在 Document 模型设计的时候,写入的时候就完成。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">另外对于一些太复杂的操作,比如 join/nested/parent-child 搜索都要尽量避免,性能都很差的。</span></p> <p style="line-height: normal;"><br></p> <section class="" data-tools="135编辑器" data-id="86122" data-color="#138bde"> <section> <section> <section> <section style="box-sizing: border-box;font-size: 16px;"> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">分页性能优化</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> </section> </section> </section> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">ES 的分页是较坑的,为啥呢?</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">举个例子吧,假如你每页是 10 条数据,你现在要查询第 100 页,实际上是会把每个 Shard 上存储的前 1000 条数据都查到一个协调节点上。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">如果你有 5 个 Shard,那么就有 5000 条数据,接着协调节点对这 5000 条数据进行一些合并、处理,再获取到最终第 100 页的 10 条数据。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">分布式的,你要查第 100 页的 10 条数据,不可能说从 5 个 Shard,每个 Shard 就查 2 条数据,最后到协调节点合并成 10 条数据吧?</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">你必须得从每个 Shard 都查 1000 条数据过来,然后根据你的需求进行排序、筛选等等操作,最后再次分页,拿到里面第 100 页的数据。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">你翻页的时候,翻的越深,每个 Shard 返回的数据就越多,而且协调节点处理的时间越长,非常坑爹。所以用 ES 做分页的时候,你会发现越翻到后面,就越是慢。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">我们之前也是遇到过这个问题,用 ES 作分页,前几页就几十毫秒,翻到 10 页或者几十页的时候,基本上就要 5~10 秒才能查出来一页数据了。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;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="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">类似于 App 里的推荐商品不断下拉出来一页一页的;</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">类似于微博中,下拉刷微博,刷出来一页一页的,你可以用 Scroll API,关于如何使用,自行上网搜索。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Scroll 会一次性给你生成所有数据的一个快照,然后每次滑动向后翻页就是通过游标 scroll_id 移动,获取下一页、下一页这样子,性能会比上面说的那种分页性能要高很多很多,基本上都是毫秒级的。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">但是,唯一的一点就是,这个适合于那种类似微博下拉翻页的,不能随意跳到任何一页的场景。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">也就是说,你不能先进入第 10 页,然后去第 120 页,然后又回到第 58 页,不能随意乱跳页。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">所以现在很多产品,都是不允许你随意翻页的,App,也有一些网站,做的就是你只能往下拉,一页一页的翻。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">初始化时必须指定 Scroll 参数,告诉 ES 要保存此次搜索的上下文多长时间。你需要确保用户不会持续不断翻页翻几个小时,否则可能因为超时而失败。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">除了用 Scroll API,你也可以用 search_after 来做。search_after 的思想是使用前一页的结果来帮助检索下一页的数据。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">显然,这种方式也不允许你随意翻页,你只能一页页往后翻。初始化时,需要使用一个唯一值的字段作为 Sort 字段。</span></p> <p style="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;">编辑:陶家龙、孙淑娟</span></em></span><br></p> <p style="line-height: 1.75em;"><span style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;"><em>出处:https://zhuanlan.zhihu.com/p/60458049</em></span></p> <p style="text-align: center;"><img class="rich_pages" data-copyright="0" data-ratio="0.3939393939393939" src="/upload/58a14061a632a0fe87d40beb73c1aa.gif" data-type="gif" data-w="660" style=""></p> <section style="box-sizing: border-box;font-size: 16px;"> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section 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> </section> <p style="line-height: 2em;"><a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&amp;mid=2655824391&amp;idx=1&amp;sn=ac183b0cc7b1ee2ea0c10c1d4abf6fc0&amp;chksm=bd74e7d08a036ec6bc54c31519a1391758741977f646f711bc17e2be69b1f333ea2ee0078137&amp;scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;text-decoration: none;" data-linktype="2"><span style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;">搞定高并发,岂能不懂Synchronized底层原理?</span></a><br></p> <p style="line-height: 2em;"><a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&amp;mid=2655824323&amp;idx=1&amp;sn=2cd6852e7baae4b00c68d2678b5e23cc&amp;chksm=bd74e4148a036d02831449be2301f2c83faabdd03760ba970a63a1d5c2c19c603b4108ebcc77&amp;scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;text-decoration: none;" data-linktype="2"><span style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;">超详细的Elasticsearch高性能优化实践</span></a><br></p> <p style="line-height: 2em;"><a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&amp;mid=2655824393&amp;idx=1&amp;sn=5a31b81285dca2818cecc898d42760ab&amp;chksm=bd74e7de8a036ec81b2c08d19a71a04e43b575e5a1959d852d59410fb7bd3a31087dde560d5d&amp;scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;text-decoration: none;" data-linktype="2"><span style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;">在阿里,我如何做好一个项目的启动?</span></a><br></p>

Java设计模式(转)——12.享元模式

作者:じ☆ve宝贝

## 12.享元模式(Flyweight) 享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。 ![享元设计模式](/upload/content21.png "享元设计模式") FlyWeightFactory负责创建和管理享元单元,当一个客户端请求时,工厂需要检查当前对象池中是否有符合条件的对象,如果有,就返回已经存在的对象,如果没有,则创建一个新对象,FlyWeight是超类。一提到共享池,我们很容易联想到Java里面的JDBC连接池,想想每个连接的特点,我们不难总结出:适用于作共享的一些对象,他们有一些共有的属性,就拿数据库连接池来说,url、driverClassName、username、password及dbname,这些属性对于每个连接来说都是一样的,所以就适合用享元模式来处理,建一个工厂类,将上述类似属性作为内部数据,其它的作为外部数据,在方法调用时,当做参数传进来,这样就节省了空间,减少了实例的数量。 看个例子: ![享元设计模式](/upload/content22.png "享元设计模式") 看下数据库连接池的代码: ``` public class ConnectionPool { private Vector<Connection> pool; /*公有属性*/ private String url = "jdbc:mysql://localhost:3306/test"; private String username = "root"; private String password = "root"; private String driverClassName = "com.mysql.jdbc.Driver"; private int poolSize = 100; private static ConnectionPool instance = null; Connection conn = null; /*构造方法,做一些初始化工作*/ private ConnectionPool() { pool = new Vector<Connection>(poolSize); for (int i = 0; i < poolSize; i++) { try { Class.forName(driverClassName); conn = DriverManager.getConnection(url, username, password); pool.add(conn); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } } /* 返回连接到连接池 */ public synchronized void release() { pool.add(conn); } /* 返回连接池中的一个数据库连接 */ public synchronized Connection getConnection() { if (pool.size() > 0) { Connection conn = pool.get(0); pool.remove(conn); return conn; } else { return null; } } } ``` 通过连接池的管理,实现了数据库连接的共享,不需要每一次都重新创建连接,节省了数据库重新创建的开销,提升了系统的性能!

面试官:“谈谈MyBatis中都用到了那些设计模式?”。

作者:微信小助手

<section class="xmteditor" style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn"></section> <blockquote style="box-sizing: border-box;margin: 20px 10px;padding-top: 1px;padding-bottom: 1px;font-size: 16px;white-space: normal;text-align: left;color: rgb(91, 91, 91);line-height: 1.5;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;background: rgba(158, 158, 158, 0.1);border-left-color: rgb(158, 158, 158);"> <p style="box-sizing: border-box;margin: 10px;color: rgb(63, 63, 63);line-height: 2em;"><span style="font-size: 14px;">本文转载自:&nbsp;</span><span style="font-size: 14px;box-sizing: border-box;color: rgb(20, 140, 228);line-height: 1.5;">https://dwz.cn/KFgol1De </span><span style="font-size: 14px;">由Ja</span><span style="font-size: 14px;">vaGuide整理排版。</span><br></p> </blockquote> <p><span style="color: rgb(62, 62, 62);font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);">之前总结过一篇Spring中用到了哪些设计模式:</span><a href="https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&amp;mid=2247485303&amp;idx=1&amp;sn=9e4626a1e3f001f9b0d84a6fa0cff04a&amp;chksm=cea248bcf9d5c1aaf48b67cc52bac74eb29d6037848d6cf213b0e5466f2d1fda970db700ba41&amp;token=1449566086&amp;lang=zh_CN&amp;scene=21#wechat_redirect" style="box-sizing: border-box;color: rgb(30, 107, 184);font-size: 16px;line-height: inherit;overflow-wrap: break-word;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);" data-linktype="2">《面试官:“谈谈Spring中都用到了那些设计模式?”》</a><span style="color: rgb(62, 62, 62);font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);">,昨晚看到了一篇很不错的一篇介绍MyBatis中都用到了那些设计模式的文章,今天分享给各位。</span></p> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">虽然我们都知道有26个设计模式,但是大多停留在概念层面,真实开发中很少遇到,Mybatis源码中使用了大量的设计模式,阅读源码并观察设计模式在其中的应用,能够更深入的理解设计模式。</p> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">Mybatis至少遇到了以下的设计模式的使用:</p> <ol style="" class=" list-paddingleft-2"> <li><p><strong style="box-sizing: border-box;color: rgb(255, 87, 34);font-size: inherit;line-height: inherit;">Builder模式</strong>&nbsp;:</p><p>例如&nbsp;<code style="font-size: inherit;box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">SqlSessionFactoryBuilder</code>、<code style="font-size: inherit;box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">XMLConfigBuilder</code>、<code style="font-size: inherit;box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">XMLMapperBuilder</code>、<code style="font-size: inherit;box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">XMLStatementBuilder</code>、<code style="font-size: inherit;box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">CacheBuilder</code>;</p></li> <li><p><strong style="box-sizing: border-box;color: rgb(255, 87, 34);font-size: inherit;line-height: inherit;">工厂模式</strong>&nbsp;:</p><p>例如<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">SqlSessionFactory</code>、<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">ObjectFactory</code>、<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">MapperProxyFactory</code>;</p></li> <li><p><strong style="box-sizing: border-box;color: rgb(255, 87, 34);font-size: inherit;line-height: inherit;">单例模式</strong>&nbsp;:例如<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">ErrorContext</code>和<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">LogFactory</code>;</p></li> <li><p><strong style="box-sizing: border-box;color: rgb(255, 87, 34);font-size: inherit;line-height: inherit;">代理模式</strong>&nbsp;:Mybatis实现的核心,比如<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">MapperProxy</code>、<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">ConnectionLogger</code>,用的jdk的动态代理;还有<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">executor.loader</code>包使用了cglib或者javassist达到延迟加载的效果;</p></li> <li><p><strong style="box-sizing: border-box;color: rgb(255, 87, 34);font-size: inherit;line-height: inherit;">组合模式</strong>&nbsp;:例如<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">SqlNode</code>和各个子类<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">ChooseSqlNode</code>等;</p></li> <li><p><strong style="box-sizing: border-box;color: rgb(255, 87, 34);font-size: inherit;line-height: inherit;">模板方法模式</strong>&nbsp;: 例如<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">BaseExecutor</code>和<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">SimpleExecutor</code>,还有<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">BaseTypeHandler</code>和所有的子类例如<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">IntegerTypeHandler</code>;</p></li> <li><p><strong style="box-sizing: border-box;color: rgb(255, 87, 34);font-size: inherit;line-height: inherit;">适配器模式</strong>&nbsp;: 例如Log的Mybatis接口和它对jdbc、log4j等各种日志框架的适配实现;</p></li> <li><p><strong style="box-sizing: border-box;color: rgb(255, 87, 34);font-size: inherit;line-height: inherit;">装饰者模式</strong>&nbsp;: 例如<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">cache</code>包中的<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">cache.decorators</code>子包中等各个装饰者的实现;</p></li> <li><p><strong style="box-sizing: border-box;color: rgb(255, 87, 34);font-size: inherit;line-height: inherit;">迭代器模式</strong>&nbsp;: 例如迭代器模式<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">PropertyTokenizer</code>;</p></li> </ol> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">接下来挨个模式进行解读,先介绍模式自身的知识,然后解读在Mybatis中怎样应用了该模式。</p> <h2 style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.4em;color: rgb(63, 63, 63);line-height: inherit;text-align: center;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;">1、Builder 模式</span></h2> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">Builder模式的定义是“将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。”,它属于创建类模式,一般来说,如果一个对象的构建比较复杂,超出了构造函数所能包含的范围,就可以使用工厂模式和Builder模式,相对于工厂模式会产出一个完整的产品,Builder应用于更加复杂的对象的构建,甚至只会构建产品的一个部分。《effective-java》中第2条也提到:<strong style="box-sizing: border-box;color: rgb(255, 87, 34);font-size: inherit;line-height: inherit;">遇到多个构造器参数时,考虑用构建者(Builder)模式</strong>。</p> <figure style="box-sizing: border-box;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"> <img class="" data-ratio="0.7992125984251969" src="/upload/56e28e528e24db4fa67832f907d30354.png" data-type="png" data-w="508" style="box-sizing: border-box;margin-right: auto;margin-left: auto;border-width: 2px;border-style: solid;border-color: rgb(238, 238, 238);font-size: inherit;color: inherit;line-height: inherit;display: block;box-shadow: rgb(221, 221, 221) 0px 0px 15px 3px;border-radius: 8px !important;" title="Builder模式"> <figcaption style="box-sizing: border-box;margin-top: 10px;font-size: 0.7em;color: rgb(153, 153, 153);line-height: inherit;text-align: center;"> Builder模式 </figcaption> </figure> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">在Mybatis环境的初始化过程中,<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">SqlSessionFactoryBuilder</code>会调用<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">XMLConfigBuilder</code>读取所有的<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">MybatisMapConfig.xml</code>和所有的<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">*Mapper.xml</code>文件,构建Mybatis运行的核心对象<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">Configuration</code>对象,然后将该<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">Configuration</code>对象作为参数构建一个<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">SqlSessionFactory</code>对象。</p> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">其中<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">XMLConfigBuilder</code>在构建<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">Configuration</code>对象时,也会调用<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">XMLMapperBuilder</code>用于读取<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">*.Mapper</code>文件,而<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">XMLMapperBuilder</code>会使用<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">XMLStatementBuilder</code>来读取和build所有的SQL语句。</p> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">在这个过程中,有一个相似的特点,就是这些Builder会读取文件或者配置,然后做大量的XpathParser解析、配置或语法的解析、反射生成对象、存入结果缓存等步骤,这么多的工作都不是一个构造函数所能包括的,因此大量采用了Builder模式来解决。</p> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">对于builder的具体类,方法都大都用<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">build*</code>开头,比如<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">SqlSessionFactoryBuilder</code>为例,它包含以下方法:</p> <figure style="box-sizing: border-box;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"> <img class="" data-ratio="0.5225464190981433" src="/upload/6d73981cfc1d17ab4101fc8ea4ae20e0.png" data-type="png" data-w="754" style="box-sizing: border-box;margin-right: auto;margin-left: auto;border-width: 2px;border-style: solid;border-color: rgb(238, 238, 238);font-size: inherit;color: inherit;line-height: inherit;display: block;box-shadow: rgb(221, 221, 221) 0px 0px 15px 3px;border-radius: 8px !important;" title="SqlSessionFactoryBuilder"> <figcaption style="box-sizing: border-box;margin-top: 10px;font-size: 0.7em;color: rgb(153, 153, 153);line-height: inherit;text-align: center;"> SqlSessionFactoryBuilder </figcaption> </figure> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">即根据不同的输入参数来构建<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">SqlSessionFactory</code>这个工厂对象。</p> <h2 style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.4em;color: rgb(63, 63, 63);line-height: inherit;text-align: center;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;">2、工厂模式</span></h2> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">在Mybatis中比如<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">SqlSessionFactory</code>使用的是工厂模式,该工厂没有那么复杂的逻辑,是一个简单工厂模式。</p> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。</p> <figure style="box-sizing: border-box;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"> <img class="" data-ratio="0.6251851851851852" src="/upload/471647a6981ede08f944b396d49e7cf2.jpg" data-type="jpeg" data-w="675" style="box-sizing: border-box;margin-right: auto;margin-left: auto;border-width: 2px;border-style: solid;border-color: rgb(238, 238, 238);font-size: inherit;color: inherit;line-height: inherit;display: block;box-shadow: rgb(221, 221, 221) 0px 0px 15px 3px;border-radius: 8px !important;" title="简单工厂模式"> <figcaption style="box-sizing: border-box;margin-top: 10px;font-size: 0.7em;color: rgb(153, 153, 153);line-height: inherit;text-align: center;"> 简单工厂模式 </figcaption> </figure> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">SqlSession</code>可以认为是一个Mybatis工作的核心的接口,通过这个接口可以执行执行SQL语句、获取Mappers、管理事务。类似于连接MySQL的<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">Connection</code>对象。</p> <figure style="box-sizing: border-box;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"> <img class="" data-ratio="0.44921875" src="/upload/47301923bf04bc5506fee2e18ab4a0ec.png" data-type="png" data-w="768" style="box-sizing: border-box;margin-right: auto;margin-left: auto;border-width: 2px;border-style: solid;border-color: rgb(238, 238, 238);font-size: inherit;color: inherit;line-height: inherit;display: block;box-shadow: rgb(221, 221, 221) 0px 0px 15px 3px;border-radius: 8px !important;" title="SqlSessionFactory"> <figcaption style="box-sizing: border-box;margin-top: 10px;font-size: 0.7em;color: rgb(153, 153, 153);line-height: inherit;text-align: center;"> SqlSessionFactory </figcaption> </figure> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">可以看到,该Factory的<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">openSession()</code>方法重载了很多个,分别支持<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">autoCommit</code>、<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">Executor</code>、<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">Transaction</code>&nbsp;等参数的输入,来构建核心的<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">SqlSession</code>对象。</p> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">在<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">DefaultSqlSessionFactory</code>的默认工厂实现里,有一个方法可以看出工厂怎么产出一个产品:</p> <pre style="box-sizing: border-box;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;text-align: start;background-color: rgb(255, 255, 255);"><code class="java language-java hljs" style="box-sizing: border-box;padding: 0.5em;font-size: 14px;color: rgb(67, 79, 84);line-height: 18px;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;display: block;font-family: Consolas, Inconsolata, Courier, monospace;overflow-x: auto;letter-spacing: 0px;box-shadow: rgb(221, 221, 221) 0px 0px 15px 3px;margin: 30px 8px 20px !important;overflow-wrap: normal !important;border-radius: 8px !important;word-break: normal !important;overflow-y: auto !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="box-sizing: border-box;font-size: inherit;color: rgb(114, 142, 0);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">private</span>&nbsp;SqlSession&nbsp;<span class="hljs-title" style="box-sizing: border-box;font-size: inherit;color: rgb(136, 0, 0);line-height: inherit;font-weight: bold;overflow-wrap: inherit !important;word-break: inherit !important;">openSessionFromDataSource</span><span class="hljs-params" style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">(ExecutorType&nbsp;execType,&nbsp;TransactionIsolationLevel&nbsp;level,<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">boolean</span>&nbsp;autoCommit)</span>&nbsp;</span>{<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Transaction&nbsp;tx&nbsp;=&nbsp;<span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">null</span>;<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">try</span>&nbsp;{<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">final</span>&nbsp;Environment&nbsp;environment&nbsp;=&nbsp;configuration.getEnvironment();<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">final</span>&nbsp;TransactionFactory&nbsp;transactionFactory&nbsp;=&nbsp;getTransactionFactoryFromEnvironment(environment);<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tx&nbsp;=&nbsp;transactionFactory.newTransaction(environment.getDataSource(),&nbsp;level,&nbsp;autoCommit);<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">final</span>&nbsp;Executor&nbsp;executor&nbsp;=&nbsp;configuration.newExecutor(tx,&nbsp;execType);<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">return</span>&nbsp;<span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">new</span>&nbsp;DefaultSqlSession(configuration,&nbsp;executor,&nbsp;autoCommit);<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">catch</span>&nbsp;(Exception&nbsp;e)&nbsp;{<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;closeTransaction(tx);&nbsp;<span class="hljs-comment" style="box-sizing: border-box;font-size: inherit;color: rgba(149, 165, 166, 0.8);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">//&nbsp;may&nbsp;have&nbsp;fetched&nbsp;a&nbsp;connection&nbsp;so&nbsp;lets&nbsp;call</span><br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="box-sizing: border-box;font-size: inherit;color: rgba(149, 165, 166, 0.8);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">//&nbsp;close()</span><br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">throw</span>&nbsp;ExceptionFactory.wrapException(<span class="hljs-string" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 92, 95);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">"Error&nbsp;opening&nbsp;session.&nbsp;&nbsp;Cause:&nbsp;"</span>&nbsp;+&nbsp;e,&nbsp;e);<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">finally</span>&nbsp;{<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ErrorContext.instance().reset();<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;}<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"></code></pre> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">这是一个openSession调用的底层方法,该方法先从configuration读取对应的环境配置,然后初始化<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">TransactionFactory</code>获得一个<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">Transaction</code>对象,然后通过<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">Transaction</code>获取一个<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">Executor</code>对象,最后通过configuration、Executor、是否autoCommit三个参数构建了<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">SqlSession</code>。</p> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">在这里其实也可以看到端倪,<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">SqlSession</code>的执行,其实是委托给对应的<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">Executor</code>来进行的。</p> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">而对于<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">LogFactory</code>,它的实现代码:</p> <pre style="box-sizing: border-box;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;text-align: start;background-color: rgb(255, 255, 255);"><code class="java language-java hljs" style="box-sizing: border-box;padding: 0.5em;font-size: 14px;color: rgb(67, 79, 84);line-height: 18px;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;display: block;font-family: Consolas, Inconsolata, Courier, monospace;overflow-x: auto;letter-spacing: 0px;box-shadow: rgb(221, 221, 221) 0px 0px 15px 3px;margin: 30px 8px 20px !important;overflow-wrap: normal !important;border-radius: 8px !important;word-break: normal !important;overflow-y: auto !important;"><span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">public</span>&nbsp;<span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">final</span>&nbsp;<span class="hljs-class" style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">class</span>&nbsp;<span class="hljs-title" style="box-sizing: border-box;font-size: inherit;color: rgb(136, 0, 0);line-height: inherit;font-weight: bold;overflow-wrap: inherit !important;word-break: inherit !important;">LogFactory</span>&nbsp;</span>{<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">private</span>&nbsp;<span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">static</span>&nbsp;Constructor&lt;?&nbsp;extends&nbsp;Log&gt;&nbsp;logConstructor;<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="box-sizing: border-box;font-size: inherit;color: rgb(114, 142, 0);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">private</span>&nbsp;<span class="hljs-title" style="box-sizing: border-box;font-size: inherit;color: rgb(136, 0, 0);line-height: inherit;font-weight: bold;overflow-wrap: inherit !important;word-break: inherit !important;">LogFactory</span><span class="hljs-params" style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">()</span>&nbsp;</span>{<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="box-sizing: border-box;font-size: inherit;color: rgba(149, 165, 166, 0.8);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">//&nbsp;disable&nbsp;construction</span><br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;}<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="box-sizing: border-box;font-size: inherit;color: rgb(114, 142, 0);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">public</span>&nbsp;<span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">static</span>&nbsp;Log&nbsp;<span class="hljs-title" style="box-sizing: border-box;font-size: inherit;color: rgb(136, 0, 0);line-height: inherit;font-weight: bold;overflow-wrap: inherit !important;word-break: inherit !important;">getLog</span><span class="hljs-params" style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">(Class&lt;?&gt;&nbsp;aClass)</span>&nbsp;</span>{<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">return</span>&nbsp;getLog(aClass.getName());<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;}<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"></code></pre> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">这里有个特别的地方,是Log变量的的类型是<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">Constructor&lt;? extendsLog&gt;</code>,也就是说该工厂生产的不只是一个产品,而是具有Log公共接口的一系列产品,比如<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">Log4jImpl</code>、<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">Slf4jImpl</code>等很多具体的Log。</p> <h2 style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.4em;color: rgb(63, 63, 63);line-height: inherit;text-align: center;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);"><span style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;">3、单例模式</span></h2> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">单例模式(Singleton Pattern):单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。</p> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。单例模式是一种对象创建型模式。单例模式又名单件模式或单态模式。</p> <figure style="box-sizing: border-box;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"> <img class="" data-ratio="0.49636363636363634" src="/upload/cc9d8a4e886add947e7100c9887508dd.jpg" data-type="jpeg" data-w="550" style="box-sizing: border-box;margin-right: auto;margin-left: auto;border-width: 2px;border-style: solid;border-color: rgb(238, 238, 238);font-size: inherit;color: inherit;line-height: inherit;display: block;box-shadow: rgb(221, 221, 221) 0px 0px 15px 3px;border-radius: 8px !important;" title="单例模式"> <figcaption style="box-sizing: border-box;margin-top: 10px;font-size: 0.7em;color: rgb(153, 153, 153);line-height: inherit;text-align: center;"> 单例模式 </figcaption> </figure> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">在Mybatis中有两个地方用到单例模式,<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">ErrorContext</code>和<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">LogFactory</code>,其中<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">ErrorContext</code>是用在每个线程范围内的单例,用于记录该线程的执行环境错误信息,而<code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">LogFactory</code>则是提供给整个Mybatis使用的日志工厂,用于获得针对项目配置好的日志对象。</p> <p style="box-sizing: border-box;margin-top: 1.5em;margin-bottom: 1.5em;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;color: rgb(233, 105, 0);line-height: inherit;overflow-wrap: break-word;border-radius: 4px;background: rgb(248, 248, 248);">ErrorContext</code>的单例实现代码:</p> <pre style="box-sizing: border-box;font-size: 16px;color: rgb(62, 62, 62);line-height: inherit;text-align: start;background-color: rgb(255, 255, 255);"><code class="java language-java hljs" style="box-sizing: border-box;padding: 0.5em;font-size: 14px;color: rgb(67, 79, 84);line-height: 18px;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;display: block;font-family: Consolas, Inconsolata, Courier, monospace;overflow-x: auto;letter-spacing: 0px;box-shadow: rgb(221, 221, 221) 0px 0px 15px 3px;margin: 30px 8px 20px !important;overflow-wrap: normal !important;border-radius: 8px !important;word-break: normal !important;overflow-y: auto !important;"><span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">public</span>&nbsp;<span class="hljs-class" style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">class</span>&nbsp;<span class="hljs-title" style="box-sizing: border-box;font-size: inherit;color: rgb(136, 0, 0);line-height: inherit;font-weight: bold;overflow-wrap: inherit !important;word-break: inherit !important;">ErrorContext</span>&nbsp;</span>{<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">private</span>&nbsp;<span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">static</span>&nbsp;<span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">final</span>&nbsp;ThreadLocal&lt;ErrorContext&gt;&nbsp;LOCAL&nbsp;=&nbsp;<span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">new</span>&nbsp;ThreadLocal&lt;ErrorContext&gt;();<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="box-sizing: border-box;font-size: inherit;color: rgb(114, 142, 0);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">private</span>&nbsp;<span class="hljs-title" style="box-sizing: border-box;font-size: inherit;color: rgb(136, 0, 0);line-height: inherit;font-weight: bold;overflow-wrap: inherit !important;word-break: inherit !important;">ErrorContext</span><span class="hljs-params" style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">()</span>&nbsp;</span>{<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;}<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="box-sizing: border-box;font-size: inherit;color: rgb(114, 142, 0);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">public</span>&nbsp;<span class="hljs-keyword" style="box-sizing: border-box;font-size: inherit;color: rgb(0, 151, 157);line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">static</span>&nbsp;ErrorContext&nbsp;<span class="hljs-title" style="box-sizing: border-box;font-size: inherit;color: rgb(136, 0, 0);line-height: inherit;font-weight: bold;overflow-wrap: inherit !important;word-break: inherit !important;">instance</span><span class="hljs-params" style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">()</span>&nbsp;</span>{<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ErrorContext&nbsp;context&nbsp;=&nbsp;LOCAL.get();<br style="box-sizing: border-box;font-size: inherit;color: inherit;line-height: inherit;overflow-wrap: inherit !important;word-break: inherit !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="box-sizing: borde

ons-client无法使用log4j2输出日志

作者:じ☆ve宝贝

> 项目中使用了阿里云的MQ,在使用中发现如果使用的是log4j2的话日志框架无法兼容。 ![阿里云ons日志配置官网截图](/upload/e770cadbc8b54739b13a55f73943eb2c.png) 官网明确说明1.7.8一下不支持log4j2,但是1.7.8日志怎么使用呢?官网没有明确说明。追了日志源码才发现,代码中会通过System.getProperty("rocketmq.client.logUseSlf4j", "false"); 来判断是否启用sl4j作为日志数据框架,默认是false,所以即便用了也无法输出日志。在项目启动的参数中增加通过指定 -Drocketmq.client.logUseSlf4j=true 来启动 或者 使用System.setProperty("rocketmq.client.logUseSlf4j", "true"); ### 例如jar方式启动: ``` java -Drocketmq.client.logUseSlf4j=true -cp /lib/* ${MAIN_CLASS} & ``` ### tomcat(windows)方式启动: ![](/upload/01dccd34b46145629fb0e7f168d61e99.png) ### tomat(Linux)方式启动: ![](/upload/021e173df1f8463bb3cd298451d16d9a.png) ## ons-client-1.7.0-Final怎么使用log4j2 在log4j同级目录复制一个名为log4j2_rocketmq_client.xml的log4j2的xml。然后配备sl4j插件使用,项目初期使用log4j2.xml启动,然后启动mq后会使用log4j2_rocketmq_client.xml进行日志输出。

Redis从入门到精通,至少要看看这篇!

作者:微信小助手

<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="" class="horizontal-tb" opera-tn-ra-cell="_$.pages:0.layers:0.comps:0.txt1"> <section style="box-sizing: border-box;"> “ </section></span> <section class="horizontal-tb" 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;">常用的 SQL 数据库的数据都是存在磁盘中的,虽然在数据库底层也做了对应的缓存来减少数据库的 IO 压力。</span></p> </section> <section style="clear: both;box-sizing: border-box;"></section> </section> </section> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;"> <img class="rich_pages" data-ratio="0.66640625" data-s="300,640" src="/upload/90bc233f89edb4acf24c40b0e91867cd.jpg" data-type="jpeg" data-w="1280" style=""> </section> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;"><em>图片来自&nbsp;Pexels</em></span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">由于数据库的缓存一般是针对查询的内容,而且粒度也比较小,一般只有表中的数据没有发生变动的时候,数据库的缓存才会产生作用。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">但这并不能减少业务逻辑对数据库的增删改操作的 IO 压力,因此缓存技术应运而生,该技术实现了对热点数据的高速缓存,可以大大缓解后端数据库的压力。</span></p> <section style="line-height: normal;"> <br> </section> <section style="box-sizing: border-box;font-size: 16px;"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;" powered-by="xiumi.us"> <section class="horizontal-tb" style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">主流应用架构</p> </section> </section> </section> <section style="line-height: normal;"> <br> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <img class="rich_pages " data-ratio="0.4988272087568413" data-s="300,640" data-type="png" data-w="1279" src="/upload/3d6f6109d085d8b17ebea1d432f58d7f.png" style="box-sizing: border-box !important;word-wrap: break-word !important;width: 677px !important;visibility: visible !important;"> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">客户端在对数据库发起请求时,先到缓存层查看是否有所需的数据,如果缓存层存有客户端所需的数据,则直接从缓存层返回,否则进行穿透查询,对数据库进行查询。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果在数据库中查询到该数据,则将该数据回写到缓存层,以便下次客户端再次查询能够直接从缓存层获取数据。</span></p> <section style="line-height: normal;"> <br> </section> <section style="box-sizing: border-box;font-size: 16px;"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;" powered-by="xiumi.us"> <section class="horizontal-tb" style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">缓存中间件&nbsp;Memcache&nbsp;和 Redis 的区别</p> </section> </section> </section> <section style="line-height: normal;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><br></span> </section> <h3 style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">Memcache 的代码层类似 Hash,特点如下:</span></h3> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">支持简单数据类型</span></strong></p></li> <li><p><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">不支持数据持久化存储</span></strong></p></li> <li><p><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">不支持主从</span></strong></p></li> <li><p><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></li> </ul> <section style="text-align: justify;line-height: 1.75em;"></section> <section style="text-align: justify;line-height: 1.75em;"></section> <section style="text-align: justify;line-height: 1.75em;"></section> <section style="text-align: justify;line-height: 1.75em;"></section> <h3 style="line-height: normal;"><br></h3> <h3 style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">Redis 特点如下:</span><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"></span></h3> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">数据类型丰富</span></strong></p></li> <li><p><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">支持数据磁盘持久化存储</span></strong></p></li> <li><p><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">支持主从</span></strong></p></li> <li><p><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></li> </ul> <section style="text-align: justify;line-height: 1.75em;"></section> <section style="text-align: justify;line-height: 1.75em;"></section> <section style="text-align: justify;line-height: 1.75em;"></section> <section style="text-align: justify;line-height: 1.75em;"></section> <h2 style="line-height: normal;"><br></h2> <section style="box-sizing: border-box;font-size: 16px;"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;" powered-by="xiumi.us"> <section class="horizontal-tb" style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">为什么&nbsp;Redis&nbsp;能这么快</p> </section> </section> </section> <section style="line-height: normal;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><br></span> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">Redis 的效率很高,官方给出的数据是 100000+QPS,这是因为:</span> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"></span> </section> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Redis 完全基于内存,绝大部分请求是纯粹的内存操作,执行效率高。</span></p></li> <li><p><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Redis 使用单进程单线程模型的(K,V<span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 24px;">)</span>数据库,将数据存储在内存中,存取均不会受到硬盘 IO 的限制,因此其执行速度极快。</span></p><p><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">另外单线程也能处理高并发请求,还可以避免频繁上下文切换和锁的竞争,如果想要多核运行也可以启动多个实例。</span></p></li> <li><p><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">数据结构简单,对数据操作也简单,Redis 不使用表,不会强制用户对各个关系进行关联,不会有复杂的关系限制,其存储结构就是键值对,类似于 HashMap,HashMap 最大的优点就是存取的时间复杂度为 O(1)。</span></p></li> <li><p><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Redis 使用多路 I/O 复用模型,为非阻塞 IO。</span></p></li> </ul> <section style="text-align: justify;line-height: 1.75em;"></section> <section style="text-align: justify;line-height: 1.75em;"></section> <section style="text-align: justify;line-height: 1.75em;"></section> <section style="text-align: justify;line-height: 1.75em;"></section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">注:Redis 采用的 I/O 多路复用函数:epoll/kqueue/evport/select。</span> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">选用策略:</span> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"></span> </section> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">因地制宜,优先选择时间复杂度为 O(1)&nbsp;的 I/O 多路复用函数作为底层实现。</span></p></li> <li><p><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">由于 Select 要遍历每一个 IO,所以其时间复杂度为 O(n),通常被作为保底方案。</span></p></li> <li><p><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">基于 React 设计模式监听 I/O 事件。</span></p></li> </ul> <section style="text-align: justify;line-height: 1.75em;"></section> <section style="text-align: justify;line-height: 1.75em;"></section> <section style="text-align: justify;line-height: 1.75em;"></section> <h2 style="line-height: normal;"><br></h2> <section style="box-sizing: border-box;font-size: 16px;"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;" powered-by="xiumi.us"> <section class="horizontal-tb" style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">Redis 的数据类型</p> </section> </section> </section> <p style="line-height: normal;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><br></span></p> <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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </section> <section class="horizontal-tb" style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>String</strong></p> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">最基本的数据类型,其值最大可存储 512M,二进制安全(Redis 的 String 可以包含任何二进制数据,包含 jpg 对象等)。</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="rich_pages " data-ratio="0.47063903281519864" data-s="300,640" data-type="png" data-w="1158" src="/upload/7529f277f6de294bf93faae8c4d2ceb6.png" style="box-sizing: border-box !important;word-wrap: break-word !important;width: 677px !important;visibility: visible !important;"></p> <h3 style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">注:如果重复写入 key 相同的键值对,后写入的会将之前写入的覆盖。</span></h3> <h3 style="line-height: normal;"><br></h3> <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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section class="horizontal-tb" style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>Hash</strong></p> </section> </section> </section> <section style="line-height: normal;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><br></span> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">String 元素组成的字典,适用于存储对象。</span></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <img class="rich_pages " data-ratio="0.23686405337781485" data-s="300,640" data-type="png" data-w="1199" src="/upload/2b1945e25622673e66ec200862173924.png" style="box-sizing: border-box !important;word-wrap: break-word !important;width: 677px !important;visibility: visible !important;"> </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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section class="horizontal-tb" style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>List</strong></p> </section> </section> </section> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">列表,按照 String 元素插入顺序排序。其顺序为后进先出。由于其具有栈的特性,所以可以实现如“最新消息排行榜”这类的功能。</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="rich_pages " data-ratio="0.3140625" data-s="300,640" data-type="png" data-w="1280" src="/upload/dbe0a78e3388b8d6c95f08aef601f6ca.png" style="box-sizing: border-box !important;word-wrap: break-word !important;width: 677px !important;visibility: visible !important;"></p> <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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section class="horizontal-tb" style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>Set</strong></p> </section> </section> </section> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">String 元素组成的无序集合,通过哈希表实现(增删改查时间复杂度为 O(1)),不允许重复。</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="rich_pages " data-ratio="0.29123468426013194" data-s="300,640" data-type="png" data-w="1061" src="/upload/89d813ac02c6202570c690d886ad604.png" style="box-sizing: border-box !important;word-wrap: break-word !important;width: 677px !important;visibility: visible !important;"></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">另外,当我们使用 Smembers 遍历 Set 中的元素时,其顺序也是不确定的,是通过 Hash 运算过后的结果。</span> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Redis 还对集合提供了求交集、并集、差集等操作,可以实现如同共同关注,共同好友等功能。</span> </section> <section style="line-height: normal;"> <br> </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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section class="horizontal-tb" style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>Sorted Set</strong></p> </section> </section> </section> <section style="line-height: normal;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><br></span> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">通过分数来为集合中的成员进行从小到大的排序。</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="rich_pages " data-ratio="0.41015625" data-s="300,640" data-type="png" data-w="1280" src="/upload/6bd38e5e6460fbf2a263a8b4cb7c88df.png" style="box-sizing: border-box !important;word-wrap: break-word !important;width: 677px !important;visibility: visible !important;"></p> <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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </section> <section class="horizontal-tb" style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>更高级的 Redis&nbsp;类型</strong></p> </section> </section> </section> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">用于计数的 HyperLogLog、用于支持存储地理位置信息的 Geo。</span> </section> <section style="line-height: normal;"> <br> </section> <section style="box-sizing: border-box;font-size: 16px;"> <section style="transform: rotate(0deg);-webkit-transform: rotate(0deg);-moz-transform: rotate(0deg);-o-transform: rotate(0deg);box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section class="horizontal-tb" style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">从海量 Key 里查询出某一个固定前缀的 Key</p> </section> </section> </section> </section> <section style="line-height: normal;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><br></span> </section> <h3 style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">假设 Redis 中有十亿条 Key,如何从这么多 Key 中找到固定前缀的 Key?</span></h3> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">方法 1:</span></strong> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">使用 Keys [pattern]:查找所有符合给定模式 Pattern 的 Key</span> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">使用 Keys [pattern] 指令可以找到所有符合 Pattern 条件的 Key,但是 Keys 会一次性返回所有符合条件的 Key,所以会造成 Redis 的卡顿。</span> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">假设 Redis 此时正在生产环境下,使用该命令就会造成隐患,另外如果一次性返回所有 Key,对内存的消耗在某些条件下也是巨大的。</span> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">例:</span> </section> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;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;"><code class="hljs bash" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);padding: 0.5em;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;background: rgb(40, 43, 46);">keys&nbsp;<span class="hljs-built_in" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">test</span>*&nbsp;//返回所有以<span class="hljs-built_in" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">test</span>为前缀的key<br></code></pre> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">方法 2:</span></strong> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">使用&nbsp;SCAN cursor [MATCH pattern] [COUNT count]</span> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">注:</span><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">cursor:游标</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">MATCH pattern:查询 Key 的条件</span></p></li> <li><p style="text-align: justify;line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">Count:返回的条数</span></p></li> </ul> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">SCAN 是一个基于游标的迭代器,需要基于上一次的游标延续之前的迭代过程。</span> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">SCAN 以 0 作为游标,开始一次新的迭代,直到命令返回游标&nbsp;0&nbsp;完成一次遍历。</span> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">此命令并不保证每次执行都返回某个给定数量的元素,甚至会返回 0 个元素,但只要游标不是 0,程序都不会认为 SCAN 命令结束,但是返回的元素数量大概率符合 Count 参数。另外,SCAN 支持模糊查询。</span> </section> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">例:</span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;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;"><code class="hljs bash" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);padding: 0.5em;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;background: rgb(40, 43, 46);">SCAN&nbsp;0&nbsp;MATCH&nbsp;<span class="hljs-built_in" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">test</span>*&nbsp;COUNT&nbsp;10&nbsp;//每次返回10条以<span class="hljs-built_in" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">test</span>为前缀的key<br></code></pre> </section> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;font-size: 16px;"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;" powered-by="xiumi.us"> <section class="horizontal-tb" style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">如何通过 Redis 实现分布式锁</p> </section> </section> </section> <p style="line-height: normal;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><br></span></p> <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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section class="horizontal-tb" style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>分布式锁</strong></p> </section> </section> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">分布式锁是控制分布式系统之间共同访问共享资源的一种锁的实现。如果一个系统,或者不同系统的不同主机之间共享某个资源时,往往需要互斥,来排除干扰,满足数据一致性。</span> </section> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">分布式锁需要解决的问题如下:</span> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"></span> </section> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><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></li> <li><p><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></li> <li><p><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></li> <li><p><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;">当各个节点,如某个 Redis 节点宕机的时候,客户端仍然能够获取锁或释放锁。</span></p></li> </ul> <section style="text-align: justify;line-height: 1.75em;"></section> <section style="text-align: justify;line-height: 1.75em;"></section> <section style="text-align: justify;line-height: 1.75em;"></section> <section style="text-align: justify;line-height: 1.75em;"></section> <h2 style="line-height: normal;"><br></h2> <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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section class="horizontal-tb" style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>如何使用 Redis 实现分布式锁</strong></p> </section> </section> </section> <section style="line-height: normal;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><br></span> </section> <h3 style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">使用 SETNX 实现,</span></strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">SETNX key value:如果 Key 不存在,则创建并赋值。</span></h3> <h3 style="line-height: normal;"><br></h3> <h3 style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">该命令时间复杂度为 O(1),如果设置成功,则返回 1,否则返回 0。</span></h3> <section style="text-align: center;line-height: 1.75em;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"> <img class="rich_pages " data-ratio="0.2359375" data-s="300,640" data-type="png" data-w="1280" src="/upload/7691490bfaf2fe5014e0921db70bd332.png" style="box-sizing: border-box !important;word-wrap: break-word !important;width: 677px !important;visibility: visible !important;"> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">由于 SETNX 指令操作简单,且是原子性的,所以初期的时候经常被人们作为分布式锁,我们在应用的时候,可以在某个共享资源区之前先使用 SETNX 指令,查看是否设置成功。</span> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果设置成功则说明前方没有客户端正在访问该资源,如果设置失败则说明有客户端正在访问该资源,那么当前客户端就需要等待。</span> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">但是如果真的这么做,就会存在一个问题,因为 SETNX 是长久存在的,所以假设一个客户端正在访问资源,并且上锁,那么当这个客户端结束访问时,该锁依旧存在,后来者也无法成功获取锁,这个该如何解决呢?</span> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">由于 SETNX 并不支持传入 EXPIRE 参数,所以我们可以直接使用 EXPIRE 指令来对特定的 Key 来设置过期时间。</span> </section> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">用法:</span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;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;"><code class="hljs nginx" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);padding: 0.5em;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;background: rgb(40, 43, 46);"><span class="hljs-attribute" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">EXPIRE</span>&nbsp;key&nbsp;seconds<br></code></pre> </section> <p style="line-height: normal;"><br></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="rich_pages" data-ratio="0.24761904761904763" data-s="300,640" src="/upload/59e6b0380b15c143ce96535091143d3e.png" data-type="png" data-w="1260" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">程序:</span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;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;"><code class="hljs lua" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);padding: 0.5em;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;background: rgb(40, 43, 46);">RedisService&nbsp;redisService&nbsp;=&nbsp;SpringUtils.getBean(RedisService.class);<br>long&nbsp;<span class="hljs-built_in" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">status</span>&nbsp;=&nbsp;redisService.setnx(key,<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"1"</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;">if</span>(<span class="hljs-built_in" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">status</span>&nbsp;==&nbsp;<span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">1</span>){<br>&nbsp;&nbsp;redisService.expire(key,expire);<br>&nbsp;&nbsp;doOcuppiedWork();<br>}<br></code></pre> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这段程序存在的问题:假设程序运行到第二行出现异常,那么程序来不及设置过期时间就结束了,则 Key 会一直存在,等同于锁一直被持有无法释放。</span> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">出现此问题的根本原因为:原子性得不到满足。</span> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;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;">从 Redis 2.6.12 版本开始,我们就可以使用 Set 操作,将 SETNX 和 EXPIRE&nbsp;融合在一起执行,具体做法如下:</span> </section> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">EX second:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">设置键的过期时间为 Second 秒。</span></p></li> <li><p><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">PX millisecond:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">设置键的过期时间为 MilliSecond 毫秒。</span></p></li> <li><p><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">NX:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">只在键不存在时,才对键进行设置操作。</span></p></li> <li><p style="margin-bottom: 5px;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">XX:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">只在键已经存在时,才对键进行设置操作。</span></p></li> </ul> <section style="text-align: justify;line-height: 1.75em;"></section> <section style="text-align: justify;line-height: 1.75em;"></section> <section style="text-align: justify;line-height: 1.75em;"></section> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;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;"><code class="hljs css" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);padding: 0.5em;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;background: rgb(40, 43, 46);"><span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">SET</span>&nbsp;<span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">KEY</span>&nbsp;<span class="hljs-selector-tag" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">value</span>&nbsp;<span class="hljs-selector-attr" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">[EX&nbsp;seconds]</span>&nbsp;<span class="hljs-selector-attr" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">[PX&nbsp;milliseconds]</span>&nbsp;<span class="hljs-selector-attr" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">[NX|XX]</span><br></code></pre> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">注:SET 操作成功完成时才会返回 OK,否则返回 nil。</span> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">有了 SET 我们就可以在程序中使用类似下面的代码实现分布式锁了:</span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;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;"><code class="hljs dart" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);padding: 0.5em;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;background: rgb(40, 43, 46);">RedisService&nbsp;redisService&nbsp;=&nbsp;SpringUtils.getBean(RedisService.<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>);<br><span class="hljs-built_in" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">String</span>&nbsp;result&nbsp;=&nbsp;redisService.<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">set</span>(lockKey,requestId,SET_IF_NOT_EXIST,SET_WITH_EXPIRE_TIME,expireTime);<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;">if</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;">"OK.equals(result)"</span>){<br>&nbsp;&nbsp;doOcuppiredWork();<br>}<br></code></pre> </section> <section style="line-height: normal;"> <br> </section> <section style="box-sizing: border-box;font-size: 16px;"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;" powered-by="xiumi.us"> <section class="horizontal-tb" style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">如何实现异步队列</p> </section> </section> </section> <section style="line-height: normal;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><br></span> </section> <h3 style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><strong>①使用&nbsp;Redis 中的&nbsp;List 作为队列</strong></span></h3> <section style="line-height: normal;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><br></span> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">使用上文所说的 Redis 的数据结构中的 List 作为队列 Rpush 生产消息,LPOP 消费消息。</span></p> <section style="text-align: center;line-height: 1.75em;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"> <img class="rich_pages " data-ratio="0.33069082672706684" data-s="300,640" data-type="png" data-w="883" src="/upload/25aabd2041cbc9e8a56e6450c911263f.png" style="box-sizing: border-box !important;word

你连微服务的网关都说不清楚,还天天鼓捣着要把项目拆分微服务?

作者:微信小助手

<section class="KolEditor" style="white-space: normal;" data-mpa-powered-by="yiban.io"> <section style="margin: 10px auto;display: flex;justify-content: center;align-items: flex-end;"> <p style="color: rgb(88, 16, 13);line-height: 1.5;">还没关注?</p> <section style="margin-right: 10px;margin-left: 10px;width: 60px;"> <img class="" data-ratio="0.6666666666666666" data-type="png" data-w="112" src="/upload/7f9c30bbc2fff4ed4fe68471b8552ba7.png" style="vertical-align: bottom;width:auto !important;max-width:100% !important;height:auto !important;"> </section> <p style="color: rgb(88, 16, 13);line-height: 1.5;">快动动手指!</p> <p style="color: rgb(88, 16, 13);line-height: 1.5;"><br></p> </section> </section> <section class="KolEditor" data-tools-id="85858" style="white-space: normal;"> <section class="Powered-by-KolEditor V5" powered-by="KolEditor.us"> <section class=""> <section class="" style="margin-bottom: 5px;height: 5px;background-color: rgb(154, 157, 170);"></section> <section class="" style="margin-bottom: 5px;height: 5px;background-color: rgb(154, 157, 170);"></section> <section class="" style="margin-bottom: 5px;height: 5px;background-color: rgb(154, 157, 170);"></section> <section class="" style="margin-bottom: 5px;height: 5px;background-color: rgb(154, 157, 170);"></section> <section class="" style="margin-bottom: 5px;height: 5px;background-color: rgb(154, 157, 170);"></section> <section class="" style="margin-bottom: 5px;height: 5px;background-color: rgb(154, 157, 170);"></section> <section class="" style="margin-top: -40px;margin-right: 1.5em;margin-left: 1.5em;padding: 10px;background-color: rgb(226, 230, 0);"> <section class="Powered-by-KolEditor V5" powered-by="KolEditor.us"> <section class=""> <section class="" style="text-align: center;color: rgb(255, 255, 255);"> <section> <span style="color: rgb(0, 0, 0);font-size: 15px;">聊技术、论职场!</span> </section> <section> <span style="color: rgb(0, 0, 0);"><span style="font-size: 15px;">为IT人打造一个“有温度”的</span><strong style="font-size: 15px;"><span style="color: rgb(201, 56, 28);">狸猫技术窝</span></strong></span> </section> </section> </section> </section> </section> </section> </section> </section> <p style="white-space: normal;text-align: right;line-height: normal;"><br></p> <p style="line-height: 2em;"><span style="font-size: 14px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 14px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;">一、API 网关的用处</span></p> <p style="line-height: 2em;"><span style="font-size: 14px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;">二、API网关在企业架构中的地位</span></p> <p style="line-height: 2em;"><span style="font-size: 14px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;">三、企业中如何应用API网关</span></p> <p style="line-height: 2em;"><span style="font-size: 14px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;">四、API网关有哪些竞争方案</span></p> <p style="line-height: 2em;"><span style="font-size: 14px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;">五、API网关解决方案</span></p> <p style="line-height: 2em;"><span style="font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><span style="font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 14px;">六、企业怎么选择API网关</span><span style="font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;line-height: 21px;letter-spacing: 1px;"></span></span></p> <p style="margin: 10px auto;white-space: normal;color: rgb(0, 0, 0);font-family: verdana, &quot;ms song&quot;, 宋体, Arial, 微软雅黑, Helvetica, sans-serif;font-size: 12px;line-height: 2em;"><br></p> <p style="margin: 10px auto;font-stretch: normal;font-size: 12px;font-family: Trebuchet MS-webkit-text-stroke-color: rgb(0, 0, 0);color: rgb(0, 0, 0);text-align: left;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"><span style="font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><span style="font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;color: rgb(61, 167, 66);"><strong><span style="font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-kerning: none;font-stretch: normal;line-height: 21px;font-size: 18px;">一、API网关的用处</span></strong></span><span style="font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-kerning: none;font-stretch: normal;line-height: 21px;letter-spacing: 1px;"></span></span></p> <p style="margin: 10px auto;font-stretch: normal;font-size: 12px;font-family: Trebuchet MS-webkit-text-stroke-color: rgb(0, 0, 0);color: rgb(0, 0, 0);text-align: left;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"><span style="line-height: 21px;font-kerning: none;letter-spacing: 1px;font-size: 17px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;">API网关我的分析中会用到以下三种场景。</span></p> <p style="line-height: 2em;"><span style="font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><strong><span style="font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-kerning: none;line-height: normal;font-stretch: normal;letter-spacing: 1px;">1、Open API</span></strong></span></p> <p style="line-height: 2em;"><span style="font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><span style="font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-kerning: none;line-height: normal;font-stretch: normal;letter-spacing: 1px;">企业需要将自身数据、能力等作为开发平台向外开放,通常会以rest的方式向外提供。</span><span style="font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 1px;">最好的例子就是淘宝开放平台、腾讯公司的QQ开发平台、微信开放平台。</span><br></span></p> <p style="line-height: 2em;"><span style="letter-spacing: 1px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><br></span></p> <p style="line-height: 2em;"><span style="letter-spacing: 1px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;">Open API开放平台必然涉及到客户应用的接入、API权限的管理、调用次数管理等,必然会有一个统一的入口进行管理,这正是API网关可以发挥作用的时候。</span></p> <p style="line-height: 2em;"><span style="letter-spacing: 1px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><br></span></p> <p style="line-height: 2em;"><span style="font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><strong><span style="font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-kerning: none;line-height: normal;font-stretch: normal;letter-spacing: 1px;">2、微服务网关</span></strong></span></p> <p style="line-height: 2em;"><span style="font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 1px;">微服务的概念最早在2012年提出,在Martin Fowler的大力推广下,微服务在2014年后得到了大力发展。<br></span></p> <p style="line-height: 2em;"><span style="font-kerning: none;line-height: normal;font-stretch: normal;letter-spacing: 1px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><br></span></p> <p style="line-height: 2em;"><span style="font-kerning: none;line-height: normal;font-stretch: normal;letter-spacing: 1px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;">在微服务架构中,有一个组件可以说是必不可少的,那就是微服务网关,微服务网关处理了负载均衡,缓存,路由,访问控制,服务代理,监控,日志等。</span></p> <p style="line-height: 2em;"><span style="font-kerning: none;line-height: normal;font-stretch: normal;letter-spacing: 1px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><br></span></p> <p style="line-height: 2em;"><span style="font-kerning: none;line-height: normal;font-stretch: normal;letter-spacing: 1px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;">API 网关在微服务架构中正是以微服务网关的身份存在。</span></p> <p style="line-height: 2em;"><span style="font-kerning: none;line-height: normal;font-stretch: normal;letter-spacing: 1px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><br></span></p> <p style="line-height: 2em;"><span style="font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><strong><span style="font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-kerning: none;line-height: normal;font-stretch: normal;letter-spacing: 1px;">3、API服务管理平台</span></strong></span></p> <p style="line-height: 2em;"><span style="letter-spacing: 1px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;">上述的微服务架构对企业来说有可能实施上是困难的,企业有很多遗留系统,要全部抽取为微服务改动太大,对企业来说成本太高。</span></p> <p style="line-height: 2em;"><span style="letter-spacing: 1px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><br></span></p> <p style="line-height: 2em;"><span style="letter-spacing: 1px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;">但是由于不同系统间存在大量的API服务互相调用,因此需要对系统间服务调用进行管理,清晰地看到各系统调用关系,对系统间调用进行监控等。</span></p> <p style="line-height: 2em;"><span style="letter-spacing: 1px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><br></span></p> <p style="line-height: 2em;"><span style="font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 1px;">API网关可以解决这些问题,我们可以认为如果没有大规模的实施微服务架构,那么对企业来说微服务网关就是企业的API服务管理平台。<br></span></p> <p style="line-height: 2em;"><span style="font-kerning: none;line-height: normal;font-stretch: normal;letter-spacing: 1px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"></span></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><br></p> <p style="margin: 10px auto;color: rgb(0, 0, 0);font-family: verdana, ms song, 宋体, Arial, 微软雅黑, Helvetica, sans-serif;font-size: 12px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"><span style="font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><strong><span style="font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-kerning: none;line-height: normal;font-stretch: normal;font-size: 18px;color: rgb(61, 167, 66);">二、API网关在企业架构中的地位</span></strong><span style="font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-kerning: none;line-height: normal;font-stretch: normal;letter-spacing: 1px;"></span></span></p> <p style="margin: 10px auto;color: rgb(0, 0, 0);font-family: verdana, ms song, 宋体, Arial, 微软雅黑, Helvetica, sans-serif;font-size: 12px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"><span style="letter-spacing: 1px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;">一个企业随着信息系统复杂度的提高,必然出现外部合作伙伴应用、企业自身的公网应用、企业内网应用等。</span></p> <p style="margin: 10px auto;color: rgb(0, 0, 0);font-family: verdana, ms song, 宋体, Arial, 微软雅黑, Helvetica, sans-serif;font-size: 12px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"><span style="letter-spacing: 1px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;"><br></span></p> <p style="margin: 10px auto;color: rgb(0, 0, 0);font-family: verdana, ms song, 宋体, Arial, 微软雅黑, Helvetica, sans-serif;font-size: 12px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"><span style="letter-spacing: 1px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;">在架构上应该将这三种应用区别开,三种应用的安排级别、访问方式也不一样。</span></p> <p style="margin: 10px auto;color: rgb(0, 0, 0);font-family: verdana, ms song, 宋体, Arial, 微软雅黑, Helvetica, sans-serif;font-size: 12px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"><span style="letter-spacing: 1px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;"><br></span></p> <p style="margin: 10px auto;color: rgb(0, 0, 0);font-family: verdana, ms song, 宋体, Arial, 微软雅黑, Helvetica, sans-serif;font-size: 12px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"><span style="letter-spacing: 1px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;">因此在我的设计中将这三种应用分别用不同的网关进行API管理,分别是:API网关(OpenAPI合伙伙伴应用)、API网关(内部应用)、API网关(内部公网应用)。</span></p> <p style="margin: 10px auto;color: rgb(0, 0, 0);font-family: verdana, ms song, 宋体, Arial, 微软雅黑, Helvetica, sans-serif;font-size: 12px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"><span style="letter-spacing: 1px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;"><br></span></p> <p style="margin: 10px auto;color: rgb(0, 0, 0);font-family: verdana, ms song, 宋体, Arial, 微软雅黑, Helvetica, sans-serif;font-size: 12px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"><span style="letter-spacing: 1px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;">如下图所示:</span></p> <p style="margin: 10px auto;color: rgb(0, 0, 0);font-family: verdana, ms song, 宋体, Arial, 微软雅黑, Helvetica, sans-serif;font-size: 12px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><span style="font-stretch: normal;font-size: 14px;font-kerning: none;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><img class="" data-ratio="0.6666666666666666" data-type="png" data-w="1734" src="/upload/12c8bfd1145

Win10 开启VPN后无法上网

作者:不要哭啦

# Win10链接vpn后wifi无法上网解决办法: ``` C:\Users\用户名\AppData\Roaming\Microsoft\Network\Connections\Pbk(或者rasphone.pbk)搜索rasphone.pbk) 用记事本打开rasphone.pbk找到IpPrioritizeRemote=1 改成0就取消 “从远程网络上使用默认网关” ``` 这样就解决了,如果遇到类似问题,您可以试试

抖音抓包教程,抖音无水印地址解析

作者:じ☆ve宝贝

> 前几天在吾爱破解发的 [《抖音无水印下载Java源码》](https://www.52pojie.cn/thread-742575-1-1.html) 很多朋友留言,抖音无水印地址在哪里获取到的,今天给大家讲解一下。 ## 准备工具 1. Charles 2. 装有抖音APP的手机 ## 提前学习的资料 1. [Charles抓包学习](https://www.studyjava.cn/topic/view/397721a9b8e641e6b8ab5c981df40c05.html) **Charles抓包如果准备好了,就进行我们接下来的学习步骤** ## 清空链接 1. 点击该图标,清空链接 ![](/upload/b84634a4d9684f4782b8c2f2490c3d47.png) 2. 同时打开手机抖音APP(会看到有很多链接在变化) ![](/upload/0b63b83a81a848e78cac553bc2c8f8b6.png) 我们可以看到[《抖音无水印下载Java源码》](https://www.52pojie.cn/thread-742575-1-1.html)最后文章介绍的无水印url的前缀就是上图中标红框的部分,这样我们就拿到抖音的无水印地址,只要更替对应的video_id即可下载,高清无码视频了。 ## 本站小程序: ![Java学习者](/upload/762fd13b77664616808c3983a9896b5a.jpg)

高并发场景下,如何保证生产者投递到消息中间件的消息不丢失?【石杉的架构笔记】

作者:微信小助手

<p style="white-space: normal;background-color: rgb(255, 255, 255);text-align: center;letter-spacing: 1px;line-height: 1.5em;overflow-wrap: break-word !important;" data-mpa-powered-by="yiban.io"><span style="font-size: 15px;"><span style="color: rgb(136, 136, 136);border-width: 0px;border-style: initial;border-color: initial;font-style: inherit;font-variant: inherit;font-weight: inherit;font-stretch: inherit;line-height: inherit;font-family: PingFangSC-Regular;vertical-align: baseline;overflow-wrap: break-word !important;">点击上方</span><span style="border-width: 0px;border-style: initial;border-color: initial;font-style: inherit;font-variant: inherit;font-weight: inherit;font-stretch: inherit;line-height: inherit;font-family: PingFangSC-Regular;vertical-align: baseline;color: rgb(61, 170, 214);overflow-wrap: break-word !important;"><strong>"</strong><strong>蓝字",&nbsp;</strong></span><span style="border-width: 0px;border-style: initial;border-color: initial;font-style: inherit;font-variant: inherit;font-weight: inherit;font-stretch: inherit;line-height: inherit;font-family: PingFangSC-Regular;vertical-align: baseline;color: rgb(136, 136, 136);overflow-wrap: break-word !important;">右上角</span><span style="color: rgb(136, 136, 136);border-width: 0px;border-style: initial;border-color: initial;font-style: inherit;font-variant: inherit;font-weight: inherit;font-stretch: inherit;line-height: inherit;font-family: PingFangSC-Regular;vertical-align: baseline;overflow-wrap: break-word !important;">选择“设为星标”</span></span></p> <p style="white-space: normal;background-color: rgb(255, 255, 255);text-align: center;letter-spacing: 1px;line-height: 1.5em;overflow-wrap: break-word !important;"><span style="border-width: 0px;border-style: initial;border-color: initial;font-style: inherit;color: rgb(136, 136, 136);font-variant: inherit;font-weight: inherit;font-stretch: inherit;line-height: inherit;font-family: PingFangSC-Regular;vertical-align: baseline;font-size: 15px;overflow-wrap: break-word !important;">&nbsp;周一至周五早8点半!精品技术文章准时送上!</span></p> <section class="KolEditor" data-tools-id="56190" style="white-space: normal;"> <section style="margin: 10px auto;width: 56px;"> <img class="" data-ratio="0.6666666666666666" data-type="gif" data-w="56" src="/upload/c95cc8787dcfb4cd58c2a98eda4513fa.gif"> </section> </section> <section style="margin:12px;display: flex;align-items: center;justify-content: center;"> <section style="border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);background: rgb(167, 225, 238);"> <section style="border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);background: rgb(255, 244, 168);margin: 10px 10px -10px -10px;"> <section style="padding: 15px 10px;border-width: 1px;border-style: dashed;border-color: rgb(0, 0, 0);background: rgb(255, 255, 255);margin: -5px -5px 5px 5px;"> <p style="white-space: normal;line-height: 2em;"><span style="font-size: 15px;"><strong>&nbsp; 目录</strong></span></p> <p style="white-space: normal;line-height: 2em;"><span style="font-size: 14px;">(1)<span style="color: rgb(64, 179, 230);">前情提示</span></span></p> <p style="white-space: normal;line-height: 2em;"><span style="font-size: 14px;">(2)<span style="color: rgb(64, 179, 230);">保证投递消息不丢失的confirm机制</span></span></p> <p style="white-space: normal;line-height: 2em;"><span style="font-size: 14px;">(3)<span style="font-size: 15px;color: rgb(64, 179, 230);">confirm机制的代码实现</span></span></p> <p style="white-space: normal;line-height: 2em;"><span style="font-size: 15px;">(4)<span style="color: rgb(64, 179, 230);">confirm机制投递消息的高延迟性</span></span></p> <p style="white-space: normal;line-height: 2em;"><span style="font-size: 15px;">(5)<span style="color: rgb(64, 179, 230);">高并发下如何投递消息才能不丢失</span></span></p> <p style="white-space: normal;line-height: 2em;"><span style="font-size: 15px;">(6)<span style="color: rgb(64, 179, 230);">消息中间件全链路100%数据不丢失能做到吗?</span></span></p> </section> </section> </section> </section> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <section class="KolEditor" data-tools-id="51912"> <section style="margin-top: 8px;font-weight: bold;font-size: 16px;line-height: 28px;color: rgb(170, 166, 149);min-height: 32px;border-bottom: 1.5px solid rgb(170, 166, 149);border-top-color: rgb(170, 166, 149);border-right-color: rgb(170, 166, 149);border-left-color: rgb(170, 166, 149);"> <p data-original-title="" placeholder="1" class="count" style="color: rgb(255, 255, 255);float: left;line-height: 20px;margin-right: 8px;padding: 4px 10px;background-color: rgb(222, 187, 47);">1</p> <p style="display:inline-block;border-color: rgb(170, 166, 149);color: inherit;"><span style="color: rgb(158, 135, 30);">前情提示</span></p> </section> </section> <p><br></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="letter-spacing: 2px;"><span style="font-size: 15px;">上篇文章:</span><a href="http://mp.weixin.qq.com/s?__biz=MzU0OTk3ODQ3Ng==&amp;mid=2247484372&amp;idx=1&amp;sn=2bfa84b2e26569d60db15a82cb684a56&amp;chksm=fba6ebd7ccd162c13771c5345d45d40658e3815d5ed3104490a19bb4bf20fec6060a690e305b&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2"><span style="letter-spacing: normal;"><span style="font-size: 15px;color: rgb(0, 112, 192);">《</span><span style="color: rgb(0, 112, 192);font-size: 15px;text-decoration: underline;">面试大杀器:消息中间件如何实现消费吞吐量的百倍优化?</span><span style="font-size: 15px;color: rgb(0, 112, 192);">》</span></span></a><span style="font-size: 15px;"><span style="font-size: 15px;color: rgb(51, 51, 51);" class="author-p-53348239 color:#0070c0">,我们</span>分析了RabbitMQ开启手动ack机制保证消费端数据不丢失的时候,prefetch机制对消费者的吞吐量以及内存消耗的影响。</span></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">通过分析,我们知道了prefetch过大容易导致内存溢出,prefetch过小又会导致消费吞吐量过低,所以在实际项目中需要慎重测试和设置。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">这篇文章,我们转移到消息中间件的<span style="letter-spacing: 2px;font-size: 15px;color: rgb(64, 179, 230);">生产端<span style="letter-spacing: 2px;font-size: 15px;color: rgb(51, 51, 51);">,一起来</span></span>看看如何保证投递到MQ的数据不丢失。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">如果投递出去的消息在网络传输过程中丢失,或者在RabbitMQ的内存中还没写入磁盘的时候宕机,都会导致生产端投递到MQ的数据丢失。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">而且丢失之后,生产端自己还感知不到,同时还没办法来补救。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">下面的图就展示了这个问题。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><img class="inline-img" data-ratio="0.37962962962962965" data-type="png" src="/upload/f28b09d5f78310b56faf1eb29cd7ed9.png" data-w="1080"></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">所以本文呢,我们就来逐步分析一下。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><br></p> <section class="KolEditor" data-tools-id="51912"> <section style="margin-top: 8px;font-weight: bold;font-size: 16px;line-height: 28px;color: rgb(170, 166, 149);min-height: 32px;border-bottom: 1.5px solid rgb(170, 166, 149);border-top-color: rgb(170, 166, 149);border-right-color: rgb(170, 166, 149);border-left-color: rgb(170, 166, 149);"> <p data-original-title="" placeholder="1" class="count" style="color: rgb(255, 255, 255);float: left;line-height: 20px;margin-right: 8px;padding: 4px 10px;background-color: rgb(222, 187, 47);">2</p> <p style="display:inline-block;border-color: rgb(170, 166, 149);color: inherit;"><span style="color: rgb(158, 135, 30);"><strong style="white-space: normal;"><span style="font-size: 15px;">保证投递消息不丢失的confirm机制</span></strong></span></p> </section> </section> <p><br></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">其实要解决这个问题,相信大家看过之前的消费端ack机制之后,也都猜到了。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">很简单,就是生产端(比如上图的订单服务)首先需要开启一个confirm模式,接着投递到MQ的消息,如果MQ一旦将消息持久化到磁盘之后,必须也要回传一个confirm消息给生产端。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">这样的话,如果生产端的服务接收到了这个confirm消息,就知道是已经持久化到磁盘了。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">否则如果没有接收到confirm消息,那么就说明这条消息半路可能丢失了,此时你就可以重新投递消息到MQ去,确保消息不要丢失。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">而且一旦你开启了confirm模式之后,每次消息投递也同样是有一个delivery tag的,也是起到唯一标识一次消息投递的作用。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">这样,MQ回传ack给生产端的时候,会带上这个delivery tag。你就知道具体对应着哪一次消息投递了,可以删除这条消息。</span></p> <p style="line-height: 2em;"><br></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">此外,如果RabbitMQ接收到一条消息之后,结果内部出错发现无法处理这条消息,那么他会回传一个nack消息给生产端。此时你就会感知到这条消息可能处理有问题,你可以选择重新再次投递这条消息到MQ去。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">或者另一种情况,如果某条消息很长时间都没给你回传ack/nack,那可能是极端意外情况发生了,数据也丢了,你也可以自己重新投递消息到MQ去。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">通过这套confirm机制,就可以实现生产端投递消息不会丢失的效果。大家来看看下面的图,一起来感受一下。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><img class="inline-img" data-ratio="0.37962962962962965" data-type="png" src="/upload/8be2320e623f69017618e1f0e1bdd6b4.png" data-w="1080"></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><br></p> <section class="KolEditor" data-tools-id="51912"> <section style="margin-top: 8px;font-weight: bold;font-size: 16px;line-height: 28px;color: rgb(170, 166, 149);min-height: 32px;border-bottom: 1.5px solid rgb(170, 166, 149);border-top-color: rgb(170, 166, 149);border-right-color: rgb(170, 166, 149);border-left-color: rgb(170, 166, 149);"> <p data-original-title="" placeholder="1" class="count" style="color: rgb(255, 255, 255);float: left;line-height: 20px;margin-right: 8px;padding: 4px 10px;background-color: rgb(222, 187, 47);">3</p> <p style="display:inline-block;border-color: rgb(170, 166, 149);color: inherit;"><span style="color: rgb(158, 135, 30);"><strong style="white-space: normal;"><span style="font-size: 15px;">confirm机制的代码实现</span></strong></span></p> </section> </section> <p><br></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">下面,我们再来看看confirm机制的代码实现:</span></p> <p style="line-height: 2em;"><br></p> <p style="text-align: center;"><img class="" data-copyright="0" data-ratio="0.8465063001145475" data-s="300,640" src="/upload/168b66ca5ad72749a204a2dc4fe85317.png" data-type="png" data-w="873" style=""></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><br></p> <section class="KolEditor" data-tools-id="51912"> <section style="margin-top: 8px;font-weight: bold;font-size: 16px;line-height: 28px;color: rgb(170, 166, 149);min-height: 32px;border-bottom: 1.5px solid rgb(170, 166, 149);border-top-color: rgb(170, 166, 149);border-right-color: rgb(170, 166, 149);border-left-color: rgb(170, 166, 149);"> <p data-original-title="" placeholder="1" class="count" style="color: rgb(255, 255, 255);float: left;line-height: 20px;margin-right: 8px;padding: 4px 10px;background-color: rgb(222, 187, 47);">4</p> <p style="display:inline-block;border-color: rgb(170, 166, 149);color: inherit;"><span style="color: rgb(158, 135, 30);"><strong style="white-space: normal;"><span style="font-size: 15px;">confirm机制投递消息的高延迟性</span></strong></span></p> </section> </section> <p><br></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">这里有一个很关键的点,就是一旦启用了confirm机制投递消息到MQ之后,MQ是不保证什么时候会给你一个ack或者nack的。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">因为RabbitMQ自己内部将消息持久化到磁盘,本身就是通过异步批量的方式来进行的。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">正常情况下,你投递到RabbitMQ的消息都会先驻留在内存里,然后过了几百毫秒的延迟时间之后,再一次性批量把多条消息持久化到磁盘里去。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">这样做,是为了兼顾高并发写入的吞吐量和性能的,因为要是你来一条消息就写一次磁盘,那么性能会很差,每次写磁盘都是一次fsync强制刷入磁盘的操作,是很耗时的。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">所以正是因为这个原因,你打开了confirm模式之后,很可能你投递出去一条消息,要间隔几百毫秒之后,MQ才会把消息写入磁盘,接着你才会收到MQ回传过来的ack消息,这个就是所谓<strong>confirm机制投递消息的高延迟性</strong>。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">大家看看下面的图,一起来感受一下。</span></p> <p style="line-height: 2em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><img class="inline-img" data-ratio="0.37962962962962965" data-type="png" src="/upload/3a39322b613785fafd14ec7d4e1c87fd.png" data-w="1080"></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><br></p> <section class="KolEditor" data-tools-id="51912"> <section style="margin-top: 8px;font-weight: bold;font-size: 16px;line-height: 28px;color: rgb(170, 166, 149);min-height: 32px;border-bottom: 1.5px solid rgb(170, 166, 149);border-top-color: rgb(170, 166, 149);border-right-color: rgb(170, 166, 149);border-left-color: rgb(170, 166, 149);"> <p data-original-title="" placeholder="1" class="count" style="color: rgb(255, 255, 255);float: left;line-height: 20px;margin-right: 8px;padding: 4px 10px;background-color: rgb(222, 187, 47);">5</p> <p style="display:inline-block;border-color: rgb(170, 166, 149);color: inherit;"><span style="color: rgb(158, 135, 30);"><strong style="white-space: normal;"><span style="font-size: 15px;">高并发下如何投递消息才能不丢失</span></strong></span></p> </section> </section> <p><br></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">大家可以考虑一下,在生产端高并发写入MQ的场景下,你会面临两个问题:</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;margin-left: 8px;margin-right: 8px;"> <li><p style="line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">1、你每次写一条消息到MQ,为了等待这条消息的ack,必须把消息保存到一个存储里。</span></p></li> </ul> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">并且这个存储不建议是内存,因为高并发下消息是很多的,每秒可能都几千甚至上万的消息投递出去,消息的ack要等几百毫秒的话,放内存可能有内存溢出的风险。</span></p> <p><br></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;margin-left: 8px;margin-right: 8px;"> <li><p style="line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">2、绝对不能以同步写消息 + 等待ack的方式来投递,那样会导致每次投递一个消息都同步阻塞等待几百毫秒,会导致投递性能和吞吐量大幅度下降。</span></p></li> </ul> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">针对这两个问题,相对应的方案其实也呼之欲出了。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">首先,用来临时存放未ack消息的存储需要承载高并发写入,而且我们不需要什么复杂的运算操作,这种存储首选绝对不是MySQL之类的数据库,而<span style="letter-spacing: 2px;font-size: 15px;color: rgb(201, 56, 28);"><strong>建议采用kv存储</strong></span>。kv存储承载高并发能力极强,而且kv操作性能很高。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">其次,投递消息之后等待ack的过程必须是异步的,也就是类似上面那样的代码,已经给出了一个初步的异步回调的方式。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">消息投递出去之后,这个投递的线程其实就可以返回了,至于每个消息的异步回调,是通过在channel注册一个confirm监听器实现的。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">收到一个消息ack之后,就从kv存储中删除这条临时消息;收到一个消息nack之后,就从kv存储提取这条消息然后重新投递一次即可;也可以自己对kv存储里的消息做监控,如果超过一定时长没收到ack,就主动重发消息。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">大家看看下面的图,一起来体会一下:</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><img class="inline-img" data-ratio="0.31574074074074077" data-type="png" src="/upload/5a2af89afcba829809cd3e4b9f438723.png" data-w="1080"></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><br></p> <section class="KolEditor" data-tools-id="51912"> <section style="margin-top: 8px;font-weight: bold;font-size: 16px;line-height: 28px;color: rgb(170, 166, 149);min-height: 32px;border-bottom: 1.5px solid rgb(170, 166, 149);border-top-color: rgb(170, 166, 149);border-right-color: rgb(170, 166, 149);border-left-color: rgb(170, 166, 149);"> <p data-original-title="" placeholder="1" class="count" style="color: rgb(255, 255, 255);float: left;line-height: 20px;margin-right: 8px;padding: 4px 10px;background-color: rgb(222, 187, 47);">6</p> <p style="display:inline-block;border-color: rgb(170, 166, 149);color: inherit;"><span style="color: rgb(158, 135, 30);"><strong style="white-space: normal;"><span style="font-size: 15px;">消息中间件全链路100%数据不丢失能做到吗?</span></strong></span></p> </section> </section> <p><br></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">到此为止,我们已经把生产端和消费端如何保证消息不丢失的相关技术方案结合RabbitMQ这种中间件都给大家分析过了。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">其实,架构思想是通用的, 无论你用的是哪一种MQ中间件,他们提供的功能是不太一样的,但是你都需要考虑如下几点:</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <ol start="1" class="list-number1 list-paddingleft-2" style="margin-left: 8px;margin-right: 8px;"> <li><p style="line-height: 2em;"><span style="letter-spacing: 2px;"><strong><span style="letter-spacing: 2px;font-size: 15px;">生产端如何保证投递出去的消息不丢失:</span></strong><span style="letter-spacing: 2px;font-size: 15px;">消息在半路丢失,或者在MQ内存中宕机导致丢失,此时你如何基于MQ的功能保证消息不要丢失?</span></span></p></li> <li><p style="line-height: 2em;"><span style="letter-spacing: 2px;"><strong><span style="letter-spacing: 2px;font-size: 15px;">MQ自身如何保证消息不丢失:</span></strong><span style="letter-spacing: 2px;font-size: 15px;">起码需要让MQ对消息是有持久化到磁盘这个机制。</span></span></p></li> <li><p style="line-height: 2em;"><span style="letter-spacing: 2px;"><strong><span style="letter-spacing: 2px;font-size: 15px;">消费端如何保证消费到的消息不丢失:</span></strong><span style="letter-spacing: 2px;font-size: 15px;">如果你处理到一半消费端宕机,导致消息丢失,此时怎么办?</span></span></p></li> </ol> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">目前来说,我们初步的借着RabbitMQ举例,已经把从前到后一整套技术方案的原理、设计和实现都给大家分析了一遍了。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="letter-spacing: 2px;"><strong><span style="letter-spacing: 2px;font-size: 15px;color: rgb(201, 56, 28);">但是此时真的能做到100%数据不丢失吗?</span></strong><span style="letter-spacing: 2px;color: rgb(51, 51, 51);font-size: 15px;">恐怕未必,大家再考虑一下个特殊的场景。</span></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">生产端投递了消息到MQ,而且持久化到磁盘并且回传ack给生产端了。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">但是此时MQ还没投递消息给消费端,结果MQ部署的机器突然宕机,而且因为未知的原因磁盘损坏了,直接在<span style="letter-spacing: 2px;font-size: 15px;color: rgb(64, 179, 230);">物理层面</span>导致MQ持久化到磁盘的数据找不回来了。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">这个大家千万别以为是开玩笑的,大家如果留意留意行业新闻,这种磁盘损坏导致数据丢失的是真的有的。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">那么此时即使你把MQ重启了,磁盘上的数据也丢失了,数据是不是还是丢失了?</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">你说,我可以用MQ的集群机制啊,给一个数据做多个副本,比如后面我们就会给大家分析<span style="letter-spacing: 2px;font-size: 15px;color: rgb(64, 179, 230);">RabbitMQ的镜像集群机制</span>,确实可以做到数据多副本。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">但是即使数据多副本,<strong>一定可以做到100%数据不丢失</strong><strong>?</strong></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">比如说你的机房突然遇到地震,结果机房里的机器全部没了,数据是不是还是全丢了?</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">说这个,并不是说要抬杠。而是告诉大家,技术这个东西,100%都是理论上的期望。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">应该说,我们凡事都朝着100%去做,但是理论上是不可能完全做到100%保证的,可能就是做到99.9999%的可能性数据不丢失,但是还是有千万分之一的概率会丢失。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 2em;"><span style="font-size: 15px;letter-spacing: 2px;">当然,从实际的情况来说,能做到这种地步,其实基本上已经基本数据不会丢失了。</span></p> <section class="KolEditor checkSelected" data-tools-id="56033" style="white-space: normal;"> <p style="vertical-align: baseline;letter-spacing: 1px;line-height: 2em;text-align: center;"><br></p> <p style="vertical-align: baseline;letter-spacing: 1px;line-height: 2em;text-align: center;"><br></p> <p style="vertical-align: baseline;letter-spacing: 1px;line-height: 2em;text-align: center;"><strong><span style="font-size: 20px;">End</span></strong></p> <p style="vertical-align: baseline;letter-spacing: 1px;line-height: 2em;text-align: center;"><strong><span style="font-size: 20px;"><br></span></strong></p> </section> <section class="KolEditor checkSelected" data-tools-id="56033" style="white-space: normal;"> <p style="vertical-align: baseline;letter-spacing: 1px;line-height: 1.5em;text-align: center;"><span style="font-size: 15px;"><strong><span style="font-family: 宋体;color: rgb(64, 179, 230);">如有收获,请帮忙转发,您的鼓励是作者最大的动力,谢谢!</span></strong></span></p> <p style="vertical-align: baseline;letter-spacing: 1px;line-height: 1.5em;text-align: center;"><br></p> <section class="KolEditor"> <section style="margin-top: 20px;"> <section style="margin-top: 5px;height: 4px;background: rgb(248, 212, 151);"></section> <section style="margin-top: -24px;text-align: center;"> <section style="padding: 5px 10px;background: rgb(248, 212, 151);display: inline-block;"> <p class="white title" style="min-width: 1px;font-size: 18px;color: rgb(255, 255, 255);"><span style="color: rgb(255, 76, 65);"><strong>推荐阅读</strong></span></p> </section> </section> <ul class=" list-paddingleft-2" style="list-style-type: square;"> <li><p style="line-height: 2em;"><a href="http://mp.weixin.qq.com/s?__biz=MzU0OTk3ODQ3Ng==&amp;mid=2247484255&amp;idx=1&amp;sn=60425e65474a4f2d895e05447e48d59c&amp;chksm=fba6eb5cccd1624a8988e90fc38a86c61b99e3eeb1e0f677d4c375547736109183828d8f0e9e&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2"><span style="font-size: 15px;text-decoration: underline;color: rgb(110, 191, 248);">精品专栏之微服务系列</span></a></p></li> <li><p style="line-height: 2em;"><a href="http://mp.weixin.qq.com/s?__biz=MzU0OTk3ODQ3Ng==&amp;mid=2247484255&amp;idx=1&amp;sn=60425e65474a4f2d895e05447e48d59c&amp;chksm=fba6eb5cccd1624a8988e90fc38a86c61b99e3eeb1e0f677d4c375547736109183828d8f0e9e&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2"><span style="font-size: 15px;text-decoration: underline;color: rgb(110, 191, 248);">精品专栏之分布式系列</span></a></p></li> <li><p style="line-height: 2em;"><a href="http://mp.weixin.qq.com/s?__biz=MzU0OTk3ODQ3Ng==&amp;mid=2247484255&amp;idx=1&amp;sn=60425e65474a4f2d895e05447e48d59c&amp;chksm=fba6eb5cccd1624a8988e90fc38a86c61b99e3eeb1e0f677d4c375547736109183828d8f0e9e&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2"><span style="font-size: 15px;text-decoration: underline;color: rgb(110, 191, 248);">精品专栏之亿级流量架构演进系列</span></a></p></li> <li><p style="line-height: 2em;"><a href="http://mp.weixin.qq.com/s?__biz=MzU0OTk3ODQ3Ng==&amp;mid=2247484255&amp;idx=1&amp;sn=60425e65474a4f2d895e05447e48d59c&amp;chksm=fba6eb5cccd1624a8988e90fc38a86c61b99e3eeb1e0f677d4c375547736109183828d8f0e9e&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2"><span style="font-size: 15px;text-decoration: underline;color: rgb(110, 191, 248);">精品专栏之并发系列</span></a></p></li> <li><p style="line-height: 2em;"><a href="http://mp.weixin.qq.com/s?__biz=MzU0OTk3ODQ3Ng==&amp;mid=2247484255&amp;idx=1&amp;sn=60425e65474a4f2d895e05447e48d59c&amp;chksm=fba6eb5cccd1624a8988e90fc38a86c61b99e3eeb1e0f677d4c375547736109183828d8f0e9e&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2"><span style="font-size: 15px;text-decoration: underline;color: rgb(110, 191, 248);">精品专栏之大数据系列</span></a></p></li> <li><p style="line-height: 2em;"><a href="https://mp.weixin.qq.com/s?__biz=MzU0OTk3ODQ3Ng==&amp;mid=2247484238&amp;idx=1&amp;sn=4bf9f7419b3d2b8061e69f698bcbf163&amp;chksm=fba6eb4dccd1625be631baf6b8cc664e98f5adb6e0d6722a5544d46439b58d08b8d8075366b5&amp;token=1921565673&amp;lang=zh_CN&amp;scene=21#wechat_redirect" target="_blank" data-linktype="2"><span style="font-size: 15px;text-decoration: underline;color: rgb(110, 191, 248);">精品专栏之Java进阶面试系列</span></a></p></li> </ul> <section style="margin-top: 10px;height: 4px;background: rgb(57, 207, 202);"></section> <section style="margin-top: 5px;height: 2px;background: rgb(248, 212, 151);"></section> </section> </section> <p style="vertical-align: baseline;letter-spacing: 1px;line-height: 1.5em;"><strong><span style="font-size: 16px;"><span style="font-family: 宋体;"><br></span></span></strong></p> <p style="vertical-align: baseline;letter-spacing: 1px;line-height: 1.5em;"><strong><span style="font-size: 16px;"><span style="font-family: 宋体;"><br></span></span></strong></p> <p style="vertical-align: baseline;letter-spacing: 1px;line-height: 1.5em;text-align: center;"><strong><span style="font-size: 16px;"><span style="font-family: 宋体;">一大波</span><strong><span style="font-family: 宋体;">微服务、分布式、高并发、高可用</span></strong><span style="font-family: 宋体;">的</span><em><span style="font-family: 宋体;">原创系列</span></em><span style="font-family: 宋体;">文章正在路上。</span></span></strong></p> <p style="vertical-align: baseline;letter-spacing: 1px;line-height: 1.5em;"><br></p> <p style="vertical-align: baseline;letter-spacing: 1px;line-height: 1.5em;text-align: center;"><strong><span style="font-size: 16px;font-family: 宋体;"><span style="color: rgb(201, 56, 28);"><strong>欢迎扫描下方二维码</strong></span>,持续关注:</span></strong></p> <section class="KolEditor" data-tools-id="66348"> <section style="margin: 10px;display: flex;flex-direction: column;align-items: center;"> <section style="margin-bottom: -20px;padding: 8px;background: rgb(255, 255, 255);z-index: 4;"> <section class="KolEditor"> <section style="margin-top: 20px;"> <section style="margin-right: auto;margin-left: auto;background-image: url(https://mmbiz.qpic.cn/mmbiz_gif/1J6IbIcPCLYzapicOfUCIWp88Nib7dlgHDtCCKAEVAKI0cxmYTxOicyZJ6TsIPw1N4gJ4zU2szB8WjkUa3HfBbN6w/640?wx_fmt=gif);background-repeat: no-repeat;width: 240px;background-size: 100%;text-align: center;"> <section class="" style="margin-right: auto;margin-left: auto;padding-top: 32px;padding-bottom: 30px;width: 70px;"> <img class="" data-ratio="0.6666666666666666" data-type="jpeg" data-w="344" src="/upload/364e64b5ea4be7b9382dd0f8d4c10f86.jpg" style="width: 70px;"> </section> </section> </section> </section> <p><br></p> <p style="font-size: 15px;letter-spacing: 5px;color: rgb(59, 88, 49);text-align: center;"><strong style="letter-spacing: 1px;"><span style="font-style: inherit;font-variant-ligatures: inherit;font-variant-caps: inherit;-webkit-font-smoothing: antialiased;border-width: 0px;border-style: initial;border-color: initial;font-weight: bolder;font-stretch: inherit;vertical-align: baseline;user-select: text;color: rgba(13, 0, 19, 0.72);font-family: PingFangSC-Regular;font-variant-numeric: normal;font-variant-east-asian: normal;line-height: 23.324px;text-align: justify;widows: 1;font-size: 16px;">石杉的架构笔记(id:shishan100)</span></strong></p> <p style="font-size: 15px;letter-spacing: 5px;color: rgb(59, 88, 49);text-align: center;"><strong><span style="color: rgba(13, 0, 19, 0.72);font-family: PingFangSC-Regular;font-size: 14px;text-align: justify;widows: 1;-webkit-font-smoothing: antialiased;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: normal;font-stretch: inherit;line-height: 23.324px;vertical-align: baseline;user-select: text;">十余年<span style="-webkit-font-smoothing: antialiased;font-variant: inherit;font-weight: inherit;font-stretch: inherit;line-height: inherit;vertical-align: baseline;user-select: text;color: rgb(247, 150, 70);">BAT架构经验</span>倾囊相授</span></strong></p> </section> </section> </section> <p><br></p> <p><br></p> </section> <section class="KolEditor" style="white-space: normal;"> <section style="font-size: 16px;border-width: 0px;border-style: none;border-color: initial;"> <section style="margin-top: 0.5em;margin-bottom: 0.5em;"> <img class="" data-ratio="0.6666666666666666" data-type="png" data-w="640" src="/upload/2a91c95850337a2b69e8d27fe83c5285.null" style="width:auto !important;max-width:100% !important;height:auto !important;"> </section> </section> </section>

详解 Tomcat 的连接数与线程池

作者:微信小助手

<p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(255, 0, 0);font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">(点击</span><span style="max-width: 100%;line-height: 22.4px;color: rgb(0, 128, 255);">上方公众号</span><span style="max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">,可快速关注)</span></span></p> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;text-align: justify;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <blockquote style="max-width: 100%;color: rgb(51, 51, 51);font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;text-align: justify;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <p style="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;">来源:编程迷思 ,</span></p> <p style="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;">www.cnblogs.com/kismetv/p/7806063.html</span></p> </blockquote> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">前言</span></strong></p> <p><br></p> <p>在使用tomcat时,经常会遇到连接数、线程数之类的配置问题,要真正理解这些概念,必须先了解Tomcat的连接器(Connector)。</p> <p><br></p> <p>在前面的文章 详解Tomcat配置文件server.xml 中写到过:Connector的主要功能,是接收连接请求,创建Request和Response对象用于和请求端交换数据;然后分配线程让Engine(也就是Servlet容器)来处理这个请求,并把产生的Request和Response对象传给Engine。当Engine处理完请求后,也会通过Connector将响应返回给客户端。</p> <p><br></p> <p>可以说,Servlet容器处理请求,是需要Connector进行调度和控制的,Connector是Tomcat处理请求的主干,因此Connector的配置和使用对Tomcat的性能有着重要的影响。这篇文章将从Connector入手,讨论一些与Connector有关的重要问题,包括NIO/BIO模式、线程池、连接数等。</p> <p><br></p> <p>根据协议的不同,Connector可以分为HTTP Connector、AJP Connector等,本文只讨论HTTP Connector。</p> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">一、Nio、Bio、APR</span></strong></p> <p><br></p> <p><strong><span style="color: rgb(123, 12, 0);">1、Connector的protocol</span></strong></p> <p><br></p> <p>Connector在处理HTTP请求时,会使用不同的protocol。不同的Tomcat版本支持的protocol不同,其中最典型的protocol包括BIO、NIO和APR(Tomcat7中支持这3种,Tomcat8增加了对NIO2的支持,而到了Tomcat8.5和Tomcat9.0,则去掉了对BIO的支持)。</p> <p><br></p> <p>BIO是Blocking IO,顾名思义是阻塞的IO;NIO是Non-blocking IO,则是非阻塞的IO。而APR是Apache Portable Runtime,是Apache可移植运行库,利用本地库可以实现高可扩展性、高性能;Apr是在Tomcat上运行高并发应用的首选模式,但是需要安装apr、apr-utils、tomcat-native等包。</p> <p><br></p> <p><strong><span style="color: rgb(123, 12, 0);">2、如何指定protocol</span></strong></p> <p><br></p> <p>Connector使用哪种protocol,可以通过&lt;connector&gt;元素中的protocol属性进行指定,也可以使用默认值。</p> <p><br></p> <p>指定的protocol取值及对应的协议如下:</p> <p><br></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p>HTTP/1.1:默认值,使用的协议与Tomcat版本有关</p></li> <li><p>org.apache.coyote.http11.Http11Protocol:BIO</p></li> <li><p>org.apache.coyote.http11.Http11NioProtocol:NIO</p></li> <li><p>org.apache.coyote.http11.Http11Nio2Protocol:NIO2</p></li> <li><p>org.apache.coyote.http11.Http11AprProtocol:APR</p></li> </ul> <p><br></p> <p>如果没有指定protocol,则使用默认值HTTP/1.1,其含义如下:在Tomcat7中,自动选取使用BIO或APR(如果找到APR需要的本地库,则使用APR,否则使用BIO);在Tomcat8中,自动选取使用NIO或APR(如果找到APR需要的本地库,则使用APR,否则使用NIO)。</p> <p><br></p> <p><strong><span style="color: rgb(123, 12, 0);">3、BIO/NIO有何不同</span></strong></p> <p><br></p> <p>无论是BIO,还是NIO,Connector处理请求的大致流程是一样的:</p> <p><br></p> <p>在accept队列中接收连接(当客户端向服务器发送请求时,如果客户端与OS完成三次握手建立了连接,则OS将该连接放入accept队列);在连接中获取请求的数据,生成request;调用servlet容器处理请求;返回response。为了便于后面的说明,首先明确一下连接与请求的关系:连接是TCP层面的(传输层),对应socket;请求是HTTP层面的(应用层),必须依赖于TCP的连接实现;一个TCP连接中可能传输多个HTTP请求。</p> <p><br></p> <p>在BIO实现的Connector中,处理请求的主要实体是JIoEndpoint对象。JIoEndpoint维护了Acceptor和Worker:Acceptor接收socket,然后从Worker线程池中找出空闲的线程处理socket,如果worker线程池没有空闲线程,则Acceptor将阻塞。其中Worker是Tomcat自带的线程池,如果通过&lt;Executor&gt;配置了其他线程池,原理与Worker类似。</p> <p><br></p> <p>在NIO实现的Connector中,处理请求的主要实体是NIoEndpoint对象。NIoEndpoint中除了包含Acceptor和Worker外,还是用了Poller,处理流程如下图所示(图片来源:http://gearever.iteye.com/blog/1844203)。</p> <p><br></p> <p style="text-align: center;"><img class="aligncenter size-full wp-image-27310" data-ratio="0.2075812274368231" src="/upload/c8c3ff545dcf59ab18c7f283bb3a30e7.png" data-type="png" data-w="554" style="border-width: 0px;border-style: initial;border-color: initial;font-size: 0px;color: transparent;vertical-align: middle;text-align: center;font-family: &quot;Microsoft YaHei&quot;, 宋体, &quot;Myriad Pro&quot;, Lato, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif;white-space: normal;background-color: rgb(255, 255, 255);" title="1174710-20171108203711513-1728828893"></p> <p><br></p> <p>Acceptor接收socket后,不是直接使用Worker中的线程处理请求,而是先将请求发送给了Poller,而Poller是实现NIO的关键。Acceptor向Poller发送请求通过队列实现,使用了典型的生产者-消费者模式。在Poller中,维护了一个Selector对象;当Poller从队列中取出socket后,注册到该Selector中;然后通过遍历Selector,找出其中可读的socket,并使用Worker中的线程处理相应请求。与BIO类似,Worker也可以被自定义的线程池代替。</p> <p><br></p> <p>通过上述过程可以看出,在NIoEndpoint处理请求的过程中,无论是Acceptor接收socket,还是线程处理请求,使用的仍然是阻塞方式;但在“读取socket并交给Worker中的线程”的这个过程中,使用非阻塞的NIO实现,这是NIO模式与BIO模式的最主要区别(其他区别对性能影响较小,暂时略去不提)。而这个区别,在并发量较大的情形下可以带来Tomcat效率的显著提升:</p> <p><br></p> <p>目前大多数HTTP请求使用的是长连接(HTTP/1.1默认keep-alive为true),而长连接意味着,一个TCP的socket在当前请求结束后,如果没有新的请求到来,socket不会立马释放,而是等timeout后再释放。如果使用BIO,“读取socket并交给Worker中的线程”这个过程是阻塞的,也就意味着在socket等待下一个请求或等待释放的过程中,处理这个socket的工作线程会一直被占用,无法释放;因此Tomcat可以同时处理的socket数目不能超过最大线程数,性能受到了极大限制。而使用NIO,“读取socket并交给Worker中的线程”这个过程是非阻塞的,当socket在等待下一个请求或等待释放时,并不会占用工作线程,因此Tomcat可以同时处理的socket数目远大于最大线程数,并发性能大大提高。</p> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">二、3个参数:acceptCount、maxConnections、maxThreads</span></strong></p> <p><br></p> <p>再回顾一下Tomcat处理请求的过程:在accept队列中接收连接(当客户端向服务器发送请求时,如果客户端与OS完成三次握手建立了连接,则OS将该连接放入accept队列);在连接中获取请求的数据,生成request;调用servlet容器处理请求;返回response。</p> <p><br></p> <p>相对应的,Connector中的几个参数功能如下:</p> <p><br></p> <p><strong><span style="color: rgb(123, 12, 0);">1、acceptCount</span></strong></p> <p><br></p> <p>accept队列的长度;当accept队列中连接的个数达到acceptCount时,队列满,进来的请求一律被拒绝。默认值是100。</p> <p><br></p> <p><strong><span style="color: rgb(123, 12, 0);">2、maxConnections</span></strong></p> <p><br></p> <p>Tomcat在任意时刻接收和处理的最大连接数。当Tomcat接收的连接数达到maxConnections时,Acceptor线程不会读取accept队列中的连接;这时accept队列中的线程会一直阻塞着,直到Tomcat接收的连接数小于maxConnections。如果设置为-1,则连接数不受限制。</p> <p><br></p> <p>默认值与连接器使用的协议有关:NIO的默认值是10000,APR/native的默认值是8192,而BIO的默认值为maxThreads(如果配置了Executor,则默认值是Executor的maxThreads)。</p> <p><br></p> <p>在windows下,APR/native的maxConnections值会自动调整为设置值以下最大的1024的整数倍;如设置为2000,则最大值实际是1024。</p> <p><br></p> <p><strong><span style="color: rgb(123, 12, 0);">3、maxThreads</span></strong></p> <p><br></p> <p>请求处理线程的最大数量。默认值是200(Tomcat7和8都是的)。如果该Connector绑定了Executor,这个值会被忽略,因为该Connector将使用绑定的Executor,而不是内置的线程池来执行任务。</p> <p><br></p> <p>maxThreads规定的是最大的线程数目,并不是实际running的CPU数量;实际上,maxThreads的大小比CPU核心数量要大得多。这是因为,处理请求的线程真正用于计算的时间可能很少,大多数时间可能在阻塞,如等待数据库返回数据、等待硬盘读写数据等。因此,在某一时刻,只有少数的线程真正的在使用物理CPU,大多数线程都在等待;因此线程数远大于物理核心数才是合理的。</p> <p><br></p> <p>换句话说,Tomcat通过使用比CPU核心数量多得多的线程数,可以使CPU忙碌起来,大大提高CPU的利用率。</p> <p><br></p> <p><strong><span style="color: rgb(123, 12, 0);">4、参数设置</span></strong></p> <p><br></p> <p>(1)maxThreads的设置既与应用的特点有关,也与服务器的CPU核心数量有关。通过前面介绍可以知道,maxThreads数量应该远大于CPU核心数量;而且CPU核心数越大,maxThreads应该越大;应用中CPU越不密集(IO越密集),maxThreads应该越大,以便能够充分利用CPU。当然,maxThreads的值并不是越大越好,如果maxThreads过大,那么CPU会花费大量的时间用于线程的切换,整体效率会降低。</p> <p><br></p> <p>(2)maxConnections的设置与Tomcat的运行模式有关。如果tomcat使用的是BIO,那么maxConnections的值应该与maxThreads一致;如果tomcat使用的是NIO,那么类似于Tomcat的默认值,maxConnections值应该远大于maxThreads。</p> <p><br></p> <p>(3)通过前面的介绍可以知道,虽然tomcat同时可以处理的连接数目是maxConnections,但服务器中可以同时接收的连接数为maxConnections+acceptCount 。acceptCount的设置,与应用在连接过高情况下希望做出什么反应有关系。如果设置过大,后面进入的请求等待时间会很长;如果设置过小,后面进入的请求立马返回connection refused。</p> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">三、线程池Executor</span></strong></p> <p><br></p> <p>Executor元素代表Tomcat中的线程池,可以由其他组件共享使用;要使用该线程池,组件需要通过executor属性指定该线程池。</p> <p><br></p> <p>Executor是Service元素的内嵌元素。一般来说,使用线程池的是Connector组件;为了使Connector能使用线程池,Executor元素应该放在Connector前面。Executor与Connector的配置举例如下:</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&lt;Executor name="tomcatThreadPool" namePrefix ="catalina-exec-" maxThreads="150" minSpareThreads="4" /&gt;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&lt;Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" acceptCount="1000" /&gt;</span></p> </blockquote> <p><br></p> <p>Executor的主要属性包括:</p> <p><br></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p>name:该线程池的标记</p></li> <li><p>maxThreads:线程池中最大活跃线程数,默认值200(Tomcat7和8都是)</p></li> <li><p>minSpareThreads:线程池中保持的最小线程数,最小值是25</p></li> <li><p>maxIdleTime:线程空闲的最大时间,当空闲超过该值时关闭线程(除非线程数小于minSpareThreads),单位是ms,默认值60000(1分钟)</p></li> <li><p>daemon:是否后台线程,默认值true</p></li> <li><p>threadPriority:线程优先级,默认值5</p></li> <li><p>namePrefix:线程名字的前缀,线程池中线程名字为:namePrefix+线程编号</p></li> </ul> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">四、查看当前状态</span></strong></p> <p><br></p> <p>上面介绍了Tomcat连接数、线程数的概念以及如何设置,下面说明如何查看服务器中的连接数和线程数。</p> <p><br></p> <p>查看服务器的状态,大致分为两种方案:(1)使用现成的工具,(2)直接使用Linux的命令查看。</p> <p><br></p> <p>现成的工具,如JDK自带的jconsole工具可以方便的查看线程信息(此外还可以查看CPU、内存、类、JVM基本信息等),Tomcat自带的manager,收费工具New Relic等。下图是jconsole查看线程信息的界面:</p> <p><br></p> <p style="text-align: center;"><img class="aligncenter size-full wp-image-27311" data-ratio="0.8321299638989169" src="/upload/29b204706469786a6b8c7723272008e1.png" data-type="png" data-w="554" style="border-width: 0px;border-style: initial;border-color: initial;font-size: 0px;color: transparent;vertical-align: middle;margin: 0px;top: 0px;left: 0px;right: 0px;bottom: 0px;" title="1174710-20171108204908856-1788241471"></p> <p><br></p> <p>下面说一下如何通过Linux命令行,查看服务器中的连接数和线程数。</p> <p><br></p> <p><strong><span style="color: rgb(123, 12, 0);">1、连接数</span></strong></p> <p><br></p> <p>假设Tomcat接收http请求的端口是8083,则可以使用如下语句查看连接情况:</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">netstat –nat | grep 8083</span></p> </blockquote> <p><br></p> <p>结果如下所示:</p> <p><br></p> <p style="text-align: center;"><img class="aligncenter size-full wp-image-27312" data-ratio="0.2003610108303249" src="/upload/cb83c8e3fdfc23d8f219d1d475aea5e6.png" data-type="png" data-w="554" style="border-width: 0px;border-style: initial;border-color: initial;font-size: 0px;color: transparent;vertical-align: middle;margin: 0px;top: 0px;left: 0px;right: 0px;bottom: 0px;" title="1174710-20171108205043325-1785419465"></p> <p><br></p> <p>可以看出,有一个连接处于listen状态,监听请求;除此之外,还有4个已经建立的连接(ESTABLISHED)和2个等待关闭的连接(CLOSE_WAIT)。</p> <p><br></p> <p><strong><span style="color: rgb(123, 12, 0);">2、线程</span></strong></p> <p><br></p> <p>ps命令可以查看进程状态,如执行如下命令:</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">ps –e | grep java</span></p> </blockquote> <p><br></p> <p>结果如下图:</p> <p><br></p> <p style="text-align: center;"><img class="aligncenter size-full wp-image-27313" data-ratio="0.14102564102564102" src="/upload/460b2ac0f7df7eb90eb32d022664a1d5.png" data-type="png" data-w="234" style="border-width: 0px;border-style: initial;border-color: initial;font-size: 0px;color: transparent;vertical-align: middle;margin: 0px;top: 0px;left: 0px;right: 0px;bottom: 0px;" title="1174710-20171108205102403-750285056"></p> <p><br></p> <p>可以看到,只打印了一个进程的信息;27989是线程id,java是指执行的java命令。这是因为启动一个tomcat,内部所有的工作都在这一个进程里完成,包括主线程、垃圾回收线程、Acceptor线程、请求处理线程等等。</p> <p><br></p> <p>通过如下命令,可以看到该进程内有多少个线程;其中,nlwp含义是number of light-weight process。</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">ps –o nlwp 27989</span></p> </blockquote> <p><br></p> <p style="text-align: center;"><img class="aligncenter size-full wp-image-27314" data-ratio="0.22807017543859648" src="/upload/cf7e329b0323c9a2099f649185fe9bc8.png" data-type="png" data-w="228" style="border-width: 0px;border-style: initial;border-color: initial;font-size: 0px;color: transparent;vertical-align: middle;margin: 0px;top: 0px;left: 0px;right: 0px;bottom: 0px;" title="1174710-20171108205109356-1650242943"></p> <p><br></p> <p>可以看到,该进程内部有73个线程;但是73并没有排除处于idle状态的线程。要想获得真正在running的线程数量,可以通过以下语句完成:</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">ps -eLo pid ,stat | grep 27989 | grep running | wc -l</span></p> </blockquote> <p><br></p> <p>其中ps -eLo pid ,stat可以找出所有线程,并打印其所在的进程号和线程当前的状态;两个grep命令分别筛选进程号和线程状态;wc统计个数。其中,ps -eLo pid ,stat | grep 27989输出的结果如下:</p> <p><br></p> <p style="text-align: center;"><img class="aligncenter size-full wp-image-27315" data-ratio="0.26506024096385544" src="/upload/16e987462b501d4e1985841d409201bd.png" data-type="png" data-w="498" style="border-width: 0px;border-style: initial;border-color: initial;font-size: 0px;color: transparent;vertical-align: middle;margin: 0px;top: 0px;left: 0px;right: 0px;bottom: 0px;" title="1174710-20171108205116559-949248660"></p> <p><br></p> <p>图中只截图了部分结果;Sl表示大多数线程都处于空闲状态。</p> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">参考文献</span></strong></p> <p><br></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p>Tomcat 7.0官方文档</p><p><br></p><p>http://tomcat.apache.org/tomcat-7.0-doc/config/http.html<br></p></li> <li><p>Tomcat 8.0官方文档</p><p>http://tomcat.apache.org/tomcat-8.0-doc/config/http.html<br></p></li> <li><p>Tomcat 8.5官方文档</p><p>http://tomcat.apache.org/tomcat-8.0-doc/config/http.html<br></p></li> <li><p>Tomcat maxThreads maxConnections acceptCount参数说明</p><p>http://www.jianshu.com/p/2bc3fca12623<br></p></li> <li><p>tomcat架构分析(connector BIO 实现)</p><p>http://gearever.iteye.com/blog/1841586<br></p></li> <li><p>tomcat架构分析 (connector NIO 实现)</p><p>http://gearever.iteye.com/blog/1844203<br></p></li> <li><p>Why is the tomcat default thread pool size so large?</p><p>https://stackoverflow.com/questions/14249824/why-is-the-tomcat-default-thread-pool-size-so-large<br></p></li> <li><p>Howto find Tomcat current thread count</p></li> <li><p>https://serverfault.com/questions/594877/howto-find-tomcat-current-thread-count</p></li> </ul> <p><br></p> <section class="" powered-by="xiumi.us" style="white-space: normal;max-width: 100%;box-sizing: border-box;color: rgb(51, 51, 51);font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;text-align: justify;background-color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <section class="" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;box-sizing: border-box;text-align: left;overflow-wrap: break-word !important;"> <section class="" style="padding: 10px;max-width: 100%;box-sizing: border-box;display: inline-block;width: 668px;border-width: 1px;border-style: solid;border-color: rgb(226, 226, 226);box-shadow: rgb(226, 226, 226) 0px 16px 1px -13px;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 class="" style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <section class="" style="max-width: 100%;box-sizing: border-box;color: rgb(93, 93, 93);overflow-wrap: break-word !important;"> <p class="" style="max-width: 100%;box-sizing: border-box;min-height: 1em;font-size: 13px;overflow-wrap: break-word !important;">【关于投稿】</p> <p class="" style="max-width: 100%;box-sizing: border-box;min-height: 1em;font-size: 13px;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p class="" style="max-width: 100%;box-sizing: border-box;min-height: 1em;font-size: 13px;overflow-wrap: break-word !important;">如果大家有原创好文投稿,请直接给公号发送留言。</p> <p class="" style="max-width: 100%;box-sizing: border-box;min-height: 1em;font-size: 13px;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p class="" style="max-width: 100%;box-sizing: border-box;min-height: 1em;font-size: 13px;overflow-wrap: break-word !important;"><span style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;">①&nbsp;留言格式:</span><br style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;">【投稿】+《&nbsp;文章标题》+&nbsp;文章链接</span><br style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;">②&nbsp;示例:</span><br style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;">【投稿】《不要自称是程序员,我十多年的&nbsp;IT&nbsp;职场总结》:http://blog.jobbole.com/94148/</span><br style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;overflow-wrap: break-word !important;">③&nbsp;最后请附上您的个人简介哈~</span></span></p> <p style="max-width: 100%;min-height: 1em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> </section> </section> </section> </section> </section> </section> <p style="white-space: normal;max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;text-align: justify;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;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;color: rgb(51, 51, 51);font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;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;color: rgb(51, 51, 51);font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;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>