文章列表

Redis只能做缓存?太out了!

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 14px;padding: 10px;"> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;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: rgba(0, 0, 0, 0.65);border-right: 1px solid rgba(0, 0, 0, 0.65);background: rgb(249, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;color: black;line-height: 26px;">大多数数据库,由于经常和磁盘打交道,在高并发场景下,响应会非常的慢。为了解决这种速度差异,大多数系统都习惯性的加入一个缓存层,来加速数据的读取。redis由于它优秀的处理能力和丰富的数据结构,已经成为了事实上的分布式缓存标准</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">但是,如果你以为redis只能做缓存的话,那就太小看它了。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">redis丰富的数据结构,使得它的业务使用场景非常广泛,加上rdb的持久化特性,它甚至能够被当作落地的数据库使用。在这种情况下,redis能够撑起大多数互联网公司,尤其是社交、游戏、直播类公司的半壁江山。</p> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="float: left;display: block;width: 90%;border-top: 1px solid #000;height: 1px;line-height: 1px;margin-left: -5px;margin-top: -17px;"> </span><span style="display: block;width: 3px;margin-left: 5%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">1. Redis能够胜任存储工作</a></span><span style="display: block;width: 3px;margin-left: 95%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="float: right;display: block;width: 90%;border-bottom: 1px solid #000;height: 1px;line-height: 1px;margin-right: -5px;margin-top: 16px;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">redis提供了非常丰富的集群模式:<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">主从</code>、<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">哨兵</code>、<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">cluster</code>,满足服务高可用的需求。同时,redis提供了两种持久化方式:<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">aof</code>和<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">rdb</code>,常用的是rdb。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">通过<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">bgsave</code>指令,主进程会fork出新的进程,回写磁盘。bgsave相当于做了一个快照,由于它并没有WAL日志和checkpoint机制,是无法做到实时备份的。如果机器突然断电,那就很容易丢失数据。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">幸运的是,redis是内存型的数据库,主丛同步的速度是非常快的。如果你的集群维护的好,内存分配的合理,那么除非机房断电,否则redis的SLA,会一直保持在非常高的水平。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><a target="_blank" href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" textvalue="你已选中了添加链接的内容" tab="innerlink" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 10px 0px 0px;right: auto;bottom: auto;"><img data-ratio="0.42724458204334365" src="/upload/7e742e74c964cf75bfb76f7dc098427b.jpg" data-type="jpeg" data-w="646" style="display: block;margin: 0px;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;"></span></a><br></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">听起来不是绝对可靠啊,有丢失数据的可能!这在一般CRUD的业务中,是无法忍受的。但为什么redis能够满足大多数互联网公司的需求?这也是由业务属性所决定的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在决定最大限度拥抱redis之前,你需要确认你的业务是否有以下特点:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">除了核心业务,是否大多数业务对于数据的可靠性要求较低,丢失一两条数据是可以忍受的?</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 面对的是C端用户,可根据用户ID快速定位到一类数据,数据集合普遍较小?无大量范围查询需求? </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 是否能忍受内存型数据的成本需求? </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 是否业务几乎不需要事务操作? </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">很幸运的是,这类业务需求特别的多。比如常见的社交,游戏、直播、运营类业务,都是可以完全依赖Redis的。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;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: rgba(0, 0, 0, 0.65);border-right: 1px solid rgba(0, 0, 0, 0.65);background: rgb(249, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;color: black;line-height: 26px;">推荐下自己做的 Spring Boot 的实战项目:</p> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;color: black;line-height: 26px;">https://github.com/YunaiV/ruoyi-vue-pro</p> </blockquote> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="float: left;display: block;width: 90%;border-top: 1px solid #000;height: 1px;line-height: 1px;margin-left: -5px;margin-top: -17px;"> </span><span style="display: block;width: 3px;margin-left: 5%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">2. Redis 应用场景</a></span><span style="display: block;width: 3px;margin-left: 95%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="float: right;display: block;width: 90%;border-bottom: 1px solid #000;height: 1px;line-height: 1px;margin-right: -5px;margin-top: 16px;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Redis具有松散的文档结构,丰富的数据类型,能够适应千变万化的scheme变更需求,接下来我将介绍Redis除缓存外的大量的应用场景。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><a target="_blank" href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" textvalue="你已选中了添加链接的内容" tab="innerlink" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 10px 0px 0px;right: auto;bottom: auto;"><img data-ratio="0.6873198847262247" src="/upload/c1b5caa21d38156d4b2076d9c5786713.jpg" data-type="jpeg" data-w="694" style="display: block;margin: 0px;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;"></span></a><br></p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">2.1 基本用户数据存储</a></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在传统的数据库设计中,用户表是非常难以设计的,变更的时候会伤筋动骨。使用Redis的<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">hash</code>结构,可以实现松散的数据模型设计。某些不固定,验证型的功能属性,可以以JSON接口直接存储在hash的value中。使用hash结构,可以采用HGET和HMGET等指令,只获取自己所需要的数据,在使用上也是非常便捷的。</p> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG3g2ZGN2gJDURC2fRLCvZta0xfTKM2EvpfRP51qtI0pibAlzficDyibolrzaiclbYmzia3NR0a4rppsLGoFGtNJg0mUc/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;">&gt;HSET&nbsp;user:199929&nbsp;sex&nbsp;m<br>&gt;HSET&nbsp;user:199929&nbsp;age&nbsp;22<br>&gt;HGETALL&nbsp;user:199929<br>1)&nbsp;<span style="color: #D69D85;line-height: 26px;">"sex"</span><br>2)&nbsp;<span style="color: #D69D85;line-height: 26px;">"m"</span><br>3)&nbsp;<span style="color: #D69D85;line-height: 26px;">"age"</span><br>4)&nbsp;<span style="color: #D69D85;line-height: 26px;">"22"</span><br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这种非统计型的、读多写少的场景,是非常适合使用KV结构进行存储的。Redis的hash结构提供了非常丰富的指令,某个属性也可以使用<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">HINCRBY</code>进行递增递减,非常的方便。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">2.2 实现计数器</a></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">上面稍微提了一下HINCRBY指令,而对于Redis的Key本身来说,也有<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">INCRBY</code>指令,实现<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">某个值</code>的递增递减。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">比如以下场景:统计某个帖子的点赞数;存放某个话题的关注数;存放某个标签的粉丝数;存储一个大体的评论数;某个帖子热度;红点消息数;点赞、喜欢、收藏数等。</p> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG3g2ZGN2gJDURC2fRLCvZta0xfTKM2EvpfRP51qtI0pibAlzficDyibolrzaiclbYmzia3NR0a4rppsLGoFGtNJg0mUc/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;">&gt;&nbsp;INCRBY&nbsp;feed:e3kk38j4kl:like&nbsp;1<br>&gt;&nbsp;INCRBY&nbsp;feed:e3kk38j4kl:like&nbsp;1<br>&gt;&nbsp;GET&nbsp;feed:e3kk38j4kl:like<br><span style="color: #D69D85;line-height: 26px;">"2"</span><br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">像微博这样容易出现热点的业务,传统的数据库,肯定是撑不住的,就要借助于内存数据库。由于Redis的速度非常快,就不用再采用传统DB非常慢的<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">count</code>操作,所有这种递增操作都是毫秒级别的,而且效果都是实时的。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">2.3 排行榜</a></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">排行榜能提高参与者的积极性,所以这项业务非常常见,它本质上是一个topn的问题。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Redis中有一个叫做zset的数据结构,使用跳表实现的有序列表,可以很容易实现排行榜一类的问题。当存入zset中的数据,达到千万甚至是亿的级别,依然能够保持非常高的并发读写,且拥有非常棒的平均响应时间(5ms以内)。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">使用<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">zadd</code> 可以添加新的记录,我们会使用排行相关的分数,作为记录的score值,然后使用<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">zrevrange</code>指令即可获取实时的排行榜数据,而<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">zrevrank</code>则可以非常容易的获取用户的实时排名。</p> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG3g2ZGN2gJDURC2fRLCvZta0xfTKM2EvpfRP51qtI0pibAlzficDyibolrzaiclbYmzia3NR0a4rppsLGoFGtNJg0mUc/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;">&gt;ZADD&nbsp;sorted:xjjdog:2021-07&nbsp;&nbsp;55&nbsp;dog0<br>&gt;ZADD&nbsp;sorted:xjjdog:2021-07&nbsp;&nbsp;89&nbsp;dog1<br>&gt;ZADD&nbsp;sorted:xjjdog:2021-07&nbsp;&nbsp;32&nbsp;dog2<br>&gt;ZCARD&nbsp;sorted:xjjdog:2021-07<br>&gt;3<br>&gt;&nbsp;ZREVRANGE&nbsp;&nbsp;sorted:xjjdog:2021-07&nbsp;&nbsp;0&nbsp;-10&nbsp;WITHSCORES&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">#&nbsp;top10排行榜</span><br>1)&nbsp;<span style="color: #D69D85;line-height: 26px;">"dog1"</span><br>2)&nbsp;<span style="color: #D69D85;line-height: 26px;">"89"</span><br>3)&nbsp;<span style="color: #D69D85;line-height: 26px;">"dog0"</span><br>4)&nbsp;<span style="color: #D69D85;line-height: 26px;">"55"</span><br>5)&nbsp;<span style="color: #D69D85;line-height: 26px;">"dog2"</span><br>6)&nbsp;<span style="color: #D69D85;line-height: 26px;">"32"</span><br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">2.4 好友关系</a></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">set</code>结构,是一个没有重复数据的集合,你可以将某个用户的关注列表、粉丝列表、双向关注列表、黑名单、点赞列表等,使用独立的zset进行存储。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">使用<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">ZADD</code>、<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">ZRANK</code>等,将用户的黑名单使用ZADD添加,ZRANK使用返回的sorce值判断是否存在黑名单中。使用<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">sinter</code>指令,可以获取A和B的共同好友。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">除了好友关系,有着明确黑名单、白名单业务场景的数据,都可以使用set结构进行存储。这种业务场景还有很多,比如某个用户上传的通讯录,计算通讯录的好友关系等等。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在实际使用中,使用zset存储这类关系的更多一些。zset同set一样,都不允许有重复值,但zset多了一个score字段,我们可以存储一个时间戳,用来标明关系建立所发生的时间,有更明确的业务含义。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">2.5 统计活跃用户数</a></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">类似统计每天的活跃用户、用户签到、用户在线状态,这种零散的需求,实在是太多了。如果为每一个用户存储一个bool变量,那占用的空间就太多了。这种情况下,我们可以使用<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">bitmap</code>结构,来节省大量的存储空间。</p> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG3g2ZGN2gJDURC2fRLCvZta0xfTKM2EvpfRP51qtI0pibAlzficDyibolrzaiclbYmzia3NR0a4rppsLGoFGtNJg0mUc/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;">&gt;SETBIT&nbsp;online:2021-07-23&nbsp;3876520333&nbsp;1<br>&gt;SETBIT&nbsp;online:2021-07-24&nbsp;3876520333&nbsp;1<br>&gt;GETBIT&nbsp;online:2021-07-23&nbsp;3876520333<br>1<br>&gt;BITOP&nbsp;AND&nbsp;active&nbsp;online:2021-07-23&nbsp;online:2021-07-24<br>&gt;GETBIT&nbsp;active&nbsp;3876520333<br>1<br>&gt;DEBUG&nbsp;OBJECT&nbsp;online:2021-07-23<br>Value&nbsp;at:0x7fdfde438bf0&nbsp;refcount:1&nbsp;encoding:raw&nbsp;serializedlength:5506446&nbsp;lru:16410558&nbsp;lru_seconds_idle:5<br>(0.96s)<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">注意,如果你的id很大,你需要先进行一次预处理,否则它会占用非常多的内存。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">bitmap包含一串连续的2进制数字,使用1bit来表示真假问题。在bitmap上,可以使用and、or、xor等位操作(<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">bitop</code>)。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">2.6 分布式锁</a></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Redis的分布式锁,是一种轻量级的解决方案。虽然它的可靠性比不上Zookeeper之类的系统,但Redis分布式锁有着极高的吞吐量。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">一个最简陋的加锁动作,可以使用redis带nx和px参数的set指令去完成。下面是一小段简单的分布式样例代码。</p> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG3g2ZGN2gJDURC2fRLCvZta0xfTKM2EvpfRP51qtI0pibAlzficDyibolrzaiclbYmzia3NR0a4rppsLGoFGtNJg0mUc/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;"><span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span>&nbsp;String&nbsp;<span style="line-height: 26px;">lock</span><span style="line-height: 26px;">(String&nbsp;key,&nbsp;<span style="color: #569CD6;line-height: 26px;">int</span>&nbsp;timeOutSecond)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">for</span>&nbsp;(;&nbsp;;&nbsp;)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;stamp&nbsp;=&nbsp;String.valueOf(System.nanoTime());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">boolean</span>&nbsp;exist&nbsp;=&nbsp;redisTemplate.opsForValue().setIfAbsent(key,&nbsp;stamp,&nbsp;timeOutSecond,&nbsp;TimeUnit.SECONDS);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">if</span>&nbsp;(exist)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">return</span>&nbsp;stamp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br><span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span>&nbsp;<span style="color: #569CD6;line-height: 26px;">void</span>&nbsp;<span style="line-height: 26px;">unlock</span><span style="line-height: 26px;">(String&nbsp;key,&nbsp;String&nbsp;stamp)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;redisTemplate.execute(script,&nbsp;Arrays.asList(key),&nbsp;stamp);<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">删除操作的lua为。</p> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG3g2ZGN2gJDURC2fRLCvZta0xfTKM2EvpfRP51qtI0pibAlzficDyibolrzaiclbYmzia3NR0a4rppsLGoFGtNJg0mUc/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;"><span style="color: #4EC9B0;line-height: 26px;">local</span>&nbsp;stamp&nbsp;=&nbsp;ARGV[1]<br><span style="color: #4EC9B0;line-height: 26px;">local</span>&nbsp;key&nbsp;=&nbsp;KEYS[1]<br><span style="color: #4EC9B0;line-height: 26px;">local</span>&nbsp;current&nbsp;=&nbsp;redis.call(<span style="color: #D69D85;line-height: 26px;">"GET"</span>,key)<br><span style="color: #569CD6;line-height: 26px;">if</span>&nbsp;stamp&nbsp;==&nbsp;current&nbsp;<span style="color: #569CD6;line-height: 26px;">then</span><br>&nbsp;&nbsp;&nbsp;&nbsp;redis.call(<span style="color: #D69D85;line-height: 26px;">"DEL"</span>,key)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #4EC9B0;line-height: 26px;">return</span>&nbsp;<span style="color: #D69D85;line-height: 26px;">"OK"</span><br>end<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">redisson的RedLock,是使用最普遍的分布式锁解决方案,有读写锁的差别,并处理了多redis实例情况下的异常问题。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">2.7 分布式限流</a></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">使用计数器去实现简单的限流,在Redis中是非常方便的,只需要使用incr配合expire指令即可。</p> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG3g2ZGN2gJDURC2fRLCvZta0xfTKM2EvpfRP51qtI0pibAlzficDyibolrzaiclbYmzia3NR0a4rppsLGoFGtNJg0mUc/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;">&nbsp;incr&nbsp;key<br>&nbsp;expire&nbsp;key&nbsp;1<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这种简单的实现,通常来说不会有问题,但在流量比较大的情况下,在时间跨度上会有流量突然飙升的风险。根本原因,就是这种时间切分方式太固定了,没有类似滑动窗口这种平滑的过度方案。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">同样是redisson的RRateLimiter,实现了与<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">guava</code>中类似的分布式限流工具类,使用非常便捷。下面是一个简短的例子:</p> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG3g2ZGN2gJDURC2fRLCvZta0xfTKM2EvpfRP51qtI0pibAlzficDyibolrzaiclbYmzia3NR0a4rppsLGoFGtNJg0mUc/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;">&nbsp;RRateLimiter&nbsp;limiter&nbsp;=&nbsp;redisson.getRateLimiter(<span style="color: #D69D85;line-height: 26px;">"xjjdogLimiter"</span>);<br>&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">//&nbsp;只需要初始化一次</span><br>&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">//&nbsp;每2秒钟5个许可</span><br>&nbsp;limiter.trySetRate(RateType.OVERALL,&nbsp;<span style="color: #B8D7A3;line-height: 26px;">5</span>,&nbsp;<span style="color: #B8D7A3;line-height: 26px;">2</span>,&nbsp;RateIntervalUnit.SECONDS);<br>&nbsp;<br>&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">//&nbsp;没有可用的许可,将一直阻塞&nbsp;&nbsp;&nbsp;&nbsp;</span><br>&nbsp;limiter.acquire(<span style="color: #B8D7A3;line-height: 26px;">3</span>);<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">2.8 消息队列</a></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">redis可以实现简单的队列。在生产者端,使用LPUSH加入到某个列表中;在消费端,不断的使用RPOP指令取出这些数据,或者使用阻塞的BRPOP指令获取数据,适合小规模的抢购需求。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Redis还有PUB/SUB模式,不过pubsub更适合做消息广播之类的业务。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在Redis5.0中,增加了stream类型的数据结构。它比较类似于Kafka,有主题和消费组的概念,可以实现多播以及持久化,已经能满足大多数业务需求了。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">2.9 LBS应用</a></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">早早在Redis3.2版本,就推出了GEO功能。通过<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">GEOADD</code>指令追加lat、lng经纬数据,可以实现坐标之间的距离计算、包含关系计算、附近的人等功能。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">关于GEO功能,最强大的开源方案是基于PostgreSQL的PostGIS,但对于一般规模的GEO服务,redis已经足够用了。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">2.10 更多扩展应用场景</a></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">要看redis能干什么,就不得不提以下java的客户端类库redisson。redisson包含丰富的分布式数据结构,全部是基于redis进行设计的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">redisson提供了比如Set、 SetMultimap、 ScoredSortedSet、 SortedSet, Map、 ConcurrentMap、 List、 ListMultimap、 Queue、BlockingQueue等非常多的数据结构,使得基于redis的编程更加的方便。在github上,可以看到有上百个这样的数据结构:https://github.com/redisson/redisson/tree/master/redisson/src/main/java/org/redisson/api。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">对于某个语言来说,基本的数组、链表、集合等api,配合起来能够完成大部分业务的开发。Redis也不例外,它拥有这些基本的api操作能力,同样能够组合成分布式的、线程安全的高并发应用。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">由于Redis是基于内存的,所以它的速度非常快,我们也会把它当作一个中间数据的存储地去使用。比如一些公用的配置,放到redis中进行分享,它就充当了一个配置中心的作用;比如把JWT的令牌存放到Redis中,就可以突破JWT的一些限制,做到安全登出。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;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: rgba(0, 0, 0, 0.65);border-right: 1px solid rgba(0, 0, 0, 0.65);background: rgb(249, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;color: black;line-height: 26px;">推荐下自己做的 Spring Cloud 的实战项目:</p> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;color: black;line-height: 26px;">https://github.com/YunaiV/onemall</p> </blockquote> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="float: left;display: block;width: 90%;border-top: 1px solid #000;height: 1px;line-height: 1px;margin-left: -5px;margin-top: -17px;"> </span><span style="display: block;width: 3px;margin-left: 5%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">3. 一站式Redis面临的挑战</a></span><span style="display: block;width: 3px;margin-left: 95%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="float: right;display: block;width: 90%;border-bottom: 1px solid #000;height: 1px;line-height: 1px;margin-right: -5px;margin-top: 16px;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">redis的数据结构丰富,一般不会在功能性上造成困扰。但随着请求量的增加,SLA要求的提高,我们势必会对Redis进行一些改造和定制性开发。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">3.1 高可用挑战</a></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">redis提供了主从、哨兵、cluster等三种集群模式,其中cluster模式为目前大多数公司所采用的方式。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">但是,redis的cluster模式,有不少的硬伤。redis cluster采用虚拟槽的概念,把所有的key映射到 0~16383个整数槽内,属于无中心化的架构。但它的维护成本较高,slave也不能够参与读取操作。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">它的主要问题,在于一些批量操作的限制。由于key被hash到多台机器上,所以mget、hmset、sunion等操作就非常的不友好,经常发生性能问题。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">redis的主从模式是最简单的模式,但无法做到自动failover,通常在主从切换后,还需要修改业务代码,这是不能忍受的。即使加上haproxy这样的负载均衡组件,复杂性也是非常高的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">哨兵模式在主从数量比较多的时候,能够显著的体现它的价值。一个哨兵集群,能够监控成百上千个集群,但是哨兵集群本身的维护是比较困难的。幸运的是,redis的文本协议非常简单,在netty中,甚至直接提供了redis的codec。自研一套哨兵系统,加强它的功能,是可行的。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">3.2 冷热数据分离</a></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">redis的特点是,不管什么数据,都一股脑地搞到内存里做计算,这对于有时间序列概念,有冷热数据之分的业务,造成了非常大的成本考验。为什么大多数开发者喜欢把数据存放在MySQL中,而不是Redis中?除了事务性要求以外,很大原因是历史数据的问题。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">通常,这种冷热数据的切换,是由中间件完成的。我们上面也谈到了,Redis是一个文本协议,非常简单。做一个中间件,或者做一个协议兼容的Redis模拟存储,是比较容易的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">比如我们Redis中,只保留最近一年的活跃用户。一个好几年不活跃的用户,突然间访问了系统,这时候我们获取数据的时候,就需要中间件进行转换,从容量更大,速度更慢的存储中查找。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这个时候,Redis的作用,更像是一个热库,更像是一个传统cache层做的事情,发生在业务已经上规模的时候。但是注意,直到此时,我们的业务层代码,一直都是操作的redis的api。它们使用这众多的函数指令,并不关心数据到底是真正存储在redis中,还是在ssdb中。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">3.3 功能性需求</a></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">redis还能玩很多花样。举个例子,全文搜索。很多人都会首选es,但redis生态就提供了一个模块:RediSearch,可以做查询,可以做filter。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">但我们通常还会有更多的需求,比如统计类、搜索类、运营效果分析等。这类需求与大数据相关,即使是传统的DB也不能胜任。这时候,我们当然要把redis中的数据,导入到其他平台进行计算啦。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如果你选择的是redis数据库,那么dba打交道的,就是rdb,而不是binlog。有很多的rdb解析工具(比如redis-rdb-tools),能够定期把rdb解析成记录,导入到hadoop等其他平台。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">此时,rdb成为所有团队的中枢,成为基本的数据交换格式。导入到其他db后的业务,该怎么玩怎么玩,完全不会因为业务系统选用了redis就无法运转。</p> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="float: left;display: block;width: 90%;border-top: 1px solid #000;height: 1px;line-height: 1px;margin-left: -5px;margin-top: -17px;"> </span><span style="display: block;width: 3px;margin-left: 5%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">4. 总结</a></span><span style="display: block;width: 3px;margin-left: 95%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="float: right;display: block;width: 90%;border-bottom: 1px solid #000;height: 1px;line-height: 1px;margin-right: -5px;margin-top: 16px;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">大多数业务系统,跑在redis上,这是很多一直使用MySQL做业务系统的同学所不能想象的。看完了上面的介绍,相信你能够对redis能够实现的存储功能有个大体的了解。打开你的社交app、游戏app、视频app,看一下它们的功能,能够涵盖多少呢?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">我这里要强调的是,某些数据,并不是一定要落地到RDBMS才算安全,它们并不是一个强需求。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">那既然redis这么厉害,为什么还要有mysql、tidb这样的存储呢?关键还在于业务属性上。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如果一个业务系统,每次交互的数据,都是一个非常大的结果集,并涉及到非常复杂的统计、过滤工作,那么RDBMS是必须的;但如果一个系统,能够通过某个标识,快速定位到一类数据,这一类数据在可以预见的未来,是有限的,那就非常适合Redis<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">存储</code>。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">一个电商系统,选用redis做存储就是作死,但一个社交系统就快活的多。在合适的场景选用合适的工具,才是我们应该做的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">但是一个系统,能否在产品验证期,就能快速的响应变化,快速开发上线,才是成功的关键。这也是使用redis做数据库,所能够带来的最大好处。千万别被那概率极低的丢数据场景,给吓怕了。比起产品成功,你的系统即使是牢如钢铁,也一文不值。</p> </section>

一文理解 Elasticsearch 快速检索原理!

作者:微信小助手

<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;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 15px;letter-spacing: 0.05em;color: rgb(89, 89, 89);"> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzA3ODIxNjYxNQ==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/Baq5lYpIw7WIvzVfHZ61VvJoaTzb7HDxtsJoicXicBJJ5uc9FrPG3eVztG6eBfUfdwIYmoqu8ibM5APCTDBUBPLrg/0?wx_fmt=png" data-nickname="架构文摘" data-alias="ArchDigest" data-signature="每天一篇架构领域重磅好文,涉及一线互联网公司应用架构(高可用、高性能、高稳定)、大数据、机器学习、Java架构等各个热门领域。" data-from="0"></mpprofile> </section> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">最近接触的几个项目都使用到了 Elasticsearch (以下简称 ES ) 来存储数据和对数据进行搜索分析,就对 ES 进行了一些学习。本文整理自我自己的一次技术分享。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">本文不会关注 ES 里面的分布式技术、相关 API 的使用,而是专注分享下“ES 如何快速检索”这个主题上面。这个也是我在学习之前对 ES 最感兴趣的部分。</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;"> 传统关系型数据库和 ES 的差别 </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;">细究倒排索引:</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;"> 倒排索引具体是个什么样子的(posting list→term dic→term index) </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 关于 postings list 的一些巧技(FOR、Roaring Bitmaps) </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 如何快速做联合查询? </section></li> </ul> <h2 data-tool="mdnice编辑器" style="line-height: 32px;color: rgb(53, 179, 120);display: inline-block;border-bottom: 0px solid rgb(53, 179, 120);border-top-color: rgb(53, 179, 120);border-right-color: rgb(53, 179, 120);border-left-color: rgb(53, 179, 120);font-size: 23px;margin-top: 1em;margin-bottom: 0rem;padding-top: 0.5em;padding-bottom: 0.5em;font-weight: bold;"><span style="display: none;"></span>关于搜索</h2> <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;"> <img src="/upload/d3d0bab0dff5d70885aac539d37eee1a.jpg" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-ratio="0.5806451612903226" data-w="620"> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">用传统关系型数据库和 ES 实现会有什么差别?如果用像 MySQL 这样的 RDBMS 来存储古诗的话,我们应该会去使用这样的 SQL 去查询:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/98Nz5LFElxzrK8Ricc4gWglEIKrEfiaqPG0QDEDGdIiawLTkWLl3dnMRLKfxrQBKKZhYtoIlibBssNzuNyPia7KHo0Ikh9W7kKwgS/640?wx_fmt=svg&quot;) 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;">select&nbsp;name&nbsp;from&nbsp;poems&nbsp;<span style="color: #e6c07b;line-height: 26px;">where</span>&nbsp;content&nbsp;like&nbsp;<span style="color: #98c379;line-height: 26px;">"%前%"</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;">这种我们称为顺序扫描法,需要遍历所有的记录进行匹配。不但效率低,而且不符合我们搜索时的期望。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">比如我们在搜索“ABCD"这样的关键词时,通常还希望看到"A","AB","CD",“ABC”的搜索结果。于是乎就有了专业的搜索引擎,比如我们今天的主角 ES。</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> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img src="/upload/b440c091eaac5a0bf0cdda346b4ac953.jpg" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-ratio="0.38425925925925924" data-w="1080"> </figure> <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;"> 内容分词,提取关键词 </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;"> 用户输入关键词进行搜索 </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;">这里我们就引出了一个概念,也是我们今天的要剖析的重点倒排索引。也是 ES 的核心知识点。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">如果你了解 ES 应该知道,ES 可以说是对 Lucene 的一个封装,里面关于倒排索引的实现就是通过 lucene 这个 jar 包提供的 API 实现的,所以下面讲的关于倒排索引的内容实际上都是 lucene 里面的内容。</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> <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;"> <img src="/upload/cc5ac53add3f3e58a0d55933a42bc252.jpg" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-ratio="0.657608695652174" data-w="736"> </figure> <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;">当然这只是一个很大白话的形式来描述倒排索引的简要工作原理。在 ES &nbsp;中,这个倒排索引是具体是个什么样的,怎么存储的等等,这些才是倒排索引的精华内容。</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> <h5 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;"><span style="display: none;"></span><strong style="color: rgb(53, 179, 120);">term:</strong> 关键词这个东西是我自己的讲法,在 ES 中,关键词被称为 term。<span style="display: none;"></span></h5> <h5 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;"><span style="display: none;"></span><strong style="color: rgb(53, 179, 120);">postings list:</strong> 还是用上面的例子,{静夜思,望庐山瀑布}是 "前" 这个 term 所对应列表。在 ES 中,这些被描述为所有包含特定 term 文档的 id 的集合。<span style="display: none;"></span></h5> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">由于整型数字 integer 可以被高效压缩的特质,integer 是最适合放在 postings list 作为文档的唯一标识的,ES 会对这些存入的文档进行处理,转化成一个唯一的整型 id。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">再说下这个 id 的范围,在存储数据的时候,在每一个 shard 里面,ES 会将数据存入不同的 segment,这是一个比 shard 更小的分片单位,这些 segment 会定期合并。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">在每一个 segment 里面都会保存最多 2^31 个文档,每个文档被分配一个唯一的 id,从 0 到 (2^31)-1。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img src="/upload/14f3c94f21b124b2002c1e18dad2f93d.jpg" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-ratio="1.0125" data-w="480"> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">相关的名词都是 ES 官方文档给的描述,后面参考材料中都可以找到出处。</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;">在实际生产场景中,比如 ES 最常用的日志分析,日志内容进行分词之后,可以得到多少的 term?</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">那么如何快速的在海量 term 中查询到对应的 term 呢?遍历一遍显然是不现实的。</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);">term dictionary:</strong> 于是乎就有了 term dictionary,ES 为了能快速查找到 term,将所有的 term 排了一个序,二分法查找。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">是不是感觉有点眼熟,这不就是 MySQL 的索引方式的,直接用 B+树建立索引词典指向被索引的数据。</p> <h5 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;"><span style="display: none;"></span><strong style="color: rgb(53, 179, 120);">term index:</strong> 但是问题又来了,你觉得 Term Dictionary 应该放在哪里?肯定是放在内存里面吧?磁盘 io 那么慢。就像 MySQL 索引就是存在内存里面了。<span style="display: none;"></span></h5> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">但是如果把整个 term dictionary 放在内存里面会有什么后果呢?内存爆了...</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">别忘了,ES 默认可是会对全部 text 字段进行索引,必然会消耗巨大的内存,为此 ES 针对索引进行了深度的优化。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">在保证执行效率的同时,尽量缩减内存空间的占用。于是乎就有了 term index。</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);">Term index:</strong> 从数据结构上分类算是一个“Trie 树”,也就是我们常说的字典树。</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;">这棵树不会包含所有的 term,它包含的是 term 的一些前缀(这也是字典树的使用场景,公共前缀)。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">通过 term index 可以快速地定位到 term dictionary 的某个 offset,然后从这个位置再往后顺序查找。就想右边这个图所表示的。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">怎么样,像不像我们查英文字典,我们定位 S 开头的第一个单词,或者定位到 Sh 开头的第一个单词,然后再往后顺序查询?</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">lucene 在这里还做了两点优化,一是 term dictionary 在磁盘上面是分 block 保存的,一个 block 内部利用公共前缀压缩,比如都是 Ab 开头的单词就可以把 Ab 省去。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">二是 term index 在内存中是以 FST(finite state transducers)的数据结构保存的。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">FST 有两个优点:</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;"> <p 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> </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> <p 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> O(len(str)) 的查询时间复杂度。</p> </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;">FST 的理论比较复杂,本文不细讲,延伸阅读:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/98Nz5LFElxzrK8Ricc4gWglEIKrEfiaqPG0QDEDGdIiawLTkWLl3dnMRLKfxrQBKKZhYtoIlibBssNzuNyPia7KHo0Ikh9W7kKwgS/640?wx_fmt=svg&quot;) 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;">https://www.shenyanchao.cn/blog/2018/12/04/lucene-fst/<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;">OK,现在我们能得到 lucene 倒排索引大致是个什么样子的了。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img src="/upload/1414c5927df02fc7072a9c3a8ea7f173.jpg" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-ratio="0.5213178294573644" data-w="1032"> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">关于 postings list 的一些巧技</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">在实际使用中,postings list 还需要解决几个痛点:</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;"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">postings list 如果不进行压缩,会非常占用磁盘空间。</p> </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">联合查询下,如何快速求交并集(intersections and unions)。</p> </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;">对于如何压缩,可能会有人觉得没有必要,”posting list 不是已经只存储文档 id 了吗?还需要压缩?”,但是如果在 posting list 有百万个 doc id 的情况,压缩就显得很有必要了。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">比如按照朝代查询古诗,至于为啥需要求交并集,ES 是专门用来搜索的,肯定会有很多联合查询的需求吧 (AND、OR)。按照上面的思路,我们先将如何压缩。</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> <h5 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;"><span style="display: none;"></span><strong style="color: rgb(53, 179, 120);">Frame of Reference:</strong> 在 lucene 中,要求 postings lists 都要是有序的整形数组。<span style="display: none;"></span></h5> <h5 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;"><span style="display: none;"></span>这样就带来了一个很好的好处,可以通过 增量编码(delta-encode)这种方式进行压缩。<span style="display: none;"></span></h5> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">比如现在有 id 列表&nbsp;[73, 300, 302, 332, 343, 372],转化成每一个 id 相对于前一个 id 的增量值(第一个 id 的前一个 id 默认是 0,增量就是它自己)列表是 [73, 227, 2, 30, 11, 29]。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">在这个新的列表里面,所有的 id 都是小于 255 的,所以每个 id 只需要一个字节存储。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">实际上 ES 会做的更加精细:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img src="/upload/e11272311c76e6ed7301fe6ae6bf5d5.jpg" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-ratio="0.8037037037037037" data-w="1080"> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">它会把所有的文档分成很多个 block,每个 block 正好包含 256 个文档,然后单独对每个文档进行增量编码。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">计算出存储这个 block 里面所有文档最多需要多少位来保存每个 id,并且把这个位数作为头信息(header)放在每个 block 的前面。这个技术叫 Frame of Reference。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">上图也是来自于 ES 官方博客中的一个示例(假设每个 block 只有 3 个文件而不是 256)。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">FOR 的步骤可以总结为:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img src="/upload/b07e6b0597028c90db663dc4b239d59e.jpg" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-ratio="0.14898989898989898" data-w="792"> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">进过最后的位压缩之后,整型数组的类型从固定大小(8,16,32,64 位)4 种类型,扩展到了 [1-64] 位共 64 种类型。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">通过以上的方式可以极大的节省 posting list 的空间消耗,提高查询性能。不过 ES 为了提高 filter 过滤器查询的性能,还做了更多的工作,那就是缓存。</p> <h5 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;"><span style="display: none;"></span><strong style="color: rgb(53, 179, 120);">Roaring Bitmaps (for filter cache):</strong> 在 ES 中,可以使用 filters 来优化查询,filter 查询只处理文档是否匹配与否,不涉及文档评分操作,查询的结果可以被缓存。<span style="display: none;"></span></h5> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">对于 filter 查询,es 提供了 filter cache 这种特殊的缓存,filter cache 用来存储 filters 得到的结果集。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">缓存 filters 不需要太多的内存,它只保留一种信息,即哪些文档与 filter 相匹配。同时它可以由其它的查询复用,极大地提升了查询的性能。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">我们上面提到的 Frame Of Reference 压缩算法对于 postings list 来说效果很好,但对于需要存储在内存中的 filter cache 等不太合适。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">filter cache 会存储那些经常使用的数据,针对 filter 的缓存就是为了加速处理效率,对压缩算法要求更高。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">对于这类 postings list,ES 采用不一样的压缩方式。那么让我们一步步来。首先我们知道 postings list 是 Integer 数组,具有压缩空间。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">假设有这么一个数组,我们第一个压缩的思路是什么?用位的方式来表示,每个文档对应其中的一位,也就是我们常说的位图,bitmap。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">它经常被作为索引用在数据库、查询引擎和搜索引擎中,并且位操作(如 and 求交集、or 求并集)之间可以并行,效率更好。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img src="/upload/82afa7e71b553cea521c5e57a71ff0e.jpg" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-ratio="0.8554216867469879" data-w="664"> </figure> <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;">也就是说不适用于稀疏存储。业内对于稀疏位图也有很多成熟的压缩方案,lucene 采用的就是 roaring bitmaps。</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;"> <img src="/upload/c02483a31798eec90dae1f20bc0daeb1.jpg" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-ratio="0.8935185185185185" data-w="1080"> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">将 doc id 拆成高 16 位,低 16 位。对高位进行聚合 (以高位做 key,value 为有相同高位的所有低位数组),根据低位的数据量 (不同高位聚合出的低位数组长度不相同),使用不同的 container(数据结构) 存储。</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;"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">len&lt;4096 ArrayContainer 直接存值</p> </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">len&gt;=4096 BitmapContainer 使用 bitmap 存储</p> </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;">分界线的来源:value 的最大总数是为2^16=65536. 假设以 bitmap 方式存储需要 65536bit=8kb,而直接存值的方式,一个值 2 byte,4K 个总共需要2byte*4K=8kb。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">所以当 value 总量 &lt;4k 时,使用直接存值的方式更节省空间。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img src="/upload/3dad6690417f8357afd645895d65dc0c.jpg" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-ratio="0.75" data-w="1080"> </figure> <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;"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">高位聚合(假设数据中有 100w 个高位相同的值,原先需要 100w2byte,现在只要 12byte)</p> </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">低位压缩</p> </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;">缺点就在于位操作的速度相对于原生的 bitmap 会有影响。这就是 trade-off 呀。平衡的艺术。</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;">讲完了压缩,我们再来讲讲联合查询。先讲简单的,如果查询有 filter cache,那就是直接拿 filter cache 来做计算,也就是说位图来做 AND 或者 OR 的计算。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">如果查询的 filter 没有缓存,那么就用 skip list 的方式去遍历磁盘上的 postings list。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img src="/upload/88f0e5dc19dfc5a4224d2c7f187963b0.jpg" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-ratio="1.3819444444444444" data-w="288"> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">以上是三个 posting list。我们现在需要把它们用 AND 的关系合并,得出 posting list 的交集。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">首先选择最短的 posting list,逐个在另外两个 posting list 中查找看是否存在,最后得到交集的结果。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">遍历的过程可以跳过一些元素,比如我们遍历到绿色的 13 的时候,就可以跳过蓝色的 3 了,因为 3 比 13 要小。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">用 skip list 还会带来一个好处,还记得前面说的吗,postings list 在磁盘里面是采用 FOR 的编码方式存储的。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">会把所有的文档分成很多个 block,每个 block 正好包含 256 个文档,然后单独对每个文档进行增量编码,计算出存储这个 block 里面所有文档最多需要多少位来保存每个 id,并且把这个位数作为头信息(header)放在每个 block 的前面。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">因为这个 FOR 的编码是有解压缩成本的。利用 skip list,除了跳过了遍历的成本,也跳过了解压缩这些压缩过的 block 的过程,从而节省了 cpu。</p> <h2 data-tool="mdnice编辑器" style="line-height: 32px;color: rgb(53, 179, 120);display: inline-block;border-bottom: 0px solid rgb(53, 179, 120);border-top-color: rgb(53, 179, 120);border-right-color: rgb(53, 179, 120);border-left-color: rgb(53, 179, 120);font-size: 23px;margin-top: 1em;margin-bottom: 0rem;padding-top: 0.5em;padding-bottom: 0.5em;font-weight: bold;"><span style="display: none;"></span>总结</h2> <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;">①为了能够快速定位到目标文档,ES 使用倒排索引技术来优化搜索速度,虽然空间消耗比较大,但是搜索性能提高十分显著。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">②为了能够在数量巨大的 terms 中快速定位到某一个 term,同时节约对内存的使用和减少磁盘 io 的读取。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">lucene 使用 "term index→term dictionary→postings list" 的倒排索引结构,通过 FST 压缩放入内存,进一步提高搜索效率。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">③为了减少 &nbsp;postings list 的磁盘消耗,lucene 使用了 FOR(Frame of Reference)技术压缩,带来的压缩效果十分明显。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">④ES 的 filter 语句采用了 Roaring Bitmap 技术来缓存搜索结果,保证高频 filter 查询速度的同时降低存储空间消耗。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">⑤在联合查询时,在有 filter cache 的情况下,会直接利用位图的原生特性快速求交并集得到联合查询结果,否则使用 skip list 对多个 postings list 求交并集,跳过遍历成本并且节省部分数据的解压缩 cpu 成本。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">Elasticsearch 的索引思路</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;">所以,对于使用 Elasticsearch 进行索引时需要注意:</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;"> 同样的道理,对于 String 类型的字段,不需要 analysis 的也需要明确定义出来,因为默认也是会 analysis 的。 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 选择有规律的 ID 很重要,随机性太大的 ID(比如 Java 的 UUID) 不利于查询。 </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;">这篇文章讲的虽是 Lucene 如何实现倒排索引,如何精打细算每一块内存、磁盘空间、如何用诡谲的位运算加快处理速度。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">但往高处思考,再类比一下 MySQL,你就会发现,虽然都是索引,但是实现起来,截然不同。笼统的来说,B-tree 索引是为写入优化的索引结构。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">当我们不需要支持快速的更新的时候,可以用预先排序等方式换取更小的存储空间,更快的检索速度等好处,其代价就是更新慢,就像 ES。</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);"></blockquote> </section>

万字长文:选 Redis 还是 MQ,终于说明白了!

作者:微信小助手

<p style="white-space: normal;"><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;text-align: left;"></span></strong></span></p> <blockquote data-tool="mdnice编辑器" data-mpa-powered-by="yiban.io" style="margin-top: 0px;margin-bottom: 20px;padding: 8px 10px 4px 15px;outline: 0px;border-left-width: 2px;border-left-color: rgb(239, 112, 96);color: rgb(106, 115, 125);font-size: 0.9em;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;white-space: normal;border-top: none;border-right: none;border-bottom: none;overflow: auto;background: rgb(255, 249, 249);letter-spacing: 0.5444px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <p style="margin-bottom: 10px;outline: 0px;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 15px;color: rgb(51, 51, 51);line-height: 30px;letter-spacing: 0.5444px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;letter-spacing: 0.5444px;box-sizing: border-box !important;overflow-wrap: break-word !important;">经常听到很多人讨论:把 Redis 当作消息队列来用,是否合适?</span><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> </blockquote> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;outline: 0px;max-width: 100%;min-height: 1em;white-space: normal;background-color: rgb(255, 255, 255);line-height: 30px;font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 15px;letter-spacing: 0.5444px;box-sizing: border-box !important;overflow-wrap: break-word !important;">有人表示赞成,他们认为 Redis 很轻量,用作队列很方便。</p> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;outline: 0px;max-width: 100%;min-height: 1em;white-space: normal;background-color: rgb(255, 255, 255);line-height: 30px;font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 15px;letter-spacing: 0.5444px;box-sizing: border-box !important;overflow-wrap: break-word !important;">也有人反对,认为 Redis 会「丢」数据,最好还是用「专业」的消息中间件更稳妥。</p> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;outline: 0px;max-width: 100%;min-height: 1em;white-space: normal;background-color: rgb(255, 255, 255);line-height: 30px;font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 15px;letter-spacing: 0.5444px;box-sizing: border-box !important;overflow-wrap: break-word !important;">究竟哪种方案更好呢?</p> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;outline: 0px;max-width: 100%;min-height: 1em;white-space: normal;background-color: rgb(255, 255, 255);line-height: 30px;font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 15px;letter-spacing: 0.5444px;box-sizing: border-box !important;overflow-wrap: break-word !important;">这篇文章,我就和你聊一聊把 Redis 当作队列,究竟是否合适这个问题。</p> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;outline: 0px;max-width: 100%;min-height: 1em;white-space: normal;background-color: rgb(255, 255, 255);line-height: 30px;font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 15px;letter-spacing: 0.5444px;box-sizing: border-box !important;overflow-wrap: break-word !important;">我会从简单到复杂,一步步带你梳理其中的细节,把这个问题真正讲清楚。</p> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;outline: 0px;max-width: 100%;min-height: 1em;white-space: normal;background-color: rgb(255, 255, 255);line-height: 30px;font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 15px;letter-spacing: 0.5444px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;color: rgb(123, 12, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;">在文章的最后,我还会告诉你关于「技术选型」的思路,文章有点长,希望你可以耐心读完。</span></p> <p style="outline: 0px;max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages" data-galleryid="" data-ratio="0.5702479338842975" data-s="300,640" src="/upload/e1cbd018cd7f28f7fa2b05d8672eb87e.png" data-type="png" data-w="847" style="outline: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;width: 677px !important;visibility: visible !important;"></p> <h1 data-tool="mdnice编辑器" style="margin-top: 15px;margin-bottom: 15px;"><span style="color: rgb(123, 12, 0);"><strong><span style="color: rgb(123, 12, 0);font-size: 16px;">01 从最简单的开始:List 队列</span></strong></span></h1> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;outline: 0px;max-width: 100%;min-height: 1em;white-space: normal;background-color: rgb(255, 255, 255);line-height: 30px;font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 15px;letter-spacing: 0.5444px;box-sizing: border-box !important;overflow-wrap: break-word !important;">如果你的业务需求足够简单,想把 Redis 当作队列来使用,最先想到的肯定是 List 这个数据类型。</p> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;outline: 0px;max-width: 100%;min-height: 1em;white-space: normal;background-color: rgb(255, 255, 255);line-height: 30px;font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 15px;letter-spacing: 0.5444px;box-sizing: border-box !important;overflow-wrap: break-word !important;">因为 List 底层的实现就是一个「链表」,在头部和尾部操作元素,时间复杂度都是 O(1),这意味着它非常符合消息队列的模型。</p> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;outline: 0px;max-width: 100%;min-height: 1em;white-space: normal;background-color: rgb(255, 255, 255);line-height: 30px;font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 15px;letter-spacing: 0.5444px;box-sizing: border-box !important;overflow-wrap: break-word !important;">如果把 List 当作队列,你可以这么来用。</p> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;outline: 0px;max-width: 100%;min-height: 1em;white-space: normal;background-color: rgb(255, 255, 255);line-height: 30px;font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 15px;letter-spacing: 0.5444px;box-sizing: border-box !important;overflow-wrap: break-word !important;">生产者使用 LPUSH 发布消息:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><code style="padding: 15px 16px 16px;outline: 0px;max-width: 100%;overflow-x: auto;color: rgb(171, 178, 191);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;box-sizing: border-box !important;overflow-wrap: break-word !important;">127.0.0.1:6379&gt;&nbsp;LPUSH&nbsp;queue&nbsp;msg1<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">(integer)&nbsp;1<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">127.0.0.1:6379&gt;&nbsp;LPUSH&nbsp;queue&nbsp;msg2<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">(integer)&nbsp;2<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></code></pre> <p data-tool="mdnice编辑器" style="margin-bottom: 20px;outline: 0px;max-width: 100%;min-height: 1em;white-space: normal;background-color: rgb(255, 255, 255);line-height: 30px;font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 15px;letter-spacing: 0.5444px;box-sizing: border-box !important;overflow-wrap: break-word !important;">消费者这一侧,使用 RPOP 拉取消息:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><code style="padding: 15px 16px 16px;outline: 0px;max-width: 100%;overflow-x: auto;color: rgb(171, 178, 191);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;box-sizing: border-box !important;overflow-wrap: break-word !important;">127.0.0.1:6379&gt;&nbsp;RPOP&nbsp;queue<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !impor

16 条 yyds 的代码规范

作者:微信小助手

<pre data-mpa-powered-by="yiban.io"> <p><strong style="text-align: left;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;font-family: Arial, Helvetica, sans-serif;"></span></strong><br></p><p><strong style="text-align: left;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;font-family: Arial, Helvetica, sans-serif;"># MyBatis 不要为了多个查询条件而写 1 = 1</span></strong><br></p></pre> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"><br></span></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">当遇到多个查询条件,使用where 1=1 可以很方便的解决我们的问题,但是这样很可能会造成非常大的性能损失,因为添加了 “where 1=1 ”的过滤条件之后,数据库系统就无法使用索引等查询优化策略,数据库系统将会被迫对每行数据进行扫描(即全表扫描) 以比较此行是否满足过滤条件,当表中的数据量较大时查询速度会非常慢;此外,还会存在SQL 注入的风险。</span></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"><br></span></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">反例:</span></p> <section class="code-snippet__fix code-snippet__js"> <pre class="code-snippet__js" data-lang="cs"><code><span class="code-snippet_outer">&lt;<span class="code-snippet__keyword">select</span> id=<span class="code-snippet__string">"queryBookInfo"</span> parameterType=<span class="code-snippet__string">"com.tjt.platform.entity.BookInfo"</span> resultType=<span class="code-snippet__string">"java.lang.Integer"</span>&gt;</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__function"><span class="code-snippet__keyword">select</span> <span class="code-snippet__title">count</span>(<span class="code-snippet__params">*</span>) <span class="code-snippet__keyword">from</span> t_rule_BookInfo t <span class="code-snippet__keyword">where</span> 1</span>=<span class="code-snippet__number">1</span></span></code><code><span class="code-snippet_outer">&lt;<span class="code-snippet__keyword">if</span> test=<span class="code-snippet__string">"title !=null and title !='' "</span>&gt;</span></code><code><span class="code-snippet_outer"> AND title = <span class="code-snippet__meta">#{title} </span></span></code><code><span class="code-snippet_outer">&lt;/<span class="code-snippet__keyword">if</span>&gt; </span></code><code><span class="code-snippet_outer">&lt;<span class="code-snippet__keyword">if</span> test=<span class="code-snippet__string">"author !=null and author !='' "</span>&gt;</span></code><code><span class="code-snippet_outer"> AND author = <span class="code-snippet__meta">#{author}</span></span></code><code><span class="code-snippet_outer">&lt;/<span class="code-snippet__keyword">if</span>&gt; </span></code><code><span class="code-snippet_outer">&lt;/<span class="code-snippet__keyword">select</span>&gt;</span></code></pre> </section> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"></span></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">正例:</span></p> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="cs"><code><span class="code-snippet_outer">&lt;<span class="code-snippet__keyword">select</span> id=<span class="code-snippet__string">"queryBookInfo"</span> parameterType=<span class="code-snippet__string">"com.tjt.platform.entity.BookInfo"</span> resultType=<span class="code-snippet__string">"java.lang.Integer"</span>&gt;</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__function"><span class="code-snippet__keyword">select</span> <span class="code-snippet__title">count</span>(<span class="code-snippet__params">*</span>) <span class="code-snippet__keyword">from</span> t_rule_BookInfo t</span></span></code><code><span class="code-snippet_outer">&lt;<span class="code-snippet__keyword">where</span>&gt;</span></code><code><span class="code-snippet_outer"><span class="code-snippet_outer">&lt;<span class="code-snippet__keyword">if</span> test</span>=<span class="code-snippet__string">"title !=null and title !='' "</span>&gt;</span></code><code><span class="code-snippet_outer"> title = <span class="code-snippet__meta">#{title} </span></span></code><code><span class="code-snippet_outer">&lt;/<span class="code-snippet__keyword">if</span>&gt;</span></code><code><span class="code-snippet_outer">&lt;<span class="code-snippet__keyword">if</span> test=<span class="code-snippet__string">"author !=null and author !='' "</span>&gt; </span></code><code><span class="code-snippet_outer"> AND author = <span class="code-snippet__meta">#{author}</span></span></code><code><span class="code-snippet_outer">&lt;/<span class="code-snippet__keyword">if</span>&gt;</span></code><code><span class="code-snippet_outer">&lt;/<span class="code-snippet__keyword">where</span>&gt; </span></code><code><span class="code-snippet_outer">&lt;/<span class="code-snippet__keyword">select</span>&gt;</span></code></pre> </section> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"></span><br></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">UPDATE 操作也一样,可以用&lt;set&gt; 标记代替 1=1。</span></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"><br></span></p> <p style="text-align: left;"><strong><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"># 迭代entrySet() 获取Map 的key 和value</span></strong></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"><br></span></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">当循环中只需要获取Map 的主键key时,迭代keySet() 是正确的;但是,当需要主键key 和取值value 时,迭代entrySet() 才是更高效的做法,其比先迭代keySet() 后再去通过get 取值性能更佳。</span></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"><br></span></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">反例:</span></p> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="javascript"><code><span class="code-snippet_outer"><span class="code-snippet__comment">//Map&nbsp;获取value&nbsp;反例:</span></span></code><code><span class="code-snippet_outer">HashMap&lt;<span class="code-snippet__built_in">String</span>,&nbsp;<span class="code-snippet__built_in">String</span>&gt;&nbsp;map&nbsp;=&nbsp;<span class="code-snippet__keyword">new</span>&nbsp;HashMap&lt;&gt;();</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">for</span>&nbsp;(<span class="code-snippet__built_in">String</span>&nbsp;key&nbsp;:&nbsp;map.keySet()){</span></code><code><span class="code-snippet_outer"><span class="code-snippet__built_in">String</span> value = map.get(key);</span></code><code><span class="code-snippet_outer">}</span></code></pre> </section> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"><br></span></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">正例:</span></p> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="javascript"><code><span class="code-snippet_outer">&nbsp;&nbsp;<span class="code-snippet__comment">//Map&nbsp;获取key&nbsp;&amp;&nbsp;value&nbsp;正例:</span></span></code><code><span class="code-snippet_outer">HashMap&lt;<span class="code-snippet__built_in">String</span>,&nbsp;<span class="code-snippet__built_in">String</span>&gt;&nbsp;map&nbsp;=&nbsp;<span class="code-snippet__keyword">new</span>&nbsp;HashMap&lt;&gt;();</span></code><code><span class="code-snippet_outer">&nbsp;<span class="code-snippet__keyword">for</span>&nbsp;(<span class="code-snippet__built_in">Map</span>.Entry&lt;<span class="code-snippet__built_in">String</span>,<span class="code-snippet__built_in">String</span>&gt;&nbsp;entry&nbsp;:&nbsp;map.entrySet()){</span></code><code><span class="code-snippet_outer">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="code-snippet__built_in">String</span>&nbsp;key&nbsp;=&nbsp;entry.getKey();</span></code><code><span class="code-snippet_outer">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="code-snippet__built_in">String</span>&nbsp;value&nbsp;=&nbsp;entry.getValue();</span></code><code><span class="code-snippet_outer">}</span></code></pre> </section> <p style="text-align: left;"><br></p> <p style="text-align: left;"><strong><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"># 使用Collection.isEmpty() 检测空</span></strong></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"><br></span></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">使用Collection.size() 来检测是否为空在逻辑上没有问题,但是使用Collection.isEmpty() 使得代码更易读,并且可以获得更好的性能;除此之外,任何Collection.isEmpty() 实现的时间复杂度都是O(1) ,不需要多次循环遍历,但是某些通过Collection.size() 方法实现的时间复杂度可能是O(n)。O(1)纬度减少循环次数 例子</span></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"><br></span></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">反例:</span></p> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="javascript"><code><span class="code-snippet_outer" style="max-width: 1000%;">LinkedList&lt;<span class="code-snippet__built_in" style="max-width: 1000%;">Object</span>&gt;&nbsp;collection&nbsp;=&nbsp;<span class="code-snippet__keyword" style="max-width: 1000%;">new</span>&nbsp;LinkedList&lt;&gt;();</span></code><code><span class="code-snippet_outer" style="max-width: 1000%;"><span class="code-snippet__keyword" style="max-width: 1000%;">if</span>&nbsp;(collection.size()&nbsp;==&nbsp;<span class="code-snippet__number" style="max-width: 1000%;">0</span>){</span></code><code><span class="code-snippet_outer" style="max-width: 1000%;">&nbsp;&nbsp;System.out.println(<span class="code-snippet__string" style="max-width: 1000%;">"collection&nbsp;is&nbsp;empty."</span>);</span></code><code><span class="code-snippet_outer" style="max-width: 1000%;">&nbsp;}</span></code></pre> </section> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"></span><br></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">正例:</span></p> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="cs"><code><span class="code-snippet_outer" style="max-width: 1000%;">LinkedList&lt;Object&gt; collection = <span class="code-snippet__keyword" style="max-width: 1000%;">new</span> LinkedList&lt;&gt;();</span></code><code><span class="code-snippet_outer" style="max-width: 1000%;"> <span class="code-snippet__keyword" style="max-width: 1000%;">if</span> (collection.isEmpty()){</span></code><code><span class="code-snippet_outer" style="max-width: 1000%;"> System.<span class="code-snippet__keyword" style="max-width: 1000%;">out</span>.println(<span class="code-snippet__string" style="max-width: 1000%;">"collection is empty."</span>);</span></code><code><span class="code-snippet_outer" style="max-width: 1000%;"> }</span></code><code><span class="code-snippet_outer" style="max-width: 1000%;"> </span></code><code><span class="code-snippet_outer" style="max-width: 1000%;"> <span class="code-snippet__comment" style="max-width: 1000%;">//检测是否为null 可以使用CollectionUtils.isEmpty()</span></span></code><code><span class="code-snippet_outer" style="max-width: 1000%;"> <span class="code-snippet__keyword" style="max-width: 1000%;">if</span> (CollectionUtils.isEmpty(collection)){</span></code><code><span class="code-snippet_outer" style="max-width: 1000%;"> System.<span class="code-snippet__keyword" style="max-width: 1000%;">out</span>.println(<span class="code-snippet__string" style="max-width: 1000%;">"collection is null."</span>);</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer" style="max-width: 1000%;"> }</span></code></pre> </section> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"></span><br></p> <p style="text-align: left;"><strong><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"># 初始化集合时尽量指定其大小</span></strong></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"><br></span></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">尽量在初始化时指定集合的大小,能有效减少集合的扩容次数,因为集合每次扩容的时间复杂度很可能时O(n),耗费时间和性能。</span></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"></span></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">反例:</span></p> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="cpp"><code><span class="code-snippet_outer" style="max-width: 1000%;"><span class="code-snippet__comment" style="max-width: 1000%;">//初始化list,往list 中添加元素反例:</span></span></code><code><span class="code-snippet_outer" style="max-width: 1000%;"><span class="code-snippet__keyword" style="max-width: 1000%;">int</span>[] arr = <span class="code-snippet__keyword" style="max-width: 1000%;">new</span> <span class="code-snippet__keyword" style="max-width: 1000%;">int</span>[]{<span class="code-snippet__number" style="max-width: 1000%;">1</span>,<span class="code-snippet__number" style="max-width: 1000%;">2</span>,<span class="code-snippet__number" style="max-width: 1000%;">3</span>,<span class="code-snippet__number" style="max-width: 1000%;">4</span>};</span></code><code><span class="code-snippet_outer" style="max-width: 1000%;">List&lt;Integer&gt;&nbsp;<span class="code-snippet__built_in" style="max-width: 1000%;">list</span>&nbsp;=&nbsp;<span class="code-snippet__keyword" style="max-width: 1000%;">new</span>&nbsp;ArrayList&lt;&gt;();</span></code><code><span class="code-snippet_outer" style="max-width: 1000%;"><span class="code-snippet__keyword" style="max-width: 1000%;">for</span> (<span class="code-snippet__keyword" style="max-width: 1000%;">int</span> i : arr){</span></code><code><span class="code-snippet_outer" style="max-width: 1000%;"> <span class="code-snippet__built_in" style="max-width: 1000%;">list</span>.add(i);</span></code><code><span class="code-snippet_outer" style="max-width: 1000%;">}</span></code></pre> </section> <p style="text-align: left;"><br><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"></span></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">正例:</span></p> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="cpp"><code><span class="code-snippet_outer" style="max-width: 1000%;"><span class="code-snippet__comment" style="max-width: 1000%;">//初始化list,往list 中添加元素正例:</span></span></code><code><span class="code-snippet_outer" style="max-width: 1000%;"> <span class="code-snippet__keyword" style="max-width: 1000%;">int</span>[] arr = <span class="code-snippet__keyword" style="max-width: 1000%;">new</span> <span class="code-snippet__keyword" style="max-width: 1000%;">int</span>[]{<span class="code-snippet__number" style="max-width: 1000%;">1</span>,<span class="code-snippet__number" style="max-width: 1000%;">2</span>,<span class="code-snippet__number" style="max-width: 1000%;">3</span>,<span class="code-snippet__number" style="max-width: 1000%;">4</span>};</span></code><code><span class="code-snippet_outer" style="max-width: 1000%;"> <span class="code-snippet__comment" style="max-width: 1000%;">//指定集合list 的容量大小</span></span></code><code><span class="code-snippet_outer" style="max-width: 1000%;"> List&lt;Integer&gt; <span class="code-snippet__built_in" style="max-width: 1000%;">list</span> = <span class="code-snippet__keyword" style="max-width: 1000%;">new</span> ArrayList&lt;&gt;(arr.length);</span></code><code><span class="code-snippet_outer" style="max-width: 1000%;"> <span class="code-snippet__keyword" style="max-width: 1000%;">for</span> (<span class="code-snippet__keyword" style="max-width: 1000%;">int</span> i : arr){</span></code><code><span class="code-snippet_outer" style="max-width: 1000%;"> <span class="code-snippet__built_in" style="max-width: 1000%;">list</span>.add(i);</span></code><code><span class="code-snippet_outer" style="max-width: 1000%;"> }</span></code></pre> </section> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"></span><br></p> <p style="text-align: left;"><strong><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"># 使用StringBuilder 拼接字符串</span></strong></p> <p style="text-align: left;"><br></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">一般的字符串拼接在编译期Java 会对其进行优化,但是在循环中字符串的拼接Java 编译期无法执行优化,所以需要使用StringBuilder 进行替换。</span></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"><br></span></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">反例:</span></p> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="properties"><code><span class="code-snippet_outer"><span class="code-snippet__attr">//在循环中拼接字符串反例</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">String</span> <span class="code-snippet__string">str = "";</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">for</span> <span class="code-snippet__string">(int i = 0; i &lt; 10; i++){</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__meta">//在循环中字符串拼接Java</span> <span class="code-snippet__string">不会对其进行优化</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__attr">str</span> <span class="code-snippet__string">+= i;</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">}</span></span></code></pre> </section> <p style="text-align: left;"><br></p> <p style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">正例:</span></p> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="javascript"><code><span class="code-snippet_outer" style="max-width: 1000%;"><span class="code-snippet__comment" style="max-width: 1000%;">//在循环中拼接字符串正例</span></span></code><code><span class="code-snippet_outer" style="max-width: 1000%;"> <span class="code-snippet__built_in" style="max-width: 1000%;">String</span> str1 = <span class="

数据库连接池到底应该设多大?

作者:微信小助手

<article> <p style="margin-bottom: 15px;margin-top: 15px;"><strong><span style="font-size: 20px;">前言</span></strong></p> <section style="text-align: justify;line-height: 2em;margin-bottom: 10px;"> 本文内容95%译自这篇文章: </section> <section style="text-align: justify;line-height: 2em;margin-bottom: 10px;"> <span style="font-size: 14px;"><em>https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing</em></span> </section> <section style="text-align: justify;line-height: 2em;margin-bottom: 10px;"> 我在研究HikariCP(一个数据库连接池)时无意间在HikariCP的Github wiki上看到了一篇文章(即前面给出的链接),这篇文章有力地消除了我一直以来的疑虑,看完之后感觉神清气爽。故在此做译文分享。 </section> <h2 style="margin-bottom: 15px;margin-top: 15px;"><span style="color: rgb(0, 82, 255);"><strong><span style="font-size: 20px;">| </span></strong></span><span style="color: rgb(0, 0, 0);"><strong><span style="color: rgb(0, 0, 0);font-size: 20px;">正文</span></strong></span></h2> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">数据库连接池的配置是开发者们常常搞出坑的地方,在配置数据库连接池时,有几个可以说是和直觉背道而驰的原则需要明确。</p> <h4 style="margin-bottom: 10px;margin-top: 10px;"><span style="font-size: 17px;color: rgb(0, 82, 255);"><strong>&lt;/&gt; </strong></span><span style="font-size: 17px;"><strong>1万并发用户访问</strong></span></h4> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">想象你有一个网站,压力虽然还没到Facebook那个级别,但也有个1万上下的并发访问——也就是说差不多2万左右的TPS。那么这个网站的数据库连接池应该设置成多大呢?结果可能会让你惊讶,因为这个问题的正确问法是:</p> <ul class="list-paddingleft-2" style="text-align: justify;line-height: 2em;margin-bottom: 10px;"> <li style="text-align: justify;line-height: 2em;margin-bottom: 10px;"><p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">“这个网站的数据库连接池应该设置成多<strong>小</strong>呢?”</p></li> </ul> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">下面这个视频是Oracle Real World Performance Group发布的,请先看完:</p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;"><span style="font-size: 14px;"><em>http://www.dailymotion.com/video/x2s8uec</em></span></p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">(因为这视频是英文解说且没有字幕,我替大家做一下简单的概括:)<br>视频中对Oracle数据库进行压力测试,9600并发线程进行数据库操作,每两次访问数据库的操作之间sleep 550ms,一开始设置的中间件线程池大小为2048:</p> <p style="text-align: center;"><img data-ratio="0.983957219251337" src="/upload/e84eaa742c8dfdd2b245aad2a91a4aca.png" data-type="other" data-w="374" style="cursor: zoom-in;"></p> <p style="text-align: center;line-height: 2em;margin-bottom: 10px;"><span style="font-size: 14px;">初始的配置</span></p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">压测跑起来之后是这个样子的:</p> <p style="text-align: center;"><img data-ratio="0.1806615776081425" src="/upload/90d421feed0a9bfc17db3f9d0ecf37bd.png" data-type="other" data-w="393" style="cursor: zoom-in;"></p> <p style="text-align: center;line-height: 2em;margin-bottom: 10px;"><span style="font-size: 14px;">2048连接时的性能数据</span></p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">每个请求要在连接池队列里等待33ms,获得连接后执行SQL需要77ms。</p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">此时数据库的等待事件是这个熊样的:</p> <p style="text-align: center;"><img data-ratio="0.39185750636132316" src="/upload/889753f368ef97167b0b752e8de1cd17.png" data-type="other" data-w="393" style="cursor: zoom-in;"></p> <p style="text-align: center;line-height: 2em;margin-bottom: 10px;"><span style="font-size: 14px;">各种buffer busy waits</span></p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">各种buffer busy waits,数据库CPU在95%左右(这张图里没截到CPU)</p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">接下来,把中间件连接池减到1024(并发什么的都不变),性能数据变成了这样:</p> <p style="text-align: center;line-height: 2em;margin-bottom: 10px;"><img data-ratio="0.1590909090909091" src="/upload/95cd116daa1d80a465d8d41f411c0b56.png" data-type="other" data-w="396" style="cursor: zoom-in;"></p> <p style="text-align: center;line-height: 2em;margin-bottom: 10px;"><span style="font-size: 14px;">连接池降到1024后</span></p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">获取链接等待时长没怎么变,但是执行SQL的耗时减少了。</p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">下面这张图,上半部分是wait,下半部分是吞吐量:</p> <p style="text-align: center;"><img data-ratio="2.7657142857142856" src="/upload/37ea3e71431ee90927fe4faff1ccb88c.png" data-type="other" data-w="175" style="cursor: zoom-in;"></p> <p style="text-align: center;line-height: 2em;margin-bottom: 10px;"><span style="font-size: 14px;">wait和吞吐量</span></p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">能看到,中间件连接池从2048减半之后,吐吞量没变,但wait事件减少了一半。</p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">接下来,把数据库连接池减到96,并发线程数仍然是9600不变。</p> <p style="text-align: center;"><img data-ratio="0.1743295019157088" src="/upload/a5fc861d46f58e524408944d96af2963.png" data-type="other" data-w="522" style="cursor: zoom-in;"></p> <p style="text-align: center;line-height: 2em;margin-bottom: 10px;"><span style="font-size: 14px;">96个连接时的性能数据</span></p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">队列平均等待1ms,执行SQL平均耗时2ms。</p> <p style="text-align: center;"><img data-ratio="2.206422018348624" src="/upload/4f25d72909bb33ed33c7a1764baabef2.png" data-type="other" data-w="218" style="cursor: zoom-in;"></p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">wait事件几乎没了,吞吐量上升。</p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;"><strong>没有调整任何其他东西,仅仅只是缩小了中间件层的数据库连接池,就把请求响应时间从100ms左右缩短到了3ms。</strong></p> <h4 style="margin-top: 10px;margin-bottom: 10px;"><span style="font-size: 17px;color: rgb(0, 82, 255);"><strong>&lt;/&gt;</strong></span><span style="font-size: 17px;color: rgb(0, 0, 0);"><strong> But why?</strong></span></h4> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">为什么nginx只用4个线程发挥出的性能就大大超越了100个进程的Apache HTTPD?回想一下计算机科学的基础知识,答案其实是很明显的。</p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">即使是单核CPU的计算机也能“同时”运行数百个线程。但我们都[应该]知道这只不过是操作系统用时间分片玩的一个小把戏。一颗CPU核心同一时刻只能执行一个线程,然后操作系统切换上下文,核心开始执行另一个线程的代码,以此类推。给定一颗CPU核心,其顺序执行A和B永远比通过时间分片“同时”执行A和B要快,这是一条计算机科学的基本法则。一旦线程的数量超过了CPU核心的数量,再增加线程数系统就只会更慢,而不是更快。</p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">这&nbsp;<em>几乎&nbsp;</em>就是真理了……</p> <h4 style="margin-top: 10px;margin-bottom: 10px;"><span style="font-size: 17px;color: rgb(0, 82, 255);"><strong>&lt;/&gt;&nbsp;</strong></span><span style="font-size: 17px;color: rgb(0, 0, 0);"><strong>有限的资源</strong></span></h4> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">上面的说法只能说是接近真理,但还并没有这么简单,有一些其他的因素需要加入。当我们寻找数据库的性能瓶颈时,总是可以将其归为三类:CPU、磁盘、网络。把内存加进来也没有错,但比起磁盘和网络,内存的带宽要高出好几个数量级,所以就先不加了。</p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">如果我们无视磁盘和网络,那么结论就非常简单。在一个8核的服务器上,设定连接/线程数为8能够提供最优的性能,再增加连接数就会因上下文切换的损耗导致性能下降。数据库通常把数据存储在磁盘上,磁盘又通常是由一些旋转着的金属碟片和一个装在步进马达上的读写头组成的。读/写头同一时刻只能出现在一个地方,然后它必须“寻址”到另外一个位置来执行另一次读写操作。所以就有了寻址的耗时,此外还有旋回耗时,读写头需要等待碟片上的目标数据“旋转到位”才能进行操作。使用缓存当然是能够提升性能的,但上述原理仍然成立。</p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">在这一时间段(即"I/O等待")内,线程是在“阻塞”着等待磁盘,此时操作系统可以将那个空闲的CPU核心用于服务其他线程。所以,由于线程总是在I/O上阻塞,我们可以让线程/连接数比CPU核心多一些,这样能够在同样的时间内完成更多的工作。</p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">那么应该多多少呢?这要取决于磁盘。较新型的SSD不需要寻址,也没有旋转的碟片。可别想当然地认为“SSD速度更快,所以我们应该增加线程数”,恰恰相反,无需寻址和没有旋回耗时意味着<strong>更少的阻塞</strong>,所以更少的线程[更接近于CPU核心数]会发挥出更高的性能。<strong>只有当阻塞创造了更多的执行机会时,更多的线程数才能发挥出更好的性能</strong>。</p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">网络和磁盘类似。通过以太网接口读写数据时也会形成阻塞,10G带宽会比1G带宽的阻塞少一些,1G带宽又会比100M带宽的阻塞少一些。不过网络通常是放在第三位考虑的,有些人会在性能计算中忽略它们。</p> <p style="text-align: center;"><img data-ratio="0.7853810264385692" src="/upload/911c2f5822abe72e8d9d684b299fef5.png" data-type="other" data-w="643" style="cursor: zoom-in;"></p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">上图是PostgreSQL的benchmark数据,可以看到TPS增长率从50个连接数开始变缓。在上面Oracle的视频中,他们把连接数从2048降到了96,实际上96都太高了,除非服务器有16或32颗核心。</p> <h4 style="text-align: justify;line-height: 2em;margin-top: 10px;margin-bottom: 10px;"><span style="font-size: 17px;color: rgb(0, 82, 255);"><strong>&lt;/&gt; </strong></span><span style="font-size: 17px;"><strong>计算公式</strong></span></h4> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">下面的公式是由PostgreSQL提供的,不过我们认为可以广泛地应用于大多数数据库产品。你应该模拟预期的访问量,并从这一公式开始测试你的应用,寻找最合适的连接数值。</p> <p><strong>连接数 = ((核心数 * 2) + 有效磁盘数)</strong></p> <blockquote> <p>核心数不应包含超线程(hyper thread),即使打开了hyperthreading也是。如果活跃数据全部被缓存了,那么有效磁盘数是0,随着缓存命中率的下降,有效磁盘数逐渐趋近于实际的磁盘数。这一公式作用于SSD时的效果如何尚未有分析。</p> </blockquote> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">按这个公式,你的4核i7数据库服务器的连接池大小应该为((4 * 2) + 1) = 9。取个整就算是是10吧。是不是觉得太小了?跑个性能测试试一下,我们保证它能轻松搞定3000用户以6000TPS的速率并发执行简单查询的场景。如果连接池大小超过10,你会看到响应时长开始增加,TPS开始下降。</p> <blockquote> <p>笔者注:<br>这一公式其实不仅适用于数据库连接池的计算,大部分涉及计算和I/O的程序,线程数的设置都可以参考这一公式。我之前在对一个使用Netty编写的消息收发服务进行压力测试时,最终测出的最佳线程数就刚好是CPU核心数的一倍。</p> </blockquote> <h4 style="text-align: justify;line-height: 2em;margin-bottom: 10px;margin-top: 10px;"><span style="font-size: 17px;color: rgb(0, 82, 255);"><strong>&lt;/&gt;&nbsp;</strong></span><span style="font-size: 17px;"><strong>公理</strong></span></h4> <h4 style="text-align: justify;line-height: 2em;margin-bottom: 10px;"><strong>你需要一个小连接池,和一个充满了等待连接的线程的队列。</strong></h4> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">如果你有10000个并发用户,设置一个10000的连接池基本等于失了智。1000仍然很恐怖。即使100也太多了。你需要一个10来个连接的小连接池,然后让剩下的业务线程都在队列里等待。连接池中的连接数量应该等于你的数据库能够有效同时进行的查询任务数(通常不会高于2*CPU核心数)。</p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">我们经常见到一些小规模的web应用,应付着大约十来个的并发用户,却使用着一个100连接数的连接池。这会对你的数据库造成极其不必要的负担。</p> <h4 style="margin-top: 15px;margin-bottom: 15px;"><span style="font-size: 20px;"><span style="font-size: 20px;color: rgb(0, 82, 255);"><strong>| </strong></span><strong>请注意</strong></span></h4> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">连接池的大小最终与系统特性相关。</p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">比如一个混合了长事务和短事务的系统,通常是任何连接池都难以进行调优的。最好的办法是创建两个连接池,一个服务于长事务,一个服务于短事务。</p> <p style="text-align: justify;line-height: 2em;margin-bottom: 10px;">再例如一个系统执行一个任务队列,只允许一定数量的任务同时执行,此时并发任务数应该去适应连接池连接数,而不是反过来。</p> </article> <p style="outline: 0px;max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="23.729411764705883" data-s="300,640" src="/upload/a2c431a171a0a73a374d4b0c2d3ab682.png" data-type="png" data-w="765" style="outline: 0px;box-sizing: border-box !important;overflow-wrap: break-word !important;width: 677px !important;visibility: visible !important;"></p> <p><br></p>

从1到2000个微服务,史上最落地的实践云原生25个步骤

作者:微信小助手

<p line="init" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">在上一篇文章<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI1NzYzODk4OQ==&amp;mid=2247485447&amp;idx=1&amp;sn=5aed0de927f7e799948db417bc7942dc&amp;chksm=ea151f25dd629633663276fb90308b944394bee88f58d1f285311ee93eb12c485d24cb84e56d&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2">以业务为核心的云原生体系建设</a>中,我们给出了一张云原生体系建设的总图,并且从演进的角度讲述了云原生落地的三个阶段。<br></p> <p line="init" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p line="init" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">有的同学留言说,还是不够落地呀,所谓“听了很多道理,还是过不好这一生”,同理“看了很多文章,还是落地不好云原生”。<br></p> <p line="init" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p line="init" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">从今天这一篇开始,我们开始落地篇,从此会进入大量的技术细节,学了落地篇,基本可以回去编码落地了。</p> <p line="init" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p line="init" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">其实我们在很多的技术大会上,看到的都是分层架构图,<span style="color: rgb(73, 73, 73);font-size: 14.6667px;">就像上一节我们分的六个层次一样,这容易给希望落地云原生的企业造成误解,因</span><span style="font-size: 11pt;">为大部分公司的云原生体系的建设都不是按层次来建设的,不会IaaS完全建设完毕,再建设PaaS,一定是根据业务的演进,交替迭代出来的。</span></p> <p line="oJnA" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="EH3O" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">一定是业务遇到问题了,需要底层的技术,底层技术提升了,促进业务的发展。如下图所示,应用层和技术底座之间的良性循环,才是云原生这个词的本质,我把他称为云原生怪圈。</p> <p line="1d8l" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><span line-inline="nrvo"></span></p> <p line="rXWy" linespacing="150" style="text-align: left;line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><span line-inline="Uena"> &nbsp; &nbsp; &nbsp; &nbsp;<img data-ratio="3.277083333333333" src="/upload/fc0f226b4625dc2322ab882564f767f6.png" data-type="png" data-w="1920" height="auto" width="2503"></span></p> <p>&nbsp; &nbsp; &nbsp; &nbsp;</p> <p line="bR36" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="wnFz" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">虽然按照这个顺序来讲,你会感觉体系有点乱,但是才是最落地的演进路径。而沿着这个演进路径发展,你才能感受到“云原生”三个字的准确含义。</p> <p line="wnFz" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p line="wnFz" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">很多企业应用层服务化或者微服务化,而技术底座没有跟上,从而系统陷入混乱,没日没夜加班,却怪服务化不好,另外一些企业花了大价钱卖了一个技术底座平台,但是应用层没有跟上,无法促进业务发展,嫌技术底座白花了钱,这两个误区都在于没有沿着这个怪圈逐渐演进。</p> <p line="yhvg" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p line="yhvg" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">为了解决体系混乱的问题,所以在展开落地篇之前,先来一个总论,整体梳理一下。</p> <p line="yhvg" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p line="5Cru" linespacing="150" style="text-align: left;line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p line="5Cru" linespacing="150" style="text-align: left;line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><span style="color: rgb(73, 73, 73);font-size: 14.6667px;">对于每一个落地的步骤</span>,我们要经常问自己下面几个问题,我们成为四项基本问题吧:</p> <ul class="list-paddingleft-2"> <li style="margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);line-height: 1.7;"><p>遇到什么样的问题?</p></li> <li style="margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);line-height: 1.7;"><p>应该采取什么样的技术解决这个问题?如何解决这个问题的?</p></li> <li style="margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);line-height: 1.7;"><p>这个技术的实现有很多种,应该如何选型?</p></li> <li style="margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);line-height: 1.7;"><p>使用这个技术有没有最佳实践,能不能形成企业的相关规范?</p></li> </ul> <p><br></p> <p line="yhvg" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><span style="color: rgb(51, 51, 51);font-size: 17px;"></span></p> <p line="JUzV" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">整个落地的演进过程要有一个起点:</p> <ul class="list-paddingleft-2"> <li style="margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);line-height: 1.7;"><p>在应用层,是一个单体应用,主要包含Online服务,他是对外提供服务的,Offline服务,他是一些定时任务的,MS服务,他是一些后台服务,这基本是一个单体应用,因为对外服务是Online,接下来的拆分也是围绕这个服务展开。</p></li> <li style="margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);line-height: 1.7;"><p>对于数据库,使用的是Oracle,部署在物理机上</p></li> <li style="margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);line-height: 1.7;"><p>在基础实施层,用的是Vmware虚拟化</p></li> <li style="margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);line-height: 1.7;"><p>部署上线方式是脚本化</p></li> </ul> <p line="ekwz" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="ec3q" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">接下来,我们要开始演进了。</p> <p line="gjNn" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="fMoj" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">就像前面我们讲过,构建中台的企业都是有一定积累的企业,而非创业企业,因而不可能没有任何计划的的盲人摸象,这是很多企业的管理层不允许的。所以在动手之前,要有一个总的地图,就是规划,当然真正云原生演进的时候,我们不建议使用瀑布模型,而是迭代模型,但是迭代模型不代表漫无目的的迭代,而是地图要在心中,所以落地的第一个阶段是规划。</p> <p line="0s3b">&nbsp;</p> <h1><p><strong><span style="font-size: 20px;">第一:规划——在架构委员会领导下的梳理与规划</span></strong></p><p><br></p></h1> <p line="qmPD" linespacing="150" style="line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">首先,组织架构先行:成立架构师组。哪怕人很少,只有两三个人,但是这个组织一定要有,这是将来的军机处,是架构委员会的发起者,是横向拉通各个组,并落地规范与最佳实践的负责人。</p> <p line="qmPD" linespacing="150" style="line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p line="Xdgt" linespacing="150" style="line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">有了人以后,接下来,我们应该从业务架构出发:进行业务流程和领域梳理。在云原生怪圈的循环中,我建议从业务层出发,因为IT是为业务服务的,只有业务方的需求,才是真正应该服务和花钱建设的地方。</p> <p line="Xdgt" linespacing="150" style="line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p line="Xdgt" linespacing="150" style="line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p line="Xdgt" linespacing="150" style="line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><strong><span style="font-size: 18px;">(步骤1) 从1到2:领域驱动建模<br></span></strong></p> <p line="Xdgt" linespacing="150" style="line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <h2><p><span style="color: rgb(73, 73, 73);font-size: 11pt;">从标题你可以看出,这是按照领域驱动设计的方式来进行规划的。</span></p><p><span style="color: rgb(73, 73, 73);font-size: 11pt;"><br></span></p><p><span style="color: rgb(73, 73, 73);font-size: 11pt;"></span></p><p line="I8mC" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">这里很多技术人员都会犯的错误是,从数据库出发,看数据库结构如何设计的,按照数据库最容易拆分的方式进行拆分。这样是不对的,没有站在业务的角度去考虑问题。应该借鉴领域驱动设计的思路,从业务流程的梳理和业务领域的划分出发,来划分不同的服务,虽然最后映射到数据库可能会拆分的比较难受,但是方向是对的,只有这样才能适应未来业务的快速变化。</p><p line="XRpU" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p><p line="OfqO" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">我个人认为,方向比手段要重要,方向对,当前痛一点没什么,但是当前不痛,方向错了,还是解决不了问题。</p><p><span style="color: rgb(73, 73, 73);font-size: 11pt;"><br></span></p><p><span style="color: rgb(73, 73, 73);font-size: 11pt;"></span></p><p line="lQwl" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">首先,我们要做的是梳理业务流程,通过这个流程,可以了解业务运行的逻辑,使得技术人员对于业务模式有所理解。</p><p line="yzEZ" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p><p line="UkrR" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">这里主要梳理的是电商业务,也许你对电商业务不是非常感兴趣,但是仍然建议你把这一节看完。因为后面在架构设计的部分,都要基于对于业务流程的理解。其实作为一个架构师,越是到后期,越是要距离业务要近,而不仅仅是单点做一部分的技术。电商平台是一个典型的应用云原生架构的案例。虽然你当前所在的行业。有可能是金融,制造,或者零售。对于方法论来讲,你总能从电商平台的流程和业务模式中,找到类似的部分。他山之石,可以攻玉。</p><p line="URps" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p><p line="mLMO" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">下面是电商平台的一个典型的业务流程图。</p><p><br></p><p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.56953125" data-s="300,640" src="/upload/51533100a054ea2597921dad95b3b7e6.png" data-type="png" data-w="1280" style=""></p><p><span style="color: rgb(73, 73, 73);font-size: 11pt;"><br></span></p><p><span style="color: rgb(73, 73, 73);font-size: 11pt;">具体的业务流程,我们另外解析,这里不赘述。<br></span></p><p><span style="color: rgb(73, 73, 73);font-size: 11pt;"><br></span></p><p><span style="color: rgb(73, 73, 73);font-size: 11pt;"></span></p></h2> <h1><p>接下来就是,划分业务领域。<span style="color: rgb(73, 73, 73);font-size: 11pt;">梳理好了业务流程,我们就可以根据他来划分业务领域。</span><span style="color: rgb(73, 73, 73);font-size: 11pt;">这里不必严格按照DDD的图,为了方便,我这里用了脑图。</span></p></h1> <p line="WZz3" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.9015625" data-s="300,640" src="/upload/44ed6f37857ec33eb9ae5599af24093c.png" data-type="png" data-w="1280" style=""></p> <p line="WZz3" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p line="ukF0" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">另外,对于每一个服务,我都起了名字,这里先不用管它,后面自然有用。</p> <p line="Qgrf" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="cyxY" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">在实践中,你在这个阶段可能没必要划分的这么细。这些服务都是在后期的逐渐拆分过程中演进出来的。</p> <p><br></p> <p><br></p> <p line="x624" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">那接下来后台技术部门不应该闷头开始就按这个拆了?其实不是的!</p> <p line="1mo7" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="SpHa" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">传统的领域驱动设计是瀑布式的模型,经过长时间的闭门讨论,贴纸条,最终输出各种架构图,但是当落地的时候,发现情况变了,因为领域知识从业务部门到技术部门的传递一定有信息的丢失,这也是DDD落地被诟病的地方,就是业务方规划的时候是这样说的,落地来需求的时候,却是另外一种说法,导致根据DDD落地好的领域,接需求接的更加困难了。</p> <p line="K8f7" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="GAxp" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">所以一个更加落地的方式是,随着新需求的不断到来,渐进的进行拆分,而变化多,复用性是两大考虑要素。</p> <p line="zCya" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="fpMB" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">所以赵本山说,不看广告,看疗效。对于服务拆分,DDD是一个完整的地图,但是具体怎么走,要不要调整,需要随着新需求的不断到来,渐进的进行拆分,DDD领域设计的时候,业务方会说不清,但是真的需求来的时候,却是实实在在的,甚至接口和原型都能做出来跟业务看。</p> <p line="jNK0" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="bQKV" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">这么说有点虚,我们举个现实的例子。例如按照领域的划分,对于电商业务来讲,一个单体的电商服务,应该拆分成下面这些服务。</p> <p line="gEOf" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="Txkp" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><span line-inline="yp40"> &nbsp; &nbsp; &nbsp; &nbsp;<img data-ratio="0.45364583333333336" src="/upload/2f757195bf887431dd491f516598b011.png" data-type="png" data-w="1920" height="auto" width="2746"> &nbsp; &nbsp; &nbsp;</span></p> <p line="tz9P" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="eZ2z" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">需求到来的时候,技术部门是能感受到上一篇文章讲过的架构耦合导致的两个现象:</p> <ul class="list-paddingleft-2"> <li style="margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);line-height: 1.7;"><p>耦合现象一:你改代码,你要上线,要我配合</p></li> <li style="margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);line-height: 1.7;"><p>耦合现象二:明明有某个功能,却拿不出来</p></li> </ul> <p line="Uhnt" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="yTs4" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">第一个现象就是变化多,在业务的某个阶段,有的领域的确比其他的领域有更多的变化,如果耦合在一起,上线,稳定性都会相互影响。例如图中,供应链越来越多,活动方式越来越多,物流越来越多,如果都耦合在Online里面,每对接一家物流公司,都会影响下单,那太恐怖了。</p> <p line="qzXE" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="7TSs" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">第二个现象就是可复用,例如用户中心和认证中心,根本整个公司应该只有一套。</p> <p line="vUXY" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="wpQF" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">在《重构:改善代码的既有设计》有一个三次法则——事不过三,三则重构。</p> <p line="hshY" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="ZUqq" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><span line-inline="wA8G"> &nbsp; &nbsp; &nbsp; &nbsp;<img data-ratio="0.30854430379746833" src="/upload/7a22e41c552c94ab099dd0acd96cba74.png" data-type="png" data-w="632" height="auto" width="632"> &nbsp; &nbsp; &nbsp;</span></p> <p line="aPIJ" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="aRTl" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="Ztxn" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="dyHB" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">这个原则也可以用作服务化上,也即当物流模块的负责人发现自己接到第三家物流公司的时候,应该就考虑要从原来的单体应用中拆分出来了。另外就是,当有一个功能,领导或者业务方发现明明有,还需要再做一遍,这种现象出现第三次的时候,就应该拆分出来作为一个独立的服务了。</p> <p line="LFko" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="SMh7" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">这种根据业务需求逐渐拆分的过程,会使得系统的修改一定是能够帮助到业务方的,同时系统处在一种可控的状态,随着工具链,流程、团队、员工能力的增强慢慢匹配到服务化的状态。</p> <p line="BsvC" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="HHdv" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">那你可能会问,如果有个系统,里面的代码已经垃圾的一塌糊涂,我都看不下去了,但是暂时没有新需求进来,那应不应该拆分呢?</p> <p line="17jc" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="Kk0Q" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">不!没有需求不拆!再烂也不拆!<span style="font-size: 11pt;">我们不是要解决所有的腐</span><span style="font-size: 11pt;">化问题,别按DDD的理想情况来。</span></p> <p line="qEel" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><span line-inline="dqjP"></span></p> <p line="Bifq" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="xsC4" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><span line-inline="dqjP">至此,理论的划分基本就结束了,接下来,咱们就要动代码啦!</span></p> <p><br></p> <h1><p><strong style="font-size: 16pt;">第二:试点——选一个项目试点,汲取经验,培养团队,建立规范</strong></p></h1> <p line="cH3f" linespacing="150" style="line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p line="cH3f" linespacing="150" style="line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><strong style="white-space: normal;"><span style="font-size: 18px;">(步骤2) 从1到2:选取试点业务进行拆分</span></strong></p> <p line="cH3f" linespacing="150" style="line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p line="KhEa" style="margin-top: 0pt;margin-bottom: 0pt;white-space: normal;line-height: 1.7;font-size: 11pt;color: rgb(73, 73, 73);">我们来选择一个领域将他拆分出来,我们就选择最核心的交易领域吧。</p> <p line="hWK2" style="margin-top: 0pt;margin-bottom: 0pt;white-space: normal;line-height: 1.7;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="SdSM" style="margin-top: 0pt;margin-bottom: 0pt;white-space: normal;line-height: 1.7;font-size: 11pt;color: rgb(73, 73, 73);"><span line-inline="kOLt">&nbsp; &nbsp; &nbsp; &nbsp;<img data-ratio="0.7229166666666667" src="/upload/7487f73f1150a0f376382ac42a1045f0.png" data-type="png" data-w="1920" height="auto" width="2926">&nbsp; &nbsp; &nbsp; &nbsp;</span><span style="font-size: 11pt;">&nbsp;</span></p> <p line="IbWr" style="margin-top: 0pt;margin-bottom: 0pt;white-space: normal;line-height: 1.7;font-size: 11pt;color: rgb(73, 73, 73);">在上面这个庞大的单体应用中,我们将订单拆分出来,需要考虑以下几个事情:</p> <ul class="list-paddingleft-2" style="width: 577.417px;white-space: normal;"> <li style="margin-top: 0pt;margin-bottom: 0pt;font-size: 11pt;color: rgb(73, 73, 73);line-height: 1.7;"><p>在诸多的功能中,将属于订单的功能梳理出来</p></li> <li style="margin-top: 0pt;margin-bottom: 0pt;font-size: 11pt;color: rgb(73, 73, 73);line-height: 1.7;"><p>梳理订单和其他模块之间的关系,从而知道将来会和哪些模块进行相互调用</p></li> <li style="margin-top: 0pt;margin-bottom: 0pt;font-size: 11pt;color: rgb(73, 73, 73);line-height: 1.7;"><p>将订单模块中的不同功能也进行划分,虽然目前不用拆分,为了将来拆分做准备</p></li> </ul> <p line="b0DF" style="margin-top: 0pt;margin-bottom: 0pt;white-space: normal;line-height: 1.7;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="B3na" style="margin-top: 0pt;margin-bottom: 0pt;white-space: normal;line-height: 1.7;font-size: 11pt;color: rgb(73, 73, 73);">第一件事情,我们从上面的图中可以看出,黄色底色的就是属于订单的功能。</p> <p line="W8RB" style="margin-top: 0pt;margin-bottom: 0pt;white-space: normal;line-height: 1.7;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="2MhY" style="margin-top: 0pt;margin-bottom: 0pt;white-space: normal;line-height: 1.7;font-size: 11pt;color: rgb(73, 73, 73);">第二件事情,我们需要梳理一个关系图,如下所示。</p> <p line="OCYg" style="margin-top: 0pt;margin-bottom: 0pt;white-space: normal;line-height: 1.7;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;<span style="font-size: 11pt;">&nbsp; &nbsp;&nbsp;</span><img data-ratio="1" src="/upload/b761dd4a899a2953468801e844094622.png" data-type="png" data-w="1303" height="auto" style="font-size: 11pt;" width="1303"><span style="font-size: 11pt;">&nbsp; &nbsp; &nbsp; &nbsp;</span></p> <p line="zNtw" style="margin-top: 0pt;margin-bottom: 0pt;white-space: normal;line-height: 1.7;font-size: 11pt;color: rgb(73, 73, 73);"><span style="font-size: 11pt;">第三件事情,将订单内部的功能也划分一下,因为将来可能会因为性能问题,进一步的划分。</span></p> <p line="XyRH" style="margin-top: 0pt;margin-bottom: 0pt;white-space: normal;line-height: 1.7;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="qEel" style="margin-top: 0pt;margin-bottom: 0pt;white-space: normal;line-height: 1.7;font-size: 11pt;color: rgb(73, 73, 73);"><span line-inline="dqjP">&nbsp; &nbsp; &nbsp; &nbsp;<img data-ratio="0.3192738921516284" src="/upload/6b4dc4f5903d0e16321e9ad69b7f8ed8.png" data-type="png" data-w="1873" height="auto" width="1873">&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;</span></p> <p line="cH3f" linespacing="150" style="line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p line="cH3f" linespacing="150" style="line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">接下来马上应该找拆分了,先别忙,你会不会拆出一堆Bug来,让原来单体应用玩儿挺好的,后来Bug成堆呢?这也是经常服务化被诟病的地方,Bug更多更不稳定。</p> <p line="LlGN" linespacing="150" style="line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="H1uu" linespacing="150" style="line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">所以首先要有持续集成,这是云原生架构的基石。</p> <p line="H1uu" linespacing="150" style="line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p line="H1uu" linespacing="150"><strong><span style="font-size: 18px;">(步骤3) 从1到2:持续集成平台建设</span></strong></p> <p line="H1uu" linespacing="150"><br></p> <p line="H1uu" linespacing="150" style="line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p line="r0py" style="line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">持续集成就是制定一系列流程,或者一个系列规则,将需要在一起的各个层次规范起来,方便大家在一起,强迫大家在一起。&nbsp;</p> <p line="h53g" style="line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="HVru" style="line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">接下来,我们一起来搭建一个持续集成的系统。</p> <p line="mMuH" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="X3hz" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><span line-inline="6Mw6"> &nbsp; &nbsp; &nbsp; &nbsp;<img data-ratio="0.6453125" src="/upload/d922614bc8497463bc47c20b961d58fd.png" data-type="png" data-w="1920" height="auto" width="3043"> &nbsp; &nbsp; &nbsp;</span></p> <p line="MYrJ" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;<span style="font-size: 11pt;">上面的这幅图呢,是一个持续集成的总体流程图,里面包含非常多的工具,这些工具呢,有非常多的选型。</span><span style="font-size: 11pt;">下面的这个脑图,就总结了持续集成中所常用到的主流工具。</span><span style="font-size: 11pt;">你可以根据自己公司的情况。</span><span style="font-size: 11pt;">来选择合适的工具。</span></p> <p line="lhWT" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="1EOq" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><span line-inline="83gG"> &nbsp; &nbsp; &nbsp; &nbsp;<img data-ratio="1.0979166666666667" src="/upload/3d7a9a3830bdf36f8ee39fca6739e477.png" data-type="png" data-w="1920" height="auto" width="2323"> &nbsp; &nbsp; &nbsp;</span></p> <p line="eG8U" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="ltPf" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">系统的搭建只是其中一部分,要做好持续集成,还需要有规范,还需要配合敏捷开发的流程。</p> <p style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">持续集成的规范都有哪些呢?</p> <p style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">工程名规范:这个名称非常关键,以后在公司内部所有的系统中,只要看到这个名称,无论是开发,运维,发布人员,QA任何角色,在持续集成平台,云平台,容器平台,微服务平台等任何平台,都以这个名称为准绳,这样所有人看到名称,马上就知道这个工程什么功能以及应该如何操作他,如果是一个核心交易的前台工程,就应该小心一点,如果是一个后台的管理系统,则小的功能白天也可以发布。<br></p></li> <li><p style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><span style="font-size: 11pt;">代码结构</span><span style="font-size: 11pt;">规范:</span><span style="font-size: 11pt;">代码结构规范希望达到的效果是所有人打开一份代码,都能看到熟悉的结构,以及有大概的思路如何入手,这在快速迭代的场景下很重要,因为人员也可能在不同的团队频繁的调动。</span></p></li> <li><h1 style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><p style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><span style="font-size: 11pt;">代码设计规范:</span><span style="font-size: 11pt;">包括</span><span style="font-size: 11pt;">命名规范,例如package, class, interface,变量的命名;包括注释规范,</span><span style="font-size: 11pt;">我们要根据注释生成文档,这在注册中心和知识库还会提到;资源管理规范,例如对于文件,线程,网络,数据库连接等的使用;异常管理规范,如何捕捉异常和处理异常;日志规范,日志的分级,敏感信息过滤,格式规约;多线程规范,并发,加锁,线程安全;数据库操作规范;编码规范,例如方法长度,类长度,数据模型定义,相等判断,异常抛出等;</span></p></h1></li> <li><h1 style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><p style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><span style="font-size: 11pt;"></span><span style="font-size: 11pt;">代码提交规范</span><span style="font-size: 11pt;">:</span><span style="font-size: 11pt;">提供</span><span style="font-size: 11pt;">注释规范</span><span style="font-size: 11pt;">,冲突处理规范,</span><span style="font-size: 11pt;">warning处理规范,</span><span style="font-size: 11pt;">格式化代码</span><span style="font-size: 11pt;">规范等。</span></p></h1></li> <li><h1 style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><p style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><span style="font-size: 11pt;"></span><span style="font-size: 11pt;">单元测试规范:</span><span style="font-size: 11pt;">单元测试</span><span style="font-size: 11pt;">覆盖率,</span><span style="font-size: 11pt;">有效性,M</span><span style="font-size: 11pt;">ock规范。</span></p></h1></li> </ul> <p style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><strong style="color: rgb(73, 73, 73);white-space: normal;font-size: 11pt;"><span style="font-size: 18px;">(步骤4) 从2到10:</span></strong><span style="color: rgb(73, 73, 73);font-size: 18px;"><strong>构建注册中心与知识库</strong></span></p> <p style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p line="F44C" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">我们构建了持续集成的系统,规范,流程,还有一个问题没有解决。</p> <p line="p7p1" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">&nbsp;</p> <p line="Kjad" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">一旦订单中心从电商的单体应用中拆分出去了,这就存在当订单中心和其他业务相互调用的时候,如何知道订单中心在哪里的问题。我们假设原来的单体应用为Online,而新的订单中心称为Order。<span style="color: rgb(51, 51, 51);font-size: 17px;"></span></p> <p><br></p> <p line="fsHe" linespacing="150" style="line-height: 150%;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);"><br></p> <p line="4by0" style="line-height: 1.7;margin-bottom: 0pt;margin-top: 0pt;font-size: 11pt;color: rgb(73, 73, 73);">因而这里我们需要配备一个工具链,那就是注册中心。</p> <p line="cddH"

Nginx 常用配置清单

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;outline: 0px;white-space: normal;background-color: rgb(255, 255, 255);line-height: 1.6;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 15px;letter-spacing: 0.05em;color: rgb(89, 89, 89);"> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;outline: 0px;font-size: 16px;line-height: 26px;color: black;">Nginx 是一个高性能的 HTTP 和反向代理 web 服务器,同时也提供了 IMAP/POP3/SMTP 服务,其因丰富的功能集、稳定性、示例配置文件和低系统资源的消耗受到了开发者的欢迎。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;outline: 0px;font-size: 16px;line-height: 26px;color: black;">本文,我们总结了一些常用的 Nginx 配置代码,希望对大家有所帮助。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;outline: 0px;font-size: 16px;line-height: 26px;color: black;"><strong style="outline: 0px;color: rgb(119, 48, 152);">侦听端口</strong></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;outline: 0px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="margin-bottom: -7px;outline: 0px;display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/bofA1vl6EUZZ2lqsN8BjkVgQWVejwgmAdm52d4ibB22ibNtMVspMCo0icAyRdqIrb53wzZ23NCCu1tt72fhmicmF0AhEUdYL3Re4/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 536px;border-radius: 5px;"></span><code style="padding: 15px 16px 16px;outline: 0px;overflow-x: auto;color: rgb(171, 178, 191);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">server&nbsp;{<br style="outline: 0px;"><span style="outline: 0px;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">#&nbsp;Standard&nbsp;HTTP&nbsp;Protocol</span><br style="outline: 0px;">listen&nbsp;80;<br style="outline: 0px;"><span style="outline: 0px;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">#&nbsp;Standard&nbsp;HTTPS&nbsp;Protocol</span><br style="outline: 0px;">listen&nbsp;443&nbsp;ssl;<br style="outline: 0px;"><span style="outline: 0px;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">#&nbsp;For&nbsp;http2</span><br style="outline: 0px;">listen&nbsp;443&nbsp;ssl&nbsp;http2;<br style="outline: 0px;"><span style="outline: 0px;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">#&nbsp;Listen&nbsp;on&nbsp;80&nbsp;using&nbsp;IPv6</span><br style="outline: 0px;">listen&nbsp;[::]:80;<br style="outline: 0px;"><span style="outline: 0px;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">#&nbsp;Listen&nbsp;only&nbsp;on&nbsp;using&nbsp;IPv6</span><br style="outline: 0px;">listen&nbsp;[::]:80&nbsp;ipv6only=on;<br style="outline: 0px;">}<br style="outline: 0px;"></code></pre> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;outline: 0px;font-size: 16px;line-height: 26px;color: black;"><strong style="outline: 0px;color: rgb(119, 48, 152);">访问日志</strong></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;outline: 0px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="margin-bottom: -7px;outline: 0px;display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/bofA1vl6EUZZ2lqsN8BjkVgQWVejwgmAdm52d4ibB22ibNtMVspMCo0icAyRdqIrb53wzZ23NCCu1tt72fhmicmF0AhEUdYL3Re4/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 536px;border-radius: 5px;"></span><code style="padding: 15px 16px 16px;outline: 0px;overflow-x: auto;color: rgb(171, 178, 191);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">server&nbsp;{<br style="outline: 0px;"><span style="outline: 0px;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">#&nbsp;Relative&nbsp;or&nbsp;full&nbsp;path&nbsp;to&nbsp;log&nbsp;file</span><br style="outline: 0px;">access_log&nbsp;/path/to/file.log;<br style="outline: 0px;"><span style="outline: 0px;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">#&nbsp;Turn&nbsp;'on'&nbsp;or&nbsp;'off'&nbsp;&nbsp;</span><br style="outline: 0px;">access_log&nbsp;on;<br style="outline: 0px;">}<br style="outline: 0px;"></code></pre> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;outline: 0px;font-size: 16px;line-height: 26px;color: black;"><strong style="outline: 0px;color: rgb(119, 48, 152);">域名</strong></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;outline: 0px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="margin-bottom: -7px;outline: 0px;display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/bofA1vl6EUZZ2lqsN8BjkVgQWVejwgmAdm52d4ibB22ibNtMVspMCo0icAyRdqIrb53wzZ23NCCu1tt72fhmicmF0AhEUdYL3Re4/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 536px;border-radius: 5px;"></span><code style="padding: 15px 16px 16px;outline: 0px;overflow-x: auto;color: rgb(171, 178, 191);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">server&nbsp;{<br style="outline: 0px;"><span style="outline: 0px;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">#&nbsp;Listen&nbsp;to&nbsp;yourdomain.com</span><br style="outline: 0px;">server_name&nbsp;yourdomain.com;<br style="outline: 0px;"><span style="outline: 0px;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">#&nbsp;Listen&nbsp;to&nbsp;multiple&nbsp;domains&nbsp;server_name&nbsp;yourdomain.com&nbsp;www.yourdomain.com;</span><br style="outline: 0px;"><span style="outline: 0px;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">#&nbsp;Listen&nbsp;to&nbsp;all&nbsp;domains</span><br style="outline: 0px;">server_name&nbsp;*.yourdomain.com;<br style="outline: 0px;"><span style="outline: 0px;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">#&nbsp;Listen&nbsp;to&nbsp;all&nbsp;top-level&nbsp;domains</span><br style="outline: 0px;">server_name&nbsp;yourdomain.*;<br style="outline: 0px;"><span style="outline: 0px;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">#&nbsp;Listen&nbsp;to&nbsp;unspecified&nbsp;Hostnames&nbsp;(Listens&nbsp;to&nbsp;IP&nbsp;address&nbsp;itself)</span><br style="outline: 0px;">server_name&nbsp;<span style="outline: 0px;color: rgb(152, 195, 121);line-height: 26px;">""</span>;<br style="outline: 0px;">}<br style="outline: 0px;"></code></pre> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;outline: 0px;font-size: 16px;line-height: 26px;color: black;"><strong style="outline: 0px;color: rgb(119, 48, 152);">静态资产</strong></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;outline: 0px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="margin-bottom: -7px;outline: 0px;display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/bofA1vl6EUZZ2lqsN8BjkVgQWVejwgmAdm52d4ibB22ibNtMVspMCo0icAyRdqIrb53wzZ23NCCu1tt72fhmicmF0AhEUdYL3Re4/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 536px;border-radius: 5px;"></span><code style="padding: 15px 16px 16px;outline: 0px;overflow-x: auto;color: rgb(171, 178, 191);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">server&nbsp;{<br style="outline: 0px;">listen&nbsp;80;<br style="outline: 0px;">server_name&nbsp;yourdomain.com;<br style="outline: 0px;">location&nbsp;/&nbsp;{<br style="outline: 0px;">root&nbsp;/path/to/website;<br style="outline: 0px;">}<br style="outline: 0px;">}<br style="outline: 0px;"></code></pre> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;outline: 0px;font-size: 16px;line-height: 26px;color: black;"><strong style="outline: 0px;color: rgb(119, 48, 152);">重定向</strong></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;outline: 0px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="margin-bottom: -7px;outline: 0px;display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/bofA1vl6EUZZ2lqsN8BjkVgQWVejwgmAdm52d4ibB22ibNtMVspMCo0icAyRdqIrb53wzZ23NCCu1tt72fhmicmF0AhEUdYL3Re4/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 536px;border-radius: 5px;"></span><code style="padding: 15px 16px 16px;outline: 0px;overflow-x: auto;color: rgb(171, 178, 191);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">server&nbsp;{<br style="outline: 0px;">listen&nbsp;80;<br style="outline: 0px;">server_name&nbsp;www.yourdomain.com;<br style="outline: 0px;"><span style="outline: 0px;color: rgb(230, 192, 123);line-height: 26px;">return</span>&nbsp;301&nbsp;http://yourdomain.com<span style="outline: 0px;color: rgb(209, 154, 102);line-height: 26px;">$request_uri</span>;<br style="outline: 0px;">}<br style="outline: 0px;">server&nbsp;{<br style="outline: 0px;">listen&nbsp;80;<br style="outline: 0px;">server_name&nbsp;www.yourdomain.com;<br style="outline: 0px;">location&nbsp;/redirect-url&nbsp;{<br style="outline: 0px;"><span style="outline: 0px;color: rgb(230, 192, 123);line-height: 26px;">return</span>&nbsp;301&nbsp;http://otherdomain.com;<br style="outline: 0px;">}<br style="outline: 0px;">}<br style="outline: 0px;"></code></pre> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;outline: 0px;font-size: 16px;line-height: 26px;color: black;"><strong style="outline: 0px;color: rgb(119, 48, 152);">反向代理</strong></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;outline: 0px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="margin-bottom: -7px;outline: 0px;display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/bofA1vl6EUZZ2lqsN8BjkVgQWVejwgmAdm52d4ibB22ibNtMVspMCo0icAyRdqIrb53wzZ23NCCu1tt72fhmicmF0AhEUdYL3Re4/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 536px;border-radius: 5px;"></span><code style="padding: 15px 16px 16px;outline: 0px;overflow-x: auto;color: rgb(171, 178, 191);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">server&nbsp;{<br style="outline: 0px;">listen&nbsp;80;<br style="outline: 0px;">server_name&nbsp;yourdomain.com;<br style="outline: 0px;">location&nbsp;/&nbsp;{<br style="outline: 0px;">proxy_pass&nbsp;http://0.0.0.0:3000;<br style="outline: 0px;"><span style="outline: 0px;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">#&nbsp;where&nbsp;0.0.0.0:3000&nbsp;is&nbsp;your&nbsp;application&nbsp;server&nbsp;(Ex:&nbsp;node.js)&nbsp;bound&nbsp;on&nbsp;0.0.0.0&nbsp;listening&nbsp;on&nbsp;port&nbsp;3000</span><br style="outline: 0px;">}<br style="outline: 0px;">}<br style="outline: 0px;"></code></pre> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;outline: 0px;font-size: 16px;line-height: 26px;color: black;"><strong style="outline: 0px;color: rgb(119, 48, 152);">负载均衡</strong></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;outline: 0px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="margin-bottom: -7px;outline: 0px;display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/bofA1vl6EUZZ2lqsN8BjkVgQWVejwgmAdm52d4ibB22ibNtMVspMCo0icAyRdqIrb53wzZ23NCCu1tt72fhmicmF0AhEUdYL3Re4/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 536px;border-radius: 5px;"></span><code style="padding: 15px 16px 16px;outline: 0px;overflow-x: auto;color: rgb(171, 178, 191);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">upstream&nbsp;node_js&nbsp;{<br style="outline: 0px;">server&nbsp;0.0.0.0:3000;<br style="outline: 0px;">server&nbsp;0.0.0.0:4000;<br style="outline: 0px;">server&nbsp;123.131.121.122;<br style="outline: 0px;">}<br style="outline: 0px;">server&nbsp;{<br style="outline: 0px;">listen&nbsp;80;<br style="outline: 0px;">server_name&nbsp;yourdomain.com;<br style="outline: 0px;">location&nbsp;/&nbsp;{<br style="outline: 0px;">proxy_pass&nbsp;http://node_js;<br style="outline: 0px;">}<br style="outline: 0px;">}<br style="outline: 0px;"></code></pre> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;outline: 0px;font-size: 16px;line-height: 26px;color: black;"><strong style="outline: 0px;color: rgb(119, 48, 152);">SSL 协议</strong></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;outline: 0px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="margin-bottom: -7px;outline: 0px;display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/bofA1vl6EUZZ2lqsN8BjkVgQWVejwgmAdm52d4ibB22ibNtMVspMCo0icAyRdqIrb53wzZ23NCCu1tt72fhmicmF0AhEUdYL3Re4/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 536px;border-radius: 5px;"></span><code style="padding: 15px 16px 16px;outline: 0px;overflow-x: auto;color: rgb(171, 178, 191);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;">server&nbsp;{<br style="outline: 0px;">listen&nbsp;443&nbsp;ssl;<br style="outline: 0px;">server_name&nbsp;yourdomain.com;<br style="outline: 0px;">ssl&nbsp;on;<br style="outline: 0px;">ssl_certificate&nbsp;/path/to/cert.pem;<br style="outline: 0px;">ssl_certificate_key&nbsp;/path/to/privatekey.pem;<br style="outline: 0px;">ssl_stapling&nbsp;on;<br style="outline: 0px;">ssl_stapling_verify&nbsp;on;<br style="outline: 0px;">ssl_trusted_certificate&nbsp;/path/to/fullchain.pem;<br style="outline: 0px;">ssl_protocols&nbsp;TLSv1&nbsp;TLSv1.1&nbsp;TLSv1.2;<br style="outline: 0px;">ssl_session_timeout&nbsp;1h;<br style="outline: 0px;">ssl_session_cache&nbsp;shared:SSL:50m;<br style="outline: 0px;">add_header&nbsp;Strict-Transport-Security&nbsp;max-age=15768000;<br style="outline: 0px;">}<br style="outline: 0px;"><span style="outline: 0px;color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">#&nbsp;Permanent&nbsp;Redirect&nbsp;for&nbsp;HTTP&nbsp;to&nbsp;HTTPS</span><br style="outline: 0px;">server&nbsp;<br style="outline: 0px;">{<br style="outline: 0px;">listen&nbsp;80;<br style="outline: 0px;">server_name&nbsp;yourdomain.com;<br style="outline: 0px;"><span style="outline: 0px;color: rgb(230, 192, 123);line-height: 26px;">return</span>&nbsp;301&nbsp;https://<span style="outline: 0px;color: rgb(209, 154, 102);line-height: 26px;">$host</span><span style="outline: 0px;color: rgb(209, 154, 102);line-height: 26px;">$request_uri</span>;<br style="outline: 0px;">}<br style="outline: 0px;"></code></pre> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;outline: 0px;font-size: 16px;line-height: 26px;color: black;">其实可以采用可视化的方式对 Nginx 进行配置,我在 GitHub 上发现了一款可以一键生成 Nginx 配置的神器,相当给力。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;outline: 0px;font-size: 16px;line-height: 26px;color: black;">先来看看它都支持什么功能的配置:反向代理、HTTPS、HTTP/2、IPv6, 缓存、WordPress、CDN、Node.js 支持、 Python (Django) 服务器等等。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;outline: 0px;font-size: 16px;line-height: 26px;color: black;">如果你想在线进行配置,只需要打开网站:https://nginxconfig.io/,按照自己的需求进行操作就行了。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;outline: 0px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.49444444444444446" src="/upload/282e9071f30ebc10179f743f942897c1.png" data-type="png" data-w="1080" style="margin-right: auto;margin-left: auto;outline: 0px;display: block;box-sizing: border-box !important;width: 556px !important;visibility: visible !important;"> <figcaption style="margin-top: 5px;outline: 0px;text-align: center;color: rgb(136, 136, 136);font-size: 14px;"> 图片 </figcaption> </figure> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;outline: 0px;font-size: 16px;line-height: 26px;color: black;">选择你的场景,填写好参数,系统就会自动生成配置文件。</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;outline: 0px;font-size: 16px;line-height: 26px;color: black;">开源地址:github.com/digitalocean/nginxconfig.io</p> <p data-tool="mdnice编辑器" style="margin: 1em 4px;padding-top: 8px;padding-bottom: 8px;outline: 0px;font-size: 16px;line-height: 26px;color: black;">网站:digitalocean.com/community/tools/nginx</p> </section>

如何使用 Redis 实现 “附近的人” 这个功能?

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 14px;padding: 10px;"> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;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: rgba(0, 0, 0, 0.65);border-right: 1px solid rgba(0, 0, 0, 0.65);background: rgb(249, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;color: black;line-height: 26px;">前言:针对“附近的人”这一位置服务领域的应用场景,常见的可使用PG、MySQL和MongoDB等多种DB的空间索引进行实现。而Redis另辟蹊径,结合其有序队列zset以及geohash编码,实现了空间搜索功能,且拥有极高的运行效率。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><em>本文将从源码角度对其算法原理进行解析,并推算查询时间复杂度。</em></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">要提供完整的“附近的人”服务,最基本的是要实现“增”、“删”、“查”的功能。以下将分别进行介绍,其中会重点对查询功能进行解析。</p> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="float: left;display: block;width: 90%;border-top: 1px solid #000;height: 1px;line-height: 1px;margin-left: -5px;margin-top: -17px;"> </span><span style="display: block;width: 3px;margin-left: 5%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">操作命令</a></span><span style="display: block;width: 3px;margin-left: 95%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="float: right;display: block;width: 90%;border-bottom: 1px solid #000;height: 1px;line-height: 1px;margin-right: -5px;margin-top: 16px;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">自Redis 3.2开始,Redis基于geohash和有序集合提供了地理位置相关功能。Redis Geo模块包含了以下6个命令:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: square;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <strong style="color: black;">GEOADD</strong> : 将给定的位置对象(纬度、经度、名字)添加到指定的key; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> GEOPOS: 从key里面返回所有给定位置对象的位置(经度和纬度); </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> GEODIST: 返回两个给定位置之间的距离; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> GEOHASH: 返回一个或多个位置对象的Geohash表示; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <strong style="color: black;">GEORADIUS</strong> : 以给定的经纬度为中心,返回目标集合中与中心的距离不超过给定最大距离的所有位置对象; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> GEORADIUSBYMEMBER: 以给定的位置对象为中心,返回与其距离不超过给定最大距离的所有位置对象。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">其中,组合使用GEOADD和GEORADIUS可实现“附近的人”中“增”和“查”的基本功能。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">要实现微信中“附近的人”功能,可直接使用GEORADIUSBYMEMBER命令。其中“给定的位置对象”即为用户本人,搜索的对象为其他用户。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">不过本质上,GEORADIUSBYMEMBER = GEOPOS + GEORADIUS,即先查找用户位置再通过该位置搜索附近满足位置相互距离条件的其他用户对象。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">以下会从源码角度入手对GEOADD和GEORADIUS命令进行分析,剖析其算法原理。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;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: rgba(0, 0, 0, 0.65);border-right: 1px solid rgba(0, 0, 0, 0.65);background: rgb(249, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;color: black;line-height: 26px;">Redis geo操作中只包含了“增”和“查”的操作,并没有专门的“删除”命令。主要是因为Redis内部使用有序集合(zset)保存位置对象,可用zrem进行删除。</p> </blockquote> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;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: rgba(0, 0, 0, 0.65);border-right: 1px solid rgba(0, 0, 0, 0.65);background: rgb(249, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;color: black;line-height: 26px;">在Redis源码geo.c的文件注释中,只说明了该文件为GEOADD、GEORADIUS和GEORADIUSBYMEMBER的实现文件(其实在也实现了另三个命令)。从侧面看出

五十五张图告诉你微服务的灵魂摆渡者Nacos究竟有多强?

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: -apple-system, system-ui, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;" data-mpa-powered-by="yiban.io"> <h2 data-tool="mdnice编辑器" style="font-weight: bold;padding-top: 30px;padding-bottom: 30px;color: rgb(19, 92, 224);font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;">前言</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;">Nacos是阿里巴巴开源的服务注册中心以及配置中心,致力于给开发者提供一款便捷、简单上手的开源框架。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;margin-top: 16px;">Nacos究竟有什么惊人的地方呢?看下图:</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 data-ratio="0.7321711568938193" src="/upload/56f0877000ec54b3fb656b30416d8efc.png" data-type="png" data-w="631" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;">从上图不难看出阿里巴巴的野心,一个Nacos干掉了Spring Cloud的三大组件,分别是<code style="font-size: 14px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">注册中心Eureka</code>、<code style="font-size: 14px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">服务配置Config</code>,<code style="font-size: 14px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">服务总线Bus</code>。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;margin-top: 16px;">本文目录结构如下图:</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 data-ratio="0.8537313432835821" src="/upload/e1c4715de1ef279f55e861df048aee8c.png" data-type="png" data-w="1340" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <h2 data-tool="mdnice编辑器" style="font-weight: bold;padding-top: 30px;padding-bottom: 30px;color: rgb(19, 92, 224);font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;">为什么Nacos这么受欢迎?</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;">Nacos官方文档的介绍中有这么一句话,如下:</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;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(178, 174, 197);background: rgb(255, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 16px;color: rgb(102, 102, 102);line-height: 2;">Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;">什么意思呢?不着急,有对比才有伤害。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;margin-top: 16px;"><code style="font-size: 14px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">Eureka</code>、<code style="font-size: 14px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">Config</code>这两个组件相信大家都用过,有什么感受?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;margin-top: 16px;">当然,这两个组件给我最直观的感受就是繁琐,原因如下:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;color: rgb(1, 1, 1);line-height: 2;"> 无论是Eureka还是Config都必须自己搭建个服务 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;color: rgb(1, 1, 1);line-height: 2;"> 英文界面不是那么友好 </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;">用过Nacos的开发者都说很爽,不用自己搭建服务,阿里给你准备好了服务,只需要启动即可;界面中英文都有,很适合初学者。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;margin-top: 16px;">当然最重要的原因就是以上组件很可能面临<code style="font-size: 14px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">停更</code>、比如Eureka已经停更了,谁知道后面其他的组件会不会如此呢?</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;padding-top: 30px;padding-bottom: 30px;color: rgb(19, 92, 224);font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;">如何自学呢?</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;">对于初学者当然是官方文档了,下面作者列出了Nacos相关的官方文档:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;color: rgb(1, 1, 1);line-height: 2;"> https://nacos.io/zh-cn/docs/what-is-nacos.html(中英文兼备) </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;color: rgb(1, 1, 1);line-height: 2;"> https://spring-cloud-alibaba-group.github.io/github-pages/hoxton/en-us/index.html(英文) </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;color: rgb(1, 1, 1);line-height: 2;"> https://github.com/alibaba/nacos(Nacos项目仓库) </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;">当然很多人不愿意看官方文档,作者也在为大家准备了视频教程。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;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(178, 174, 197);background: rgb(255, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 16px;color: rgb(102, 102, 102);line-height: 2;">公众号<code style="font-size: 14px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">码猿技术专栏</code>回复关键词<code style="font-size: 14px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">9527</code>免费获取。</p> </blockquote> <h2 data-tool="mdnice编辑器" style="font-weight: bold;padding-top: 30px;padding-bottom: 30px;color: rgb(19, 92, 224);font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;">本文版本说明</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;">基于Maven构建的微服务项目,各个组件版本如下:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;color: rgb(1, 1, 1);line-height: 2;"> JDK1.8+ </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;color: rgb(1, 1, 1);line-height: 2;"> Spring Boot-2.2.2.RELEASE </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;color: rgb(1, 1, 1);line-height: 2;"> SpringCloud-Hoxton.SR3 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;color: rgb(1, 1, 1);line-height: 2;"> SpringCloud Alibaba-2.2.1.RELEASE </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;"><span style="background-image: linear-gradient(to right, rgb(50, 153, 210), rgb(239, 189, 181));background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;color: rgb(255, 255, 255);padding-right: 4px;padding-left: 4px;display: inline-block;border-radius: 4px;margin-right: 2px;margin-left: 2px;letter-spacing: 1px;">注意</span>:Spring Boot、Spring Cloud、Spring Cloud Alibaba的版本可不是随便选择的,官网明确规定了各个版本的适配:https://github.com/alibaba/spring-cloud-alibaba/wiki,如下图:</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 data-ratio="0.7535816618911175" src="/upload/39acb8fd63abe48d795b642d3a3d6b48.png" data-type="png" data-w="698" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;">不同版本的Alibaba也对应了不同组件的版本,如下图:</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 data-ratio="0.6076352067868505" src="/upload/2f544cb6cc8cdf46670f43435827bfe6.png" data-type="png" data-w="943" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;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(178, 174, 197);background: rgb(255, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 16px;color: rgb(102, 102, 102);line-height: 2;">一定要完全按照文档给出的版本来选择,不然会出现意想不到的BUG,那岂不是鸡鸡....</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;">作者使用的是分模块的聚合项目演示,其中<code style="font-size: 14px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">dependencyManagement</code>依赖如下,对应着上文提到的版本:</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 data-ratio="0.861995753715499" src="/upload/83f844c0038f63afb42a0894ffe13602.png" data-type="png" data-w="942" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;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(178, 174, 197);background: rgb(255, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 16px;color: rgb(102, 102, 102);line-height: 2;">注意:如果你的版本的不是和作者一样,请一定严格按照官方文档给的版本进行适配,否则会有意想不到的BUG....</p> </blockquote> <h2 data-tool="mdnice编辑器" style="font-weight: bold;padding-top: 30px;padding-bottom: 30px;color: rgb(19, 92, 224);font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;">启动Nacos服务</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;">根据上面作者选择的Spring Cloud Alibaba的版本,对应的Nacos版本是<code style="font-size: 14px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">1.2.1</code>,直接去GitHub(https://github.com/alibaba/nacos/tags)下载对应的版本即可,可以选择windows或者Linux,如下图:</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 data-ratio="0.44149908592321757" src="/upload/54eab490061a0c4eb9cc8e58123ee343.png" data-type="png" data-w="1094" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;">下载完成之后直接解压即可,从它的目录结构和文件名称一看这就是一个Spring Boot 项目。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;margin-top: 16px;">进入<code style="font-size: 14px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">/bin</code>目录,有两个脚本,如下:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;color: rgb(1, 1, 1);line-height: 2;"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">startup.cmd</code>:windows平台的启动脚本 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;color: rgb(1, 1, 1);line-height: 2;"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">startup.sh</code>:Linux平台的启动脚本 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;">由于作者本地是windows,直接双击<code style="font-size: 14px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">startup.cmd</code>启动项目,出现以下界面则启动完成:</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 data-ratio="0.45549738219895286" src="/upload/6c7779df0953cf216bbbec097109e418.png" data-type="png" data-w="955" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;">在浏览器输入<code style="font-size: 14px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">http://localhost:8848/nacos</code>进入Nacos的登录界面。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;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(178, 174, 197);background: rgb(255, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 16px;color: rgb(102, 102, 102);line-height: 2;">用户名:nacos;密码:nacos</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;">登录成功的界面如下:</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 data-ratio="0.33581533879374537" src="/upload/b68fd94e55130138b17b7f8678b1b7e0.png" data-type="png" data-w="1343" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <h2 data-tool="mdnice编辑器" style="font-weight: bold;padding-top: 30px;padding-bottom: 30px;color: rgb(19, 92, 224);font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;">服务注册发现</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;">微服务的服务注册和发现相信都用过Eureka,要自己本地构建一个Eureka微服务,但是整合了Alibaba的Nacos则不用那么复杂,直接启动Alibaba提供的Nacos服务即可,这样让程序员把全部精力放在业务上,下面是一个简单的架构图:</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 data-ratio="0.5494296577946768" src="/upload/765f70f20bbd194182a05ca99e394a4d.png" data-type="png" data-w="1052" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <h3 data-tool="mdnice编辑器" style="font-weight: bold;padding-top: 30px;padding-bottom: 30px;color: rgb(19, 92, 224);"><span style="display: none;"></span>如何演示效果呢?<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;">参照上面架构图,作者分别创建了两个模块,分别是<code style="font-size: 14px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">nacos-provider</code>(服务提供者)、<code style="font-size: 14px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">nacos-consumer</code>(服务消费者),职责如下:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bot

openFeign夺命连环9问,这谁受得了?

作者:微信小助手

<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, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, 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: &quot;Operator Mono&quot;, 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: &quot;Operator Mono&quot;, 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: &quot;Operator Mono&quot;, 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: &quot;Operator Mono&quot;, 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 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 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: &quot;Operator Mono&quot;, 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: &quot;Operator Mono&quot;, 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: &quot;Operator Mono&quot;, 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==&amp;mid=2247493854&amp;idx=1&amp;sn=4b3fb7f7e17a76000733899f511ef915&amp;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: &quot;Operator Mono&quot;, 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: &quot;Operator Mono&quot;, 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 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: &quot;Operator Mono&quot;, 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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #986801;line-height: 26px;">server:</span><br>&nbsp;&nbsp;<span style="color: #986801;line-height: 26px;">port:</span>&nbsp;<span style="color: #986801;line-height: 26px;">9005</span><br><span style="color: #986801;line-height: 26px;">spring:</span><br>&nbsp;&nbsp;<span style="color: #986801;line-height: 26px;">application:</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">##&nbsp;指定服务名称,在nacos中的名字</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #986801;line-height: 26px;">name:</span>&nbsp;<span style="color: #50a14f;line-height: 26px;">openFeign-provider</span><br>&nbsp;&nbsp;<span style="color: #986801;line-height: 26px;">cloud:</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #986801;line-height: 26px;">nacos:</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #986801;line-height: 26px;">discovery:</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">#&nbsp;nacos的服务地址,nacos-server中IP地址:端口号</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #986801;line-height: 26px;">server-addr:</span>&nbsp;<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>&nbsp;&nbsp;<span style="color: #986801;line-height: 26px;">endpoints:</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #986801;line-height: 26px;">web:</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #986801;line-height: 26px;">exposure:</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">##&nbsp;yml文件中存在特殊字符,必须用单引号包含,否则启动报错</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #986801;line-height: 26px;">include:</span>&nbsp;<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: &quot;Operator Mono&quot;, 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: &quot;Operator Mono&quot;, 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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="line-height: 26px;">&lt;<span style="color: #e45649;line-height: 26px;">dependency</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;">&lt;<span style="color: #e45649;line-height: 26px;">groupId</span>&gt;</span>org.springframework.cloud<span style="line-height: 26px;">&lt;/<span style="color: #e45649;line-height: 26px;">groupId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;">&lt;<span style="color: #e45649;line-height: 26px;">artifactId</span>&gt;</span>spring-cloud-starter-openfeign<span style="line-height: 26px;">&lt;/<span style="color: #e45649;line-height: 26px;">artifactId</span>&gt;</span><br><span style="line-height: 26px;">&lt;/<span style="color: #e45649;line-height: 26px;">dependency</span>&gt;</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: &quot;Operator Mono&quot;, 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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@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>&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span>&nbsp;<span style="color: #c18401;line-height: 26px;">OpenFeignConsumer9006Application</span><br></span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="color: #a626a4;line-height: 26px;">static</span>&nbsp;<span style="color: #a626a4;line-height: 26px;">void</span>&nbsp;<span style="color: #4078f2;line-height: 26px;">main</span><span style="line-height: 26px;">(String[]&nbsp;args)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SpringApplication.run(OpenFeignConsumer9006Application<span style="line-height: 26px;">.<span style="color: #a626a4;line-height: 26px;">class</span>,&nbsp;<span style="color: #c18401;line-height: 26px;">args</span>)</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<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: &quot;Operator Mono&quot;, 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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@FeignClient</span>(value&nbsp;=&nbsp;<span style="color: #50a14f;line-height: 26px;">"openFeign-provider"</span>)<br><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">interface</span>&nbsp;<span style="color: #c18401;line-height: 26px;">OpenFeignService</span>&nbsp;</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: &quot;Operator Mono&quot;, 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: &quot;Operator Mono&quot;, 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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@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>&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span>&nbsp;<span style="color: #c18401;line-height: 26px;">OpenFeignController</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<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: &quot;Operator Mono&quot;, 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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@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>&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span>&nbsp;<span style="color: #c18401;line-height: 26px;">OpenFeignProviderController</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #4078f2;line-height: 26px;">@PostMapping</span>(<span style="color: #50a14f;line-height: 26px;">"/order2"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;Order&nbsp;<span style="color: #4078f2;line-height: 26px;">createOrder2</span><span style="line-height: 26px;">(@RequestBody&nbsp;Order&nbsp;order)</span></span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a626a4;line-height: 26px;">return</span>&nbsp;order;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@FeignClient</span>(value&nbsp;=&nbsp;<span style="color: #50a14f;line-height: 26px;">"openFeign-provider"</span>)<br><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">interface</span>&nbsp;<span style="color: #c18401;line-height: 26px;">OpenFeignService</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">/**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;参数默认是<span style="color: #a626a4;line-height: 26px;">@RequestBody</span>标注的,这里的<span style="color: #a626a4;line-height: 26px;">@RequestBody</span>可以不填<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;方法名称任意<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #4078f2;line-height: 26px;">@PostMapping</span>(<span style="color: #50a14f;line-height: 26px;">"/openfeign/provider/order2"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;">Order&nbsp;<span style="color: #4078f2;line-height: 26px;">createOrder2</span><span style="line-height: 26px;">(@RequestBody&nbsp;Order&nbsp;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: &quot;Operator Mono&quot;, 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: &quot;Operator Mono&quot;, 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: &quot;Operator Mono&quot;, 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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@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>&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span>&nbsp;<span style="color: #c18401;line-height: 26px;">OpenFeignProviderController</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #4078f2;line-height: 26px;">@PostMapping</span>(<span style="color: #50a14f;line-height: 26px;">"/order1"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;Order&nbsp;<span style="color: #4078f2;line-height: 26px;">createOrder1</span><span style="line-height: 26px;">(Order&nbsp;order)</span></span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a626a4;line-height: 26px;">return</span>&nbsp;order;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@FeignClient</span>(value&nbsp;=&nbsp;<span style="color: #50a14f;line-height: 26px;">"openFeign-provider"</span>)<br><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">interface</span>&nbsp;<span style="color: #c18401;line-height: 26px;">OpenFeignService</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">/**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;参数默认是<span style="color: #a626a4;line-height: 26px;">@RequestBody</span>标注的,如果通过POJO表单传参的,使用<span style="color: #a626a4;line-height: 26px;">@SpringQueryMap</span>标注<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #4078f2;line-height: 26px;">@PostMapping</span>(<span style="color: #50a14f;line-height: 26px;">"/openfeign/provider/order1"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;">Order&nbsp;<span style="color: #4078f2;line-height: 26px;">createOrder1</span><span style="line-height: 26px;">(@SpringQueryMap&nbsp;Order&nbsp;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 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: &quot;Operator Mono&quot;, 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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@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>&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span>&nbsp;<span style="color: #c18401;line-height: 26px;">OpenFeignProviderController</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #4078f2;line-height: 26px;">@GetMapping</span>(<span style="color: #50a14f;line-height: 26px;">"/test/{id}"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;String&nbsp;<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&nbsp;id)</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a626a4;line-height: 26px;">return</span>&nbsp;<span style="color: #50a14f;line-height: 26px;">"accept&nbsp;one&nbsp;msg&nbsp;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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@FeignClient</span>(value&nbsp;=&nbsp;<span style="color: #50a14f;line-height: 26px;">"openFeign-provider"</span>)<br><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">interface</span>&nbsp;<span style="color: #c18401;line-height: 26px;">OpenFeignService</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #4078f2;line-height: 26px;">@GetMapping</span>(<span style="color: #50a14f;line-height: 26px;">"/openfeign/provider/test/{id}"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;">String&nbsp;<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&nbsp;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: &quot;Operator Mono&quot;, 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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@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>&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span>&nbsp;<span style="color: #c18401;line-height: 26px;">OpenFeignProviderController</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #4078f2;line-height: 26px;">@PostMapping</span>(<span style="color: #50a14f;line-height: 26px;">"/test2"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;String&nbsp;<span style="color: #4078f2;line-height: 26px;">test2</span><span style="line-height: 26px;">(String&nbsp;id,String&nbsp;name)</span></span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a626a4;line-height: 26px;">return</span>&nbsp;MessageFormat.format(<span style="color: #50a14f;line-height: 26px;">"accept&nbsp;on&nbsp;msg&nbsp;id={0},name={1}"</span>,id,name);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oex3Qib1rrJgboa2oKbWuuiavdE35cZCh25RatYflXk4GibeOtNmPCibFq1fPeAgJQl9UnznqPvyJKwnYUt59eIvBjGA/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@FeignClient</span>(value&nbsp;=&nbsp;<span style="color: #50a14f;line-height: 26px;">"openFeign-provider"</span>)<br><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">interface</span>&nbsp;<span style="color: #c18401;line-height: 26px;">OpenFeignService</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">/**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;必须要<span style="color: #a626a4;line-height: 26px;">@RequestParam</span>注解标注,且value属性必须填上参数名<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;方法参数名可以任意,但是<span style="color: #a626a4;line-height: 26px;">@RequestParam<