作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: PingFangSC-Light, STHeitiSC-Light, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;" data-mpa-powered-by="yiban.io"> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="font-family: STHeitiSC-Light,-apple-system-font,BlinkMacSystemFont,Helvetica Neue,PingFang SC,Hiragino Sans GB,Microsoft YaHei UI,Microsoft YaHei,Arial,sans-serif;font-size: 18px;font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248,57,41);">我是Redis</span><span style="font-family: STHeitiSC-Light,-apple-system-font,BlinkMacSystemFont,Helvetica Neue,PingFang SC,Hiragino Sans GB,Microsoft YaHei UI,Microsoft YaHei,Arial,sans-serif;font-size: 18px;font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248,57,41);"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">你好,我是<strong style="font-weight: border;color: rgb(248,57,41);">Redis</strong>,一个叫<strong style="font-weight: border;color: rgb(248,57,41);">Antirez</strong>的男人把我带到了这个世界上。<img class="rich_pages wxw-img" data-ratio="0.5173501577287066" src="/upload/d3f5d33e7bbb6524c08092e0fd912038.png" data-type="png" data-w="1268" style="border-radius: 5px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">说起我的诞生,跟关系数据库<strong style="font-weight: border;color: rgb(248,57,41);">MySQL</strong>还挺有渊源的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">在我还没来到这个世界上的时候,MySQL过的很辛苦,互联网发展的越来越快,它容纳的数据也越来越多,用户请求也随之暴涨,而每一个用户请求都变成了对它的一个又一个读写操作,MySQL是苦不堪言。尤其是到“双11”、“618“这种全民购物狂欢的日子,都是MySQL受苦受难的日子。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">据后来MySQL告诉我说,其实有一大半的用户请求都是读操作,而且经常都是重复查询一个东西,浪费它很多时间去进行磁盘I/O。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">后来有人就琢磨,是不是可以学学CPU,给数据库也加一个缓存呢?于是我就诞生了!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">出生不久,我就和MySQL成为了好朋友,我们俩常常携手出现在后端服务器中。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">应用程序们从MySQL查询到的数据,在我这里登记一下,后面再需要用到的时候,就先找我要,我这里没有再找MySQL要。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.684044233807267" src="/upload/c3ba27f9ae2fe27ef3f2c6586f808394.png" data-type="png" data-w="1266" style="border-radius: 5px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">为了方便使用,我支持好几种数据结构的存储:</p> <blockquote data-tool="mdnice编辑器" style="font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(239, 112, 96);background: rgb(255, 249, 249);"> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-size: 14px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> String </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> Hash </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> List </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> Set </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> SortedSet </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> Bitmap </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> ······ </section></li> </ul> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">因为我把登记的数据都记录在内存中,不用去执行慢如蜗牛的I/O操作,所以找我要比找MySQL要省去了不少的时间呢。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">可别小瞧这简单的一个改变,我可为MySQL减轻了不小的负担!随着程序的运行,我缓存的数据越来越多,有相当部分时间我都给它挡住了用户请求,这一下它可乐得清闲自在了!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">有了我的加入,网络服务的性能提升了不少,这都归功于我为数据库挨了不少枪子儿。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="font-family: STHeitiSC-Light,-apple-system-font,BlinkMacSystemFont,Helvetica Neue,PingFang SC,Hiragino Sans GB,Microsoft YaHei UI,Microsoft YaHei,Arial,sans-serif;font-size: 18px;font-weight: bolder;padding-left: 10px;border-left: 5px solid rgb(248,57,41);display: none;"></span><span style="font-family: STHeitiSC-Light,-apple-system-font,BlinkMacSystemFont,Helvetica Neue,PingFang SC,Hiragino Sans GB,Microsoft YaHei UI,Microsoft YaHei,Arial,sans-serif;font-size: 18px;font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248,57,41);">缓存过期 && 缓存淘汰</span><span style="font-family: STHeitiSC-Light,-apple-system-font,BlinkMacSystemFont,Helvetica Neue,PingFang SC,Hiragino Sans GB,Microsoft YaHei UI,Microsoft YaHei,Arial,sans-serif;font-size: 18px;font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248,57,41);"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">不过很快我发现事情不妙了,我缓存的数据都是在内存中,可是就算是在服务器上,内存的空间资源还是很有限的,不能无节制的这么存下去,我得想个办法,不然吃枣药丸。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">不久,我想到了一个办法:<strong style="font-weight: border;color: rgb(248,57,41);">给缓存内容设置一个超时时间</strong>,具体设置多长交给应用程序们去设置,我要做的就是把过期了的内容从我里面删除掉,及时腾出空间就行了。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.4457627118644068" src="/upload/796bb409745293e1cea49d54ee61925d.png" data-type="png" data-w="1180" style="border-radius: 5px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">超时时间有了,我该在什么时候去干这个清理的活呢?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">最简单的就是<strong style="font-weight: border;color: rgb(248,57,41);">定期删除</strong>,我决定<strong style="font-weight: border;color: rgb(248,57,41);">100ms</strong>就做一次,一秒钟就是10次!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">我清理的时候也不能一口气把所有过期的都给删除掉,我这里面存了大量的数据,要全面扫一遍的话那不知道要花多久时间,会严重影响我接待新的客户请求的!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">时间紧任务重,我只好随机选择一部分来清理,能缓解内存压力就行了。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.45546218487394957" src="/upload/ee7d1293bf3cc2742a2230084417623d.png" data-type="png" data-w="1190" style="border-radius: 5px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">就这样过了一段日子,我发现有些个键值运气比较好,每次都没有被我的随机算法选中,每次都能幸免于难,这可不行,这些长时间过期的数据一直霸占着不少的内存空间!气抖冷!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">我眼里可揉不得沙子!于是在原来定期删除的基础上,又加了一招:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">那些原来逃脱我随机选择算法的键值,一旦遇到查询请求,被我发现已经超期了,那我就绝不客气,立即删除。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">这种方式因为是被动式触发的,不查询就不会发生,所以也叫<strong style="font-weight: border;color: rgb(248,57,41);">惰性删除</strong>!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">可是,还是有部分键值,既逃脱了我的随机选择算法,又一直没有被查询,导致它们一直逍遥法外!而于此同时,可以使用的内存空间却越来越少。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.6507666098807495" src="/upload/9fafeaf46233329c197c0e41557d0dcf.png" data-type="png" data-w="1174" style="border-radius: 5px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">而且就算退一步讲,我能够把过期的数据都删除掉,那万一过期时间设置的很长,还没等到我去清理,内存就吃满了,一样要吃枣药丸,所以我还得想个办法。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">我苦思良久,终于憋出了个大招:<strong style="font-weight: border;color: rgb(248,57,41);">内存淘汰策略</strong>,这一次我要彻底解决问题!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">我提供了8种策略供应用程序选择,用于我遇到内存不足时该如何决策:</p> <blockquote data-tool="mdnice编辑器" style="font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(239, 112, 96);background: rgb(255, 249, 249);"> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-size: 14px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <strong style="font-weight: border;color: rgb(248,57,41);">noeviction</strong>:返回错误,不会删除任何键值 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <strong style="font-weight: border;color: rgb(248,57,41);">allkeys-lru</strong>:使用LRU算法删除最近最少使用的键值 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <strong style="font-weight: border;color: rgb(248,57,41);">volatile-lru</strong>:使用LRU算法从设置了过期时间的键集合中删除最近最少使用的键值 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <strong style="font-weight: border;color: rgb(248,57,41);">allkeys-random</strong>:从所有key随机删除 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <strong style="font-weight: border;color: rgb(248,57,41);">volatile-random</strong>:从设置了过期时间的键的集合中随机删除 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <strong style="font-weight: border;color: rgb(248,57,41);">volatile-ttl</strong>:从设置了过期时间的键中删除剩余时间最短的键 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <strong style="font-weight: border;color: rgb(248,57,41);">volatile-lfu</strong>:从配置了过期时间的键中删除使用频率最少的键 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 13px;"> <strong style="font-weight: border;color: rgb(248,57,41);">allkeys-lfu</strong>:从所有键中删除使用频率最少的键 </section></li> </ul> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">有了上面几套组合拳,我再也不用担心过期数据多了把空间撑满的问题了~</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="font-family: STHeitiSC-Light,-apple-system-font,BlinkMacSystemFont,Helvetica Neue,PingFang SC,Hiragino Sans GB,Microsoft YaHei UI,Microsoft YaHei,Arial,sans-serif;font-size: 18px;font-weight: bolder;padding-left: 10px;border-left: 5px solid rgb(248,57,41);display: none;"></span><span style="font-family: STHeitiSC-Light,-apple-system-font,BlinkMacSystemFont,Helvetica Neue,PingFang SC,Hiragino Sans GB,Microsoft YaHei UI,Microsoft YaHei,Arial,sans-serif;font-size: 18px;font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248,57,41);">缓存穿透 && 布隆过滤器</span><span style="font-family: STHeitiSC-Light,-apple-system-font,BlinkMacSystemFont,Helvetica Neue,PingFang SC,Hiragino Sans GB,Microsoft YaHei UI,Microsoft YaHei,Arial,sans-serif;font-size: 18px;font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248,57,41);"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">我的日子过的还挺舒坦,不过MySQL大哥就没我这么舒坦了,有时候遇到些烦人的请求,查询的数据不存在,MySQL就要白忙活一场!不仅如此,因为不存在,我也没法缓存啊,导致同样的请求来了每次都要去让MySQL白忙活一场。我作为缓存的价值就没得到体现啦!这就是人们常说的<strong style="font-weight: border;color: rgb(248,57,41);">缓存穿透</strong>。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.3962900505902192" src="/upload/dcd6de0930cf71779f2cd243bbaf81cc.png" data-type="png" data-w="1186" style="border-radius: 5px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">这一来二去,MySQL大哥忍不住了:“唉,兄弟,能不能帮忙想个办法,把那些明知道不会有结果的查询请求给我挡一下”</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">这时我想到了我的另外一个好朋友:<strong style="font-weight: border;color: rgb(248,57,41);">布隆过滤器</strong></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5212962962962963" src="/upload/5c9931b7ab25779173b8cdf1e22236eb.png" data-type="png" data-w="1080" style="border-radius: 5px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">我这位朋友别的本事没有,就擅长从超大的数据集中快速告诉你查找的数据存不存在(<strong style="font-weight: border;color: rgb(248,57,41);">悄悄告诉你,我的这位朋友有一点不靠谱,它告诉你存在的话不能全信,其实有可能是不存在的,不过它他要是告诉你不存在的话,那就一定不存在</strong>)。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.43039591315453385" src="/upload/b23410e1a3b7470f5b1e3782b9f54bac.png" data-type="png" data-w="783" style="border-radius: 5px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">如果你对我这位朋友感兴趣的话,可以看看这里<a href="https://mp.weixin.qq.com/s?__biz=MzI1MzYzMTI2Ng==&mid=2247483840&idx=1&sn=351b3c648a724b28bce767d9345400dd&scene=21#wechat_redirect" style="font-weight: bold;color: rgb(248, 57, 41);border-bottom: 1px solid rgb(255, 53, 2);font-family: STHeitiSC-Light, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;" data-linktype="2">《白话布隆过滤器BloomFilter》</a>。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">我把这位朋友介绍给了应用程序,不存在的数据就不必去叨扰MySQL了,轻松帮忙解决了缓存穿透的问题。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="font-family: STHeitiSC-Light,-apple-system-font,BlinkMacSystemFont,Helvetica Neue,PingFang SC,Hiragino Sans GB,Microsoft YaHei UI,Microsoft YaHei,Arial,sans-serif;font-size: 18px;font-weight: bolder;padding-left: 10px;border-left: 5px solid rgb(248,57,41);display: none;"></span><span style="font-family: STHeitiSC-Light,-apple-system-font,BlinkMacSystemFont,Helvetica Neue,PingFang SC,Hiragino Sans GB,Microsoft YaHei UI,Microsoft YaHei,Arial,sans-serif;font-size: 18px;font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248,57,41);">缓存击穿 && 缓存雪崩</span><span style="font-family: STHeitiSC-Light,-apple-system-font,BlinkMacSystemFont,Helvetica Neue,PingFang SC,Hiragino Sans GB,Microsoft YaHei UI,Microsoft YaHei,Arial,sans-serif;font-size: 18px;font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248,57,41);"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">这之后过了一段时间太平日子,直到那一天···</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">有一次,MySQL那家伙正优哉游哉的摸鱼,突然一大堆请求给他怼了过去,给他打了一个措手不及。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">一阵忙活之后,MySQL怒气冲冲的找到了我,“兄弟,咋回事啊,怎么一下子来的这么猛”<img class="rich_pages wxw-img" data-ratio="0.42028985507246375" src="/upload/e523d0055c130845c46d12eaf07b9585.png" data-type="png" data-w="1656" style="border-radius: 5px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;">我查看了日志,赶紧解释到:“大哥,实在不好意思,刚刚有一个热点数据到了过期时间,被我删掉了,不巧的是随后就有对这个数据的大量查询请求来了,我这里已经删了,所以请求都发到你那里来了”</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">“你这干的叫啥事,下次注意点啊”,MySQL大哥一脸不高兴的离开了。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">这一件小事我也没怎么放在心上,随后就抛之脑后了,却没曾想几天之后竟捅了更大的篓子。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">那一天,又出现了大量的网络请求发到了MySQL那边,比上一次的规模大得多,MySQL大哥一会儿功夫就给干趴下了好几次!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">等了好半天这一波流量才算过去,MySQL才缓过神来。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">“老弟,这一次又是什么原因?”,MySQL大哥累的没了力气。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">“这一次比上一次更不巧,这一次是一大批数据几乎同时过了有效期,然后又发生了很多对这些数据的请求,所以比起上一次这规模更大了”</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.401468788249694" src="/upload/4cc2871d1fd8a7f549a3e473e7946a2c.png" data-type="png" data-w="1634" style="border-radius: 5px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">MySQL大哥听了眉头一皱,“那你倒是想个办法啊,三天两头折磨我,<strong style="font-weight: border;color: rgb(248,57,41);">这谁顶得住啊?</strong>”</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">“其实我也很无奈,这个时间也不是我设置的,要不我去找应用程序说说,让他把缓存过期时间设置的均匀一些?至少别让大量数据集体失效”</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">“走,咱俩一起去”</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">后来,我俩去找应用程序商量了,不仅把键值的过期时间随机了一下,还设置了热点数据永不过期,这个问题缓解了不少。哦对了,我们还把这两次发生的问题分别取了个名字:<strong style="font-weight: border;color: rgb(248,57,41);">缓存击穿</strong>和<strong style="font-weight: border;color: rgb(248,57,41);">缓存雪崩</strong>。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">我们终于又过上了舒适的日子···</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><em style="font-size: 12px;word-spacing: 0.1em;background-color: rgb(255, 249, 249);font-family: PingFangSC-Light, STHeitiSC-Light, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;color: rgb(248, 57, 41);letter-spacing: 0.3em;"></em></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">原创很多,质量很硬通俗易懂,用故事说技术,强烈建议关注!</p> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzIyNjMxOTY0NA==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/jXQDbLkGBYUry2U8Do9dGIVjPWylXsYhplHwmDdiaB5Gdia2AdH6M8g3dSPp1kicNGMXFzlzEIIA92HbAQjEBFMfw/0?wx_fmt=png" data-nickname="编程技术宇宙" data-alias="xuanyuancoding" data-signature="用故事说技术,编程从未如此有趣" data-from="0"></mpprofile> </section> </section>
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;" data-mpa-powered-by="yiban.io"> <section style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-top: 5px;"> 很多APP的目前都支持“本机号码一键登录”功能。本机号码一键登录是基于运营商独有网关认证能力推出的账号认证产品。 <br> </section> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" src="/upload/d3acb53c6feba1af2f4ebc9ef65d201d.png" data-cropx1="0" data-cropx2="500" data-cropy1="59" data-cropy2="414" data-ratio="0.71" data-s="300,640" src="https://mmbiz.qpic.cn/mmbiz_jpg/R3InYSAIZkEDVHBVY7fdp7AOY5yiaQR2atY37kAibtibKMB3xd2oYGmdibmDoFqVkVPgWEw5zXwcE2O3maBwlH77bg/640?wx_fmt=jpeg" data-type="jpeg" data-w="500" style="height: 355px;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;text-align: center;white-space: normal;width: 500px;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;">用户只需一键授权,即可实现以本机号码注册/登录,相比先前的短信验证码流程体验更优。<br></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;">目前市面上有很多厂商提供三网验证的服务,只不过是对三大运营商的包装。要了解具体的原理可直接看三大运营商相关的介绍。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;font-weight: bold;font-size: 22px;border-bottom: 2px solid rgb(89, 89, 89);margin-bottom: 30px;color: rgb(89, 89, 89);"><span style="display: none;"></span><span style="font-size: 19px;display: inline-block;border-bottom: 2px solid rgb(89,89,89);">中国移动</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;">中国移动号码认证服务支支持移动、联通、电信三网号码。主要产品功能:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;font-size: 15px;line-height: 30px;letter-spacing: 1.5px;color: rgb(51, 51, 51);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 30px;"> 一键登录:依托运营商的移动通信网络,采用通信网关取号技术,准确识别用户流量卡归属的手机号码。在获得用户授权后,App端(适配iOS和Android)可使用本机号码实现一键免密登录。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 30px;"> 本机号码校验:通过SDK/JSSDK提供的本机号码校验功能,调用网关鉴权方式,验证用户输入的手机号码或后台绑定的手机号码是否为本机流量卡归属号码,保证机卡不分离,用于快捷登入和安全风控等场景。本机号码校验现已适配iOS、Android、H5、小程序、快应用。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;">获取手机号码(一键登录):</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.644404332129964" src="/upload/c347a510d17a2523bdf7cc461ecf3828.png" data-type="png" data-w="554" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgb(210, 210, 210) 3px 3px 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> 图片 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;">本机号码校验:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.572202166064982" src="/upload/fb5cc817ba753830a1fabba98b82eb79.png" data-type="png" data-w="554" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgb(210, 210, 210) 3px 3px 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> 图片 </figcaption> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;color: rgb(89, 89, 89);"><span style="display: none;"></span>取号方法<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;">通过调用安卓的getPhoneInfo或iOS的getPhoneNumberCompletion,在用户无感知的情况下进行网络判断、蜂窝数据网络切换和网关取号等操作(以上操作均需消耗一定时间),返回取号是否成功的结果。</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;font-size: 15px;line-height: 30px;letter-spacing: 1.5px;color: rgb(51, 51, 51);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 30px;"> 取号所需网络环境:运营商取号能力是通过数据网关实现,取号过程须在数据流量打开的情况下才能进行。因此,用户如果关闭数据流量将无法取号;若当前信号弱或者网络有干扰时,时延会高于平均值,取号成功率降低。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 30px;"> 超时设置:SDK默认超时设置为8000ms,同时提供设置取号超的方法:安卓通过setOverTime设置,iOS通过setTimeoutInterval设置。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 30px;"> 运营商判断:SDK提供判断用户当前网络状态和流量卡所属运营商的方法,通过调用安卓SDK的getNetworkType或iOS的networkInfo可获得以上信息,以便对不同用户选择不同的运营商的SDK进行取号或选择不同的登录方式。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 30px;"> 关于取号缓存:应用取号或者授权成功后,SDK将取号的一个临时凭证缓存在本地,缓存能有效提高取号成功率、降低时延,并允许用户在未开启蜂窝网络时成功取号。SDK本身对缓存有处理逻辑,在某些场景下(如换卡)会让缓存提前失效,但若应用对安全性要求较高,也可以通过SDK提供的方法(安卓的delScrip和iOS的delectScrip)让缓存马上失效。 </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;color: rgb(89, 89, 89);"><span style="display: none;"></span>本机号码校验<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;">通过调用安卓的mobileAuth或iOS的mobileAuthCompletion方法,可在不拉起授权页的情况下获得token。此时获得的token不能用于兑换用户的完整号码,只能用于校验本机号码与待校验号码的一致性。</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;font-size: 15px;line-height: 30px;letter-spacing: 1.5px;color: rgb(51, 51, 51);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 30px;"> 预取号:安卓的getPhoneInfo或iOS的getPhoneNumberCompletion所形成取号缓存scrip同样适用于本机号码校验,可提前进行取号以提高后续获取token的效率。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 30px;"> 适用场景:可在用户无感知的情况下校验本机号码与待校验号码的一致性,适用于所有基于手机号码进行风控的场景。 </section></li> </ul> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;font-weight: bold;font-size: 22px;border-bottom: 2px solid rgb(89, 89, 89);margin-bottom: 30px;color: rgb(89, 89, 89);"><span style="display: none;"></span><span style="font-size: 19px;display: inline-block;border-bottom: 2px solid rgb(89,89,89);">中国电信</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;">中国电信天翼账号开放平台提供了:免密登录、手机号码认证、二次卡校验等服务。目前只支持中国电信用户。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;">免密认证:天翼账号免密认证方案,依托运营商的移动数据网络,采用“通信网关预登录”及 SIM卡识别等技术,准确识别用户手机号码,实现一键登录,并可有效规避短信验证码泄露风险</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.42418772563176893" src="/upload/d4967abef8835cf63dfad4a67ccb1cf8.png" data-type="png" data-w="554" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgb(210, 210, 210) 3px 3px 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> <br> </figcaption> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.48736462093862815" src="/upload/ec026fe35204f89c95fd688e0ce8b4ce.png" data-type="png" data-w="554" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgb(210, 210, 210) 3px 3px 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> <br> </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;">手机号码校验:确认本机号码信息是否为当前用户本机号码。依托运营商的移动数据网络,采用“通信网关预登录”及 SIM卡识别等技术,判断用户输入的手机号与本机号码是否一致。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.2833935018050541" src="/upload/7b75015891b0ef4d95f9df7b5bfc8206.png" data-type="png" data-w="554" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgb(210, 210, 210) 3px 3px 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> <br> </figcaption> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.30866425992779783" src="/upload/5a5040e02101ffd854b2b1df8386f81.png" data-type="png" data-w="554" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgb(210, 210, 210) 3px 3px 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> <br> </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;">二次卡校验:众所周知,三大运营商每月注销的手机号约有2000多万,为避免手机号资源浪费,运营商会先回收已注销的手机号,然后重新销售,重新销售的卡即为二次卡。如果用户更换了手机号,且未与原账号解绑,可能存在安全风险。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;">天翼账号二次卡校验方案, 使用独有的运营商二次号数据库,可快速检测用户号码更换状态,保障号码旧用户隐私安全。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.27256317689530685" src="/upload/b053472623ba824e46483bd6e29c0a3c.png" data-type="png" data-w="554" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgb(210, 210, 210) 3px 3px 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> <br> </figcaption> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.44584837545126355" src="/upload/5015eab44ec9deedda001f4bdf81d263.png" data-type="png" data-w="554" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgb(210, 210, 210) 3px 3px 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> <br> </figcaption> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;font-weight: bold;font-size: 22px;border-bottom: 2px solid rgb(89, 89, 89);margin-bottom: 30px;color: rgb(89, 89, 89);"><span style="display: none;"></span><span style="font-size: 19px;display: inline-block;border-bottom: 2px solid rgb(89,89,89);">中国联通</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;">中国联通通信创新能力平台提供了号码认证(一键登录)、匿名设备标识、匿名用户标识、空号识别、二次号验证、三要素验证等服务。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;color: rgb(89, 89, 89);"><span style="display: none;"></span>号码认证<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;">依托运营商网关认证能力,面向互联网应用提供的本机手机号码一键注册登录及本机手机号码校验服务,支持APP、H5页面多场景应用。官方SDK,支持联通、移动、电信三网,智能化程度高,交互时间短,提升用户体验、提高拉新转化率;专利技术,性能可靠,降低空号注册登录、密码拦截盗取风险。</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>应用场景:<span style="display: none;"></span></h4> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;font-size: 15px;line-height: 30px;letter-spacing: 1.5px;color: rgb(51, 51, 51);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 30px;"> 一键登录 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 30px;"> APP一键登录。手机在有蜂窝信号的环境中(若WIFI接入,SDK可瞬间切换至蜂窝信号再切回),可自动获取手机号码,帮助用户实现一键验证快捷登录,无需手动输入号码和短信验证,有利于提升用户体验,提高登录安全性。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 30px;"> H5一键登录。适用于手机接入蜂窝信号时,H5页面登录场景,用户只需输入4位本机号码即可实现快捷登录,减少降登录等待时间。 </section></li> </ul> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.855595667870036" src="/upload/e176a7e3d0fb4464067fec71ade1ead0.png" data-type="png" data-w="554" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgb(210, 210, 210) 3px 3px 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> 图片 </figcaption> </figure> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;font-size: 15px;line-height: 30px;letter-spacing: 1.5px;color: rgb(51, 51, 51);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 30px;"> 本机校验。自动校验用户手机号与当前本机卡号的一致性,免输登录密码或短信验证码;适用于如手机号绑定、支付确认、积分兑换等需要具备安全校验能力的业务场景,提供仅限本机操作的安全风控机制。支持有蜂窝信号环境下的APP及正在使用蜂窝信号下的H5。 </section></li> </ul> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.855595667870036" src="/upload/17b338c468220a782d5a843526f1d035.png" data-type="png" data-w="554" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgb(210, 210, 210) 3px 3px 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> 图片 </figcaption> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;color: rgb(89, 89, 89);"><span style="display: none;"></span>匿名设备标识<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;">通过识别移动设备唯一ID,为客户提供基于用户和设备的标识方案,支持IOS和安卓系统,不依赖设备厂商,具备稳定性和唯一性,可关联安卓设备资产数据,帮助企业找回历史关联资产,实现基于用户画像的精准投放,有效识别设备篡改和营销作假,防止薅羊毛,避免金融风险等。</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>应用场景:<span style="display: none;"></span></h4> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;font-size: 15px;line-height: 30px;letter-spacing: 1.5px;color: rgb(51, 51, 51);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 30px;"> 风险识别:企业开展营销活动时,面对薅羊毛、黑产等,通过设备识别可有效识别判断参与用户,防止有限资源被无价值用户占用,支持APP/H5/小程序等全场景,覆盖安卓/IOS生态系统。适用于开展各类营销活动的企业,如电商、金融、游戏、生活等。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 30px;"> 场景营销:企业可基于不同营销场景(APP/H5/小程序)下的用户标识,进行跨应用用户分析与画像生成,实现精准营销推送。适用于需分析用户偏好、阅读习惯的企业,如购物、新闻、视频、娱乐、阅读等. </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;color: rgb(89, 89, 89);"><span style="display: none;"></span>匿名用户标识<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;">通过手机用户的公私网IP返回唯一串码,可以在保护用户手机号不泄露的情况下,提供用户唯一标识(即伪码)方案。仅支持联通用户。</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>应用场景:<span style="display: none;"></span></h4> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;font-size: 15px;line-height: 30px;letter-spacing: 1.5px;color: rgb(51, 51, 51);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 30px;"> 会员营销:会员营销活动时,用户领取会团优惠,平台方通过联通唯一识别平台将手机号转换成伪码供商户进行维系和发放。适用于入住商户无法获取平台用户手机号的场景。 </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;color: rgb(89, 89, 89);"><span style="display: none;"></span>空号识别<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;">识别号码是否为真实有效的开机使用号码,对于不可达号码(例如关机、养卡等情况)采取相应的运营措施。识别过程一秒以内,支持大规模并发查询,且对用户无感知。帮助企业快速、高效、精准开展营销、维系等商业活动,减少营销成本。</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>应用场景:<span style="display: none;"></span></h4> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;font-size: 15px;line-height: 30px;letter-spacing: 1.5px;color: rgb(51, 51, 51);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 30px;"> 外呼中心:用于外呼中心,回访或者推介产品之前先对用户手机状态进行有效识别,关机、离网及不在服务区用户免拨叫,节省人力物力。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 30px;"> 存量用户维系:用于检测用户手机是否处于在网状态,便于企业精准开展后续营销活动。例如优惠券发放、权益下达等,为真实客户送权益,送利益。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 30px;"> 注册用户清洗:对注册会员的号码进行检测,对短期内高频次的注册行为进行监控,对可疑号码进行监控,及时清理批量注册的垃圾用户和数据、被占用数据,将恶意注册用户拒之门外,防止“薅羊毛党”恶意套利现象。使用号码检测功能,可以减少企业客户的营销维护成本,保护真实用户的权益。 </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;color: rgb(89, 89, 89);"><span style="display: none;"></span>二次号验证<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;">次号验证产品是指核验手机号码在指定时间之后是否重新开户。</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>应用场景:<span style="display: none;"></span></h4> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;font-size: 15px;line-height: 30px;letter-spacing: 1.5px;color: rgb(51, 51, 51);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 30px;"> 注册/登录/密码找回:针对注册/登录/密码找回等场景,企业可通过使用联通二次号码验证产品识别当前注册登录号码是否为二次放号,从而避免二次放号用户使用原号码账号可能带来的用户隐私泄露和经济损失。 </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;color: rgb(89, 89, 89);"><span style="display: none;"></span>三要素验证<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;">三要素验证产品提供联通用户的姓名、身份证号、手机号三要素的一致性核验服务。</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>应用场景:<span style="display: none;"></span></h4> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;font-size: 15px;line-height: 30px;letter-spacing: 1.5px;color: rgb(51, 51, 51);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 30px;"> 实名认证:针对金融、婚恋等应用系统中的实名注册场景,企业可通过联通三要素验证产品识别用户身份真实性和信息一致性,有助于防范用户风险。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 30px;"> 贷前审核:针对借贷、信用卡申请等金融场景,企业可以通过使用联通三要素验证产品识别申请用户信息真实度,助力用户风险评估和贷款决策。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: rgb(51, 51, 51);margin-top: 8px;margin-bottom: 8px;line-height: 35px;letter-spacing: 1.5px;"><span style="font-size: 13px;color: rgb(136, 136, 136);">来源 | https://www.cnblogs.com/Frankada/p/15689036.html</span></p> </section>
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Roboto, Oxygen, Ubuntu, Cantarell, PingFangSC-light, PingFangTC-light, "Open Sans", "Helvetica Neue", sans-serif;"> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;line-height: 1.5em;margin-top: 2.2em;margin-bottom: 35px;"><span style="display: inline-block;background-image: linear-gradient(rgb(255, 255, 255) 60%, rgb(255, 177, 27) 40%);background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;color: rgb(81, 81, 81);padding: 2px 13px;margin-right: 3px;height: 50%;">一、准备工作</span></h2> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;line-height: 1.8em;color: rgb(58, 58, 58);">Linux系统安装Tomcat,使用默认端口8080,启动Tomcat服务器</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.1394080996884735" src="/upload/6b2474dbb3d2af5eed71ee91d3437a2a.png" data-type="png" data-w="1284" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;line-height: 1.8em;color: rgb(58, 58, 58);">可以正常访问</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5056818181818182" src="/upload/9f32c7d763662bed58a7d2ca0196e459.png" data-type="png" data-w="1584" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;line-height: 1.8em;color: rgb(58, 58, 58);">接下来想要通过Nginx反向代理,转发请求到Tomcat服务器。对外暴露的是Nginx反向代理服务器的端口号,而Tomcat不对外暴露。浏览器不能直接访问到Tomcat,而是通过Nginx反向代理服务器才能访问到Tomcat</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.3098311817279047" src="/upload/1b1844ffa7d0bdc773df4e3bb7b799ae.png" data-type="png" data-w="1007" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;line-height: 1.5em;margin-top: 2.2em;margin-bottom: 35px;"><span style="display: none;"></span><span style="display: inline-block;background-image: linear-gradient(rgb(255, 255, 255) 60%, rgb(255, 177, 27) 40%);background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;color: rgb(81, 81, 81);padding: 2px 13px;margin-right: 3px;height: 50%;">二、反向代理配置</span></h2> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;line-height: 1.8em;color: rgb(58, 58, 58);">在Windows系统的host文件进行域名和IP地址映射关系的配置</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.3431372549019608" src="/upload/a6782e8483a323c51f499d1200963cbd.png" data-type="png" data-w="612" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.20642201834862386" src="/upload/4ffbca947b0201d712d26518a31066f0.png" data-type="png" data-w="654" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;line-height: 1.8em;color: rgb(58, 58, 58);">可以通过域名8080端口访问到Tomcat服务器</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.6368932038834951" src="/upload/467c53dc743e7fc2635a087f5737b34.png" data-type="png" data-w="1030" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;line-height: 1.8em;color: rgb(58, 58, 58);">然后在Nginx进行请求转发的配置,配置反向代理</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.337909186906019" src="/upload/a0d8e741c4ef92c18aa2fb7322bd0586.png" data-type="png" data-w="947" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;line-height: 1.8em;color: rgb(58, 58, 58);">此时没有访问8080端口,而是直接访问的Nginx代理服务器,Nginx会反向代理转发请求到8080端口的Tomcat服务器</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.6557213930348259" src="/upload/80fa6dba830003595f051ceafa3e6f48.png" data-type="png" data-w="1005" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;line-height: 1.8em;color: rgb(58, 58, 58);">还可以配置Nginx的反向代理,根据不同的访问路径跳转到不同端口的服务中</p> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;line-height: 1.8em;color: rgb(58, 58, 58);">首先启动两个不同端口的Tomcat服务器,一个配置端口为8081,一个配置端口为8082</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.22354694485842028" src="/upload/aa6d2dc3f6f1230d8d94e6f9c182952c.png" data-type="png" data-w="671" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.21739130434782608" src="/upload/921561569d93297d1408b42f0cf6e767.png" data-type="png" data-w="690" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;line-height: 1.8em;color: rgb(58, 58, 58);">启动两个Tomcat服务器</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.11815181518151815" src="/upload/a069cdd1ff4637772df6f8f963c7e552.png" data-type="png" data-w="1515" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.11764705882352941" src="/upload/24d0b0ddfbce8e194fff75c944a7ab01.png" data-type="png" data-w="1547" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;line-height: 1.8em;color: rgb(58, 58, 58);">此时8081和8082端口都可以访问</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5195996663886572" src="/upload/93be24fa66685e5f8d303b9faefe21ee.png" data-type="png" data-w="1199" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5225733634311512" src="/upload/ff3869e8d02dedd3679f2b62d25b11d1.png" data-type="png" data-w="886" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;line-height: 1.8em;color: rgb(58, 58, 58);">配置Nginx反向代理服务器</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.46751740139211134" src="/upload/79aa74fa2c0f182c0af361c607ffe6b.png" data-type="png" data-w="862" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;line-height: 1.8em;color: rgb(58, 58, 58);">配置好反向代理之后,访问/test8081/路径下的file8081页面,Nginx会反向代理访问8081端口的Tomcat服务器</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.3970149253731343" src="/upload/e69a4c7a075f16c8be4dc170f9501fdb.png" data-type="png" data-w="670" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;line-height: 1.8em;color: rgb(58, 58, 58);">访问/test8082/路径下的file8082页面,Nginx会反向代理访问8082端口的Tomcat服务器</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.3008356545961003" src="/upload/2da3771442be81cdd031765e0f754746.png" data-type="png" data-w="718" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;line-height: 1.8em;color: rgb(58, 58, 58);">这样就实现了根据不同的访问路径通过反向代理转发请求到不同的服务器</p> </section>
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;line-height: 1.6;word-break: break-word;text-align: left;font-size: 15px;letter-spacing: 0.05em;color: rgb(89, 89, 89);font-family: Roboto, Oxygen, Ubuntu, Cantarell, PingFangSC-light, PingFangTC-light, "Open Sans", "Helvetica Neue", sans-serif;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">写接口时经常碰到要把某个枚举值返回给前端展示,然后前端要把这个枚举值显示成文本。在没有典文本映射功能之前前端就需要把这些枚举值和文本对应起来然后做展示,或者后台直接把枚举值转换成文本然后返回给前端。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">如果后端能直接把枚举值的文本自动的设置到返回实体的某个属性上,这样就能解决了枚举值文体的转换问题了,话不多说开整。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(53, 179, 120);"><span style="display: none;"></span>实体设计<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">字典本身并不复杂,所以设计实体也比较简洁:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/Kwg1Hs1pPD1icvSaZ1BFevTFhUpmXlB3XzMic9M53xEL2eofSyVt1ica5YLMt2ZlcLWme0vRqKZqCEGjDIx2fpcwfqoVCJH5tSD/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #c678dd;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span> <span style="color: #e6c07b;line-height: 26px;">Dict</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">private</span> String id;<br> <span style="color: #c678dd;line-height: 26px;">private</span> String name;<br> <span style="color: #c678dd;line-height: 26px;">private</span> String value;<br> <span style="color: #c678dd;line-height: 26px;">private</span> String type;<br> <span style="color: #c678dd;line-height: 26px;">private</span> String pid;<br> <span style="color: #c678dd;line-height: 26px;">private</span> String status;<br> <span style="color: #c678dd;line-height: 26px;">private</span> Integer sort;<br> <span style="color: #c678dd;line-height: 26px;">private</span> String description;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">各项属性说明如下:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> id : 字典的id </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> name: 字典的名称,一般用于前端展示 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> value:字典的值 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> type:字典的类型 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> status: 字典的状态 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> sort: 排序 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> description: 字典的描述信息 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> pid: 上级字典id,有些特殊的字典有上下级关系,比如存放城市的时候 </section></li> </ul> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">上面的字典设计是非常直观的一个字典项对应数据库中的一行,相同的字典项由类型(type)来标识。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(53, 179, 120);"><span style="display: none;"></span>字典文本映射<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">比如下面的响应就是经过字典文本映射的:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/Kwg1Hs1pPD1icvSaZ1BFevTFhUpmXlB3XzMic9M53xEL2eofSyVt1ica5YLMt2ZlcLWme0vRqKZqCEGjDIx2fpcwfqoVCJH5tSD/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">{<br> <span style="color: #98c379;line-height: 26px;">"code"</span>: <span style="color: #98c379;line-height: 26px;">"SUCCESS"</span>,<br> <span style="color: #98c379;line-height: 26px;">"success"</span>: <span style="color: #56b6c2;line-height: 26px;">true</span>,<br> <span style="color: #98c379;line-height: 26px;">"message"</span>: <span style="color: #98c379;line-height: 26px;">"操作成功"</span>,<br> <span style="color: #98c379;line-height: 26px;">"data"</span>: {<br> <span style="color: #98c379;line-height: 26px;">"id"</span>: <span style="color: #98c379;line-height: 26px;">"1496108440362811394"</span>,<br> <span style="color: #98c379;line-height: 26px;">"status"</span>: <span style="color: #98c379;line-height: 26px;">"DISABLE"</span>,<br> <span style="color: #98c379;line-height: 26px;">"statusDesc"</span>: <span style="color: #98c379;line-height: 26px;">"禁用"</span>,<br> ...<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">status</code>是一个字典值,<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">statusDesc</code>是<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">status</code>的文本表示。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">下面来一起看看如何利用<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">Spring</code>来实现字典文本映射的。</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 18px;"><span style="display: none;"></span>定义字典属性注解<span style="display: none;"></span></h4> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/Kwg1Hs1pPD1icvSaZ1BFevTFhUpmXlB3XzMic9M53xEL2eofSyVt1ica5YLMt2ZlcLWme0vRqKZqCEGjDIx2fpcwfqoVCJH5tSD/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">@Retention(RetentionPolicy.RUNTIME)<br>@Target(ElementType.FIELD)<br>public @interface DictProperty {<br><br> /**<br> * 字典类型<br> * @<span style="color: #e6c07b;line-height: 26px;">return</span><br> */<br> String <span style="color: #e6c07b;line-height: 26px;">type</span>();<br><br> /**<br> * 字典数据值属性名称<br> * @<span style="color: #e6c07b;line-height: 26px;">return</span><br> */<br> String value();<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">注解属性说明:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> type:对应字典的类型属性 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> value: 字典数据值属性名称 </section></li> </ul> <h5 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;"><span style="display: none;"></span>如何使用?<span style="display: none;"></span></h5> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/Kwg1Hs1pPD1icvSaZ1BFevTFhUpmXlB3XzMic9M53xEL2eofSyVt1ica5YLMt2ZlcLWme0vRqKZqCEGjDIx2fpcwfqoVCJH5tSD/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">@Data<br>public class UserDTO {<br><br> private String status;<br> <br> @DictProperty(<span style="color: #e6c07b;line-height: 26px;">type</span> = <span style="color: #98c379;line-height: 26px;">"user_status"</span>, value = <span style="color: #98c379;line-height: 26px;">"status"</span>)<br> private String statusDesc;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">在<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">UserDTO</code>里面的<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">status</code>属性是字典的值, <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">statusDesc</code>是字典值的名字。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(53, 179, 120);"><span style="display: none;"></span>实现方式<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">实现可以使用<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">aop</code>,但我发现<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">SpringMVC</code>提供了个非常好的切入点:<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">ResponseBodyAdvice</code>,继承此接口就能对请求的响应做一些小修改。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/Kwg1Hs1pPD1icvSaZ1BFevTFhUpmXlB3XzMic9M53xEL2eofSyVt1ica5YLMt2ZlcLWme0vRqKZqCEGjDIx2fpcwfqoVCJH5tSD/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">public interface ResponseBodyAdvice<T> {<br><br> boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);<br><br> @Nullable<br> T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,<br> Class<? extends HttpMessageConverter<?>> selectedConverterType,<br> ServerHttpRequest request, ServerHttpResponse response);<br><br>}<br></code></pre> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> supports方法:判断实体类型是否能处理 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> beforeBodyWrite: 如果 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">supports</code>返回ture,就会执行这个方法,这个方法可以修改返回的实体。 </section></li> </ul> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">大致的实现思路:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 在 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">beforeBodyWrite</code>方法利用Java反射来读取实体属性的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">DictPropty</code>注解 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 解析 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">DictPropty</code>注解的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">type</code>的值,然后通过字典类型获取到对应的字典 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 读取到对应类型的字典后,将字典的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">name</code>写入到实体的属性上 </section></li> </ul> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/Kwg1Hs1pPD1icvSaZ1BFevTFhUpmXlB3XzMic9M53xEL2eofSyVt1ica5YLMt2ZlcLWme0vRqKZqCEGjDIx2fpcwfqoVCJH5tSD/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">@Slf4j<br>@ControllerAdvice<br>public class DictResponseBodyAdvice implements ResponseBodyAdvice<Object> {<br><br> @Autowired<br> private IDictService dictService;<br><br> @Override<br> public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {<br> <span style="color: #e6c07b;line-height: 26px;">return</span> <span style="color: #56b6c2;line-height: 26px;">true</span>;<br> }<br><br> @Override<br> public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {<br> <br> <span style="color: #c678dd;line-height: 26px;">if</span>(body instanceof Result){<br> handleBody(((Result) body).getData());<br> }<span style="color: #c678dd;line-height: 26px;">else</span> <span style="color: #c678dd;line-height: 26px;">if</span>(body instanceof IPage){<br> handleBody(((IPage) body).getRecords());<br> }<span style="color: #c678dd;line-height: 26px;">else</span>{<br> handleBody(body);<br> }<br> <span style="color: #e6c07b;line-height: 26px;">return</span> body;<br> }<br>}<br><br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">篇幅有限,省略了一些代码,利用 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">SpringMV</code>C 提供扩展接口与<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">Java反射</code>就可以轻松实现字典的文本映射。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(53, 179, 120);"><span style="display: none;"></span>后续考虑<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">上面的代码已经实现了字典文本映射的功能,但还有一些可以优化一下:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 读取字典的时候要加缓存,防止数据量过大读取字典的字数过大,拖慢接口的响应速度 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 可以不通过 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">ResponseBodyAdvice</code>也能实现字典映射,只需要传入对象就能实现字典映射功能。 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> ... </section></li> </ul> </section>
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;line-height: 1.6;word-break: break-word;text-align: left;font-size: 15px;letter-spacing: 0.05em;color: rgb(89, 89, 89);font-family: Roboto, Oxygen, Ubuntu, Cantarell, PingFangSC-light, PingFangTC-light, "Open Sans", "Helvetica Neue", sans-serif;" data-mpa-powered-by="yiban.io"> <section powered-by="xiumi.us" style="margin-bottom: 6px;outline: 0px;max-width: 100%;color: rgb(89, 89, 89);white-space: normal;font-size: 16px;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <p style="outline: 0px;max-width: 100%;color: rgb(89, 89, 89);white-space: normal;font-size: 16px;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;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="rich_pages wxw-img" data-cropselx1="0" data-cropselx2="558" data-cropsely1="0" data-cropsely2="372" data-galleryid="114737466841268224" data-gallerysupplier="4" data-ratio="0.6666666666666666" data-s="300,640" src="/upload/bc4f6fab60d98a5d8e901bb3add6b36e.jpg" data-type="jpeg" data-w="1023" style="outline: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 558px;height: 372px;"></p> <section style="margin-top: 10px;margin-bottom: 10px;padding-right: 10px;padding-left: 10px;outline: 0px;max-width: 100%;font-size: 15px;white-space: normal;color: rgb(0, 0, 0);font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: right;line-height: 1.8;letter-spacing: 1px;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;letter-spacing: 0.544px;color: rgb(136, 136, 136);caret-color: rgb(136, 136, 136);font-size: 14px;widows: 1;box-sizing: border-box !important;overflow-wrap: break-word !important;">来源 | 微观技术(ID:weiguanjishu)</span> </section> <section style="margin-top: 10px;margin-bottom: 15px;padding-right: 10px;padding-left: 10px;outline: 0px;max-width: 100%;font-size: 15px;white-space: normal;color: rgb(0, 0, 0);font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: right;line-height: 1.8;letter-spacing: 1px;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;letter-spacing: 0.544px;color: rgb(136, 136, 136);caret-color: rgb(136, 136, 136);font-size: 14px;widows: 1;box-sizing: border-box !important;overflow-wrap: break-word !important;">已获得原公众号的授权转载</span> </section> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">作为一名开发同学,大家对 MySQL 一定不陌生,像常见的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">事务特性</code>、<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">隔离级别</code> 、<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">索引</code>等也都是老生常谈。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">今天,我们就来聊个深度话题,关于 MySQL 的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">高可用</code></p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(53, 179, 120);"><span style="display: none;"></span></h3> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(53, 179, 120);">一、什么是高可用?<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">维基百科定义:</strong></p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;font-size: 0.9em;overflow: auto;padding: 10px 10px 10px 20px;margin: 10px 5px;border-left-color: rgb(53, 179, 120);border-right: 0px solid rgb(53, 179, 120);color: rgb(97, 97, 97);quotes: none;background: rgb(251, 249, 253);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">高可用性(high availability,缩写 HA),指系统无中断地执行其功能的能力,代表系统的可用性程度。高可用性通常通过提高系统的容错能力来实现。</p> </blockquote> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">MySQL 的高可用是如何实现的呢?</strong></p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">首先,我们来看张图</p> </section> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.56640625" data-s="300,640" src="/upload/dc49b06efcfc98a2d5995a0e9c2862fa.jpg" data-type="jpeg" data-w="1280" style=""></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;line-height: 1.6;word-break: break-word;text-align: left;font-size: 15px;letter-spacing: 0.05em;color: rgb(89, 89, 89);font-family: Roboto, Oxygen, Ubuntu, Cantarell, PingFangSC-light, PingFangTC-light, "Open Sans", "Helvetica Neue", sans-serif;"> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">过程:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 开始时,处理流程主要是 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">场景一</code> </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 客户端 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">读、写</code>,访问的是主库 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 主库通过某种机制,将数据实时同步给备库 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 当主库突然发生故障(如:磁盘损坏等),无法正常响应客户端的请求。此时会 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">自动主备切换</code>,进入 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">场景二</code> </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 客户端读写,访问的是备库(此时备库升级为新主库) </section></li> </ul> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;line-height: 1.6;word-break: break-word;text-align: left;font-size: 15px;letter-spacing: 0.05em;color: rgb(89, 89, 89);font-family: Roboto, Oxygen, Ubuntu, Cantarell, PingFangSC-light, PingFangTC-light, "Open Sans", "Helvetica Neue", sans-serif;"> <br> </section> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.8045454545454546" data-s="300,640" src="/upload/aa43bfb2c9437032acf66c6ce4d43016.jpg" data-type="jpeg" data-w="220" style=""></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;line-height: 1.6;word-break: break-word;text-align: left;font-size: 15px;letter-spacing: 0.05em;color: rgb(89, 89, 89);font-family: Roboto, Oxygen, Ubuntu, Cantarell, PingFangSC-light, PingFangTC-light, "Open Sans", "Helvetica Neue", sans-serif;"> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">看似天衣无缝,那是不是可以高枕无忧了呢???兄弟,想多了</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">主备切换,确实能满足高可用。但有个前提,主备库的数据要同步。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">不过,<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">数据同步</code>是个异步操作,不可能做到实时,所以说<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">主备延迟</code>是一定存在的</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(53, 179, 120);">二、什么是主备延迟?<span style="display: none;"></span></h3> </section> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.50703125" data-s="300,640" src="/upload/c103f9703b9fa300d9ca112a72559e50.jpg" data-type="jpeg" data-w="1280" style=""></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;line-height: 1.6;word-break: break-word;text-align: left;font-size: 15px;letter-spacing: 0.05em;color: rgb(89, 89, 89);font-family: Roboto, Oxygen, Ubuntu, Cantarell, PingFangSC-light, PingFangTC-light, "Open Sans", "Helvetica Neue", sans-serif;"> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 主库完成一个事务,写入binlog。binlog 中有一个时间字段,用于记录主库写入的时间【时刻 t1】 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> binlog 同步给备库,备库接收并存储到中继日志 【时刻 t2】 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 备库SQL执行线程执行binlog,数据写入到备库表中 【时刻 t3】 </section></li> </ul> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">主备延迟时间计算公式:</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;font-size: 0.9em;overflow: auto;padding: 10px 10px 10px 20px;margin: 10px 5px;border-left-color: rgb(53, 179, 120);border-right: 0px solid rgb(53, 179, 120);color: rgb(97, 97, 97);quotes: none;background: rgb(251, 249, 253);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">t3 - t1</p> </blockquote> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">有没有简单命令,直接查看。在备库执行 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">show slave status</code> 命令</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;font-size: 0.9em;overflow: auto;padding: 10px 10px 10px 20px;margin: 10px 5px;border-left-color: rgb(53, 179, 120);border-right: 0px solid rgb(53, 179, 120);color: rgb(97, 97, 97);quotes: none;background: rgb(251, 249, 253);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">seconds_behind_master,表示当前备库延迟了多少秒</p> </blockquote> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">心细的同学会有疑问了, t3 和 t1 分属于两台机器,如果时钟不一致怎么办?</strong></p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">初始化时,备库连接到主库,会执行 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">SELECT UNIX_TIMESTAMP()</code> 来获得当前主库的系统时间。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">如果发现主库的系统时间与备库不一致,备库在计算 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">seconds_behind_master</code> 会自动减掉这个差值。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">注意:</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;font-size: 0.9em;overflow: auto;padding: 10px 10px 10px 20px;margin: 10px 5px;border-left-color: rgb(53, 179, 120);border-right: 0px solid rgb(53, 179, 120);color: rgb(97, 97, 97);quotes: none;background: rgb(251, 249, 253);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">binlog 数据传输的时间(t2 - t1)非常短,可以忽略。主要延迟花费在备库执行binlog日志</p> </blockquote> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(53, 179, 120);">三、<code>主备延迟</code>常见原因<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">1、备库机器配置差</strong></p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">这个不难理解,“门当户对”、“志同道合”,如果主备机器的性能差别大,直接导致备库的同步速度跟不上主库的生产节奏。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">就像跑步一样,落后差距会越来越大。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">解决方案:</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">1、升级备库的机器配置</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">2、备库干私活</strong></p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">备库除了服务于正常的<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">读业务</code>外,是否有被其他特殊业务征用,如:运营数据统计等,这类操作非常消耗系统资源,也会影响数据同步速度。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">解决方案:</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">可以借助大数据平台,数据异构,满足各种这些特殊的统计类查询。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">3、大事务</strong></p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">我们知道 binglog 是在事务提交时才生成的。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">如果是处理大事务,执行时间比较长(比如 5分钟)。虽然备库很快拿到 binlog,但是在<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">备库回放执行也要花费差不多的时间</code>,也要 5分钟 (备库中,只有这个事务执行完提交,备库才真正对外可见),从而导致主备延迟很大。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">比如 delete 操作,慎用 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">delete from 表名</code>,建议采用分批删除,减少大事务。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(53, 179, 120);">四、主库不可用,主备切换有哪些策略?<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">1、可靠优先</strong></p> </section> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.6702586206896551" data-s="300,640" src="/upload/b3b4fed9a5d82fac4d61b62128e8bb9d.jpg" data-type="jpeg" data-w="928" style=""></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;line-height: 1.6;word-break: break-word;text-align: left;font-size: 15px;letter-spacing: 0.05em;color: rgb(89, 89, 89);font-family: Roboto, Oxygen, Ubuntu, Cantarell, PingFangSC-light, PingFangTC-light, "Open Sans", "Helvetica Neue", sans-serif;"> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">当主库A 发生故障不可用时,开始进入主备切换</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 首先,判断 B库 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">seconds_behind_master</code> 是否小于设定的阈值(比如 4 秒),如果满足条件 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 将 A库 改为只读状态,将 readonly 设置为 true。断掉 A 库的写入操作,保证不会有新的写流量进来 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 判断 B库的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">seconds_behind_master</code> ,直到为 0 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 修改 B库 为 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">读、写状态</code> </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 客户端的请求打到 B库 </section></li> </ul> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">此时,主备切换完成。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">优点:</strong></p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">数据不会丢失,所以我们称为可靠性高</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">缺点:</strong></p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">中间有个阶段,A库和B库都是只读状态,此时系统对外不能提供写服务。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">2、可用优先</strong></p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">当然我们也可以不用等主备数据同步完成,在一开始时就直接将流量切到备库。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">这样备库的流量就可能有两个来源:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 主库之前的剩余流量 binlog </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 客户端新请求进来的流量 </section></li> </ul> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">两部分流量冲击,会对 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">数据一致性</code> 造成一些影响。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">我们来做个实验:</strong></p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">首次创建一个用户表:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/UsichrXlnR9IzGNtE8C8ouxERnbaLQ6r1D0eopFLkBI45mZ1EcyRGGhXpfZF8cvqDSda5qFpnCGDDHFMpGXerWDSBCUnvvjoB/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">CREATE TABLE `person` (<br> `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,<br> `name` varchar(32) ,<br> PRIMARY KEY (`id`)<br>) ENGINE=InnoDB;<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">插入2条记录</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/UsichrXlnR9IzGNtE8C8ouxERnbaLQ6r1D0eopFLkBI45mZ1EcyRGGhXpfZF8cvqDSda5qFpnCGDDHFMpGXerWDSBCUnvvjoB/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">insert into person(name) values (<span style="color: #a6e22e;line-height: 26px;">"tom"</span>);<br>insert into person(name) values (<span style="color: #a6e22e;line-height: 26px;">"jerry"</span>);<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">实验一:</strong></p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">将 binlog 的格式设为 binlog_format=row</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;font-size: 0.9em;overflow: auto;padding: 10px 10px 10px 20px;margin: 10px 5px;border-left-color: rgb(53, 179, 120);border-right: 0px solid rgb(53, 179, 120);color: rgb(97, 97, 97);quotes: none;background: rgb(251, 249, 253);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">说明:row 模式,写 binlog 时会记录所有字段的值</p> </blockquote> </section> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.596875" data-s="300,640" src="/upload/1ae6ddaf98aedd862bd672387d533fdf.jpg" data-type="jpeg" data-w="1280" style=""></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;line-height: 1.6;word-break: break-word;text-align: left;font-size: 15px;letter-spacing: 0.05em;color: rgb(89, 89, 89);font-family: Roboto, Oxygen, Ubuntu, Cantarell, PingFangSC-light, PingFangTC-light, "Open Sans", "Helvetica Neue", sans-serif;"> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">库A 、库B 在做数据同步时,都会报主键冲突,最后只有一行数据不一致,但是会丢数据。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">优点:同步过程中,出现问题能够及时发现。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">实验二:</strong></p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">将 binlog 格式设置为 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">statement</code> 或者 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">mixed</code></p> </section> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.596875" data-s="300,640" src="/upload/229e19fb1444e9e620bb665e128db1ad.jpg" data-type="jpeg" data-w="1280" style=""></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;line-height: 1.6;word-break: break-word;text-align: left;font-size: 15px;letter-spacing: 0.05em;color: rgb(89, 89, 89);font-family: Roboto, Oxygen, Ubuntu, Cantarell, PingFangSC-light, PingFangTC-light, "Open Sans", "Helvetica Neue", sans-serif;"> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">按照 SQL 原始语句同步 binlog,可以看到,数据条数不会少,但是主键id会出现混乱。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(53, 179, 120);">3、结论</strong></p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">本着 "攘外必先安内" ,保证内部的数据的正确性是我们的首选。所以,一般建议大家选择 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">可靠优先</code>。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">但是可靠优先可能会导致一定时间内,数据库不可用。这个时间值取决于主备延迟的时间大小。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">所以,我们应尽可能缩短主备库的延迟时间大小,这样一旦主库发生故障,备库才会更快的同步完数据,主备切换才能完成,服务才能更快恢复。</p> </section>
作者:微信小助手
<section style="font-size: 15px;line-height: 1.9;letter-spacing: 0.75px;box-sizing: border-box;"> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <blockquote class="js_blockquote_wrap" data-type="2" data-url="" data-author-name="" data-content-utf8-length="33" data-source-title=""> <section class="js_blockquote_digest"> <section> 作者:vivo流程IT团队-Ou Erli、Xiong Huanxin </section> </section> </blockquote> <p><br></p> </section> <section style="margin-top: 10px;margin-bottom: 10px;text-align: center;box-sizing: border-box;" powered-by="xiumi.us"> <section style="padding: 3px;display: inline-block;border-bottom: 1px solid rgb(65, 94, 255);font-size: 17px;color: rgb(65, 94, 255);box-sizing: border-box;"> <p style="box-sizing: border-box;">一、方案背景</p> </section> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">RocketMQ(以下简称MQ)作为消息中间件在事务管理,异步解耦,削峰填谷,数据同步等应用场景中有着广泛使用。当业务系统进行灰度发布时,Dubbo与HTTP的调用可以基于业界通用的灰度方式在我们的微服务治理与网关平台来实现,但MQ已有的灰度方案都不能完全解决消息的隔离与切换衔接问题,为此,我们在鲁班MQ平台(包含根因分析、资源管理、订阅关系校验、延时优化等等的扩展)增加了MQ灰度功能的扩展实现。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="margin-top: 10px;margin-bottom: 10px;text-align: center;box-sizing: border-box;" powered-by="xiumi.us"> <section style="padding: 3px;display: inline-block;border-bottom: 1px solid rgb(65, 94, 255);font-size: 17px;color: rgb(65, 94, 255);box-sizing: border-box;"> <p style="box-sizing: border-box;">二、RocketMQ技术特点</p> </section> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">为什么MQ的灰度方案迟迟没有实现呢?我们先来回顾一下RocketMQ的几个核心技术点。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">2.1 存储模型的简述</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="330" data-backw="578" data-ratio="0.5703125" data-s="300,640" src="/upload/8958453b433f0e33a354c17749fb1a4b.png" data-type="png" data-w="1280" style="width: 100%;height: auto;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(图2.1 MQ的存储模型)</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">CommitLog</strong>:消息体实际存储的地方,当我们发送的任一业务消息的时候,它最终会存储在commitLog上。MQ在Broker进行集群部署(这里为也简洁,不涉及主从部分)时,同一业务消息只会落到集群的某一个Broker节点上。而这个Broker上的commitLog就会存储所有Topic路由到它的消息,当消息数据量到达1个G后会重新生成一个新的commitLog。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">Topic</strong>:消息主题,表示一类消息的逻辑集合。每条消息只属于一个Topic,Topic中包含多条消息,是MQ进行消息发送订阅的基本单位。属于一级消息类型,偏重于业务逻辑设计。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">Tag</strong>:消息标签,二级消息类型,每一个具体的消息都可以选择性地附带一个Tag,用于区分同一个Topic中的消息类型,例如订单Topic, 可以使用Tag=tel来区分手机订单,使用Tag=iot来表示智能设备。在生产者发送消息时,可以给这个消息指定一个具体的Tag, 在消费方可以从Broker中订阅获取感兴趣的Tag,而不是全部消息(注:严谨的拉取过程,并不全是在Broker端过滤,也有可能部分在消费方过滤,在这里不展开描述)。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">Queue</strong>:实际上Topic更像是一个逻辑概念供我们使用,在源码层级看,Topic以Queue的形式分布在多个Broker上,一个topic往往包含多条Queue(注:全局顺序消息的Topic只有一条Queue,所以才能保证全局的顺序性),Queue与commitLog存在映射关系。可以理解为消息的索引,且只有通过指定Topic的具体某个Queue,才能找到消息。(注:熟悉kafka的同学可以类比partition)。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">消费组及其ID</strong>:表示一类Producer或Consumer,这类Producer或Consumer通常生产或消费同应用域的消息,且消息生产与消费的逻辑一致。每个消费组可以定义全局维一的GroupID来标识,由它来代表消费组。不同的消费组在消费时互相隔离,不会影响彼此的消费位点计算。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">2.2 消息发送与消费</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="270" data-backw="578" data-ratio="0.4677734375" data-s="300,640" src="/upload/7d8b37d7d049379e2158fe10f7f0dd1c.png" data-type="png" data-w="1024" style="width: 100%;height: auto;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(图2.2 消息发送与拉取模型)</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">2.2.1 客户端标识</strong></p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">在生产者或消费者集群中,每一个MQ客户端的运行实例,在MQ的客户端会保证产生唯一的ClientID。注:同一应用实例中,既充当生产者,也充当消费者时,其ClientID实际上是同一个取值。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">2.2.2 消息发送</strong></p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">当向某个Topic发送消息的时候,会首先获得这个Topic的元数据,元数据会包括它有多少个Queue,每个Queue属于哪个Broker。默认的情况下,发送方会选择一条Queue发送当前消息,算法类型是轮询,也就是下一条消息会选择另一条Queue进行发送。另外MQ也提供了指定某条Queue或者自定义选择Queue的方法进行消息的发送,自定义选择Queue需实现MessageQueueSelector接口。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">2.2.3 消息消费</strong></p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">进行消息的消费时,同一消费组(GroupID)的消费者会订阅Topic,消费者首先获得Topic的元数据,也就是会获得这个Topic的所有Queue信息。然后这些Queue按规则分配给各个具体的客户端(ClientID),各个客户端根据分配到的Queue计算对应的需要拉取消息的offset并生成PullRequest,拉取消息并消费完成后,客户端会生成ACK并更新消费进度。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">这里的消费进度是该批消息未消费成功的最小offset,如图2.3所示,一批消息中如果1、5未消费,其余的消息已消费,此时更新的offset仍是1,消费者如果宕机重启,会从1号开始消费消息,此时2、3、4号消息将会重复消费。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="204" data-backw="500" data-ratio="0.408" data-s="300,640" src="/upload/761f280cc54d3dc0292f7f112dcdd2f9.png" data-type="png" data-w="500" style="width: 100%;height: auto;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(图2.3 消费进度更新示意图)</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">因此RocketMQ只保证了消息不会丢失,无法保证消息不会重复消费,消息的幂等性需要业务自己实现。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">另外,消费方可以指定消费某些Tag的消息,在pullRequest进行拉取时,会在Broker里会按照存储模型的Queue索引信息按hash值进行快速过滤,返回到消费方,消费方也会对返回的消息进行精准的过滤。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">2.3 订阅关系一致性</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">在消费端,同一消费组内(GroupID相同,本节所描述的前提都是同一消费组内)的各个应用实例MQ客户端需要保持订阅关系的一致性,所谓订阅关系的一致性就是同一消费组内的所有客户端所订阅的Topic和Tag必须完全一致,如果组内订阅关系不一致,消息消费的逻辑就会混乱,甚至导致消息丢失。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">2.3.1 订阅关系的维护</strong></p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">每一个应用实例MQ客户端都有独立的ClientID,我们简单地讲解一下订阅关系的维护:</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;">各个MQ消费客户端会带着它的ClientID向所有Broker发送心跳包,心跳包中含有具体的本客户端订阅的Topic & Tag等信息,registerConsumer方法会按照消费组将客户端分组存储,同一消费组内的ClientID信息在同一个ConsumerGroupInfo中;</p><p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p></li> <li><p style="white-space: normal;box-sizing: border-box;">Broker在接收到任何消费客户端发过来的心跳包后,会在ConsumerManager类中的ConcurrentMapconsumerTable按照消费组名称GroupID作为key存储不同的消费组信息,同一消费组的订阅信息会在每一次收到心跳包后,都会按当前的订阅Topic & Tag信息进行更新维护,也就是相当于只保存最新的心跳包订阅信息(心跳包中的subVersion会标记心跳包版本,当重平衡结果发生改变后,subVersion会更新,Broker只会保存最新版本的心跳包中的订阅信息),不管这个心跳包来源于这个消费组的哪个ClientID。(由于Tag是依赖于Topic的属性,Topic和Tag订阅关系不一致时Broker对应的处理结果也略有不同,具体可见updateSubscription方法)。</p></li> </ul> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><strong style="box-sizing: border-box;">2.3.2 订阅不一致影响</strong></p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">在这里使用例子的方式来阐述订阅关系不一致带来的部分问题。假设同一组内的消费者clientA订阅了TOPIC_A,clientB订阅了TOPIC_B;TOPIC_A、TOPIC_B均为4条Queue,在进行消费分配时,最终Queue的分配结果如下:</p> <p style="white-space: normal;box-sizing: border-box;"><br></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.3020304568527919" data-s="300,640" src="/upload/b845611b37918caca514fb3182371dd6.png" data-type="png" data-w="394" style=""></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(表2.1 消息费者的Queue分配结果表)</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">因为clientB没有订阅TOPIC_A,clientA也没有订阅TOPIC_B,所以TOPIA_A中的Queue-2、Queue-3,TOPIC_B中的Queue-0、Queue-1中的消息则无法正常消费。而且在实际过程中,clientA也没法正常消费TOPIC_B的消息,这时会在客户端看到很多异常,有些Queue的消息得不到消费产生堆积。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">此外,在订阅关系中,不同client所订阅的Tag不一样时,也会发生拉取的消息与所需要订阅的消息不匹配的问题。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="margin-top: 10px;margin-bottom: 10px;text-align: center;box-sizing: border-box;" powered-by="xiumi.us"> <section style="padding: 3px;display: inline-block;border-bottom: 1px solid rgb(65, 94, 255);font-size: 17px;color: rgb(65, 94, 255);box-sizing: border-box;"> <p style="box-sizing: border-box;">三、业界MQ灰度方案</p> </section> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="170" data-backw="578" data-ratio="0.29443838604143946" data-s="300,640" src="/upload/51a5d4b3940dce892c0bbf1b492245e4.png" data-type="png" data-w="917" style="width: 100%;height: auto;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(图3.1 灰度调用示意图)<br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">通常,业务灰度只严格地保证RPC服务之间的调用,部分消息灰度流量的流失或错误是可以容忍的,如图3-1所示,V_BFF产生的灰度消息会被V_TRADE的正常版本与灰度版本收到并随机消费,导致部分灰度流量没有进入期望的环境,但整体RPC服务的调用还是隔离了灰度与非灰度环境。当业务对消息消费的逻辑进行了更改,或者不想让灰度的消息影响线上的数据时,MQ的灰度就必须要实现。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">由于订阅关系的限制,当前的业界实现的MQ灰度方案都是正常版本与灰度版本使用不同的GroupID来实现。以下的方案均使用了不同的GroupID。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">3.1 影子Topic的方案</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">新建一系列新的Topic来处理隔离灰度的消息。例如对于TOPIC_ORDER会创建TOPIC_ORDER_GRAY来给灰度环境使用。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">发送方在发送时,对灰度的消息写入到影子Topic中。消费时,灰度环境只使用灰度的分组订阅灰度的Topic。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">3.2 Tag的方案</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">发送方在发送时,对灰度环境产生的消息的Tag加注灰度标识。消费方,每次灰度版本发布时只订阅灰度Tag的消息,正常的版本订阅非灰度的Tag。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">3.3 UserProperty的方案</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">发送方在发送时,对灰度环境产生的消息的UserProperty加注灰度标识。消费方的客户端需要进行改写,根据UserProperty来过滤,正常的版本会跳过这类消息,灰度的版本会处理灰度消息。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">3.4 当前的方案缺陷</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">以上三种方案各自的优势在这里不做比较,但都存在以下共同的缺陷(也有其它的缺陷或开发诉求,但不致命),无法真正实现灰度状态切换回正常状态时消息不丢失处理,导致整个灰度方案都是从入门到放弃的过程:</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;">因为使用不同的消费组,那么灰度版本验证通过后,如何正确地衔接回原正常版本的消费组的消费位移,做到高效地不丢失信息处理呢?</p></li> <li><p style="white-space: normal;box-sizing: border-box;">灰度的消息如何确保准确地消费完毕,做到落在灰度标识的消息做到高效地不丢失信息处理呢?</p></li> <li> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">开启灰度时,灰度消息的位点从那里开始?状态的细节化如何管控?</p> </section></li> </ul> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="margin-top: 10px;margin-bottom: 10px;text-align: center;box-sizing: border-box;" powered-by="xiumi.us"> <section style="padding: 3px;display: inline-block;border-bottom: 1px solid rgb(65, 94, 255);font-size: 17px;color: rgb(65, 94, 255);box-sizing: border-box;"> <p style="box-sizing: border-box;">四、鲁班MQ平台的灰度方案</p> </section> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">本质上,MQ灰度问题的核心就是高效地将灰度与非灰度的消息隔离开,消费方按照自己的需求来准确获取到对应版本的消息;当灰度完成后,能够正确地拼接回来消息的位移,做到不丢失处理必要的消息,也就是状态细节上的管理。为了实现这个目的,本方案分别在以下几点进行了改造。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">本方案中涉及到的代码为测试代码,主要用于说明方案,实际代码会更精细处理。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">4.1 Queue的隔离使用</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="193" data-backw="578" data-ratio="0.3333333333333333" data-s="300,640" src="/upload/6b95f6a5fd4d0ab220a4eda4f4de231f.png" data-type="png" data-w="1041" style="width: 100%;height: auto;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(图4.1 Queue的区分使用)</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">我们已经知道了Queue是topic的实际执行单元,我们的思路就是<strong>借助Queue来实现v1(正常)消息、v2(灰度)消息的区分</strong>,我们可以固定首尾两条【可配置】Queue专门用来发送与接收灰度的消息,其余的Queue用来发送正常的线上消息。我们使用<strong>相同的消费组</strong>(也就是和业界的通用方案不一样,我们会使用相同的GroupID),让灰度消费者参与灰度Queue的重平衡,非灰度消费者参与非灰度Queue的重平衡。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;"><strong>这里我们解决了消息的存储隔离问题。</strong></p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><span style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;">4.2 Broker订阅关系改造</span></p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">灰度版本往往需要变更Topic或Tag,由于我们没有新增独立的灰度消费组,当灰度版本变更Topic/Tag时,消费组内订阅关系就会不一致,前文也简单解释了订阅关系一致性的原理,我们需要在Broker做出对应的改造,来兼容灰度与非灰度订阅关系不一致的情况。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">同一消费组的订阅信息会在维护在ConsumerGroupInfo的subscriptionTable中,可以在ConsumerGroupInfo中增加<strong>创建一份graySubscriptionTable用来存储灰度版本的订阅信息</strong>,客户端向Broker发送的<strong>心跳包会改造成带有自身的灰度标记grayFlag</strong>,根据<strong>灰度标记grayFlag</strong>来选择订阅关系存储在subscriptionTable还是graySubscriptionTable;在拉取消息时,同样向Broker传来grayFlag来选择从subscriptionTable还是graySubscriptionTable中获取对应的订阅信息。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;"><strong>这里我们解决了消费订阅一致性问题。</strong></p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><span style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;">4.3 Producer的改造</span></p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">发送方的改造相对简洁,只需确定发送的消息是否为灰度消息,通过实现MessageQueueSelector接口,将灰度消息投递到指定数量的灰度Queue即可。这里我们把用于灰度的grayQueueSize定义到配置中心中去,当前更多是约定使用Broker的指定Queue号作为灰度使用。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">TOPIC_V_ORDER共有6条Queue,如图4.2所示,灰度消息只会发送至首尾的0号与5号Queue,非灰度消息则会选择其余的4条Queue发送消息。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="215" data-backw="578" data-ratio="0.3724364232977851" data-s="300,640" src="/upload/8f8b0a834a891e2d57e1beb1aa95a332.png" data-type="png" data-w="1219" style="width: 100%;height: auto;"></p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="209" data-backw="578" data-ratio="0.3621533442088091" data-s="300,640" src="/upload/580391129ecdd459473b20cf63728def.png" data-type="png" data-w="1226" style="width: 100%;height: auto;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(图4.2 发送结果)</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><strong>这里我们解决了生产者正确投递的问题。</strong></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">4.4 Consumer的改造</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">消费方涉及的改造点主要是灰度Queue与非灰度Queue的重平衡分配策略,与各个客户端灰度标记grayFlag的更新与同步。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">灰度重平衡策略的核心就是<strong>分类处理灰度和非灰度的Queue</strong>,要将灰度的Queue分配至灰度ClientID,将非灰度的Queue分配至非灰度的ClientID,因此,在重平衡之前,会通过Namesrv获取同组内的所有客户端clientId对应最新的grayFlag(也就是状态会记录到Namesrv)。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">当灰度版本需要变更为线上版本时,各客户端会同步grayFlag到Namesrv,同时,为了避免灰度消息还未消费完成,在更新grayFlag之前会先判断灰度Queue中是否存在未消费的消息,在保证灰度消息消费完成后才会进行grayFlag的更新。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">消费者需使用AllocateMessageQueueGray作为重平衡策略,传入灰度Queue的数量,灰度消费者setGrayFlag为true,可以看出只消费了首尾的0号与5号Queue的消息,非灰度消费者setGrayFlag为false,可以看出只会消费中间的4条Queue的消息,在控制台也可以非常清晰的看到Queue的分配结果,grayFlag为true的v2客户端分配到了首尾的Queue,grayFlag为false的v1客户端则分配到了中间的4条Queue。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="359" data-backw="578" data-ratio="0.6203125" data-s="300,640" src="/upload/5b70676edd30d6f5747557e0736a82d3.png" data-type="png" data-w="1280" style="width: 100%;height: auto;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="366" data-backw="578" data-ratio="0.6335204490777867" data-s="300,640" src="/upload/c69019853269a1662a3f35ab7b2b476b.png" data-type="png" data-w="1247" style="width: 100%;height: auto;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="282" data-backw="578" data-ratio="0.4873853211009174" data-s="300,640" src="/upload/26826b0cfb8ee4a6de296b8cacbbe39e.png" data-type="png" data-w="872" style="width: 100%;height: auto;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(图4.3 消费与订阅结果)</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">当灰度版本需要切换至线上版本时,只需调用updateClientGrayFlag来更新状态即可,可以看出在调用updateClientGrayFlag后,原先v2的两个灰度客户端在消费完灰度Queue的消息后,grayFlag才真正变为false【状态在namesrv保存】,加入到中间的4条非灰度Queue的重平衡中,原先首尾的2条灰度Queue则没有消费者订阅。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="301" data-backw="578" data-ratio="0.5208503679476697" data-s="300,640" src="/upload/c0b7dfb34207470ec47e909e6d0b1d83.png" data-type="png" data-w="1223" style="width: 100%;height: auto;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="274" data-backw="578" data-ratio="0.473928157589803" data-s="300,640" src="/upload/426315b92990dd24eb9ba93d52f459d5.png" data-type="png" data-w="863" style="width: 100%;height: auto;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="277" data-backw="578" data-ratio="0.4797687861271676" data-s="300,640" src="/upload/982e2564d39b74ccad4fc4a8ec129318.png" data-type="png" data-w="865" style="width: 100%;height: auto;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(图4.4 grayFlag更新)</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><strong>这里我们解决了状态切换的细节控制处理问题。</strong></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">4.5 Namesrv的改造</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">前文提到过,消费者在重平衡时是需要获取组内所有客户端的灰度标识grayFlag,因此,我们需要一个地方来持久化存储这些grayFlag,这个地方是每个消费者都可以访问的,我们选择将这些信息存储在Namesrv。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;">Namesrv相对比较轻量,稳定性很好;</p></li> <li><p style="white-space: normal;box-sizing: border-box;">消费者本身就会与Namesrv建立长连接,如果该namesrv挂掉,消费者会自动连接下一个Namesrv,直到有可用连接为止;</p></li> <li><p style="white-space: normal;box-sizing: border-box;">Broker是实际存储消息的地方,自身运行压力就相对较大,用来做灰度数据的同步一定程度上会加大Broker的压力。</p></li> </ul> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">但是Namesrv本身是无状态的节点,节点之间是不会进行信息同步的,灰度数据的一致性需要借助数据库来保证,Namesrv共同访问同一套数据库就好了,数据库持久化存储灰度信息,每次更新v1、v2的灰度状态时,通过Namesrv修改数据库的数据,在每次重平衡之前,再通过Namesrv拉取自己消费组内的所有实例的灰度状态即可。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="314" data-backw="578" data-ratio="0.5436893203883495" data-s="300,640" src="/upload/3f060cd34ac1be5f9e9582962cf1573a.png" data-type="png" data-w="1030" style="width: 100%;height: auto;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(图4.5 Namesrv存储灰度数据示意图)</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><strong>这里我们解决了状态存储与同步的问题。</strong></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="margin-top: 10px;margin-bottom: 10px;text-align: center;box-sizing: border-box;" powered-by="xiumi.us"> <section style="padding: 3px;display: inline-block;border-bottom: 1px solid rgb(65, 94, 255);font-size: 17px;color: rgb(65, 94, 255);box-sizing: border-box;"> <p style="box-sizing: border-box;">五、灰度场景的校验</p> </section> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">测试是校验方案可行性的真理,下面用简单的demo来验证鲁班平台的MQ灰度方案。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><span style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;">5.1 灰度版本Topic & Tag不变</span></p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">这种场景在4.3、4.4时已经做了验证,不再赘述。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><span style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;">5.2 灰度版本Topic增加</span></p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">假设v1、v2的订阅信息如表5.1所示,则Topic订阅结果如图5.1所示,TOPIC_V_ORDER被v1、v2同时订阅,首尾两条Queue分配给灰度v2的客户端,中间4条Queue则分配给非灰度v1的客户端;TOPIC_V_PAYMENT只被灰度版本v2订阅,则只会将首尾两条Queue分配给v2的客户端,其余四条Queue不会被客户端订阅。我们向TOPIC_V_ORDER分别发送4条非灰度消息和灰度消息,向TOPIC_V_PAYMENT发送4条灰度消息,从图5.2中可以看出<strong>TOPIC_V_ORDER中的非灰度消息由v1的两个客户端成功消费,TOPIC_V_ORDER与TOPIC_V_PAYMENT的灰度消息则由v2的两个客户端成功消费。</strong></p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.38860103626943004" data-s="300,640" src="/upload/5e59da8df134a1882a1ccb029fc50403.png" data-type="png" data-w="386" style=""></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(表5.1 订阅信息表)</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="565" data-backw="578" data-ratio="0.978110599078341" data-s="300,640" src="/upload/ccba10a289988750ec2307baf009991e.png" data-type="png" data-w="868" style="width: 100%;height: auto;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(图5.1 订阅结果)</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="291" data-backw="578" data-cropselx1="0" data-cropselx2="578" data-cropsely1="0" data-cropsely2="543" data-ratio="0.5028195488721805" data-s="300,640" src="/upload/c61ad0f3afcda5de13dedda910612a33.png" data-type="png" data-w="1064" style="width: 100%;height: auto;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(图5.2 消费结果)</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">5.3 灰度版本Topic减少</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">假设v1、v2的订阅信息如表5.2所示,则Topic订阅结果如图5.3所示,TOPIC_V_ORDER被v1、v2同时订阅,首尾两条Queue分配给灰度v2的客户端,中间4条Queue则分配给非灰度v1的客户端;TOPIC_V_PAYMENT只被非灰度版本v1订阅,则只会将中间的四条Queue分配给v1的客户端,首尾两条Queue不会被客户端订阅。我们向TOPIC_V_ORDER分别发送4条非灰度消息和灰度消息,向TOPIC_V_PAYMENT发送4条非灰度消息,从图5.4中可以看出<strong>TOPIC_V_ORDER与TOPIC_V_PAYMENT的非灰度消息由v1的两个客户端成功消费,TOPIC_V_ORDER中的灰度消息则由v2的两个客户端成功消费</strong>。</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.352112676056338" data-s="300,640" src="/upload/2e1e856c70da1b8488d21049f816b8f5.png" data-type="png" data-w="426" style=""></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(表5.2 订阅信息表)</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="569" data-backw="578" data-ratio="0.9850057670126874" data-s="300,640" src="/upload/a31a8871daae2ff552152713257882f5.png" data-type="png" data-w="867" style="width: 100%;height: auto;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(图5.3 订阅结果)</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="284" data-backw="578" data-cropselx1="0" data-cropselx2="578" data-cropsely1="0" data-cropsely2="532" data-ratio="0.49063670411985016" data-s="300,640" src="/upload/3704ac30dba3bc6b82870da7f44d9eff.png" data-type="png" data-w="1068" style="width: 100%;height: auto;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(图5.4 消费结果)</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><span style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;">5.4 灰度版本Tag变化</span></p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">假设v1、v2的订阅信息如表5.3所示,则Topic订阅结果如图5.5所示,TOPIC_V_ORDER被v1、v2同时订阅,首尾两条Queue分配给灰度v2的客户端,中间4条Queue则分配给非灰度v1的客户端,我们向TOPIC_V_ORDER分别发送4条Tag=v1的非灰度消息和Tag=v2的灰度消息,从图5.6中可以看出Tag为v1的非灰度消息由v1的两个客户端成功消费,<strong>Tag为v2的灰度消息则由v2的两个客户端成功消费</strong>。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-cropselx1="42" data-cropselx2="468" data-cropsely1="0" data-cropsely2="150" data-ratio="0.29338842975206614" data-s="300,640" src="/upload/250ef7ed5822e1f7160caba2e82c86f8.png" data-type="png" data-w="484" style="width: 511px;height: 150px;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(表5.3 订阅信息表)</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="286" data-backw="578" data-ratio="0.49539170506912444" data-s="300,640" src="/upload/6923efd588b7e8b04df32504fe089b20.png" data-type="png" data-w="868" style="width: 100%;height: auto;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(图5.5 订阅结果)<br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="233" data-backw="578" data-cropselx1="0" data-cropselx2="578" data-cropsely1="0" data-cropsely2="423" data-ratio="0.4031805425631431" data-s="300,640" src="/upload/6f890d7e2dab0fb9ef6e7d4bc9d60c12.png" data-type="png" data-w="1069" style="width: 100%;height: auto;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(图5.6 消费结果)</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><span style="font-size: 16px;color: rgb(70, 97, 246);box-sizing: border-box;">5.5 灰度版本Topic & Tag混合变化</span></p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">假设v1、v2的订阅信息如表5.4所示,则Topic订阅结果如图5.7所示,与5.2情况相同不再赘述。我们向TOPIC_V_ORDER分别发送4条Tag=v1的非灰度消息和Tag=v2的灰度消息,向TOPIC_V_PAYMENT发送4条灰度消息,消费结果如图5.8所示,可以看出v2的两个客户端成功消费了TOPIC_V_PAYMENT及TOPIC_V_ORDER中Tag=v2的灰度消息,而v1的两个客户端则只消费了<strong>TOPIC_V_ORDER中Tag=v1的非灰度消息</strong>。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.2271880819366853" data-s="300,640" src="/upload/562da55cbeab5d66fe108a81bd52aafc.png" data-type="png" data-w="537" style=""></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(表5.4 订阅信息表)<br></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="572" data-backw="578" data-ratio="0.9897377423033067" data-s="300,640" src="/upload/6aaffff85f8c9ca3622997a206ff3e7a.png" data-type="png" data-w="877" style="width: 100%;height: auto;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(图5.7 订阅结果)</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-backh="400" data-backw="578" data-cropselx1="0" data-cropselx2="578" data-cropsely1="0" data-cropsely2="400" data-ratio="0.36891385767790263" data-s="300,640" src="/upload/2501683b6f3beaa716aa8ae96a0655a9.png" data-type="png" data-w="1068" style="width: 578px;height: 213px;"></p> <p style="white-space: normal;box-sizing: border-box;"><br></p> <p style="white-space: normal;box-sizing: border-box;text-align: center;">(图5.8 消费结果)</p> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="margin-top: 10px;margin-bottom: 10px;text-align: center;box-sizing: border-box;" powered-by="xiumi.us"> <section style="padding: 3px;display: inline-block;border-bottom: 1px solid rgb(65, 94, 255);font-size: 17px;color: rgb(65, 94, 255);box-sizing: border-box;"> <p style="box-sizing: border-box;">六、结语</p> </section> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;">实际的MQ灰度版本,我们还对MQ的发送与消费方做了统一的封装,业务方只需配置graySwitch、grayFlag即可,graySwtich标记是否需要开启灰度消息,在graySwitch开启的前提下,grayFlag才会生效,用来标记当前客户端是否为灰度客户端。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">在多系统交互时,业务系统可通过开关graySwitch来控制是否全量消费其他系统的灰度与非灰度消息,通过grayFlag来控制是单独消费灰度消息还是非灰度消息。graySwitch、grayFlag参数可放在配置中心做到热生效,当需要切换灰度流量时,可开发相应的脚本统一化更改grayFlag,实现全链路灰度流量的无损切换。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">另外,我们对于切换状态借助Namesrv做了充分细节上的控制,保证在真正执行切换前,未消费完的消息会被消费完毕才真正的执行切换。</p> <p style="white-space: normal;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="white-space: normal;box-sizing: border-box;">在此,也非常感谢阿里开源RocketMQ这个消息中间件!</p> </section> </section>
作者:微信小助手
<p style="background-image: linear-gradient(90deg, rgba(50, 0, 0, 0.05) 3%, rgba(0, 0, 0, 0) 3%), linear-gradient(360deg, rgba(50, 0, 0, 0.05) 3%, rgba(0, 0, 0, 0) 3%);background-size: 20px 20px;background-position: center center;" data-mpa-powered-by="yiban.io"><span style="font-size: 16px;">大家好,我是悟空呀。</span></p> <p style="background-image: linear-gradient(90deg, rgba(50, 0, 0, 0.05) 3%, rgba(0, 0, 0, 0) 3%), linear-gradient(360deg, rgba(50, 0, 0, 0.05) 3%, rgba(0, 0, 0, 0) 3%);background-size: 20px 20px;background-position: center center;"><br></p> <p style="background-image: linear-gradient(90deg, rgba(50, 0, 0, 0.05) 3%, rgba(0, 0, 0, 0) 3%), linear-gradient(360deg, rgba(50, 0, 0, 0.05) 3%, rgba(0, 0, 0, 0) 3%);background-size: 20px 20px;background-position: center center;"><span style="font-size: 16px;">时间轮在 Kafka,Netty 中都用到了,必须学它一波,这篇动图讲解时间轮的,可以快速帮助大家理解~</span></p> <section xmlns="http://www.w3.org/1999/xhtml"> <section style="background-image: linear-gradient(90deg, rgba(50, 0, 0, 0.05) 3%, rgba(0, 0, 0, 0) 3%), linear-gradient(360deg, rgba(50, 0, 0, 0.05) 3%, rgba(0, 0, 0, 0) 3%);background-size: 20px 20px;background-position: center center;"> <section label="" style="border-width: initial;border-color: initial;border-style: none;margin: 1em auto;text-align: left;" donone="shifuMouseDownStyle('shifu_bus_005')"> <section style="color: #b3b2b2;border-left: 0.4em solid #606060;padding: 1em;border-top-color: #606060;border-right-color: #606060;border-bottom-color: #606060;"> <section style="line-height: 1.75em;"> <span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">通</span> <span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">过本文你将了解到时间轮算法思想,层级时间轮,时间轮的升级和降级。</span> <br> </section> </section> <section style="width: 0.38em;overflow: hidden;background-color: #ffffff;margin-top: -3em;float:left;"> <section style="height: 1em;background-color: rgb(96, 96, 96);width: 0.4em;"> <br> </section> <section style="width: 0px;height: 0px;border-width: 0px 0.3em 0.3em 0.4em;border-style: solid;border-color: transparent transparent transparent rgb(96, 96, 96);"> <br> </section> <section style="height: 0.5em;width: 0.4em;opacity: 0.8;margin-top: 0.2em;transform: skewY(-30deg);background-color: rgb(96, 96, 96);"> <br> </section> <section style="height: 0.5em;width: 0.4em;opacity: 0.5;margin-top: 0.2em;transform: skewY(-30deg);background-color: rgb(96, 96, 96);"> <br> </section> <section style="height: 0.5em;width: 0.4em;margin-top: 0.2em;opacity: 0.3;transform: skewY(-30deg);background-color: rgb(96, 96, 96);"> <br> </section> </section> </section> <p><br></p> <p><span style="font-size: 16px;">时间轮,是一种实现延迟功能(定时器)的巧妙算法,在 Netty,Zookeeper,Kafka 等各种框架中,甚至 Linux 内核中都有用到。<br></span></p> <p><span style="font-size: 16px;"><br></span></p> <p><span style="text-align: left;font-size: 16px;">本文将参考Kafka的时间轮作为例子讲解。</span></p> <p><span style="text-align: left;font-size: 16px;"><br></span></p> <section style="text-align: left;white-space: normal;height: 4em;line-height: 4em;border-bottom: 1px solid rgb(96, 96, 96);border-top-color: rgb(96, 96, 96);border-right-color: rgb(96, 96, 96);border-left-color: rgb(96, 96, 96);"> <section style="font-size: 5em;color: rgb(96, 96, 96);font-weight: bold;vertical-align: middle;display: inline-block;"> <p>0</p> </section> <section style="margin-left: 5px;font-size: 1.25em;vertical-align: middle;display: inline-block;"> <p>设计源于生活</p> </section> </section> <section style="padding-top: 1em;padding-bottom: 1em;text-align: left;white-space: normal;line-height: 1.5;"> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;text-align: justify;">开始之前给大家看块宝珀中华年历表。</span></p> </section> <p style="text-align: center;"><img class="rich_pages wxw-img" src="/upload/bdc10602d1a78b331db9e51fc9089134.png" data-cropx1="60" data-cropx2="488" data-cropy1="35" data-cropy2="461" data-ratio="0.9976580796252927" data-s="300,640" src="https://mmbiz.qpic.cn/mmbiz_jpg/rYPAT7RhHkOV6fbKr4vm6LB6O1aLXSGJTIOLgbrdmG81ich3lZZ2iagR8lQ8t4oVvQibbqEk5E8xxAWj8G8GLxVRQ/640?wx_fmt=jpeg" data-type="jpeg" data-w="427" style="width: 428px;height: 426px;"></p> <p style="text-align: center;"><span style="font-size: 16px;">图片来自宝珀官网</span></p> <p style="text-align: center;"><br></p> <section style="line-height: 1.75em;"> <span style="font-size: 16px;">这款手表的表盘融合了中华历法中各种博大精深的计时元素。</span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span style="font-size: 16px;">上方位置的小表盘显示时辰数字及字符,24小时一周期;年份视窗显示当年所属生肖,12年一周期;</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 16px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 16px;"><span style="font-size: 16px;">左边位置显示农历月,12个月一周期; 农历日, 30天</span><span style="font-size: 16px;">一周期;</span></span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span style="font-size: 16px;">右边位置显示五行元素和十天干,10年一周期;</span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span style="font-size: 16px;">下方的表盘显示月相盈亏。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 16px;"><br></span> </section> <p><span style="font-size: 16px;">至于价格.....这个话题略过。</span></p> <p><span style="font-size: 16px;"><br></span></p> <p><span style="font-size: 16px;">而时间轮,其设计正是来源于生活中的时钟。</span></p> <p><br></p> <section label="Copyright © 2015 playhudong All Rights Reserved." style="border-width: initial;border-color: initial;border-style: none;margin: 1em auto;text-align: left;" donone="shifuMouseDownPayStyle('shifu_pay_003')"> <section style="height: 4em;line-height: 4em;border-bottom: 1px solid #606060;border-top-color: #606060;border-right-color: #606060;border-left-color: #606060;"> <section style="font-size: 5em;color: #606060;font-weight: bold;vertical-align: middle;display: inline-block;"> <p>1</p> </section> <section style="font-size: 1.25em;vertical-align: middle;margin-left: 5px;display: inline-block;"> <p>时间轮</p> </section> </section> <section style="padding-top: 1em;padding-bottom: 1em;line-height: 1.5;"> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">如图就是一个简单的时间轮:</span><br></p> <p style="font-size: 1em;line-height: 1.75em;"><br></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg wxw-img" data-ratio="0.9196891191709845" data-s="300,640" src="/upload/3ecdb3b5a6be2d82d004bbd8371daafd.png" data-type="gif" data-w="772" style="width: 359px;height: 330px;"></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;text-align: left;">图中大圆的圆心位置表示的是当前的时间,随着时间推移, 圆心处的时间也会不断跳动。</span></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;"><br></span></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">下面<span style="text-align: left;">我们</span>对着这个图,来说说Kafka的时间轮TimingWheel。</span></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;"><br></span></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;"><span style="text-align: left;">Kafka</span>时间轮的底层就是一个环形数组,而数组中每个元素都存放一个双向链表TimerTaskList,链表中封装了很多延时任务。</span></p> <p style="font-size: 1em;line-height: 1.75em;"><br></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="text-align: left;font-size: 16px;">Kafka中一个时间轮TimingWheel是由20个时间格组成,wheelSize = 20;每格的时间跨度是1ms,tickMs = 1ms。</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;">参照Kafka,上图中也用了20个灰边小圆表示时间格,为了动画演示可以看得清楚,我们这里每个小圆的时间跨度是1s。</span></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;"><br></span></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;text-align: left;">所以现在整个时间轮的时间跨度就是 tickMs * wheelSize ,也就是 20s。<span style="text-align: left;">从0s到19s,我们都分别有一个<span style="text-align: left;">灰边</span>小圆来承载。</span></span></p> <p style="font-size: 1em;line-height: 1.75em;"><br></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="text-align: left;font-size: 16px;"><span style="text-align: left;">Kafka</span>的时间轮还有一个表盘指针 currentTime,表示时间轮当前所处的时间。</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">也就是图中用黑色粗线表示的圆,随着时间推移, 这个指针也会不断前进;</span></p> <p style="font-size: 1em;text-align: center;"><img class="rich_pages wxw-img" data-ratio="1.1" data-s="300,640" src="/upload/51ad985e051f3b15d63ef590b195eb94.png" data-type="png" data-w="100" style="height: 51px;width: 46px;"></p> <p style="font-size: 1em;line-height: 1.75em;"><br></p> <p style="font-size: 1em;line-height: 1.75em;"><br></p> <section style="text-align: left;white-space: normal;height: 4em;line-height: 4em;border-bottom: 1px solid rgb(96, 96, 96);border-top-color: rgb(96, 96, 96);border-right-color: rgb(96, 96, 96);border-left-color: rgb(96, 96, 96);"> <section style="font-size: 5em;color: rgb(96, 96, 96);font-weight: bold;vertical-align: middle;display: inline-block;"> <p><br></p> </section> <section style="margin-left: 5px;font-size: 1.25em;vertical-align: middle;display: inline-block;"> <p>添加定时任务</p> </section> </section> <section style="padding-top: 1em;padding-bottom: 1em;text-align: left;white-space: normal;line-height: 1.5;"> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-size: 16px;">有了时间轮,现在可以往里面添加定时任务了。我们用一个粉红色的小圆来表示一个定时任务。</span></p> <p style="text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.9782608695652174" data-s="300,640" src="/upload/1d91a81d5667c402c2f62fa0eff72eae.png" data-type="png" data-w="92" style="width: 49px;height: 48px;"></p> <p style="font-size: 1em;line-height: 1.75em;"><br></p> <section style="font-size: 1em;line-height: 1.75em;"> <span style="font-size: 16px;">这里先讲一下设定,每一个定时任务都有延时时间<strong>delayTime</strong>,和过期时间<strong>ExpiredTime</strong>。</span> </section> </section> <section style="font-size: 1em;line-height: 1.75em;"> <span style="font-size: 16px;">比如当前时间是10s,我们添加了个延时时间为2s的任务,那么这个任务的过期时间就是12s,也就是当前时间10s再走两秒,变成了12s的时候,就到了触发这个定时任务的时间。</span> </section> <section style="font-size: 1em;line-height: 1.75em;"> <br> </section> <section style="font-size: 1em;line-height: 1.75em;"> <span style="font-size: 16px;">而时间轮上代表时间格的灰边小圆上显示的数字,可以理解为任务的过期时间。<br></span> </section> <p style="text-align: center;"><img class="rich_pages" data-ratio="1" data-s="300,640" src="/upload/1ff3398c1dc067642faa774dcb472801.png" data-type="png" data-w="88" style="width: 49px;height: 49px;"></p> <p style="font-size: 1em;line-height: 1.75em;"><br></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;">讲清楚这些设定后,我们就开始添加定时任务吧。</span></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;"><br></span></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;"><span style="text-align: left;">初</span>始的时候, 时间轮的指针定格在0。</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;">此时添加一个超时时间为2s的任务, 那么这个任务将会插入到第二个时间格中。</span></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg wxw-img" data-ratio="0.9196891191709845" data-s="300,640" src="/upload/acfd1fa54ce809c8ab43253653d0a0d5.png" data-type="gif" data-w="772" style="width: 333px;height: 306px;"></p> <p style="font-size: 1em;line-height: 1.75em;"><br></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-size: 16px;text-align: left;"><br></span></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-size: 16px;text-align: left;">当时间轮的指针到达<span style="text-align: left;">第二个时间格</span>时, 会处理该时间格上对应的任务。</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">在动画上就是让红色的小圆消失!</span></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg wxw-img" data-ratio="0.9196891191709845" data-s="300,640" src="/upload/598b34649422af1de1a720e95d37a9b3.png" data-type="gif" data-w="772" style="width: 335px;height: 308px;"></p> <p style="font-size: 1em;line-height: 1.75em;"><br></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-size: 16px;text-align: left;">如果这个时候又插入一个延时时间为8s的任务进来, 这个任务的过期时间就是在当前时间2s的基础上加8s, 也就是10s, <span style="text-align: left;">那么这个任务将会插入到过期时间为10s的时间格中。</span></span></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg wxw-img" data-ratio="0.9196891191709845" data-s="300,640" src="/upload/7923e92df0f0343871820371a37fac2a.png" data-type="gif" data-w="772" style="width: 334px;height: 307px;"></p> <section style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"> <span style="font-size: 16px;"><br></span> </section> <p><br></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-size: 16px;text-align: left;"><br></span></p> <section style="text-align: left;white-space: normal;height: 4em;line-height: 4em;border-bottom: 1px solid rgb(96, 96, 96);border-top-color: rgb(96, 96, 96);border-right-color: rgb(96, 96, 96);border-left-color: rgb(96, 96, 96);"> <section style="font-size: 5em;color: rgb(96, 96, 96);font-weight: bold;vertical-align: middle;display: inline-block;"> <p>2</p> </section> <section style="margin-left: 5px;font-size: 1.25em;vertical-align: middle;display: inline-block;"> <p>"动态"时间轮</p> </section> </section> <section style="padding-top: 1em;padding-bottom: 1em;text-align: left;white-space: normal;line-height: 1.5;"> <section style="font-size: 1em;line-height: 1.75em;"> <span style="font-size: 16px;text-align: left;"><span style="font-size: 16px;text-align: left;">到目前为止,一切都很好理解。</span></span> </section> <section style="font-size: 1em;line-height: 1.75em;"> <span style="font-size: 16px;text-align: left;"><br></span> </section> <section style="font-size: 1em;line-height: 1.75em;"> <span style="font-size: 16px;text-align: left;"><span style="font-size: 16px;text-align: left;"></span>那么如果在当前时间是2s的时候, 插入一个延时时间为19s的任务时, <span style="font-size: 16px;text-align: left;">这个任务的过期时间就是在当前时间</span><span style="font-size: 16px;text-align: left;">2s的基础上加19s, </span><span style="font-size: 16px;text-align: left;">也就是21s。<br></span></span> </section> <section style="font-size: 1em;line-height: 1.75em;"> <span style="font-size: 16px;text-align: left;"><span style="font-size: 16px;text-align: left;"><br></span></span> </section> <section style="font-size: 1em;line-height: 1.75em;"> <span style="font-size: 16px;text-align: left;"><span style="font-size: 16px;text-align: left;">请看下图,当前的时间轮是没有过期时间为21s的时间格。这个任务将会插入到过期时间为1s的时间格中,这是怎么回事呢?</span></span> </section> <p style="text-align: center;"><img class="rich_pages wxw-img" data-ratio="1.027027027027027" data-s="300,640" src="/upload/b4ec6426d539dd175f22c5730ecf39b7.png" data-type="png" data-w="666" style="width: 299px;height: 307px;"></p> </section> <section style="text-align: left;white-space: normal;height: 4em;line-height: 4em;border-bottom: 1px solid rgb(96, 96, 96);border-top-color: rgb(96, 96, 96);border-right-color: rgb(96, 96, 96);border-left-color: rgb(96, 96, 96);"> <section style="margin-left: 5px;font-size: 1.25em;vertical-align: middle;display: inline-block;"> <p><span style="font-size: 1em;">复用时间格</span></p> </section> </section> <p style="font-size: 1em;line-height: 1.75em;"><br></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">为了解答上面的问题,我们先来点魔法, 让时间轮上的时间都动起来</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">!</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;"></span><br></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg wxw-img" data-ratio="0.9196891191709845" data-s="300,640" src="/upload/480d1f3a2ddea9a5ccbc87235accc5d0.png" data-type="gif" data-w="772" style="width: 355px;height: 327px;"></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;">其实呢,当指针定格在2s的位置时, 时间格0s, 1s和2s就已经是过期的时间格。</span><br></p> <p style="font-size: 1em;line-height: 1.75em;"><br></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-size: 16px;text-align: left;">也就是说指针可以用来划分过期的时间格[0,2]和未来的时间格 [3,19]。而</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;">过期的时间格可以继续复用。比如过期的时间格0s就变成了20s, 存放过期时间为20s的任务。</span></p> <section style="font-size: 1em;line-height: 1.75em;"> <br style="text-align: left;white-space: normal;"> </section> <section style="font-size: 1em;line-height: 1.75em;"> <span style="font-size: 16px;">理解了时间格的复用之后,再看回刚刚的例子,<span style="font-size: 16px;text-align: left;">当前时间是2s</span>时,添加<span style="font-size: 16px;text-align: left;">延时时间为19s的任务,那么这个任务就会插入到过期时间为21s的时间格中。</span></span> <br> </section> <p style="text-align: center;"><img class="rich_pages wxw-img" data-ratio="1.0466472303206997" data-s="300,640" src="/upload/132752df96968e453efe55822b1e9899.png" data-type="png" data-w="686" style="width: 327px;height: 342px;"></p> </section> </section> <section label="Copyright © 2015 playhudong All Rights Reserved." donone="shifuMouseDownPayStyle('shifu_pay_003')" style="margin: 1em auto;white-space: normal;border-width: initial;border-color: initial;border-style: none;text-align: left;"> <section style="padding-top: 1em;padding-bottom: 1em;line-height: 1.5;"> <section style="text-align: left;white-space: normal;height: 4em;line-height: 4em;border-bottom: 1px solid rgb(96, 96, 96);border-top-color: rgb(96, 96, 96);border-right-color: rgb(96, 96, 96);border-left-color: rgb(96, 96, 96);"> <section style="font-size: 5em;color: rgb(96, 96, 96);font-weight: bold;vertical-align: middle;display: inline-block;"> <p>3</p> </section> <section style="margin-left: 5px;font-size: 1.25em;vertical-align: middle;display: inline-block;"> <p>时间轮<span style="font-size: 21.25px;text-align: left;">升级</span></p> </section> </section> <section style="padding-top: 1em;padding-bottom: 1em;text-align: left;white-space: normal;line-height: 1.5;"> <section style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"> <span style="font-size: 16px;">下面,新的问题来了,请坐好扶稳。</span> </section> <section style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"> <br> </section> <section style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"> <span style="font-size: 16px;">如果在当前时间是2s的时候, 插入一个延时时间为22s的任务, 这个任务的过期时间就是在2s的基础上加22s,也就是24s。</span> </section> <p style="text-align: center;"><img class="rich_pages wxw-img" data-ratio="1.0029498525073746" data-s="300,640" src="/upload/a7487ab48e25193da1b64f060c79ebd1.png" data-type="png" data-w="678" style="width: 334px;height: 335px;"></p> <section style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"> <span style="font-size: 16px;"><br></span> </section> <section style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"> <span style="font-size: 16px;">显然当前时间轮是无法找到过期时间格为24秒的时间格,因为当前过期时间最大的时间格才到21s。而且我们也没办法像前面那样再复用时间格,因为除了过期时间为2s的时间格,其他的时间格都还没过期呢。当前时间轮无法承载这个定时任务, </span> <span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">那么应该怎么办呢?</span> </section> <section style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"> <br> </section> <section style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"> <span style="font-size: 16px;">当然我们可以选择扩展时间轮上的时间格, 但是这样一来,时间轮就失去了意义。</span> </section> <section style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"> <span style="font-size: 16px;"><br></span> </section> <section style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"> <span style="font-size: 16px;">是时候要升级时间轮了!</span> </section> <section style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"> <br> </section> <section style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"> <span style="font-size: 16px;"><span style="text-align: left;">我们先来理解下多层时间轮</span>之间的联系。</span> </section> </section> <p style="font-size: 1em;white-space: normal;"><br></p> <section label="Copyright © 2015 playhudong All Rights Reserved." donone="shifuMouseDownPayStyle('shifu_pay_003')" style="font-size: 1em;margin: 1em auto;white-space: normal;border-width: initial;border-color: initial;border-style: none;text-align: left;"> <section style="height: 4em;line-height: 4em;border-bottom: 1px solid rgb(96, 96, 96);border-top-color: rgb(96, 96, 96);border-right-color: rgb(96, 96, 96);border-left-color: rgb(96, 96, 96);"> <section style="font-size: 5em;color: rgb(96, 96, 96);font-weight: bold;vertical-align: middle;display: inline-block;"> <p>4</p> </section> <section style="margin-left: 5px;font-size: 1.25em;vertical-align: middle;display: inline-block;"> <p>层级时间轮</p> </section> </section> <section style="padding-top: 1em;padding-bottom: 1em;line-height: 1.5;font-size: 1em;"> <p style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"><span style="font-size: 16px;text-align: left;">如图是一个两层的时间轮:</span></p> <p style="text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.997946611909651" data-s="300,640" src="/upload/e15c04e05aabf69fdd61e88e29a2de7b.png" data-type="png" data-w="974" style="width: 460px;height: 459px;"></p> <section style="line-height: 1.75em;"> <span style="font-size: 16px;">第二层时间轮也是由20个时间格组成, 每个时间格的跨度是20s。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 16px;"><span style="text-align: left;"></span></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 16px;text-align: left;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 16px;"><span style="text-align: left;">图中展示了每个时间格对应的过期时间范围, 我们可以清晰地看到, </span>第二层时间轮的第0个时间格的过期时间范围是 [0,19]。</span> <span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">也就是说, </span> <span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">第二层时间轮</span> <span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">的</span> <span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">一个时间格就可以表示第一层时间轮的所有(20个)时间格;</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 16px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 16px;">为了进一步理清第一层时间轮和第二层时间轮的关系, 我们拉着<span style="text-align: left;">时间的小手, 一起观看下面的动图:</span></span> </section> <p style="text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.9953125" src="/upload/f649b5f166c55a3c6b340cdd670b5229.png" data-type="gif" data-w="640" style="width: 441px;height: 439px;"></p> <section style="line-height: 1.75em;"> <span style="font-size: 16px;"><span style="font-size: 16px;text-align: left;">可以看到,第二层时间轮同样也有自己的指针, </span>每当第一层时间轮走完一个周期,<span style="font-size: 16px;text-align: left;">第二层时间轮</span>的指针就会推进一格。</span> </section> </section> </section> <section style="text-align: left;white-space: normal;height: 4em;line-height: 4em;border-bottom: 1px solid rgb(96, 96, 96);border-top-color: rgb(96, 96, 96);border-right-color: rgb(96, 96, 96);border-left-color: rgb(96, 96, 96);"> <section style="margin-left: 5px;font-size: 1.25em;vertical-align: middle;display: inline-block;"> <p>添加定时任务</p> </section> </section> <section style="padding-top: 1em;padding-bottom: 1em;text-align: left;white-space: normal;line-height: 1.5;"> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-size: 16px;text-align: left;">回到一开始的问题,在当前时间是2s的时候, 插入一个延时时间为22s的任务,该任务<span style="text-align: left;">过期时间</span>为24s。</span></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg wxw-img" data-ratio="0.9959514170040485" data-s="300,640" src="/upload/70df1bebce6e8b0083e98220dae843db.png" data-type="gif" data-w="988" style="width: 444px;height: 442px;"></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-size: 16px;">当第一层时间轮容纳不下时,进入第二层时间轮,并插入到过期时间为[20,39]的时间格中。<br></span></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-size: 16px;"><br></span></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-size: 16px;">我们再来个例子,<span style="font-size: 16px;text-align: left;">如果在当前时间是2s的时候, 插入一个延时时间为350s的任务, 这个任务的过期时间就是在2s的基础上加350s,也就是352s。</span></span></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg wxw-img" data-ratio="0.9959514170040485" data-s="300,640" src="/upload/f27672d9d4850703969f9cb50a6dfc0.png" data-type="gif" data-w="988" style="width: 433px;height: 431px;"></p> <p style="font-size: 1em;line-height: 1.75em;"><span style="font-size: 16px;text-align: left;"></span></p> </section> <p style="white-space: normal;"><span style="font-size: 16px;">从图中可以看到,该任务插入到第二层时间轮过期时间为[340,359]s的时间格中,也就是第17格的位置。</span><br></p> <p style="white-space: normal;"><br></p> <p style="white-space: normal;"><br></p> <section label="Copyright © 2015 playhudong All Rights Reserved." donone="shifuMouseDownPayStyle('shifu_pay_003')" style="margin: 1em auto;white-space: normal;border-width: initial;border-color: initial;border-style: none;text-align: left;"> <section style="height: 4em;line-height: 4em;border-bottom: 1px solid rgb(96, 96, 96);border-top-color: rgb(96, 96, 96);border-right-color: rgb(96, 96, 96);border-left-color: rgb(96, 96, 96);"> <section style="font-size: 5em;color: rgb(96, 96, 96);font-weight: bold;vertical-align: middle;display: inline-block;"> <p>5</p> </section> <section style="margin-left: 5px;font-size: 1.25em;vertical-align: middle;display: inline-block;"> <p><span style="font-size: 21.25px;text-align: left;">"动态"</span>层级时间轮</p> </section> </section> <section style="padding-top: 1em;padding-bottom: 1em;line-height: 1.5;"> <p style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"><span style="font-size: 16px;">通常来说, 第二层时间轮的第0个时间格是用来表示第一层时间轮的, 这一格是存放不了任务的, 因为超时时间0-20s的任务, 第一层时间轮就可以处理了。</span></p> <p style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"><br></p> <p style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"><span style="font-size: 16px;">但是! 事情往往没这么简单, 我们时间轮上的时间格都是可以复用的! </span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;">那么这在第二层时间轮上又是怎么体现的呢? </span></p> <p style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"><br></p> <p style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"><span style="font-size: 16px;">下面是魔法时间, 我们让时间轮上的过期时间都动起来!</span></p> <p style="font-size: 1em;text-align: center;"><img class="rich_pages js_insertlocalimg wxw-img" data-ratio="0.9959514170040485" data-s="300,640" src="/upload/50c4f9f7bbc77c84a0a1da23d86c36cf.png" data-type="gif" data-w="988" style="width: 437px;height: 435px;"></p> <p style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"><br></p> <p style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">从图中可以看到,</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">当</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">第一层时间轮的指针定格在1</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">s时,</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">超时时间</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">0s的时间格</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">就过期了</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">。而这个时候,</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">第二层时间轮</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">第0个</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">时间格的时间范围就从</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">[0,19</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">]</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">分为了过期的</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">[0</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">]</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">,和未过期的</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">[1,19</span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">]。而过期的[0]就会被新的过期时间[400]复用。</span><br></p> <p style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;"><br></span></p> <p style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;"><span style="font-size: 16px;text-align: left;">第二层时间轮<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: left;font-size: 16px;">第0个</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: left;font-size: 16px;">时间格</span></span>的过期时间范围演变如下:</span></p> <p style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">[0-19]</span></p> <p style="text-align: left;white-space: normal;line-height: 1.75em;"><span style="font-size: 16px;">[400][1,19]</span></p> <p style="text-align: left;white-space: normal;line-height: 1.75em;"><span style="font-size: 16px;">[400,401][2,19]</span></p> <p style="text-align: left;white-space: normal;line-height: 1.75em;"><span style="font-size: 16px;">......</span></p> <p style="text-align: left;white-space: normal;line-height: 1.75em;"><span style="font-size: 16px;">[400,419]</span></p> <p style="text-align: left;white-space: normal;line-height: 1.75em;"><span style="font-size: 16px;"><br></span></p> <p style="text-align: left;white-space: normal;line-height: 1.75em;"><span style="font-size: 16px;"><span style="font-size: 16px;text-align: left;">所以,如果在当前时间是2s的时候, 插入一个延时时间为399s的任务, 这个任务的过期时间就是在2s的基础上加399s,也就是401s。如图,这个任务还是会插到第二层时间轮第0个时间格中去。</span></span><span style="font-size: 16px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;"><br></span></p> <p style="text-align: center;"><img class="rich_pages wxw-img" data-ratio="1.008298755186722" data-s="300,640" src="/upload/cbe5d5db5588731adc275f9bc72637a2.png" data-type="png" data-w="964" style="width: 451px;height: 455px;"></p> <p style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"><br></p> </section> </section> <section label="Copyright © 2015 playhudong All Rights Reserved." donone="shifuMouseDownPayStyle('shifu_pay_003')" style="margin: 1em auto;white-space: normal;border-width: initial;border-color: initial;border-style: none;text-align: left;"> <section style="height: 4em;line-height: 4em;border-bottom: 1px solid rgb(96, 96, 96);border-top-color: rgb(96, 96, 96);border-right-color: rgb(96, 96, 96);border-left-color: rgb(96, 96, 96);"> <section style="font-size: 5em;color: rgb(96, 96, 96);font-weight: bold;vertical-align: middle;display: inline-block;"> <p>6</p> </section> <section style="margin-left: 5px;font-size: 1.25em;vertical-align: middle;display: inline-block;"> <p><span style="font-size: 21.25px;text-align: left;">时间轮</span>降级</p> </section> </section> <section style="padding-top: 1em;padding-bottom: 1em;line-height: 1.5;font-size: 1em;"> <section style="line-height: 1.75em;"> <span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;">还是用回这个大家都已经耳熟能详的例子,在当前时间是2s的时候, 插入一个延时时间为22s的任务,该任务过期时间为24s。最后进入第二层时间轮,并插入到过期时间为[20,39]的时间格中。</span> <br> </section> <section style="line-height: 1.75em;"> <span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;text-align: left;">当二层时间轮上的定时任务到期后,时间轮是怎么做的呢?</span> </section> <p style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"><br></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg wxw-img" data-ratio="0.9959514170040485" data-s="300,640" src="/upload/32981874757256e8b1bff6841f60a2cf.png" data-type="gif" data-w="988" style="width: 427px;height: 425px;"></p> <section style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"> <span style="font-size: 16px;">从图中可以看到,随着当前时间从2s继续往前推进,一直到20s的时候,总共经过了18s。此时第二层时间轮中,超时时间为[20-39s]的时间格上的任务到期。</span> </section> <section style="font-size: 1em;text-align: left;white-space: normal;line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span style="font-size: 16px;">原本超时时间为24s的任务会被取出来,重新加入时间轮。此时该定时任务的延时时间从原本的22s,到现在还剩下4s(22s-18s)。最后停留在第一层时间轮超时时间为24s的时间格,也就是第4个时间格。</span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span style="font-size: 16px;">随着当前时间继续推进,再经过4s后,该定时任务到期被执行。</span> </section> <section style="line-height: 1.75em;"> <span style="text-align: left;font-size: 16px;"><br></span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span style="font-size: 16px;"><span style="text-align: left;">从这里可以看出时间轮的巧妙之处,两层时间轮只用了40个数组元素,</span>却可以承载[0-399s]的定时任务。而三层时间轮用60个数组元素,就可以承载[0-7999s]的定时任务!</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 16px;"><br></span> </section> <p style="text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.9498607242339833" data-s="300,640" src="/upload/b9e7d188a7224c328efa46c4aba4834a.png" data-type="png" data-w="1436" style=""></p> </section> </section> <p style="white-space: normal;"><br></p> <section label="Copyright © 2015 playhudong All Rights Reserved." donone="shifuMouseDownPayStyle('shifu_pay_003')" style="margin: 1em auto;white-space: normal;border-width: initial;border-color: initial;border-style: none;text-align: left;"> <section style="height: 4em;line-height: 4em;border-bottom: 1px solid rgb(96, 96, 96);border-top-color: rgb(96, 96, 96);border-right-color: rgb(96, 96, 96);border-left-color: rgb(96, 96, 96);"> <section style="font-size: 5em;color: rgb(96, 96, 96);font-weight: bold;vertical-align: middle;display: inline-block;"> <p>7</p> </section> <section style="margin-left: 5px;font-size: 1.25em;vertical-align: middle;display: inline-block;"> <p>时间轮的推进<br></p> </section> </section> <section style="padding-top: 1em;padding-bottom: 1em;line-height: 1.5;"> <section style="font-size: 1em;line-height: 1.75em;"> <span style="font-size: 16px;">从动画中可以注意到, 随着时间推进, 时间轮的指针循环往复地定格在每一个时间格上, 每一次都要判断当前定格的时间格里是不是有任务存在;</span> </section> <section style="font-size: 1em;line-height: 1.75em;"> <br> </section> <section style="font-size: 1em;line-height: 1.75em;"> <span style="font-size: 16px;">其中有很多<span style="text-align: left;">时间格</span>都是没有任务的, <span style="text-align: left;">指针</span>定格在这种空的时间格中, 就是一次"<span style="text-align: left;">空推进</span>";</span> </section> <section style="font-size: 1em;line-height: 1.75em;"> <span style="font-size: 16px;"><br></span> </section> <section style="font-size: 1em;line-height: 1.75em;"> <span style="font-size: 16px;">比如说, 插入一个延时时间400s的任务, <span style="text-align: left;"> <span style="text-align: left;">指针就要执行399次"空推进", </span>这是一种浪费!</span></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 16px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 16px;">那么Kafka是怎么解决这个问题的呢?这就要从延迟队列DelayQueue开始讲起了!</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 16px;">时间轮搭配<span style="font-size: 16px;text-align: left;">延迟队列</span><span style="font-size: 16px;text-align: left;">DelayQueue</span>,会发生什么化学反应呢?请关注公众号【字节武装】后续更新。</span> </section> </section> </section> </section> </section> <section label="" style="width: 24rem;border-width: initial;border-style: none;border-color: initial;box-sizing: border-box;margin: 0.5rem auto;" donone="shifuMouseDownPayStyle('shifu_devider_009')"> <section style="margin-right: auto;margin-left: auto;width: 5rem;height: 5rem;box-sizing: border-box;border-radius: 50%;background: rgb(96, 96, 96);display: flex;align-items: center;"> <section style="margin-right: auto;margin-left: auto;"> <section style="width: 100%;text-align: center;color: #fff;font-size: 1rem;max-width: 4rem;overflow: hidden;"> <p><span style="font-size: 14px;">往期回顾</span></p> </section> </section> </section> <section style="border-bottom: 1px solid rgb(96, 96, 96);width: 100%;box-sizing: border-box;margin-top: -1rem;border-top-color: rgb(96, 96, 96);border-right-color: rgb(96, 96, 96);border-left-color: rgb(96, 96, 96);"> <br> </section> </section> <p><br></p> <p><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzAwMjI0ODk0NA==&mid=2451950743&idx=1&sn=df1c600f636c8d9b119f534750c007eb&chksm=8d1c3508ba6bbc1e6e4def2ea4c25d9c5e69013d463af31f6bc78cacbc3735ccea455842303d&scene=21#wechat_redirect" textvalue="用动图讲解分布式 Raft" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" style="text-decoration: underline;" data-linktype="2"><span style="color: rgb(255, 104, 39);">推荐:用动图讲解分布式 Raft</span></a></p> <p><br></p> </section> </section> <p style="text-align: center;"><span style="text-decoration: underline;"><em style="outline: 0px;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: center;white-space: normal;background-color: rgb(255, 255, 255);"><strong style="outline: 0px;"><span style="text-decoration: underline;outline: 0px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;font-size: 14px;letter-spacing: 2px;text-align: left;word-spacing: 2px;">我是悟空,努力变强,变身超级赛亚人!</span></strong></em></span></p>
作者:微信小助手
<section style="margin: 5px 8px 15px;white-space: normal;outline: 0px;font-family: -apple-system, BlinkMacSystemFo
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;letter-spacing: 0px;white-space: normal;font-size: 16px;padding-right: 10px;padding-left: 10px;line-height: 1.6;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Roboto, Oxygen, Ubuntu, Cantarell, PingFangSC-light, PingFangTC-light, "Open Sans", "Helvetica Neue", sans-serif;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Guava是google公司开发的一款Java类库扩展工具包,内含了丰富的API,涵盖了集合、缓存、并发、I/O等多个方面。使用这些API一方面可以简化我们代码,使代码更为优雅,另一方面它补充了很多jdk中没有的功能,能让我们开发中更为高效。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">今天Hydra要给大家分享的就是Guava中封装的一些关于<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Map</code>的骚操作,在使用了这些功能后,不得不说一句真香。先引入依赖坐标,然后开始我们的正式体验吧~</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;"><span style="line-height: 26px;"><<span style="color: rgb(224, 108, 117);line-height: 26px;">dependency</span>></span><br> <span style="line-height: 26px;"><<span style="color: rgb(224, 108, 117);line-height: 26px;">groupId</span>></span>com.google.guava<span style="line-height: 26px;"></<span style="color: rgb(224, 108, 117);line-height: 26px;">groupId</span>></span><br> <span style="line-height: 26px;"><<span style="color: rgb(224, 108, 117);line-height: 26px;">artifactId</span>></span>guava<span style="line-height: 26px;"></<span style="color: rgb(224, 108, 117);line-height: 26px;">artifactId</span>></span><br> <span style="line-height: 26px;"><<span style="color: rgb(224, 108, 117);line-height: 26px;">version</span>></span>30.1.1-jre<span style="line-height: 26px;"></<span style="color: rgb(224, 108, 117);line-height: 26px;">version</span>></span><br><span style="line-height: 26px;"></<span style="color: rgb(224, 108, 117);line-height: 26px;">dependency</span>></span><br></code></pre> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">Table - 双键Map</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid rgb(239, 235, 233);border-right: 20px solid transparent;"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">java中的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Map</code>只允许有一个<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">key</code>和一个<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">value</code>存在,但是guava中的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Table</code>允许一个<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">value</code>存在两个<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">key</code>。<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Table</code>中的两个<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">key</code>分别被称为<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">rowKey</code>和<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">columnKey</code>,也就是行和列。(但是个人感觉将它们理解为行和列并不是很准确,看作两列的话可能会更加合适一些)</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">举一个简单的例子,假如要记录员工每个月工作的天数。用java中普通的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Map</code>实现的话就需要两层嵌套:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">Map<String,Map<String,Integer>> map=<span style="color: rgb(198, 120, 221);line-height: 26px;">new</span> HashMap<>();<br><span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//存放元素</span><br>Map<String,Integer> workMap=<span style="color: rgb(198, 120, 221);line-height: 26px;">new</span> HashMap<>();<br>workMap.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Jan"</span>,<span style="color: rgb(209, 154, 102);line-height: 26px;">20</span>);<br>workMap.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Feb"</span>,<span style="color: rgb(209, 154, 102);line-height: 26px;">28</span>);<br>map.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Hydra"</span>,workMap);<br><br><span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//取出元素</span><br>Integer dayCount = map.get(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Hydra"</span>).get(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Jan"</span>);<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如果使用<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Table</code>的话就很简单了,看一看简化后的代码:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">Table<String,String,Integer> table= HashBasedTable.create();<br><span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//存放元素</span><br>table.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Hydra"</span>, <span style="color: rgb(152, 195, 121);line-height: 26px;">"Jan"</span>, <span style="color: rgb(209, 154, 102);line-height: 26px;">20</span>);<br>table.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Hydra"</span>, <span style="color: rgb(152, 195, 121);line-height: 26px;">"Feb"</span>, <span style="color: rgb(209, 154, 102);line-height: 26px;">28</span>);<br><br>table.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Trunks"</span>, <span style="color: rgb(152, 195, 121);line-height: 26px;">"Jan"</span>, <span style="color: rgb(209, 154, 102);line-height: 26px;">28</span>);<br>table.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Trunks"</span>, <span style="color: rgb(152, 195, 121);line-height: 26px;">"Feb"</span>, <span style="color: rgb(209, 154, 102);line-height: 26px;">16</span>);<br><br><span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//取出元素</span><br>Integer dayCount = table.get(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Hydra"</span>, <span style="color: rgb(152, 195, 121);line-height: 26px;">"Feb"</span>);<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">我们不需要再构建复杂的双层<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Map</code>,直接一层搞定。除了元素的存取外,下面再看看其他的实用操作。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;">1、获得key或value的集合</h3> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;"><span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//rowKey或columnKey的集合</span><br>Set<String> rowKeys = table.rowKeySet();<br>Set<String> columnKeys = table.columnKeySet();<br><br><span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//value集合</span><br>Collection<Integer> values = table.values();<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">分别打印它们的结果,<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">key</code>的集合是不包含重复元素的,<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">value</code>集合则包含了所有元素并没有去重:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">[Hydra, Trunks]<br>[Jan, Feb]<br>[20, 28, 28, 16]<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;">2、计算key对应的所有value的和</h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">以统计所有<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">rowKey</code>对应的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">value</code>之和为例:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">for</span> (String key : table.rowKeySet()) {<br> Set<Map.Entry<String, Integer>> rows = table.row(key).entrySet();<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">int</span> total = <span style="color: rgb(209, 154, 102);line-height: 26px;">0</span>;<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">for</span> (Map.Entry<String, Integer> row : rows) {<br> total += row.getValue();<br> }<br> System.out.println(key + <span style="color: rgb(152, 195, 121);line-height: 26px;">": "</span> + total);<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">打印结果:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">Hydra: <span style="color: rgb(209, 154, 102);line-height: 26px;">48</span><br>Trunks: <span style="color: rgb(209, 154, 102);line-height: 26px;">44</span><br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;">3、转换rowKey和columnKey</h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这一操作也可以理解为行和列的转置,直接调用<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Tables</code>的静态方法<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">transpose</code>:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">Table<String, String, Integer> table2 = Tables.transpose(table);<br>Set<Table.Cell<String, String, Integer>> cells = table2.cellSet();<br>cells.forEach(cell-><br> System.out.println(cell.getRowKey()+<span style="color: rgb(152, 195, 121);line-height: 26px;">","</span>+cell.getColumnKey()+<span style="color: rgb(152, 195, 121);line-height: 26px;">":"</span>+cell.getValue())<br>);<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">利用<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">cellSet</code>方法可以得到所有的数据行,打印结果,可以看到<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">row</code>和<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">column</code>发生了互换:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">Jan,Hydra:20<br>Feb,Hydra:28<br>Jan,Trunks:28<br>Feb,Trunks:16<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;">4、转为嵌套的Map</h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">还记得我们在没有使用<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Table</code>前存储数据的格式吗,如果想要将数据还原成嵌套<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Map</code>的那种形式,使用<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Table</code>的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">rowMap</code>或<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">columnMap</code>方法就可以实现了:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">Map<String, Map<String, Integer>> rowMap = table.rowMap();<br>Map<String, Map<String, Integer>> columnMap = table.columnMap();<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">查看转换格式后的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Map</code>中的内容,分别按照行和列进行了汇总:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">{Hydra={Jan=20, Feb=28}, Trunks={Jan=28, Feb=16}}<br>{Jan={Hydra=20, Trunks=28}, Feb={Hydra=28, Trunks=16}}<br></code></pre> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">BiMap - 双向Map</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid rgb(239, 235, 233);border-right: 20px solid transparent;"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在普通<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Map</code>中,如果要想根据<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">value</code>查找对应的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">key</code>,没什么简便的办法,无论是使用<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">for</code>循环还是迭代器,都需要遍历整个<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Map</code>。以循环<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">keySet</code>的方式为例:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;"><span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">public</span> List<String> <span style="color: rgb(97, 174, 238);line-height: 26px;">findKey</span><span style="line-height: 26px;">(Map<String, String> map, String val)</span></span>{<br> List<String> keys=<span style="color: rgb(198, 120, 221);line-height: 26px;">new</span> ArrayList<>();<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">for</span> (String key : map.keySet()) {<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">if</span> (map.get(key).equals(val))<br> keys.add(key);<br> }<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">return</span> keys;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">而guava中的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">BiMap</code>提供了一种<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">key</code>和<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">value</code>双向关联的数据结构,先看一个简单的例子:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">HashBiMap<String, String> biMap = HashBiMap.create();<br>biMap.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Hydra"</span>,<span style="color: rgb(152, 195, 121);line-height: 26px;">"Programmer"</span>);<br>biMap.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Tony"</span>,<span style="color: rgb(152, 195, 121);line-height: 26px;">"IronMan"</span>);<br>biMap.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Thanos"</span>,<span style="color: rgb(152, 195, 121);line-height: 26px;">"Titan"</span>);<br><span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//使用key获取value</span><br>System.out.println(biMap.get(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Tony"</span>));<br><br>BiMap<String, String> inverse = biMap.inverse();<br><span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//使用value获取key</span><br>System.out.println(inverse.get(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Titan"</span>));<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">执行结果,:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">IronMan<br>Thanos<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">看上去很实用是不是?但是使用中还有几个坑得避一下,下面一个个梳理。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;">1、反转后操作的影响</h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">上面我们用<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">inverse</code>方法反转了原来<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">BiMap</code>的键值映射,但是这个反转后的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">BiMap</code>并不是一个新的对象,它实现了一种视图的关联,所以对反转后的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">BiMap</code>执行的所有操作会作用于原先的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">BiMap</code>上。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">HashBiMap<String, String> biMap = HashBiMap.create();<br>biMap.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Hydra"</span>,<span style="color: rgb(152, 195, 121);line-height: 26px;">"Programmer"</span>);<br>biMap.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Tony"</span>,<span style="color: rgb(152, 195, 121);line-height: 26px;">"IronMan"</span>);<br>biMap.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Thanos"</span>,<span style="color: rgb(152, 195, 121);line-height: 26px;">"Titan"</span>);<br>BiMap<String, String> inverse = biMap.inverse();<br><br>inverse.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"IronMan"</span>,<span style="color: rgb(152, 195, 121);line-height: 26px;">"Stark"</span>);<br>System.out.println(biMap);<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">对反转后的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">BiMap</code>中的内容进行了修改后,再看一下原先<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">BiMap</code>中的内容:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">{Hydra=Programmer, Thanos=Titan, Stark=IronMan}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">可以看到,原先值为<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">IronMan</code>时对应的键是<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Tony</code>,虽然没有直接修改,但是现在键变成了<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Stark</code>。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;">2、value不可重复</h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">BiMap</code>的底层继承了<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Map</code>,我们知道在<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Map</code>中<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">key</code>是不允许重复的,而双向的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">BiMap</code>中<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">key</code>和<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">value</code>可以认为处于等价地位,因此在这个基础上加了限制,<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">value</code>也是不允许重复的。看一下下面的代码:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CkBYF6IYNs34076jqaBLzyNlsR2akKv1aAia7aQZ30rfWvFiaEPuDWW7nhpic4t8uYlCCG3UfXeRncF9EUCV557GPmeCT52Pk0m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 747px;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">HashBiMap<String, String> biMap = HashBiMap.create();<br>biMap.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Tony"</span>,<span style="color: rgb(152, 195, 121);line-height: 26px;">"IronMan"</span>);<br>biMap.put(<span style="color: rgb(152, 195, 121);line-height: 26px;">"Stark"</span>,<span style="color: rgb(152, 195, 121);line-height: 26px;">"IronMan"</span>);<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这样代码无法正常结束,会抛出一个<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;line-height: 1.6;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.05em;color: rgb(89, 89, 89);" data-mpa-powered-by="yiban.io"> <h2 data-tool="mdnice编辑器" style="min-height: 32px;line-height: 28px;color: rgb(119, 48, 152);border-bottom: 1px solid rgb(119, 48, 152);border-top-color: rgb(119, 48, 152);border-right-color: rgb(119, 48, 152);border-left-color: rgb(119, 48, 152);font-size: 22px;margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>1、前言</h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">前面介绍了Spring Cloud 中的灵魂摆渡者<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">Nacos</code>,和它的前辈们相比不仅仅功能强大,而且部署非常简单。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">今天介绍一款服务调用的组件:<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">OpenFeign</code>,同样是一款超越先辈(<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">Ribbon</code>、<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">Feign</code>)的狠角色。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">文章目录如下:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-ratio="0.45320197044334976" src="/upload/d0b9168cb95509d569d46f922a0c5d29.png" data-type="png" data-w="1015" style="display: block;margin-right: auto;margin-left: auto;border-radius: 3px;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <h2 data-tool="mdnice编辑器" style="min-height: 32px;line-height: 28px;color: rgb(119, 48, 152);border-bottom: 1px solid rgb(119, 48, 152);border-top-color: rgb(119, 48, 152);border-right-color: rgb(119, 48, 152);border-left-color: rgb(119, 48, 152);font-size: 22px;margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>2、Feign是什么?</h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">Feign也是一个狠角色,Feign旨在使得Java Http客户端变得更容易。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">Feign集成了Ribbon、RestTemplate实现了负载均衡的执行Http调用,只不过对原有的方式(Ribbon+RestTemplate)进行了封装,开发者不必手动使用RestTemplate调服务,而是定义一个接口,在这个接口中标注一个注解即可完成服务调用,这样更加符合面向接口编程的宗旨,简化了开发。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-ratio="1.2142857142857142" src="/upload/d169125c01b90de79271a2ba4229283b.png" data-type="png" data-w="266" style="display: block;margin-right: auto;margin-left: auto;border-radius: 3px;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">但遗憾的是Feign现在停止迭代了,当然现在也是有不少企业在用。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">有想要学习Feign的读者可以上spring Cloud官网学习,陈某这里也不再详细介绍了,不是今天的重点。</p> <h2 data-tool="mdnice编辑器" style="min-height: 32px;line-height: 28px;color: rgb(119, 48, 152);border-bottom: 1px solid rgb(119, 48, 152);border-top-color: rgb(119, 48, 152);border-right-color: rgb(119, 48, 152);border-left-color: rgb(119, 48, 152);font-size: 22px;margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>3、openFeign是什么?</h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">前面介绍过停止迭代的Feign,简单点来说:OpenFeign是springcloud在Feign的基础上支持了SpringMVC的注解,如<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">@RequestMapping</code>等等。OpenFeign的<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">@FeignClient</code>可以解析SpringMVC的<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">@RequestMapping</code>注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;font-size: 0.9em;overflow: auto;padding: 10px 10px 10px 20px;margin: 10px 5px;border-left-color: rgb(150, 84, 181);border-right: 1px solid rgb(150, 84, 181);color: rgb(97, 97, 97);quotes: none;background: rgb(251, 249, 253);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">官网地址:https://docs.spring.io/spring-cloud-openfeign/docs/2.2.10.BUILD-SNAPSHOT/reference/html</p> </blockquote> <h2 data-tool="mdnice编辑器" style="min-height: 32px;line-height: 28px;color: rgb(119, 48, 152);border-bottom: 1px solid rgb(119, 48, 152);border-top-color: rgb(119, 48, 152);border-right-color: rgb(119, 48, 152);border-left-color: rgb(119, 48, 152);font-size: 22px;margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>4、Feign和openFeign有什么区别?</h2> <section data-tool="mdnice编辑器" style="overflow-x: auto;"> <table> <thead> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <th style="font-size: 16px;border-top-width: 1px;border-color: rgb(204, 204, 204);text-align: left;background-color: rgb(240, 240, 240);min-width: 85px;">Feign</th> <th style="font-size: 16px;border-top-width: 1px;border-color: rgb(204, 204, 204);text-align: left;background-color: rgb(240, 240, 240);min-width: 85px;">openFiegn</th> </tr> </thead> <tbody style="border-width: 0px;border-style: initial;border-color: initial;"> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <td style="font-size: 16px;border-color: rgb(204, 204, 204);min-width: 85px;">Feign是SpringCloud组件中一个轻量级RESTful的HTTP服务客户端,Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式是:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务</td> <td style="font-size: 16px;border-color: rgb(204, 204, 204);min-width: 85px;">OpenFeign 是SpringCloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等。OpenFeign 的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。</td> </tr> </tbody> </table> </section> <h2 data-tool="mdnice编辑器" style="min-height: 32px;line-height: 28px;color: rgb(119, 48, 152);border-bottom: 1px solid rgb(119, 48, 152);border-top-color: rgb(119, 48, 152);border-right-color: rgb(119, 48, 152);border-left-color: rgb(119, 48, 152);font-size: 22px;margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>5、环境准备</h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">本篇文章Spring Cloud版本、JDK环境、项目环境均和上一篇Nacos的环境相同:<a href="https://mp.weixin.qq.com/s?__biz=MzU3MDAzNDg1MA==&mid=2247493854&idx=1&sn=4b3fb7f7e17a76000733899f511ef915&scene=21#wechat_redirect" style="font-weight: bold;color: rgb(119, 48, 152);border-bottom: 1px solid rgb(119, 48, 152);" data-linktype="2">五十五张图告诉你微服务的灵魂摆渡者Nacos究竟有多强?</a>。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">注册中心就不再使用<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">Eureka</code>了,直接使用<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">Nacos</code>作为注册和配置中心,有不会的可以查看Nacos文章。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">本篇文章搭建的项目结构如下图:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-ratio="0.42837653478854026" src="/upload/55cd654664af36adacae39936a7bf46f.png" data-type="png" data-w="733" style="display: block;margin-right: auto;margin-left: auto;border-radius: 3px;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;font-size: 0.9em;overflow: auto;padding: 10px 10px 10px 20px;margin: 10px 5px;border-left-color: rgb(150, 84, 181);border-right: 1px solid rgb(150, 84, 181);color: rgb(97, 97, 97);quotes: none;background: rgb(251, 249, 253);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">注册中心使用<strong style="color: rgb(119, 48, 152);">Nacos</strong>,创建个微服务,分别为服务提供者<strong style="color: rgb(119, 48, 152);">Produce</strong>,服务消费者<strong style="color: rgb(119, 48, 152);">Consumer</strong>。</p> </blockquote> <h2 data-tool="mdnice编辑器" style="min-height: 32px;line-height: 28px;color: rgb(119, 48, 152);border-bottom: 1px solid rgb(119, 48, 152);border-top-color: rgb(119, 48, 152);border-right-color: rgb(119, 48, 152);border-left-color: rgb(119, 48, 152);font-size: 22px;margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>6、创建服务提供者</h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">既然是微服务之间的相互调用,那么一定会有服务提供者了,创建<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">openFeign-provider9005</code>,注册进入Nacos中,配置如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #986801;line-height: 26px;">server:</span><br> <span style="color: #986801;line-height: 26px;">port:</span> <span style="color: #986801;line-height: 26px;">9005</span><br><span style="color: #986801;line-height: 26px;">spring:</span><br> <span style="color: #986801;line-height: 26px;">application:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 指定服务名称,在nacos中的名字</span><br> <span style="color: #986801;line-height: 26px;">name:</span> <span style="color: #50a14f;line-height: 26px;">openFeign-provider</span><br> <span style="color: #986801;line-height: 26px;">cloud:</span><br> <span style="color: #986801;line-height: 26px;">nacos:</span><br> <span style="color: #986801;line-height: 26px;">discovery:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;"># nacos的服务地址,nacos-server中IP地址:端口号</span><br> <span style="color: #986801;line-height: 26px;">server-addr:</span> <span style="color: #986801;line-height: 26px;">127.0</span><span style="color: #986801;line-height: 26px;">.0</span><span style="color: #986801;line-height: 26px;">.1</span><span style="color: #50a14f;line-height: 26px;">:8848</span><br><span style="color: #986801;line-height: 26px;">management:</span><br> <span style="color: #986801;line-height: 26px;">endpoints:</span><br> <span style="color: #986801;line-height: 26px;">web:</span><br> <span style="color: #986801;line-height: 26px;">exposure:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## yml文件中存在特殊字符,必须用单引号包含,否则启动报错</span><br> <span style="color: #986801;line-height: 26px;">include:</span> <span style="color: #50a14f;line-height: 26px;">'*'</span><br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="color: rgb(119, 48, 152);">注意</strong>:此处的<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">spring.application.name</code>指定的名称将会在openFeign接口调用中使用。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;font-size: 0.9em;overflow: auto;padding: 10px 10px 10px 20px;margin: 10px 5px;border-left-color: rgb(150, 84, 181);border-right: 1px solid rgb(150, 84, 181);color: rgb(97, 97, 97);quotes: none;background: rgb(251, 249, 253);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">项目源码都会上传,关于如何注册进入Nacos,添加什么依赖源码都会有,结合陈某上篇Nacos文章,这都不是难事!</p> </blockquote> <h2 data-tool="mdnice编辑器" style="min-height: 32px;line-height: 28px;color: rgb(119, 48, 152);border-bottom: 1px solid rgb(119, 48, 152);border-top-color: rgb(119, 48, 152);border-right-color: rgb(119, 48, 152);border-left-color: rgb(119, 48, 152);font-size: 22px;margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>7、创建服务消费者</h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">新建一个模块<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">openFeign-consumer9006</code>作为消费者服务,步骤如下。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>1、添加依赖<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">除了Nacos的注册中心的依赖,还要添加openFeign的依赖,如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">dependency</span>></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">groupId</span>></span>org.springframework.cloud<span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">groupId</span>></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">artifactId</span>></span>spring-cloud-starter-openfeign<span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">artifactId</span>></span><br><span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">dependency</span>></span><br></code></pre> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>2、添加注解@EnableFeignClients开启openFeign功能<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">老套路了,在Spring boot 主启动类上添加一个注解<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">@EnableFeignClients</code>,开启openFeign功能,如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@SpringBootApplication</span><br><span style="color: #4078f2;line-height: 26px;">@EnableDiscoveryClient</span><br><span style="color: #4078f2;line-height: 26px;">@EnableFeignClients</span><br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">OpenFeignConsumer9006Application</span><br></span>{<br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">static</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">main</span><span style="line-height: 26px;">(String[] args)</span> </span>{<br> SpringApplication.run(OpenFeignConsumer9006Application<span style="line-height: 26px;">.<span style="color: #a626a4;line-height: 26px;">class</span>, <span style="color: #c18401;line-height: 26px;">args</span>)</span>;<br> }<br>}<br></code></pre> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>3、新建openFeign接口<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">新建一个openFeign接口,使用<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">@FeignClient</code>注解标注,如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@FeignClient</span>(value = <span style="color: #50a14f;line-height: 26px;">"openFeign-provider"</span>)<br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">interface</span> <span style="color: #c18401;line-height: 26px;">OpenFeignService</span> </span>{<br>}<br></code></pre> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;font-size: 0.9em;overflow: auto;padding: 10px 10px 10px 20px;margin: 10px 5px;border-left-color: rgb(150, 84, 181);border-right: 1px solid rgb(150, 84, 181);color: rgb(97, 97, 97);quotes: none;background: rgb(251, 249, 253);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;"><strong style="color: rgb(119, 48, 152);">注意</strong>:该注解<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">@FeignClient</code>中的<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">value</code>属性指定了服务提供者在nacos注册中心的<strong style="color: rgb(119, 48, 152);">服务名</strong>。</p> </blockquote> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>4、新建一个Controller调试<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">新建一个controller用来调试接口,直接调用openFeign的接口,如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@RestController</span><br><span style="color: #4078f2;line-height: 26px;">@RequestMapping</span>(<span style="color: #50a14f;line-height: 26px;">"/openfeign"</span>)<br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">OpenFeignController</span> </span>{<br> <br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">好了,至此一个openFeign的微服务就搭建好了,并未实现具体的功能,下面一点点实现。</p> <h2 data-tool="mdnice编辑器" style="min-height: 32px;line-height: 28px;color: rgb(119, 48, 152);border-bottom: 1px solid rgb(119, 48, 152);border-top-color: rgb(119, 48, 152);border-right-color: rgb(119, 48, 152);border-left-color: rgb(119, 48, 152);font-size: 22px;margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>8、openFeign如何传参?</h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">开发中接口传参的方式有很多,但是在openFeign中的传参是有一定规则的,下面详细介绍。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>1、传递JSON数据<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">这个也是接口开发中常用的传参规则,在Spring Boot 中通过<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">@RequestBody</code>标识入参。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">provider接口中JSON传参方法如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@RestController</span><br><span style="color: #4078f2;line-height: 26px;">@RequestMapping</span>(<span style="color: #50a14f;line-height: 26px;">"/openfeign/provider"</span>)<br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">OpenFeignProviderController</span> </span>{<br> <span style="color: #4078f2;line-height: 26px;">@PostMapping</span>(<span style="color: #50a14f;line-height: 26px;">"/order2"</span>)<br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> Order <span style="color: #4078f2;line-height: 26px;">createOrder2</span><span style="line-height: 26px;">(@RequestBody Order order)</span></span>{<br> <span style="color: #a626a4;line-height: 26px;">return</span> order;<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">consumer中openFeign接口中传参代码如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@FeignClient</span>(value = <span style="color: #50a14f;line-height: 26px;">"openFeign-provider"</span>)<br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">interface</span> <span style="color: #c18401;line-height: 26px;">OpenFeignService</span> </span>{<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">/**<br> * 参数默认是<span style="color: #a626a4;line-height: 26px;">@RequestBody</span>标注的,这里的<span style="color: #a626a4;line-height: 26px;">@RequestBody</span>可以不填<br> * 方法名称任意<br> */</span><br> <span style="color: #4078f2;line-height: 26px;">@PostMapping</span>(<span style="color: #50a14f;line-height: 26px;">"/openfeign/provider/order2"</span>)<br> <span style="line-height: 26px;">Order <span style="color: #4078f2;line-height: 26px;">createOrder2</span><span style="line-height: 26px;">(@RequestBody Order order)</span></span>;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">注意:<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">openFeign</code>默认的传参方式就是JSON传参(<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">@RequestBody</code>),因此定义接口的时候可以不用<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">@RequestBody</code>注解标注,不过为了规范,一般都填上。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>2、POJO表单传参<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">这种传参方式也是比较常用,参数使用POJO对象接收。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">provider服务提供者代码如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@RestController</span><br><span style="color: #4078f2;line-height: 26px;">@RequestMapping</span>(<span style="color: #50a14f;line-height: 26px;">"/openfeign/provider"</span>)<br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">OpenFeignProviderController</span> </span>{<br> <span style="color: #4078f2;line-height: 26px;">@PostMapping</span>(<span style="color: #50a14f;line-height: 26px;">"/order1"</span>)<br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> Order <span style="color: #4078f2;line-height: 26px;">createOrder1</span><span style="line-height: 26px;">(Order order)</span></span>{<br> <span style="color: #a626a4;line-height: 26px;">return</span> order;<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">consumer消费者openFeign代码如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@FeignClient</span>(value = <span style="color: #50a14f;line-height: 26px;">"openFeign-provider"</span>)<br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">interface</span> <span style="color: #c18401;line-height: 26px;">OpenFeignService</span> </span>{<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">/**<br> * 参数默认是<span style="color: #a626a4;line-height: 26px;">@RequestBody</span>标注的,如果通过POJO表单传参的,使用<span style="color: #a626a4;line-height: 26px;">@SpringQueryMap</span>标注<br> */</span><br> <span style="color: #4078f2;line-height: 26px;">@PostMapping</span>(<span style="color: #50a14f;line-height: 26px;">"/openfeign/provider/order1"</span>)<br> <span style="line-height: 26px;">Order <span style="color: #4078f2;line-height: 26px;">createOrder1</span><span style="line-height: 26px;">(@SpringQueryMap Order order)</span></span>;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">网上很多人疑惑POJO表单方式如何传参,官方文档明确给出了解决方案,如下:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-ratio="0.7268385864374403" src="/upload/366239a71e97dd6ea4f61f34dac2a36c.png" data-type="png" data-w="1047" style="display: block;margin-right: auto;margin-left: auto;border-radius: 3px;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">openFeign提供了一个注解<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">@SpringQueryMap</code>完美解决POJO表单传参。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>3、URL中携带参数<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">此种方式针对restful方式中的GET请求,也是比较常用请求方式。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">provider服务提供者代码如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@RestController</span><br><span style="color: #4078f2;line-height: 26px;">@RequestMapping</span>(<span style="color: #50a14f;line-height: 26px;">"/openfeign/provider"</span>)<br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">OpenFeignProviderController</span> </span>{<br><br> <span style="color: #4078f2;line-height: 26px;">@GetMapping</span>(<span style="color: #50a14f;line-height: 26px;">"/test/{id}"</span>)<br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> String <span style="color: #4078f2;line-height: 26px;">test</span><span style="line-height: 26px;">(@PathVariable(<span style="color: #50a14f;line-height: 26px;">"id"</span>)</span>Integer id)</span>{<br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #50a14f;line-height: 26px;">"accept one msg id="</span>+id;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">consumer消费者openFeign接口如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@FeignClient</span>(value = <span style="color: #50a14f;line-height: 26px;">"openFeign-provider"</span>)<br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">interface</span> <span style="color: #c18401;line-height: 26px;">OpenFeignService</span> </span>{<br><br> <span style="color: #4078f2;line-height: 26px;">@GetMapping</span>(<span style="color: #50a14f;line-height: 26px;">"/openfeign/provider/test/{id}"</span>)<br> <span style="line-height: 26px;">String <span style="color: #4078f2;line-height: 26px;">get</span><span style="line-height: 26px;">(@PathVariable(<span style="color: #50a14f;line-height: 26px;">"id"</span>)</span>Integer id)</span>;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">使用注解<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">@PathVariable</code>接收url中的占位符,这种方式很好理解。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>4、普通表单参数<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">此种方式传参不建议使用,但是也有很多开发在用。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">provider服务提供者代码如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@RestController</span><br><span style="color: #4078f2;line-height: 26px;">@RequestMapping</span>(<span style="color: #50a14f;line-height: 26px;">"/openfeign/provider"</span>)<br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">OpenFeignProviderController</span> </span>{<br> <span style="color: #4078f2;line-height: 26px;">@PostMapping</span>(<span style="color: #50a14f;line-height: 26px;">"/test2"</span>)<br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> String <span style="color: #4078f2;line-height: 26px;">test2</span><span style="line-height: 26px;">(String id,String name)</span></span>{<br> <span style="color: #a626a4;line-height: 26px;">return</span> MessageFormat.format(<span style="color: #50a14f;line-height: 26px;">"accept on msg id={0},name={1}"</span>,id,name);<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">consumer消费者openFeign接口传参如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@FeignClient</span>(valu