作者:微信小助手
<p data-lake-id="f47942fff38c686ed626a530b6d84219" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;" data-mpa-powered-by="yiban.io"><span data-mce-style="font-size: 10px" style="font-size: 13px;color: rgb(136, 136, 136);">点击上方蓝色“</span><span style="color: rgb(24, 144, 255);font-size: 13px;" data-mce-style="font-size: 10px">后端面试那些事儿</span><span data-mce-style="font-size: 10px" style="font-size: 13px;color: rgb(136, 136, 136);">”,选择“设为星标”</span></p> <p data-lake-id="eca2a1864e13b3e1b84aafe9cb4abdff" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><span style="color: rgb(140, 140, 140);font-size: 13px;" data-mce-style="font-size: 10px">学最好的别人,做最好的自己</span></p> <p data-lake-id="b4f10076adf2228ecaccd921f627ed69" style="text-align: right;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><span style="color: rgb(140, 140, 140);font-size: 13px;" data-mce-style="font-size: 10px">来源:https://zhenbianshu.github.io/</span></p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Redis 作为一个非常成功的数据库,提供了非常丰富的数据类型和命令,使用这些,我们可以轻易而高效地完成很多缓存操作,可是总有一些比较特殊问题或需求需要解决,这时候可能就需要我们自己定制自己的 Redis 数据结构和命令。</p> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Redis命令问题</h3> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">“线程安全”问题</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">我们都知道 Redis 是单线程的,可是它怎么会有线程安全问题呢?</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">我们正常理解的线程安全问题是指<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">单进程多线程</code>模型内部多个线程<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">操作进程内共享内存</code>导致的数据资源充突。而 Redis 的线程安全问题的产生,并不是来自于 Redis 服务器内部。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Redis 作为数据服务器,就相当于多个客户端的共享内存,多个客户端就相当于同一进程下的多个线程,如果多个客户端之间没有良好的数据同步策略,就会产生类似线程安全的问题。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">典型场景是:</p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> Redis 内存储了一个用户的状态: <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">user5277=idle</code>; </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 客户端连接 A 读取了用户状态,获取到用户的空闲状态 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">status = get("user5277")</code>; </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 客户端连接 B 也同样读取了用户状态; </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 客户端连接 A 给用户安排了一个任务,并将 Redis 内用户状态置为忙碌 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">set("user5277", "busy")</code>; </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 客户端连接 B 同样设置用户为忙碌状态。 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 可是此时用户却被同时分配了两个任务。 </section></li> </ul> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">导致这个问题的原因就是虽然 Redis 是单线程的,能保证命令的序列化,但由于其执行效率很高,多个客户端的命令之间不做好请求同步,同样会造成命令的顺序错乱。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">当然这个问题也很好解决,给用户状态加锁就行了,使同一时间内只能有一个客户端操作用户状态。不过加锁我们就需要考虑锁粒度、死锁等问题了,无疑添加了程序的复杂性,不利于维护。</p> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"> <mpcpc js_editor_cpcad="" class="js_cpc_area cpc_iframe" src="/cgi-bin/readtemplate?t=tmpl/cpc_tmpl#1625928530584" data-category_id_list="1|11|16|17|22|24|26|27|28|29|3|31|32|35|36|37|39|41|42|43|45|46|47|48|49|5|50|51|52|53|54|55|6|7|8" data-id="1625928530584"></mpcpc></h4> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">效率问题</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Redis 作为一个极其高效的内存数据服务器,其命令执行速度极快,之前看过阿里云 Redis 的一个压测结果,执行效率可以达到 10W写QPS, 60W读QPS,那么,它的效率问题又来自何处呢?</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">答案是网络,做 Web 的都知道,效率优化要从网络做起,服务端又是优化代码,又是优化数据库,不如网络连接的一次优化,而网络优化最有效的就是减少请求数。我们要知道执行一次内存访问的耗时约是 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">100ns</code>,而不同机房之间来回一次约需要 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">500000ns</code>,其中的差距可想而知。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Redis在单机内效率超高,但工业化部署总不会把服务器和 Redis 放在同一台机器上,如果触碰到效率瓶颈的话,那就是网络。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">典型场景就是我们从 Redis 里读出一条数据,再使用这条数据做键,读取另外一条数据。这样来来回回,便有两次网络往返。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">导致这种问题的原因就是 Redis 的普通命令没有服务端计算的能力,无法在服务器进行复合命令操作,虽然有 Redis 也提供了 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">pipeline</code> 的特性,但它需要多个命令的请求和响应之间没有依赖关系。想简化多个相互依赖的命令就只能将数据拉回客户端,由客户端处理后再请求 Redis。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">综上,我们要更高效更方便的使用 Redis 就需要自己“定制”一些命令了。</p> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">内嵌Lua的执行</h3> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">万幸 Redis 内嵌了 Lua 执行环境,支持 Lua 脚本的执行,通过执行 Lua 脚本,我们可以把多个命令复合为一个 Lua 脚本,通过 Lua 脚本来实现上文中提到的 Redis 命令的次序性和 Redis 服务端计算。</p> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Lua</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Lua 是一个简洁、轻量、可扩展的脚本语言,它的特性有:</p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 轻量:源码包只有核心库,编译后体积很小。 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 高效:由 ANSI C 写的,启动快、运行快。 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 内嵌:可内嵌到各种编程语言或系统中运行,提升静态语言的灵活性。如 OpenResty 就是将 Lua 嵌入到 nginx 中执行。 </section></li> </ul> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">而且完全不需要担心语法问题,Lua 的语法很简单,分分钟使用不成问题。</p> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"> <mpcpc js_editor_cpcad="" class="js_cpc_area cpc_iframe" src="/cgi-bin/readtemplate?t=tmpl/cpc_tmpl#1625928538014" data-category_id_list="1|11|16|17|22|24|26|27|28|29|3|31|32|35|36|37|39|41|42|43|45|46|47|48|49|5|50|51|52|53|54|55|6|7|8" data-id="1625928538014"></mpcpc></h4> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">执行步骤</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Redis 在 2.6 版本后,启动时会创建 Lua 环境、载入 Lua 库、定义 Redis 全局表格、存储 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">redis.pcall</code> 等 Redis 命令,以准备 Lua 脚本的执行。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">一个典型的 Lua 脚本执行步骤如下:</p> <ol style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 检查脚本是否执行过,没执行过使用脚本的 sha1 校验和生成一个 Lua 函数; </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 为函数绑定超时、错误处理勾子; </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 创建一个伪客户端,通过这个伪客户端执行 Lua 中的 Redis 命令; </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 处理伪客户端的返回值,最终返回给客户端; </section></li> </ol> <p style="text-align: center;"><img class="rich_pages" data-backh="652" data-backw="579" data-galleryid="" data-ratio="1.1268518518518518" data-s="300,640" src="/upload/b62bae1c0f861cb9df7574a6ada6cca0.png" data-type="png" data-w="1080" style="width: 100%;height: auto;"></p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">虽然 Lua 脚本使用的是伪客户端,但 Redis 处理它会跟普通客户端一样,也会将执行的 Redis 命令进行 rdb aof 主从复制等操作。</p> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">使用</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Lua 脚本的使用可以通过 Redis 的 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">EVAL</code> 和 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">EVALSHA</code> 命令。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">EVAL</code> 适用于单次执行 Lua 脚本,执行脚本前会由脚本内容生成 sha1 校验和,在函数表内查询函数是否已定义,如未定义执行成功后 Redis 会在全局表里缓存这个脚本的校验和为函数名,后续再次执行此命令就不会再创建新的函数了。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">而要使用 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">EVALSHA</code> 命令,就得先使用 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">SCRIPT LOAD</code> 命令先将函数加载到 Redis,Redis 会返回此函数的 sha1 校验和, 后续就可以直接使用这个校验和来执行命令了。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">以下是使用上述命令的例子:</p> <pre style="font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;">127.0.0.1:6379> EVAL <span style="color: rgb(152, 195, 121);line-height: 26px;">"return 'hello'"</span> 0 0<br><span style="color: rgb(152, 195, 121);line-height: 26px;">"hello"</span><br><br>127.0.0.1:6379> SCRIPT LOAD <span style="color: rgb(152, 195, 121);line-height: 26px;">"return redis.pcall('GET', ARGV[1])"</span><br><span style="color: rgb(152, 195, 121);line-height: 26px;">"20b602dcc1bb4ba8fca6b74ab364c05c58161a0a"</span><br><br>127.0.0.1:6379> EVALSHA 20b602dcc1bb4ba8fca6b74ab364c05c58161a0a 0 <span style="color: rgb(230, 192, 123);line-height: 26px;">test</span><br><span style="color: rgb(152, 195, 121);line-height: 26px;">"zbs"</span><br></code></pre> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">EVAL 命令的原型是 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">EVAL script numkeys key [key ...] arg [arg ...]</code>,在 Lua 函数内部可以使用 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">KEYS[N]</code> 和 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ARGV[N]</code> 引用键和参数,需要注意 KEYS 和 ARGV 的参数序号都是从 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">1</code> 开始的。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">还需要注意在 Lua 脚本中,Redis 返回为空时,结果是 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">false</code>,而 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">不是 nil</code>;</p> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Lua 脚本实例</h3> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">下面写几个 Lua 脚本的实例,用来介绍语法的,仅供参考。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Redis 里 hashSet A 的 字段 B 的值是 C,取出 Redis 里键为 C 的值。</p> <pre style="font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;">// 使用: EVAL script 2 A B<br><br><span style="color: rgb(230, 192, 123);line-height: 26px;">local</span> tmpKey = redis.call(<span style="color: rgb(152, 195, 121);line-height: 26px;">'HGET'</span>, KEYS[1], KEYS[2]);<br><span style="color: rgb(230, 192, 123);line-height: 26px;">return</span> redis.call(<span style="color: rgb(152, 195, 121);line-height: 26px;">'GET'</span>, tmpKey);<br></code></pre> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">一次 lpop 出多个值,直到值为 n,或 list 为空(pipeline 也可轻易实现);</p> <pre style="font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;">// 使用: EVAL script 2 list count<br><br><span style="color: rgb(230, 192, 123);line-height: 26px;">local</span> list = {};<br><span style="color: rgb(230, 192, 123);line-height: 26px;">local</span> item = <span style="color: rgb(86, 182, 194);line-height: 26px;">false</span>;<br><span style="color: rgb(230, 192, 123);line-height: 26px;">local</span> num = tonumber(KEYS[2]);<br><span style="color: rgb(198, 120, 221);line-height: 26px;">while</span> (num > 0)<br><span style="color: rgb(198, 120, 221);line-height: 26px;">do</span><br> item = redis.call(<span style="color: rgb(152, 195, 121);line-height: 26px;">'LPOP'</span>, KEYS[1]);<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">if</span> item == <span style="color: rgb(86, 182, 194);line-height: 26px;">false</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">then</span><br> <span style="color: rgb(230, 192, 123);line-height: 26px;">break</span>;<br> end;<br> table.insert(list, item);<br> num = num - 1;<br>end;<br><span style="color: rgb(230, 192, 123);line-height: 26px;">return</span> list;<br></code></pre> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">获取 zset 内 score 最多的 n 个元素 对应 hashset 中的详细信息;</p> <pre style="font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;"><span style="color: rgb(230, 192, 123);line-height: 26px;">local</span> elements = redis.call(<span style="color: rgb(152, 195, 121);line-height: 26px;">'ZRANK'</span>, KEYS[1], 0, KEY[2]);<br><span style="color: rgb(230, 192, 123);line-height: 26px;">local</span> detail = {};<br><br><span style="color: rgb(198, 120, 221);line-height: 26px;">for</span> index,ele <span style="color: rgb(198, 120, 221);line-height: 26px;">in</span> elements <span style="color: rgb(198, 120, 221);line-height: 26px;">do</span><br> <span style="color: rgb(230, 192, 123);line-height: 26px;">local</span> info = redis.call(<span style="color: rgb(152, 195, 121);line-height: 26px;">'HGETALL'</span>, ele);<br> table.insert(detail, info);<br>end;<br><br><span style="color: rgb(230, 192, 123);line-height: 26px;">return</span> detail;<br></code></pre> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">基本使用语法就是如此,更多应用就看各个具体场景了。</p> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">一些思考</h3> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">实现之外,还要一些东西要思考:</p> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">使用场景</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">首先来总结一下 Redis 中 Lua 的使用场景:</p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 可以使用 Lua 脚本实现原子性操作,避免不同客户端访问 Redis 服务器造成的数据冲突。 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 在前后多次请求的结果有依赖时,可以使用 Lua 脚本把多个请求整合为一个请求。 </section></li> </ul> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">注意点</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">使用 Lua 脚本,我们还需要注意:</p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 要保证安全性,在 Lua 脚本中不要使用全局变量,以免污染 Lua 环境,虽然使用全局变量全报错,Lua 脚本停止执行,但还是在定义变量时添加 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">local</code> 关键字。 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 要注意 Lua 脚本的时间复杂度,Redis 的单线程同样会阻塞在 Lua 脚本的执行中。 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 使用 Lua 脚本实现原子操作时,要注意如果 Lua 脚本报错,之前的命令同样无法回滚。 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 一次发出多个 Redis 请求,但请求前后无依赖时,使用 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">pipeline</code>,比 Lua 脚本方便。 </section></li> </ul> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">小结</h3> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">最近工作有了较大的变动,从业务到技术栈都跟原来完全不同了,所有代码和业务都脱离了自己掌控的感觉真的很不爽,工作中全是“开局一个搜索引擎,语法全靠查”,每天还要熬到很晚熟悉新的东西,有点小累,果然换工作就是找罪受啊。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">不过走出舒适区后的充实感也在提醒自己正在不停进步,倒也挺有成就感的。</p> <p data-lake-id="b4f10076adf2228ecaccd921f627ed69" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><br></p> <section data-mpa-template="t" mpa-from-tpl="t"> <section mpa-from-tpl="t" style="margin-top: 10px;margin-bottom: 10px;"> <p style="margin-right: auto;margin-left: auto;width: 231.1875px;"><img data-ratio="0.16666666666666666" src="/upload/89aa6c9fe16e0dfcf56dad1a9f9078ff.png" data-type="gif" data-w="300" style="width: auto;"></p> </section> </section> <p data-lake-id="098b923ca3e65e99528d2c30d6cf4c26" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><br></p> <section data-recommend-type="list-title" data-recommend-tid="6" data-mpa-template="t" style="width: 100%;display: flex;justify-content: center;align-items: center;" data-mid="" data-from="yb-recommend"> <section style="width: 100%;padding: 14px;background: rgb(255, 255, 255);border-radius: 3px;border-width: 1px;border-style: solid;border-color: rgb(232, 232, 235);" data-mid=""> <section style="width: 100%;display: flex;justify-content: center;align-items: center;align-items: flex-end;" data-mid=""> <section data-mid="" style="height: 28px;padding: 4px 22px;font-size: 14px;font-weight: 500;color: rgb(19, 52, 86);line-height: 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/sUbvrqLicbpzB81mjeBxPuxnYdalGxNnJo30L2Hq3WwGficcq8w5YJkLeXnsNHocN53k55TfN5mBpCdicGRyfDg1g/640?wx_fmt=png");background-repeat: no-repeat;background-size: 100% 100%;margin-bottom: -14px;z-index: 10;"> <p data-mid="">往期推荐</p> </section> </section> <section style="width: 100%;border-width: 1px;border-style: solid;border-color: rgb(198, 226, 255);padding: 17px 16px 9px;" data-mid=""> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247495617_1" data-recommend-article-time="1625893260" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/HmHDU48icAtZXOWEtuzs2ynI48cxZy6WOCSjFsPlDggMicCnA6BzG90tE6eXiab4rSCOR0HEVOQI1MbsNmJOXTZ6Q/0?wx_fmt=jpeg" data-recommend-article-title="为什么 StringBuilder 不是线程安全的?" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495617&idx=1&sn=7f7240d1c35fd1c4c90eb92b43ddb882&chksm=97b471d9a0c3f8cf95cdb3ead63d6db382b67ef3f5f21c2cbe23df1570b0229d2f153b898cef#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495617&idx=1&sn=7f7240d1c35fd1c4c90eb92b43ddb882&chksm=97b471d9a0c3f8cf95cdb3ead63d6db382b67ef3f5f21c2cbe23df1570b0229d2f153b898cef&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">为什么 StringBuilder 不是线程安全的?</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247495617_2" data-recommend-article-time="1625893260" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/R3InYSAIZkFlFPC5m4TXiaxU7jlSQiaAmmZBokwbRr4KiaHyvW7sZ54vNucn1ZKqliapc45MbHWPTKPoH4UIWKOdCw/0?wx_fmt=jpeg" data-recommend-article-title="程序员写代码崩溃,路过的暖心美团骑手:我帮你看看!" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495617&idx=2&sn=7064d4821fa16c70d668db56be99e543&chksm=97b471d9a0c3f8cffc9e72348ae25742caa627902d836d01f2d24671dbc6779294a7e8b4a8b2#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495617&idx=2&sn=7064d4821fa16c70d668db56be99e543&chksm=97b471d9a0c3f8cffc9e72348ae25742caa627902d836d01f2d24671dbc6779294a7e8b4a8b2&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">程序员写代码崩溃,路过的暖心美团骑手:我帮你看看!</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247495617_3" data-recommend-article-time="1625893260" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/Rhiaqbb2uvaLfDNBc0BGGzodNWibtFj119BiaGd6NY69cd5bmH4wS1KIF6xxhScHBk3H6ExLP0HdeMzqZxwibrDcvg/0?wx_fmt=jpeg" data-recommend-article-title="搜番神器!一张图就可以告诉你所有!" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495617&idx=3&sn=56e2acdb9c434066168ebc2b7797eb55&chksm=97b471d9a0c3f8cfdc9dff4635baf40548638c9446f2997c90824cef682cb54408d4d68c5838#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495617&idx=3&sn=56e2acdb9c434066168ebc2b7797eb55&chksm=97b471d9a0c3f8cfdc9dff4635baf40548638c9446f2997c90824cef682cb54408d4d68c5838&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">搜番神器!一张图就可以告诉你所有!</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247495601_1" data-recommend-article-time="1625806860" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/HmHDU48icAtZZb5PCImFKatZjBmLyzUucRT8oh60Lq5qXQ5h9J1GWOUdSjPpXghw35DpLz0hez333TGTrvqtWKA/0?wx_fmt=jpeg" data-recommend-article-title="Java 中 long 是不是原子操作?" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495601&idx=1&sn=133b2399e236afd3725a10dd152322f4&chksm=97b471a9a0c3f8bf0ecd6f7823e0ad60e68e9a859861ffdf281572d13ca5390000b3feb6ede5#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495601&idx=1&sn=133b2399e236afd3725a10dd152322f4&chksm=97b471a9a0c3f8bf0ecd6f7823e0ad60e68e9a859861ffdf281572d13ca5390000b3feb6ede5&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">Java 中 long 是不是原子操作?</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247495601_2" data-recommend-article-time="1625806860" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/R3InYSAIZkH22OQfNUgpLsZAhXUGGwpteqNjSj4icMLyLSFqFrxyrcXsZPZN6gc41dEvOCNdlt6pzAKLhjJcZKg/0?wx_fmt=jpeg" data-recommend-article-title="Spring发布新成员:Spring GraphQL!高调出场的GraphQL能火起来了吗?" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495601&idx=2&sn=dc6a029448e1aa0d3523485f63b067b2&chksm=97b471a9a0c3f8bf426b50ee5d77c0712c8a2c924daf72ac2767a7c34a26665b8debccefcc78#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495601&idx=2&sn=dc6a029448e1aa0d3523485f63b067b2&chksm=97b471a9a0c3f8bf426b50ee5d77c0712c8a2c924daf72ac2767a7c34a26665b8debccefcc78&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">Spring发布新成员:Spring GraphQL!高调出场的GraphQL能火起来了吗?</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247495601_3" data-recommend-article-time="1625806860" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/Rhiaqbb2uvaKtRJnCKR3be0jqerWKIntiaugB3PfEicDrVoRbOpjlZw1K99zVicbxeicN7fYkJBtrP9D3OzIpbobkpA/0?wx_fmt=jpeg" data-recommend-article-title="大福利!苹果 Apple Music 会员!免费领取!" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495601&idx=3&sn=b9a6073e0458406bd43e0034f15c891c&chksm=97b471a9a0c3f8bffc9e9a92efa1c52b2813eb429cc63c941f29dec4552e7de0476f13ed7c30#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495601&idx=3&sn=b9a6073e0458406bd43e0034f15c891c&chksm=97b471a9a0c3f8bffc9e9a92efa1c52b2813eb429cc63c941f29dec4552e7de0476f13ed7c30&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;border-bottom:none !important;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">大福利!苹果 Apple Music 会员!免费领取!</p> </section></a> </section> </section> </section> </section> <p data-lake-id="098b923ca3e65e99528d2c30d6cf4c26" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><br></p> <p data-lake-id="098b923ca3e65e99528d2c30d6cf4c26" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><span style="color: rgb(140, 140, 140);"><br></span></p> <section data-mpa-template="t" mpa-from-tpl="t" style="white-space: normal;"> <p style="text-align: center;"><strong><span style="color: rgb(140, 140, 140);letter-spacing: 0.008em;font-size: 14px;">一起进大厂,每日学干货</span></strong></p> </section> <section style="margin-top: 5px;white-space: normal;text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"> <span style="color: rgb(0, 0, 0);"><strong><span style="font-size: 14px;">关注我回复【</span></strong></span> <span style="color: rgb(255, 76, 65);"><strong><span style="font-size: 14px;">加群</span></strong></span> <span style="color: rgb(0, 0, 0);"><strong><span style="color: rgb(0, 0, 0);font-size: 14px;">】,加入Java技术交流群</span></strong></span> </section> <section data-mpa-template="t" mpa-from-tpl="t"> <p style="text-align: center;"><img data-ratio="0.5982532751091703" src="/upload/4e21037b66f60f7d73d060863de4b4b5.png" data-type="gif" data-w="458" data-width="100%" style="color: rgb(62, 62, 62);font-size: 16px;vertical-align: middle;width: 62.0938px;"></p> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzIxMzQzNzMwMw==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/HmHDU48icAtYvlypOY9VaGVXQ639L63Iq6zHiclgibG0CAhgrJ2JLRibKbeCgVIx7WXcicbMW6AJL1Hos9AoJTqtVfA/0?wx_fmt=png" data-nickname="后端面试那些事" data-alias="" data-signature="专注分享后端干货!面向大厂,一起进步!" data-from="0"></mpprofile> </section> <p style="text-align: center;"><br></p> </section> <p style="text-align: center;"><br></p> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-role="paragraph" mpa-from-tpl="t" style="white-space: normal;border-width: 0px;border-style: none;border-color: initial;"> <section style="box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="margin-top: 0.5em;box-sizing: border-box;"> <section style="padding: 0.5em;border-width: 1px;border-style: solid;border-color: rgb(249, 110, 87);box-shadow: rgb(226, 226, 226) 0px 16px 1px -13px;box-sizing: border-box;"> <section powered-by="xiumi.us" style="text-align: left;box-sizing: border-box;"> <section style="text-align: justify;color: rgb(64, 84, 115);box-sizing: border-box;"> <p style="box-sizing: border-box;"><img data-ratio="0.33611111111111114" src="/upload/a53e4e397528931045198e4f89d7ba0.png" data-type="png" data-w="1080"></p> <p style="box-sizing: border-box;">点击“阅读原文”,领取 2021 年<strong>最新免费技术资料大全</strong></p> </section> </section> </section> </section> <section powered-by="xiumi.us" style="font-size: 32px;color: rgb(249, 110, 87);box-sizing: border-box;text-align: left;"> ↓↓↓ </section> </section> </section> </section>
作者:微信小助手
<p data-lake-id="f47942fff38c686ed626a530b6d84219" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;" data-mpa-powered-by="yiban.io"><span data-mce-style="font-size: 10px" style="font-size: 13px;color: rgb(136, 136, 136);">点击上方蓝色“</span><span style="color: rgb(24, 144, 255);font-size: 13px;" data-mce-style="font-size: 10px">后端面试那些事儿</span><span data-mce-style="font-size: 10px" style="font-size: 13px;color: rgb(136, 136, 136);">”,选择“设为星标”</span></p> <p data-lake-id="eca2a1864e13b3e1b84aafe9cb4abdff" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><span style="color: rgb(140, 140, 140);font-size: 13px;" data-mce-style="font-size: 10px">学最好的别人,做最好的自己</span></p> <p data-lake-id="b4f10076adf2228ecaccd921f627ed69" style="text-align: right;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><span style="color: rgb(140, 140, 140);font-size: 13px;" data-mce-style="font-size: 10px">来源:https://my.oschina.net/floor/blog/4965200</span></p> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">前言</h3> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">本文不是一个RateLimiter的详细分析,仅仅是概要分析。</p> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">令牌桶算法</h3> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">一说到RateLimiter,必然要是说的令牌桶,它的大致逻辑如下:</p> <p style="text-align: center;"><img class="rich_pages" data-backh="489" data-backw="579" data-galleryid="" data-ratio="0.8459495351925631" data-s="300,640" src="/upload/740b71e16f53b516ea22255a33e7b127.png" data-type="png" data-w="753" style="width: 100%;height: auto;"></p> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">按图实现</h3> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">令牌桶的图,网上到处可见,按图实现也非常简单,无非是定时添加令牌桶,并提供一个获取令牌的函数,博主实现了一遍代码如下:</p> <pre style="font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">import</span> java.util.concurrent.*;<br><br><span style="color: rgb(198, 120, 221);line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">class</span> <span style="color: rgb(230, 192, 123);line-height: 26px;">MyRateLimiter</span> </span>{<br> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//令牌桶</span><br> BlockingQueue<Integer>TOKEN_BUCKET=<span style="color: rgb(198, 120, 221);line-height: 26px;">new</span> LinkedBlockingDeque<>(<span style="color: rgb(209, 154, 102);line-height: 26px;">5</span>);<br><br> <span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">public</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">static</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">void</span> <span style="color: rgb(97, 174, 238);line-height: 26px;">main</span><span style="line-height: 26px;">(String[] args)</span> </span>{<br> MyRateLimiter myRateLimiter=<span style="color: rgb(198, 120, 221);line-height: 26px;">new</span> MyRateLimiter();<br> myRateLimiter.addTokenFixedRate();<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">for</span>(<span style="color: rgb(198, 120, 221);line-height: 26px;">int</span> i=<span style="color: rgb(209, 154, 102);line-height: 26px;">0</span>;i<<span style="color: rgb(209, 154, 102);line-height: 26px;">10</span>;i++){<br> myRateLimiter.acqurie();<br> System.out.println(<span style="color: rgb(152, 195, 121);line-height: 26px;">"第几次执行i:"</span> + i + <span style="color: rgb(152, 195, 121);line-height: 26px;">",执行时间为:"</span> + System.currentTimeMillis());<br> }<br> }<br> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">/**<br> * 定时添加令牌<br> */</span><br> <span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">public</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">void</span> <span style="color: rgb(97, 174, 238);line-height: 26px;">addTokenFixedRate</span><span style="line-height: 26px;">()</span></span>{<br> ScheduledExecutorService scheduledExecutorService= Executors.newSingleThreadScheduledExecutor();<br> scheduledExecutorService.scheduleAtFixedRate(()->{<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">boolean</span> suc=TOKEN_BUCKET.offer(<span style="color: rgb(209, 154, 102);line-height: 26px;">1</span>);<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">if</span>(!suc){<br> System.out.println(<span style="color: rgb(152, 195, 121);line-height: 26px;">"令牌桶满了丢弃"</span>);<br> }<br> },<span style="color: rgb(209, 154, 102);line-height: 26px;">0</span>,<span style="color: rgb(209, 154, 102);line-height: 26px;">200</span>,TimeUnit.MILLISECONDS);<br> }<br><br> <span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">public</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">void</span> <span style="color: rgb(97, 174, 238);line-height: 26px;">acqurie</span><span style="line-height: 26px;">()</span></span>{<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">while</span> (TOKEN_BUCKET.poll()==<span style="color: rgb(198, 120, 221);line-height: 26px;">null</span>){};<br> }<br><br>}<br></code></pre> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">测试结果如下,基本满足要求</p> <p style="text-align: center;"><img class="rich_pages" data-backh="320" data-backw="556" data-galleryid="" data-ratio="0.5755395683453237" data-s="300,640" src="/upload/78cdf64b927a7bf238ac878c05a999cd.png" data-type="png" data-w="556" style="width: 100%;height: auto;"></p> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"> <mpcpc js_editor_cpcad="" class="js_cpc_area cpc_iframe" src="/cgi-bin/readtemplate?t=tmpl/cpc_tmpl#1625981001068" data-category_id_list="48|32|26|49|1|27|28|45|46|55|39|8|3|47|35|41|5|31|6|7|24|37|22|11|50|54|53|52|42|29|43|16|17|51|36" data-id="1625981001068"></mpcpc></h3> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">RateLimiter概要实现</h3> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">我一开始是按照自己实现的逻辑,去查看Guava的RateLimiter的源码的,结果发现<strong style="color: rgb(53, 179, 120);">RateLimiter根本没有集合充当桶,核心是记录了下一令牌产生的时间与现存令牌数,并动态更新它们。</strong></p> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">概要逻辑图如下:</h3> <p style="text-align: center;"><img class="rich_pages" data-backh="1324" data-backw="579" data-galleryid="" data-ratio="2.2888888888888888" data-s="300,640" src="/upload/af5682981318a68b238d10ea731f68be.png" data-type="png" data-w="585" style="width: 100%;height: auto;"></p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">按照这个图看核心代码就比较容易了,摘录核心代码如下:</p> <pre style="font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;"><span style="color: rgb(97, 174, 238);line-height: 26px;">@CanIgnoreReturnValue</span><br><span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">public</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">double</span> <span style="color: rgb(97, 174, 238);line-height: 26px;">acquire</span><span style="line-height: 26px;">(<span style="color: rgb(198, 120, 221);line-height: 26px;">int</span> permits)</span> </span>{<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">long</span> microsToWait = reserve(permits);<br> stopwatch.sleepMicrosUninterruptibly(microsToWait);<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">return</span> <span style="color: rgb(209, 154, 102);line-height: 26px;">1.0</span> * microsToWait / SECONDS.toMicros(<span style="color: rgb(209, 154, 102);line-height: 26px;">1L</span>);<br>}<br><span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//Reserve 一路向下能查到如下代码 reserveEarliestAvailable</span><br><br><span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">final</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">long</span> <span style="color: rgb(97, 174, 238);line-height: 26px;">reserveEarliestAvailable</span><span style="line-height: 26px;">(<span style="color: rgb(198, 120, 221);line-height: 26px;">int</span> requiredPermits, <span style="color: rgb(198, 120, 221);line-height: 26px;">long</span> nowMicros)</span> </span>{<br> resync(nowMicros);<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">long</span> returnValue = nextFreeTicketMicros;<br> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">// 现存令牌可以提供的令牌数</span><br> <span style="color: rgb(198, 120, 221);line-height: 26px;">double</span> storedPermitsToSpend = min(requiredPermits, <span style="color: rgb(198, 120, 221);line-height: 26px;">this</span>.storedPermits);<br> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//需要刷新的令牌数</span><br> <span style="color: rgb(198, 120, 221);line-height: 26px;">double</span> freshPermits = requiredPermits - storedPermitsToSpend;<br> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//等待时间=需要刷新的令牌数*固定间隔+存储许可等待时间</span><br> <span style="color: rgb(198, 120, 221);line-height: 26px;">long</span> waitMicros =<br> storedPermitsToWaitTime(<span style="color: rgb(198, 120, 221);line-height: 26px;">this</span>.storedPermits, storedPermitsToSpend)<br> + (<span style="color: rgb(198, 120, 221);line-height: 26px;">long</span>) (freshPermits * stableIntervalMicros);<br> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//下次令牌生产时间=本次令牌生产时间+等待时间</span><br> <span style="color: rgb(198, 120, 221);line-height: 26px;">this</span>.nextFreeTicketMicros = LongMath.saturatedAdd(nextFreeTicketMicros, waitMicros);<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">this</span>.storedPermits -= storedPermitsToSpend;<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">return</span> returnValue;<br>}<br></code></pre> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">总结:RateLimiter根本没有集合充当桶,核心是记录了下一令牌产生的时间与现存令牌数,并动态更新它们。</p> <p data-lake-id="b4f10076adf2228ecaccd921f627ed69" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><br></p> <section data-mpa-template="t" mpa-from-tpl="t"> <section mpa-from-tpl="t" style="margin-top: 10px;margin-bottom: 10px;"> <p style="margin-right: auto;margin-left: auto;width: 231.1875px;"><img data-ratio="0.16666666666666666" src="/upload/89aa6c9fe16e0dfcf56dad1a9f9078ff.png" data-type="gif" data-w="300" style="width: auto;"></p> </section> </section> <p data-lake-id="098b923ca3e65e99528d2c30d6cf4c26" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><br></p> <section data-recommend-type="list-title" data-recommend-tid="6" data-mpa-template="t" style="width: 100%;display: flex;justify-content: center;align-items: center;" data-mid="" data-from="yb-recommend"> <section style="width: 100%;padding: 14px;background: rgb(255, 255, 255);border-radius: 3px;border-width: 1px;border-style: solid;border-color: rgb(232, 232, 235);" data-mid=""> <section style="width: 100%;display: flex;justify-content: center;align-items: center;align-items: flex-end;" data-mid=""> <section data-mid="" style="height: 28px;padding: 4px 22px;font-size: 14px;font-weight: 500;color: rgb(19, 52, 86);line-height: 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/sUbvrqLicbpzB81mjeBxPuxnYdalGxNnJo30L2Hq3WwGficcq8w5YJkLeXnsNHocN53k55TfN5mBpCdicGRyfDg1g/640?wx_fmt=png");background-repeat: no-repeat;background-size: 100% 100%;margin-bottom: -14px;z-index: 10;"> <p data-mid="">往期推荐</p> </section> </section> <section style="width: 100%;border-width: 1px;border-style: solid;border-color: rgb(198, 226, 255);padding: 17px 16px 9px;" data-mid=""> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247495628_1" data-recommend-article-time="1625979660" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/HmHDU48icAtZfmiannfiamzJcFRufk3wQgv7UGX43Cvsu3XlkMoZiaLvyWDH0OKWpEB46DYDPTDEXiavcmpyrUHae9w/0?wx_fmt=jpeg" data-recommend-article-title="Redis 是并发安全的吗?你确定?" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495628&idx=1&sn=e8476c5d25fcc01b8bb736348e38f6cd&chksm=97b471d4a0c3f8c2aa57026317a2dc02578ae5721f2086c3433e522e6411860ecbc500388a25#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495628&idx=1&sn=e8476c5d25fcc01b8bb736348e38f6cd&chksm=97b471d4a0c3f8c2aa57026317a2dc02578ae5721f2086c3433e522e6411860ecbc500388a25&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">Redis 是并发安全的吗?你确定?</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247495628_2" data-recommend-article-time="1625979660" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/R3InYSAIZkFlFPC5m4TXiaxU7jlSQiaAmmFOJrGicicT5BVCibgsJOXj6OEuP6P36OSQPWSB2QzhZ625kqG2SdibDRLw/0?wx_fmt=jpeg" data-recommend-article-title="群友:事务中的异常不也抛出了,为什么没catch到而回滚?" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495628&idx=2&sn=0350b1e3e1f712da0f69fd0681cbbcc8&chksm=97b471d4a0c3f8c2d43aa56729ca325885aecf2c549a0d7466c402b8fbfdec46ac3625ba42d7#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495628&idx=2&sn=0350b1e3e1f712da0f69fd0681cbbcc8&chksm=97b471d4a0c3f8c2d43aa56729ca325885aecf2c549a0d7466c402b8fbfdec46ac3625ba42d7&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">群友:事务中的异常不也抛出了,为什么没catch到而回滚?</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247495628_3" data-recommend-article-time="1625979660" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/Rhiaqbb2uvaK1svKSL9jpYiaxibeOoLWQA462xawF9ymNvqKhglFicRwIN4uxjyu6tTLXoR403MdQFVySCgElgiaAyQ/0?wx_fmt=jpeg" data-recommend-article-title="小福利,某度网盘会员白嫖,手慢无!" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495628&idx=3&sn=b39215b4f40ea429a75f0a95618079ee&chksm=97b471d4a0c3f8c2f2ca65bec7d534bb0f352d6ad0e165fb0221366af07678f8e6c134287763#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495628&idx=3&sn=b39215b4f40ea429a75f0a95618079ee&chksm=97b471d4a0c3f8c2f2ca65bec7d534bb0f352d6ad0e165fb0221366af07678f8e6c134287763&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">小福利,某度网盘会员白嫖,手慢无!</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247495617_1" data-recommend-article-time="1625893260" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/HmHDU48icAtZXOWEtuzs2ynI48cxZy6WOCSjFsPlDggMicCnA6BzG90tE6eXiab4rSCOR0HEVOQI1MbsNmJOXTZ6Q/0?wx_fmt=jpeg" data-recommend-article-title="为什么 StringBuilder 不是线程安全的?" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495617&idx=1&sn=7f7240d1c35fd1c4c90eb92b43ddb882&chksm=97b471d9a0c3f8cf95cdb3ead63d6db382b67ef3f5f21c2cbe23df1570b0229d2f153b898cef#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495617&idx=1&sn=7f7240d1c35fd1c4c90eb92b43ddb882&chksm=97b471d9a0c3f8cf95cdb3ead63d6db382b67ef3f5f21c2cbe23df1570b0229d2f153b898cef&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">为什么 StringBuilder 不是线程安全的?</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247495617_2" data-recommend-article-time="1625893260" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/R3InYSAIZkFlFPC5m4TXiaxU7jlSQiaAmmZBokwbRr4KiaHyvW7sZ54vNucn1ZKqliapc45MbHWPTKPoH4UIWKOdCw/0?wx_fmt=jpeg" data-recommend-article-title="程序员写代码崩溃,路过的暖心美团骑手:我帮你看看!" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495617&idx=2&sn=7064d4821fa16c70d668db56be99e543&chksm=97b471d9a0c3f8cffc9e72348ae25742caa627902d836d01f2d24671dbc6779294a7e8b4a8b2#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495617&idx=2&sn=7064d4821fa16c70d668db56be99e543&chksm=97b471d9a0c3f8cffc9e72348ae25742caa627902d836d01f2d24671dbc6779294a7e8b4a8b2&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">程序员写代码崩溃,路过的暖心美团骑手:我帮你看看!</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247495617_3" data-recommend-article-time="1625893260" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/Rhiaqbb2uvaLfDNBc0BGGzodNWibtFj119BiaGd6NY69cd5bmH4wS1KIF6xxhScHBk3H6ExLP0HdeMzqZxwibrDcvg/0?wx_fmt=jpeg" data-recommend-article-title="搜番神器!一张图就可以告诉你所有!" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495617&idx=3&sn=56e2acdb9c434066168ebc2b7797eb55&chksm=97b471d9a0c3f8cfdc9dff4635baf40548638c9446f2997c90824cef682cb54408d4d68c5838#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495617&idx=3&sn=56e2acdb9c434066168ebc2b7797eb55&chksm=97b471d9a0c3f8cfdc9dff4635baf40548638c9446f2997c90824cef682cb54408d4d68c5838&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;border-bottom:none !important;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">搜番神器!一张图就可以告诉你所有!</p> </section></a> </section> </section> </section> </section> <p data-lake-id="098b923ca3e65e99528d2c30d6cf4c26" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><br></p> <p data-lake-id="098b923ca3e65e99528d2c30d6cf4c26" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><span style="color: rgb(140, 140, 140);"><br></span></p> <section data-mpa-template="t" mpa-from-tpl="t" style="white-space: normal;"> <p style="text-align: center;"><strong><span style="color: rgb(140, 140, 140);letter-spacing: 0.008em;font-size: 14px;">一起进大厂,每日学干货</span></strong></p> </section> <section style="margin-top: 5px;white-space: normal;text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"> <span style="color: rgb(0, 0, 0);"><strong><span style="font-size: 14px;">关注我回复【</span></strong></span> <span style="color: rgb(255, 76, 65);"><strong><span style="font-size: 14px;">加群</span></strong></span> <span style="color: rgb(0, 0, 0);"><strong><span style="color: rgb(0, 0, 0);font-size: 14px;">】,加入Java技术交流群</span></strong></span> </section> <section data-mpa-template="t" mpa-from-tpl="t"> <p style="text-align: center;"><img data-ratio="0.5982532751091703" src="/upload/4e21037b66f60f7d73d060863de4b4b5.png" data-type="gif" data-w="458" data-width="100%" style="color: rgb(62, 62, 62);font-size: 16px;vertical-align: middle;width: 62.0938px;"></p> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzIxMzQzNzMwMw==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/HmHDU48icAtYvlypOY9VaGVXQ639L63Iq6zHiclgibG0CAhgrJ2JLRibKbeCgVIx7WXcicbMW6AJL1Hos9AoJTqtVfA/0?wx_fmt=png" data-nickname="后端面试那些事" data-alias="" data-signature="专注分享后端干货!面向大厂,一起进步!" data-from="0"></mpprofile> </section> <p style="text-align: center;"><br></p> </section> <p style="text-align: center;"><br></p> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-role="paragraph" mpa-from-tpl="t" style="white-space: normal;border-width: 0px;border-style: none;border-color: initial;"> <section style="box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="margin-top: 0.5em;box-sizing: border-box;"> <section style="padding: 0.5em;border-width: 1px;border-style: solid;border-color: rgb(249, 110, 87);box-shadow: rgb(226, 226, 226) 0px 16px 1px -13px;box-sizing: border-box;"> <section powered-by="xiumi.us" style="text-align: left;box-sizing: border-box;"> <section style="text-align: justify;color: rgb(64, 84, 115);box-sizing: border-box;"> <p style="box-sizing: border-box;"><img data-ratio="0.33611111111111114" src="/upload/a53e4e397528931045198e4f89d7ba0.png" data-type="png" data-w="1080"></p> <p style="box-sizing: border-box;">点击“阅读原文”,领取 2021 年<strong>最新免费技术资料大全</strong></p> </section> </section> </section> </section> <section powered-by="xiumi.us" style="font-size: 32px;color: rgb(249, 110, 87);box-sizing: border-box;text-align: left;"> ↓↓↓ </section> </section> </section> </section>
作者:微信小助手
<h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;color: rgb(14, 136, 235);"><span style="display: none;"></span>前言<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">什么样的代码是好代码呢?好的代码应该命名规范、可读性强、扩展性强、健壮性......而不好的代码又有哪些典型特征呢?这25种代码坏味道大家要注意啦</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;color: rgb(14, 136, 235);"><span style="display: none;"></span>1. Duplicated Code (重复代码)<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">重复代码就是<strong style="font-weight: border;color: #0e88eb;">不同地点,有着相同的程序结构</strong>。一般是因为需求迭代比较快,开发小伙伴担心影响已有功能,就复制粘贴造成的。重复代码<strong style="font-weight: border;color: #0e88eb;">很难维护</strong>的,如果你要修改其中一段的代码逻辑,就需要修改多次,很可能出现遗漏的情况。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">如何优化重复代码呢?分<strong style="font-weight: border;color: #0e88eb;">三种</strong>情况讨论:</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);font-size: 15px;"> 同一个类的两个函数含有相同的表达式 </section></li> </ol> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkqg7LvVmfl8nDk9pvQsaKXKcAxeyNswdkHGoN8ibrZxAjA2SEo1HVyiaFbJDLJQ5p6J9nWwB67a6EFnSE1n2IgstG/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">class A {<br> public void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">method1</span></span>() {<br> doSomething1<br> doSomething2<br> doSomething3<br> }<br> public void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">method2</span></span>() {<br> doSomething1<br> doSomething2<br> doSomething4<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">优化手段:可以使用<strong style="font-weight: border;color: #0e88eb;">Extract Method(提取公共函数)</strong> 抽出重复的代码逻辑,组成一个公用的方法。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkqg7LvVmfl8nDk9pvQsaKXKcAxeyNswdkHGoN8ibrZxAjA2SEo1HVyiaFbJDLJQ5p6J9nWwB67a6EFnSE1n2IgstG/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">class A {<br> public void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">method1</span></span>() {<br> commonMethod();<br> doSomething3<br> }<br> public void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">method2</span></span>() {<br> commonMethod();<br> doSomething4<br> }<br> <br> public void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">commonMethod</span></span>(){<br> doSomething1<br> doSomething2<br> }<br>}<br></code></pre> <ol start="2" 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);font-size: 15px;"> 两个互为兄弟的子类内含相同的表达式 </section></li> </ol> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkqg7LvVmfl8nDk9pvQsaKXKcAxeyNswdkHGoN8ibrZxAjA2SEo1HVyiaFbJDLJQ5p6J9nWwB67a6EFnSE1n2IgstG/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">class A extend C {<br> public void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">method1</span></span>() {<br> doSomething1<br> doSomething2<br> doSomething3<br> }<br>}<br><br>class B extend C {<br> public void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">method1</span></span>() {<br> doSomething1<br> doSomething2<br> doSomething4<br> }<br>}<br><br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">优化手段:对两个类都使用<strong style="font-weight: border;color: #0e88eb;">Extract Method(提取公共函数)</strong>,然后把<strong style="font-weight: border;color: #0e88eb;">抽取出来的函数放到父类</strong>中。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkqg7LvVmfl8nDk9pvQsaKXKcAxeyNswdkHGoN8ibrZxAjA2SEo1HVyiaFbJDLJQ5p6J9nWwB67a6EFnSE1n2IgstG/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">class C {<br> public void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">commonMethod</span></span>(){<br> doSomething1<br> doSomething2<br> }<br>}<br>class A extend C {<br> public void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">method1</span></span>() {<br> commonMethod();<br> doSomething3<br> }<br>}<br><br>class B extend C {<br> public void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">method1</span></span>() {<br> commonMethod();<br> doSomething4<br> }<br>}<br></code></pre> <ol start="3" 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);font-size: 15px;"> 两个毫不相关的类出现重复代码 </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">如果是两个毫不相关的类出现重复代码,可以使用<strong style="font-weight: border;color: #0e88eb;">Extract Class</strong>将重复代码提炼到一个类中。这个新类可以是一个普通类,也可以是一个工具类,看具体业务怎么划分吧。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;color: rgb(14, 136, 235);"><span style="display: none;"></span>2 .Long Method (长函数)<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">长函数是指一个函数方法几百行甚至上千行,可读性大大降低,不便于理解。<strong style="font-weight: border;color: #0e88eb;">反例如下:</strong></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkqg7LvVmfl8nDk9pvQsaKXKcAxeyNswdkHGoN8ibrZxAjA2SEo1HVyiaFbJDLJQ5p6J9nWwB67a6EFnSE1n2IgstG/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">public class Test {<br> private String name;<br> private Vector<Order> orders = new Vector<Order>();<br><br> public void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">printOwing</span></span>() {<br> //<span style="color: #e6c07b;line-height: 26px;">print</span> banner<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"****************"</span>);<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"*****customer Owes *****"</span>);<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"****************"</span>);<br><br> //calculate totalAmount<br> Enumeration env = orders.elements();<br> double totalAmount = 0.0;<br> <span style="color: #c678dd;line-height: 26px;">while</span> (env.hasMoreElements()) {<br> Order order = (Order) env.nextElement();<br> totalAmount += order.getAmout();<br> }<br><br> //<span style="color: #e6c07b;line-height: 26px;">print</span> details<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"name:"</span> + name);<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"amount:"</span> + totalAmount);<br> ......<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">可以使用<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">Extract Method</code>,抽取功能单一的代码段,组成命名清晰的小函数,去解决长函数问题,<strong style="font-weight: border;color: #0e88eb;">正例如下</strong>:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkqg7LvVmfl8nDk9pvQsaKXKcAxeyNswdkHGoN8ibrZxAjA2SEo1HVyiaFbJDLJQ5p6J9nWwB67a6EFnSE1n2IgstG/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">public class Test {<br> private String name;<br> private Vector<Order> orders = new Vector<Order>();<br><br> public void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">printOwing</span></span>() {<br><br> //<span style="color: #e6c07b;line-height: 26px;">print</span> banner<br> printBanner();<br> //calculate totalAmount<br> double totalAmount = getTotalAmount();<br> //<span style="color: #e6c07b;line-height: 26px;">print</span> details<br> printDetail(totalAmount);<br> }<br><br> void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">printBanner</span></span>(){<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"****************"</span>);<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"*****customer Owes *****"</span>);<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"****************"</span>);<br> }<br><br> double <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">getTotalAmount</span></span>(){<br> Enumeration env = orders.elements();<br> double totalAmount = 0.0;<br> <span style="color: #c678dd;line-height: 26px;">while</span> (env.hasMoreElements()) {<br> Order order = (Order) env.nextElement();<br> totalAmount += order.getAmout();<br> }<br> <span style="color: #e6c07b;line-height: 26px;">return</span> totalAmount;<br> }<br><br> void printDetail(double totalAmount){<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"name:"</span> + name);<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"amount:"</span> + totalAmount);<br> }<br> <br>}<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;color: rgb(14, 136, 235);"><span style="display: none;"></span>3. Large Class (过大的类)<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">一个类做太多事情,维护了太多功能,可读性变差,性能也会下降。举个例子,订单相关的功能你放到一个类A里面,商品库存相关的也放在类A里面,积分相关的还放在类A里面..<strong style="font-weight: border;color: #0e88eb;">.反例</strong>如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkqg7LvVmfl8nDk9pvQsaKXKcAxeyNswdkHGoN8ibrZxAjA2SEo1HVyiaFbJDLJQ5p6J9nWwB67a6EFnSE1n2IgstG/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">Class A{<br> public void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">printOrder</span></span>(){<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"订单"</span>);<br> }<br> <br> public void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">printGoods</span></span>(){<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"商品"</span>);<br> }<br> <br> public void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">printPoints</span></span>(){<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"积分"</span>);<br> }<br>}<br><br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">试想一下,乱七八糟的代码块都往一个类里面塞,还谈啥可读性。应该按单一职责,使用<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">Extract Class</code>把代码划分开,正例如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkqg7LvVmfl8nDk9pvQsaKXKcAxeyNswdkHGoN8ibrZxAjA2SEo1HVyiaFbJDLJQ5p6J9nWwB67a6EFnSE1n2IgstG/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">Class Order{<br> public void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">printOrder</span></span>(){<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"订单"</span>);<br> }<br>}<br><br>Class Goods{<br> public void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">printGoods</span></span>(){<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"商品"</span>);<br> }<br>}<br> <br>Class Points{ <br> public void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">printPoints</span></span>(){<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"积分"</span>);<br> }<br> }<br>}<br><br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;color: rgb(14, 136, 235);"><span style="display: none;"></span>4. Long Parameter List (过长参数列)<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">方法参数数量过多的话,可读性很差。如果有多个重载方法,参数很多的话,有时候你都不知道调哪个呢。并且,如果参数很多,做新老接口兼容处理也比较麻烦。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkqg7LvVmfl8nDk9pvQsaKXKcAxeyNswdkHGoN8ibrZxAjA2SEo1HVyiaFbJDLJQ5p6J9nWwB67a6EFnSE1n2IgstG/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">public void getUserInfo(String name,String age,String sex,String mobile){<br> // <span style="color: #c678dd;line-height: 26px;">do</span> something ...<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">如何解决过长参数列问题呢?将参数封装成结构或者类,比如我们将参数封装成一个DTO类,如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkqg7LvVmfl8nDk9pvQsaKXKcAxeyNswdkHGoN8ibrZxAjA2SEo1HVyiaFbJDLJQ5p6J9nWwB67a6EFnSE1n2IgstG/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">public void getUserInfo(UserInfoParamDTO userInfoParamDTO){<br> // <span style="color: #c678dd;line-height: 26px;">do</span> something ...<br>}<br><br>class UserInfoParamDTO{<br> private String name;<br> private String age; <br> private String sex;<br> private String mobile;<br>}<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;color: rgb(14, 136, 235);"><span style="display: none;"></span>5. Divergent Change (发散式变化)<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">对程序进行维护时, <strong style="font-weight: border;color: #0e88eb;">如果添加修改组件, 要同时修改一个类中的多个方法</strong>, 那么这就是 Divergent Change。举个汽车的例子,某个汽车厂商生产三种品牌的汽车:BMW、Benz和LaoSiLaiSi,每种品牌又可以选择燃油、纯电和混合动力。<strong style="font-weight: border;color: #0e88eb;">反例如下</strong>:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkqg7LvVmfl8nDk9pvQsaKXKcAxeyNswdkHGoN8ibrZxAjA2SEo1HVyiaFbJDLJQ5p6J9nWwB67a6EFnSE1n2IgstG/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">/**<br> * 公众号:捡田螺的小男孩<br> */<br>public class Car {<br><br> private String name;<br><br> void start(Engine engine) {<br> <span style="color: #c678dd;line-height: 26px;">if</span> (<span style="color: #98c379;line-height: 26px;">"HybridEngine"</span>.equals(engine.getName())) {<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"Start Hybrid Engine..."</span>);<br> } <span style="color: #c678dd;line-height: 26px;">else</span> <span style="color: #c678dd;line-height: 26px;">if</span> (<span style="color: #98c379;line-height: 26px;">"GasolineEngine"</span>.equals(engine.getName())) {<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"Start Gasoline Engine..."</span>);<br> } <span style="color: #c678dd;line-height: 26px;">else</span> <span style="color: #c678dd;line-height: 26px;">if</span> (<span style="color: #98c379;line-height: 26px;">"ElectricEngine"</span>.equals(engine.getName())) {<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"Start Electric Engine"</span>);<br> }<br> }<br><br> void drive(Engine engine,Car car) {<br> this.start(engine);<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"Drive "</span> + getBrand(car) + <span style="color: #98c379;line-height: 26px;">" car..."</span>);<br> }<br><br> String getBrand(Car car) {<br> <span style="color: #c678dd;line-height: 26px;">if</span> (<span style="color: #98c379;line-height: 26px;">"Baoma"</span>.equals(car.getName())) {<br> <span style="color: #e6c07b;line-height: 26px;">return</span> <span style="color: #98c379;line-height: 26px;">"BMW"</span>;<br> } <span style="color: #c678dd;line-height: 26px;">else</span> <span style="color: #c678dd;line-height: 26px;">if</span> (<span style="color: #98c379;line-height: 26px;">"BenChi"</span>.equals(car.getName())) {<br> <span style="color: #e6c07b;line-height: 26px;">return</span> <span style="color: #98c379;line-height: 26px;">"Benz"</span>;<br> } <span style="color: #c678dd;line-height: 26px;">else</span> <span style="color: #c678dd;line-height: 26px;">if</span> (<span style="color: #98c379;line-height: 26px;">"LaoSiLaiSi"</span>.equals(car.getName())) {<br> <span style="color: #e6c07b;line-height: 26px;">return</span> <span style="color: #98c379;line-height: 26px;">"LaoSiLaiSi"</span>;<br> }<br> <span style="color: #e6c07b;line-height: 26px;">return</span> null;<br> }<br> }<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">如果新增一种品牌新能源电车,然后它的启动引擎是核动力呢,那么就需要修改Car类的<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">start</code>和<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">getBrand</code>方法啦,这就是代码坏味道:<strong style="font-weight: border;color: #0e88eb;">Divergent Change (发散式变化)</strong>。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">如何优化呢?一句话总结:<strong style="font-weight: border;color: #0e88eb;">拆分类,将总是一起变化的东西放到一块</strong>。</p> <blockquote data-tool="mdnice编辑器" style="border-width: initial;border-style: none;border-color: initial;font-size: 0.9em;overflow: auto;margin-bottom: 20px;margin-top: 20px;padding-top: 10px;padding-right: 10px;padding-bottom: 10px;line-height: 1.8;border-radius: 0px 0px 10px 10px;color: rgb(14, 136, 235);background: rgb(255, 255, 255);box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> <span style="font-size: 4em;font-family: Arial, serif;line-height: 1em;font-weight: 700;">★</span> <ul 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;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 运用提炼类(Extract Class) 拆分类的行为。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 如果不同的类有相同的行为,提炼超类(Extract Superclass) 和 提炼子类(Extract Subclass)。 </section></li> </ul> <span style="float: right;font-size: 3em;line-height: 1em;">”</span> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong style="font-weight: border;color: #0e88eb;">正例如下:</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">因为Engine是独立变化的,所以提取一个Engine接口,如果新加一个启动引擎,多一个实现类即可。如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkqg7LvVmfl8nDk9pvQsaKXKcAxeyNswdkHGoN8ibrZxAjA2SEo1HVyiaFbJDLJQ5p6J9nWwB67a6EFnSE1n2IgstG/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">//IEngine<br>public interface IEngine {<br> void start();<br>}<br><br>public class HybridEngineImpl implements IEngine { <br> @Override<br> public void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">start</span></span>() {<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"Start Hybrid Engine..."</span>);<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">因为<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">drive</code>方法依赖于<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">Car,IEngine,getBand</code>方法;<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">getBand</code>方法是变化的,也跟Car是有关联的,所以可以搞个抽象Car的类,每个品牌汽车继承于它即可,如下</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/eytJa9K5jkqg7LvVmfl8nDk9pvQsaKXKcAxeyNswdkHGoN8ibrZxAjA2SEo1HVyiaFbJDLJQ5p6J9nWwB67a6EFnSE1n2IgstG/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">public abstract class AbstractCar {<br><br> protected IEngine engine;<br><br> public AbstractCar(IEngine engine) {<br> this.engine = engine;<br> }<br><br> public abstract void drive();<br>}<br><br>//奔驰汽车<br>public class BenzCar extends AbstractCar {<br><br> public BenzCar(IEngine engine) {<br> super(engine);<br> }<br><br> @Override<br> public void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">drive</span></span>() {<br> this.engine.start();<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"Drive "</span> + getBrand() + <span style="color: #98c379;line-height: 26px;">" car..."</span>);<br> }<br><br> private String <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">getBrand</span></span>() {<br> <span style="color: #e6c07b;line-height: 26px;">return</span> <span style="color: #98c379;line-height: 26px;">"Benz"</span>;<br> }<br>}<br><br>//宝马汽车<br>public class BaoMaCar extends AbstractCar {<br><br> public BaoMaCar(IEngine engine) {<br> super(engine);<br> }<br><br> @Override<br> public void <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">drive</span></span>() {<br> this.engine.start();<br> System.out.println(<span style="color: #98c379;line-height: 26px;">"Drive "</span> + getBrand() + <span style="color: #98c379;line-height: 26px;">" car..."</span>);<br> }<br><br> private String <span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">getBrand</span></span>() {<br> <span style="color: #e6c07b;line-height: 26px;">return</span> <span style="color: #98c379;line-height: 26px;">"BMW"</span>;<br> }<br>}<br><br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">细心的小伙伴,可以发现不同子类BaoMaCar和BenzCar的<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: "Operator Mono", Consolas, Monaco, Menlo, mono
作者:微信小助手
<p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">昨晚睡觉前,顺手撸了几个群聊的聊天记录。发现一个很有意思的名词“<strong>分布式单体</strong> ”,顺藤摸瓜看了一下之前的聊天记录,由于内容骂骂咧咧,我就不贴出来了。大致内容就是某公司在做微服务改造,但改的不伦不类,形式上像微服务,而本质上依然是单体,甚至连单体都不如。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这样的改造现象,其实在国内还是蛮多见的。今天我们就来聊聊这个有趣的话题:<strong>分布式单体</strong> 。各位看官,看看你们公司是不是也犯了这样的错误?</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==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&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;">先思考一个问题:从单体改造到微服务的时候,你们是不是按这样的步骤来的?</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);"> 确定业务领域,拆分存储,定义各微服务的边界 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 改造代码逻辑,将原来的内部service调用改成dubbo或feign这样的远程调用 </section></li> </ol> <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);"> 代码库分开了,减少了麻烦的解决代码冲突的困扰 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> CI/CD分开了,每个拆分后的服务都可以独立开发、部署、运行 </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;">这样一顿操作,我们把一个臃肿的单体应用变成了多个精炼的分布式应用,似乎完美的实现了改造?但这样就实现了微服务的核心目标了吗?继续思考下面的问题:</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);"> 代码库是分开了,但每个服务都在独立迭代吗?是不是每个需求都要协调一大堆同步接口? </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> CI/CD是分开了,但每次发布都是自由的吗?是不是每次功能的发布都拖上了一大推的服务要一起发布? </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;">看似我们得到了很多好处,但我们的开发效率真的得到了提升吗?虽然我们以前一个单体应用启动要3分钟,现在拆分后,一个项目启动30秒,但每次开发调试要同时开好几个项目同时启动?这样的开发体验真的爽到了吗?</p> <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;">而实际上,这样的改造,除了收益不高之外,还带出了更多的坏处。如果你们公司是这样做的,有没有发现,这样做之后,好像系统故障的频率更高了?稳定性似乎比单体应用还差?(如果没有,那一定要感谢你们的运维团队真的很给力,同时建议把这篇转给运维团队,采访下这样的改造是不是他们变得更累了?!)</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">为什么这样的改造会导致系统更加不稳定呢?其实道理很简单,原本我们在单体应用中,未拆分的远程调用都是内部调用,这个内部调用所能引发的故障率是微乎其微的,而将这部分内容拆成了远程调用后,每一个调用都增加了网络IO的因素,每一次调用的故障率都增加了。那么系统的整体故障率是随着系统拥有多少同步远程调用的数量增加而增加的。当运维团队与开发水平没有支持好这部分增加的复杂度时,那么改造的系统,必然稳定性会比原来的单体应用更差。</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==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&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;">那么为什么会造成上面所说的问题呢?我觉得主要有两方面:</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==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">1. 领域拆分的不合理,引出了过多的同步远程调用</a></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这个是最根本的问题,也是在改造过程中最常见的。这部分说实话是整个改造过程中最难的,因为需要对业务有非常深入的认识,对系统设计的领域模型、用户行为有足够的理解。在做拆分的时候,尽可能的减少同步远程调用,取而代之的是走消息的异步交互,同时根据业务需要也可以做适当的数据冗余。这样就能保证,每个被拆分后的微服务之间可以获得更低耦合度。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">因为更低的耦合度,我们才能在不做任何优化的情况下,获得更少的分布式所带来的稳定性损失。对于后面要将的第2点的工作量也就越少。同时,对于真正的独立开发、部署、运行也成为可能。</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==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">2. 简单粗暴的实现,缺少分布式的保护机制</a></span><span style="display: none;"></span></h3> <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;">但要做好这一点的核心,还是对第一点的把握,只有在领域模型上做更合理的拆分规划,才能支持开发人员做好这个点,不然随意的拆分,一大堆接口调用压给本就压力很大的开发人员,那这部分的开发质量肯定很难保障了,自然而然的系统稳定性就开始随着接口复杂度的增加而不断下降了。最后,开发人员就会开始来我们群里吐槽了...甚至大家也开始怀疑微服务根本带不来效率的提升!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">最后,思考一下,你们的微服务改造有出现这里我说的情况吗?还是有其他不一样的问题呢?欢迎留言区说说你们的问题,聊聊你的观点!</p>
作者:微信小助手
<h2 data-tool="mdnice编辑器" style="margin-top: 1em;margin-bottom: 10px;font-weight: bold;font-size: 22px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;color: black;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/A0bYOQcma0O2UA2cnlztfRib04uT92bkRUd6QFO9CywPZF88F7QXxfuqJB1bhHwWQXSHEx9T9aOx5ABCqELkqeA/640?wx_fmt=png");background-size: 50px;background-position: center center;background-repeat: no-repeat;"><span style="margin-top: 38px;margin-bottom: 10px;display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-size: 63px;font-size: 18px;background-position: left center;background-repeat: no-repeat;">显示工具条</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 1em;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">(1)效果图</strong></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;caret-color: rgb(63, 63, 63);color: rgb(63, 63, 63);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.5333333333333333" src="/upload/5aee57f105e019fdc997616aaa4605cc.png" data-type="png" data-w="1080" style="margin-right: auto;margin-bottom: 25px;margin-left: auto;display: block;border-radius: 4px;box-sizing: border-box !important;width: 677px !important;visibility: visible !important;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 1em;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">(2)设置方法</strong></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;color: black;"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 标注1:View–>Toolbar </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 标注2:View–>Tool Buttons </section></li> </ul> <h2 data-tool="mdnice编辑器" style="margin-top: 1em;margin-bottom: 10px;font-weight: bold;font-size: 22px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;color: black;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/A0bYOQcma0O2UA2cnlztfRib04uT92bkRUd6QFO9CywPZF88F7QXxfuqJB1bhHwWQXSHEx9T9aOx5ABCqELkqeA/640?wx_fmt=png");background-size: 50px;background-position: center center;background-repeat: no-repeat;"><span style="margin-top: 38px;margin-bottom: 10px;display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-size: 63px;font-size: 18px;background-position: left center;background-repeat: no-repeat;">设置鼠标悬浮提示</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 1em;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">(1)效果图</strong></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;caret-color: rgb(63, 63, 63);color: rgb(63, 63, 63);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.5333333333333333" src="/upload/172d30e6a8addcc5ea9b540e11a1029d.png" data-type="png" data-w="1080" style="margin-right: auto;margin-bottom: 25px;margin-left: auto;display: block;border-radius: 4px;box-sizing: border-box !important;width: 677px !important;visibility: visible !important;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 1em;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">(2)设置方法</strong>File–>settings–>Editor–>General–>勾选Show quick documentation…</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;caret-color: rgb(63, 63, 63);color: rgb(63, 63, 63);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.32040472175379425" src="/upload/63f9dad8d4fee4060c5907425dca0dc1.png" data-type="png" data-w="593" style="margin-right: auto;margin-bottom: 25px;margin-left: auto;display: block;border-radius: 4px;box-sizing: border-box !important;width: 593px !important;visibility: visible !important;"> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 1em;margin-bottom: 10px;font-weight: bold;font-size: 22px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;color: black;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/A0bYOQcma0O2UA2cnlztfRib04uT92bkRUd6QFO9CywPZF88F7QXxfuqJB1bhHwWQXSHEx9T9aOx5ABCqELkqeA/640?wx_fmt=png");background-size: 50px;background-position: center center;background-repeat: no-repeat;"><span style="margin-top: 38px;margin-bottom: 10px;display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-size: 63px;font-size: 18px;background-position: left center;background-repeat: no-repeat;">显示方法分隔符</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 1em;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">(1)效果图</strong></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;caret-color: rgb(63, 63, 63);color: rgb(63, 63, 63);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.4909310761789601" src="/upload/84cbcc9d93402f0b6c727ee0ef31a9a.png" data-type="png" data-w="827" style="margin-right: auto;margin-bottom: 25px;margin-left: auto;display: block;border-radius: 4px;box-sizing: border-box !important;width: 677px !important;visibility: visible !important;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 1em;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">(2)设置方法</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 1em;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;color: rgb(74, 74, 74);line-height: 1.75em;">File–>settings–>Editor–>Appearance–>勾选</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;caret-color: rgb(63, 63, 63);color: rgb(63, 63, 63);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.26126126126126126" src="/upload/a063db989c7665d557acc725bceb1928.png" data-type="png" data-w="666" style="margin-right: auto;margin-bottom: 25px;margin-left: auto;display: block;border-radius: 4px;box-sizing: border-box !important;width: 666px !important;visibility: visible !important;"> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 1em;margin-bottom: 10px;font-weight: bold;font-size: 22px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;color: black;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/A0bYOQcma0O2UA2cnlztfRib04uT92bkRUd6QFO9CywPZF88F7QXxfuqJB1bhHwWQXSHEx9T9aOx5ABCqELkqeA/640?wx_fmt=png");background-size: 50px;background-position: center center;background-repeat: no-repeat;"><span style="margin-top: 38px;margin-bottom: 10px;display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-size: 63px;font-size: 18px;background-position: left center;background-repeat: no-repeat;">忽略大小写提示</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 1em;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">(1)效果图</strong>备注:idea的默认设置是严格区分大小写提示的,例如输入string不会提示String,不方便编码</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;caret-color: rgb(63, 63, 63);color: rgb(63, 63, 63);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.3675925925925926" src="/upload/7514dc21ab3609b2556e8dbf6d9fa02f.png" data-type="png" data-w="1080" style="margin-right: auto;margin-bottom: 25px;margin-left: auto;display: block;border-radius: 4px;box-sizing: border-box !important;width: 677px !important;visibility: visible !important;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 1em;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">(2)设置方法</strong>File–>settings–>Editor–>General -->Code Completion --></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;caret-color: rgb(63, 63, 63);color: rgb(63, 63, 63);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.6138888888888889" src="/upload/b85a21e9a29f52a6596effcf6b09bba3.png" data-type="png" data-w="1080" style="margin-right: auto;margin-bottom: 25px;margin-left: auto;display: block;border-radius: 4px;box-sizing: border-box !important;width: 677px !important;visibility: visible !important;"> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 1em;margin-bottom: 10px;font-weight: bold;font-size: 22px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;color: black;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/A0bYOQcma0O2UA2cnlztfRib04uT92bkRUd6QFO9CywPZF88F7QXxfuqJB1bhHwWQXSHEx9T9aOx5ABCqELkqeA/640?wx_fmt=png");background-size: 50px;background-position: center center;background-repeat: no-repeat;"><span style="margin-top: 38px;margin-bottom: 10px;display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-size: 63px;font-size: 18px;background-position: left center;background-repeat: no-repeat;">主题设置</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 1em;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">(1)效果图</strong>备注:有黑白两种风格</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;caret-color: rgb(63, 63, 63);color: rgb(63, 63, 63);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.5342592592592592" src="/upload/36c18ac7d9b1ef4a56b5e90a49e37668.png" data-type="png" data-w="1080" style="margin-right: auto;margin-bottom: 25px;margin-left: auto;display: block;border-radius: 4px;box-sizing: border-box !important;width: 677px !important;visibility: visible !important;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;caret-color: rgb(63, 63, 63);color: rgb(63, 63, 63);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.5324074074074074" src="/upload/1a7a5b252f61669c9c46832dfc723be3.png" data-type="png" data-w="1080" style="margin-right: auto;margin-bottom: 25px;margin-left: auto;display: block;border-radius: 4px;box-sizing: border-box !important;width: 677px !important;visibility: visible !important;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 1em;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">(2)设置方法</strong>File–>settings–>Appearance & Behavior–>Appearance–></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;caret-color: rgb(63, 63, 63);color: rgb(63, 63, 63);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.21464646464646464" data-type="png" data-w="396" src="/upload/7daeb9691f29a012f75e37c045faf1d2.png" style="margin-right: auto;margin-bottom: 25px;margin-left: auto;display: block;border-radius: 4px;box-sizing: border-box !important;width: 396px !important;visibility: visible !important;"> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 1em;margin-bottom: 10px;font-weight: bold;font-size: 22px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;color: black;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/A0bYOQcma0O2UA2cnlztfRib04uT92bkRUd6QFO9CywPZF88F7QXxfuqJB1bhHwWQXSHEx9T9aOx5ABCqELkqeA/640?wx_fmt=png");background-size: 50px;background-position: center center;background-repeat: no-repeat;"><span style="margin-top: 38px;margin-bottom: 10px;display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-size: 63px;font-size: 18px;background-position: left center;background-repeat: no-repeat;">护眼主题设置</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 1em;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">(1)效果图</strong></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;caret-color: rgb(63, 63, 63);color: rgb(63, 63, 63);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.4994675186368477" src="/upload/e8a2b25a2612ad12ede1e9fbd7f30d78.png" data-type="png" data-w="939" style="margin-right: auto;margin-bottom: 25px;margin-left: auto;display: block;border-radius: 4px;box-sizing: border-box !important;width: 677px !important;visibility: visible !important;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 1em;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">(2)设置方法</strong>如果想将编辑页面变换主题,可以去设置里面调节背景颜色</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;caret-color: rgb(63, 63, 63);color: rgb(63, 63, 63);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.6138888888888889" src="/upload/9179a31ba0fe68aecc22d4ff47a07308.png" data-type="png" data-w="1080" style="margin-right: auto;margin-bottom: 25px;margin-left: auto;display: block;border-radius: 4px;box-sizing: border-box !important;width: 677px !important;visibility: visible !important;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 1em;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;color: rgb(74, 74, 74);line-height: 1.75em;">如果需要很好看的编码风格,这里有很多主题</p> <p data-tool="mdnice编辑器" style="padding-top: 1em;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;color: rgb(74, 74, 74);line-height: 1.75em;">http://color-themes.com/?view=index&layout=Generic&order=popular&search=&page=1</p> <p data-tool="mdnice编辑器" style="padding-top: 1em;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;color: rgb(74, 74, 74);line-height: 1.75em;">点击相应主题,往下滑点击按钮</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;caret-color: rgb(63, 63, 63);color: rgb(63, 63, 63);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.14512195121951219" src="/upload/d6f3ff2dc863e84e245b660f530bc020.png" data-type="png" data-w="820" style="margin-right: auto;margin-bottom: 25px;margin-left: auto;display: block;border-radius: 4px;box-sizing: border-box !important;width: 677px !important;visibility: visible !important;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 1em;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;color: rgb(74, 74, 74);line-height: 1.75em;">下载下来有很多Jar包</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;caret-color: rgb(63, 63, 63);color: rgb(63, 63, 63);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.3892128279883382" src="/upload/29876ebedebf51ac983f9c4d0c66eabf.png" data-type="png" data-w="686" style="margin-right: auto;margin-bottom: 25px;margin-left: auto;display: block;border-radius: 4px;box-sizing: border-box !important;width: 677px !important;visibility: visible !important;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;caret-color: rgb(63, 63, 63);color: rgb(63, 63, 63);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="2.005780346820809" src="/upload/a83fb933355e3833da29e911330d8c7c.png" data-type="png" data-w="346" style="margin-right: auto;margin-bottom: 25px;margin-left: auto;display: block;border-radius: 4px;box-sizing: border-box !important;width: 346px !important;visibility: visible !important;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 1em;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;color: rgb(74, 74, 74);line-height: 1.75em;">在上面的位置选择导入jar包,然后重启idea生效,重启之后去设置</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;caret-color: rgb(63, 63, 63);color: rgb(63, 63, 63);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.6138888888888889" src="/upload/ef26a13ecf7a33f163687d5db906296e.png" data-type="png" data-w="1080" style="margin-right: auto;margin-bottom: 25px;margin-left: auto;display: block;border-radius: 4px;box-sizing: border-box !important;width: 677px !important;visibility: visible !important;"> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 1em;margin-bottom: 10px;font-weight: bold;font-size: 22px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;color: black;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/A0bYOQcma0O2UA2cnlztfRib04uT92bkRUd6QFO9CywPZF88F7QXxfuqJB1bhHwWQXSHEx9T9aOx5ABCqELkqeA/640?wx_fmt=png");background-size: 50px;background-position: center center;background-repeat: no-repeat;"><span style="margin-top: 38px;margin-bottom: 10px;display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-size: 63px;font-size: 18px;background-position: left center;background-repeat: no-repeat;">自动导入包</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 1em;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">(1)效果图</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 1em;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.544px;text-align: left;white-space: normal;text-size-adjust: auto;color: rgb(74, 74, 74);line-height: 1.75em;">备注:默认情况是需要手动导入包的,比如我们需要导入Map类,那么需要手动导入,如果不需要使用了,删除了Map的实例,导入的包也需要手动删除,设置了这个功能这个就不需要手动了,自动帮你实现自动导入包和去包,不方便截图,效果请亲测~</p> <p data-tool="mdnice编辑器" style="padding-top: 1em;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia,
作者:微信小助手
<p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">你好,我是悟空。</p> <h2 data-tool="mdnice编辑器" style="color: black;font-size: 22px;font-weight: bold;line-height: 1.5em;margin-top: 2.2em;margin-bottom: 35px;"><span style="display: inline-block;background-image: linear-gradient(rgb(255, 255, 255) 60%, rgb(255, 177, 27) 40%);background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;color: rgb(81, 81, 81);padding: 2px 13px;margin-right: 3px;height: 50%;">一、背景</span></h2> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">不用想象一种异常场景了,这就真实发生了:B 站昨天晚上 11 点突然挂了,网站主页直接报 404。</p> <figure data-tool="mdnice编辑器" style="color: black;font-size: 16px;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.4431137724550898" src="/upload/1353032f2baa3d61808a7a27da71a077.png" data-type="png" data-w="1002" style="margin-right: auto;margin-left: auto;width: 388px;border-radius: 5px;display: block;margin-bottom: 15px;height: 172px;"> </figure> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">手机 APP 端数据加载不出来。</p> <figure data-tool="mdnice编辑器" style="color: black;font-size: 16px;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.9813953488372092" src="/upload/4eb8b752fb682f1a7ad501c767d4e1fc.png" data-type="png" data-w="645" style="margin-right: auto;margin-left: auto;width: 290px;border-radius: 5px;display: block;margin-bottom: 15px;height: 285px;"> </figure> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">23:30 分,B 站做了降级页面,将 404 页面跳转到了比较友好的异常页面。</p> <figure data-tool="mdnice编辑器" style="color: black;font-size: 16px;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.4967982924226254" src="/upload/19fd055cb1f1f853f087eaf749e228f2.png" data-type="png" data-w="1874" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">但是刷新下页面,又会跳转到 404 页面。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">22:35 主页可以加载出数据了,但是点击<code style="font-size: 14px;border-radius: 4px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(155, 110, 35);background-color: rgb(255, 245, 227);padding: 3px;margin: 3px;">动态</code>还是会报 502</p> <figure data-tool="mdnice编辑器" style="color: black;font-size: 16px;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.27715355805243447" src="/upload/62e0ce2edb40c86f6dee1b2393d7c605.png" data-type="png" data-w="1068" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">点击某个视频,直接报 404。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">2021-07-14 02:00 之后 B 站开始逐渐恢复。</p> <h2 data-tool="mdnice编辑器" style="color: black;font-size: 22px;font-weight: bold;line-height: 1.5em;margin-top: 2.2em;margin-bottom: 35px;"><span style="display: inline-block;background-image: linear-gradient(rgb(255, 255, 255) 60%, rgb(255, 177, 27) 40%);background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;color: rgb(81, 81, 81);padding: 2px 13px;margin-right: 3px;height: 50%;">二、什么原因</span></h2> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">今日凌晨 2 点,B 站发布公告称,昨晚,B 站的部分服务器机房发生故障,造成无法访问。技术团队随即进行了问题排查和修复,现在服务已经陆续恢复正常。而针对网友传言的 B 站大楼失火一事,上海消防官博进行了辟谣,B 站大楼并未出现火情。</p> <figure data-tool="mdnice编辑器" style="color: black;font-size: 16px;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.3372093023255814" src="/upload/b115a525909238bdfc41c3f0015514c.png" data-type="png" data-w="602" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;"><br>看来 B 站的高可用并不令我们满意。接下来我们来探讨下什么是高可用以及跨机房部署的思路。本篇正文内容如下:</p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;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: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;"> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.674468085106383" src="/upload/d585a514df00ba4fee5754fd26a50d60.png" data-type="png" data-w="940" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> </section> <h2 data-tool="mdnice编辑器" style="color: black;font-size: 22px;font-weight: bold;line-height: 1.5em;margin-top: 2.2em;margin-bottom: 35px;"><span style="display: inline-block;background-image: linear-gradient(rgb(255, 255, 255) 60%, rgb(255, 177, 27) 40%);background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;color: rgb(81, 81, 81);padding: 2px 13px;margin-right: 3px;height: 50%;">三、到底什么是高可用</span></h2> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">经过了 2 个小时,B 站才开始逐渐恢复,那 B 站系统到底算不算高可用呢?</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">首先高可用是个相对的形容词。那什么是高可用呢?</p> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 20px;font-weight: bold;line-height: 1.4;padding-top: 10px;margin-top: 10px;margin-bottom: 5px;"><span style="color: rgb(81, 81, 81);font-size: 1em;padding-left: 20px;border-left: 3px solid rgb(249, 191, 69);">3.1 高可用</span></h3> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">高可用性(High Availability,HA)我们已经耳熟能详,指的是系统具备较高的无故障运行的能力。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">B 站针对高可用架构还做过一篇分享:</p> <figure data-tool="mdnice编辑器" style="color: black;font-size: 16px;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.41116751269035534" src="/upload/f58fa97a0a5ec274440537d36f07df29.png" data-type="png" data-w="788" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <blockquote data-tool="mdnice编辑器" style="color: rgb(106, 115, 125);font-size: 0.9em;border-top: none;border-right: none;border-bottom: none;overflow: auto;padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(255, 177, 27);background: rgb(255, 245, 227);"> <p style="font-size: 16px;line-height: 26px;color: rgb(89, 89, 89);">重点:以后 B 站面试这类题不考,望周知。</p> </blockquote> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">常见的高可用的方案就是一主多从,主节点挂了,可以<code style="font-size: 14px;border-radius: 4px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(155, 110, 35);background-color: rgb(255, 245, 227);padding: 3px;margin: 3px;">快速</code>切换到从节点,从节点充当主节点,继续提供服务。比如 SQL Server 的主从架构,Redis 的主从架构,它们都是为了达到高可用性,即使某台服务器宕机了,也能继续提供服务。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">刚刚提到了快速,这是一个定性词语,那<code style="font-size: 14px;border-radius: 4px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(155, 110, 35);background-color: rgb(255, 245, 227);padding: 3px;margin: 3px;">定量</code>的高可用是怎么样的?</p> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 20px;font-weight: bold;line-height: 1.4;padding-top: 10px;margin-top: 10px;margin-bottom: 5px;"><span style="color: rgb(81, 81, 81);font-size: 1em;padding-left: 20px;border-left: 3px solid rgb(249, 191, 69);">3.2 定量分析高可用</span></h3> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">有两个相关的概念需要提及:MTBF 和 MTTR。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;"><strong style="color: black;">MTBF</strong>:故障间隔时间,可以理解为从上次故障到这次故障,间隔多久,间隔的越长,系统稳定性越高。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;"><strong style="color: black;">MTTR</strong>:故障平均恢复时间,可以理解为突然发生故障了,到系统恢复正常,经历了多长时间,这个时间越短越好,不然用户等着急了,会收到很多投诉。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;"><strong style="color: black;">可用性计算公式</strong>:MTBF/(MTBF+MTTR)* 100%,就是用故障的间隔时间除以故障间隔时间+故障平均恢复时间的总和。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">通常情况下,我们使用几个九来表示系统的可用性,之前我们项目组的系统要求达到年故障时间不超过 5 分钟,也就是五个九的标准。</p> <figure data-tool="mdnice编辑器" style="color: black;font-size: 16px;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.43862815884476536" src="/upload/5f94bdd32e0a72b84666f505b45cd58d.png" data-type="png" data-w="554" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 20px;font-weight: bold;line-height: 1.4;padding-top: 10px;margin-top: 10px;margin-bottom: 5px;"><span style="color: rgb(81, 81, 81);font-size: 1em;padding-left: 20px;border-left: 3px solid rgb(249, 191, 69);">3.3 定量分析 B 站</span></h3> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">来反观下 B 站故障了多久,2021-07-13 23:00 到 2021-07-14 02:00,系统逐渐恢复,如果按照年故障总时间来算的话:B 站故障超过 1 个小时了,只能算达到了三个九的标准。如果按照日故障时间来算,只能达到两个九的标准,也就是 99% 的高可用性,有点惨...</p> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 20px;font-weight: bold;line-height: 1.4;padding-top: 10px;margin-top: 10px;margin-bottom: 5px;"><span style="color: rgb(81, 81, 81);font-size: 1em;padding-left: 20px;border-left: 3px solid rgb(249, 191, 69);">3.4 一个九和两个九</span></h3> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">非常容易达到,一个正常的线上系统不会每天宕机 15 分钟吧,不然真用不下去了。</p> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 20px;font-weight: bold;line-height: 1.4;padding-top: 10px;margin-top: 10px;margin-bottom: 5px;"><span style="color: rgb(81, 81, 81);font-size: 1em;padding-left: 20px;border-left: 3px solid rgb(249, 191, 69);">3.5 三个九和四个九</span></h3> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">允许故障的时间很短,年故障时间是 1 小时到 8 小时,需要从架构设计、代码质量、运维体系、故障处理手册等入手,其中非常关键的一环是运维体系,如果线上出了问题,第一波收到异常通知的肯定是运维团队,根据问题的严重程度,会有不同的运维人员来处理,像 B 站这种大事故,就得运维负责人亲自上阵了。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">另外在紧急故障发生时,是否可以人工手段降级或者加开关,限制部分功能,也是需要考虑的。之前我遇到过一个问题,二维码刷卡功能出现故障,辛亏之前做了一个开关,可以将二维码功能隐藏,如果用户要使用二维码刷卡功能,统一引导用户走线下刷卡功能。</p> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 20px;font-weight: bold;line-height: 1.4;padding-top: 10px;margin-top: 10px;margin-bottom: 5px;"><span style="color: rgb(81, 81, 81);font-size: 1em;padding-left: 20px;border-left: 3px solid rgb(249, 191, 69);">3.6 五个九</span></h3> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">年故障时间 5 分钟以内,这个相当短,即使有强大的运维团队每天值班也很难在收到异常报警后,5 分钟内快速恢复,所以只能用自动化运维来解决。也就是服务器自己来保证系统的容灾和自动恢复的能力。</p> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 20px;font-weight: bold;line-height: 1.4;padding-top: 10px;margin-top: 10px;margin-bottom: 5px;"><span style="color: rgb(81, 81, 81);font-size: 1em;padding-left: 20px;border-left: 3px solid rgb(249, 191, 69);">3.7 六个九</span></h3> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">这个标准相当苛刻了,年故障时间 32 秒。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">针对不同的系统,其实对几个九也不相同。比如公司内部的员工系统,要求四个九就可以,如果是给全国用户使用,且使用人数很多,比如某宝、某饿,那么就要求五个九以上了,但是即使是数一数二的电商系统,它里面也有非核心的业务,其实也可以放宽限制,四个九足以,这个就看各家系统的要求,都是成本、人力、重要程度的权衡考虑。</p> <h2 data-tool="mdnice编辑器" style="color: black;font-size: 22px;font-weight: bold;line-height: 1.5em;margin-top: 2.2em;margin-bottom: 35px;"><span style="display: inline-block;background-image: linear-gradient(rgb(255, 255, 255) 60%, rgb(255, 177, 27) 40%);background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;color: rgb(81, 81, 81);padding: 2px 13px;margin-right: 3px;height: 50%;">四、如何做到高可用</span></h2> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">高可用的方案也是很常见,故障转移、超时控制、限流、隔离、熔断、降级,这里也做个总结。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">也可以看这篇:<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzAwMjI0ODk0NA==&mid=2451948286&idx=1&sn=ea07ba7c8deda9df6e96f652f055b9c3&chksm=8d1c3b61ba6bb2773d2f921d1a432fa2cbe2dfcfa4ec51fa20db4de89a87ca5e7d250e4075d7&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" style="text-decoration: underline;" data-linktype="2">双 11 的狂欢,干了这碗「流量防控」汤</a></p> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 20px;font-weight: bold;line-height: 1.4;padding-top: 10px;margin-top: 10px;margin-bottom: 5px;"><span style="color: rgb(81, 81, 81);font-size: 1em;padding-left: 20px;border-left: 3px solid rgb(249, 191, 69);">4.1 限流</span></h3> <figure data-tool="mdnice编辑器" style="color: black;font-size: 16px;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.3464052287581699" src="/upload/919607b392bb4193c4e44a871a41f8fd.png" data-type="png" data-w="612" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">对请求的流量进行控制, 只<code style="font-size: 14px;border-radius: 4px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(155, 110, 35);background-color: rgb(255, 245, 227);padding: 3px;margin: 3px;">放行部分请求</code>,使服务能够承担不超过自己能力的流量压力。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">常见限流算法有三种:<strong style="color: black;">时间窗口、漏桶算法、令牌桶算法</strong>。</p> <h4 data-tool="mdnice编辑器" style="color: black;font-size: 18px;line-height: 1.5em;margin-top: 30px;margin-bottom: 15px;font-weight: bold;">4.1.1 时间窗口</h4> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">时间窗口又分为固定窗口和滑动窗口。具体原理可以看这篇:<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzAwMjI0ODk0NA==&mid=2451948148&idx=1&sn=78eb2bd5fadf2680eabce0324b003f02&chksm=8d1c3bebba6bb2fd87608b6deaebbbfecd07a29658d0dcc3b36086fd342bfe93c52a0e489880&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" style="text-decoration: underline;" data-linktype="2">东汉末年,他们把「服务雪崩」玩到了极致(干货)</a></p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;"><strong style="color: black;">固定时间窗口</strong>:</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;"><strong style="color: black;">原理</strong>:固定时间内统计流量总量,超过阀值则限制流量。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;"><strong style="color: black;">缺陷</strong>:无法限制短时间之内的集中流量。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;"><strong style="color: black;">滑动窗口原理</strong>:</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;"><strong style="color: black;">原理</strong>:统计的总时间固定,但时间段是滑动的。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;"><strong style="color: black;">缺陷</strong>:无法控制流量让它们更加平滑</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">时间窗口的原理图在这里:</p> <figure data-tool="mdnice编辑器" style="color: black;font-size: 16px;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.5191489361702127" src="/upload/2b5c994a06b712b9faa1c19a12106bbe.png" data-type="png" data-w="705" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <figure data-tool="mdnice编辑器" style="color: black;font-size: 16px;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.7223382045929019" src="/upload/5680072f1fee38a2d32904f6c4734008.png" data-type="png" data-w="479" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <h4 data-tool="mdnice编辑器" style="color: black;font-size: 18px;line-height: 1.5em;margin-top: 30px;margin-bottom: 15px;font-weight: bold;">4.1.2 漏桶算法。</h4> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">原理:按照一个固定的速率将流量露出到接收端。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">缺陷:面对突发流量的时候,采用的解决方式是缓存在漏桶中,这样流量的响应时间就会增长,这就与互联网业务低延迟的要求不符。</p> <figure data-tool="mdnice编辑器" style="color: black;font-size: 16px;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.032" src="/upload/57f6c9a2ba4260cf38e368c2b50b9f87.png" data-type="png" data-w="250" style="margin-right: auto;margin-left: auto;width: 197px;border-radius: 5px;display: block;margin-bottom: 15px;height: 203px;"> </figure> <h4 data-tool="mdnice编辑器" style="color: black;font-size: 18px;line-height: 1.5em;margin-top: 30px;margin-bottom: 15px;font-weight: bold;">4.1.3 令牌桶算法</h4> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;"><strong style="color: black;">原理</strong>:一秒内限制访问次数为 N 次。每隔 1/N 的时间,往桶内放入一个令牌。分布式环境下,用 Redis 作为令牌桶。原理图如下:</p> <figure data-tool="mdnice编辑器" style="color: black;font-size: 16px;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.6081277213352685" src="/upload/38d016b4e4eb9a48eb5e6e365bda0f34.png" data-type="png" data-w="689" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">总结的思维导图在这里:</p> <figure data-tool="mdnice编辑器" style="color: black;font-size: 16px;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.47639484978540775" src="/upload/30b8d2a0911e64a37385bc307f1dbca3.png" data-type="png" data-w="1398" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 20px;font-weight: bold;line-height: 1.4;padding-top: 10px;margin-top: 10px;margin-bottom: 5px;"><span style="color: rgb(81, 81, 81);font-size: 1em;padding-left: 20px;border-left: 3px solid rgb(249, 191, 69);">4.2 隔离</span></h3> <ul data-tool="mdnice编辑器" style="color: black;font-size: 16px;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);"> 每个服务看作一个独立运行的系统,即使某一个系统有问题,也不会影响其他服务。 </section></li> </ul> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">而常规的方案是使用两款组件:Sentinel 和 Hystrix。</p> <figure data-tool="mdnice编辑器" style="color: black;font-size: 16px;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.41939120631341603" src="/upload/39052d78c6bc0efeba016991b0d5192b.png" data-type="png" data-w="887" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 20px;font-weight: bold;line-height: 1.4;padding-top: 10px;margin-top: 10px;margin-bottom: 5px;"><span style="color: rgb(81, 81, 81);font-size: 1em;padding-left: 20px;border-left: 3px solid rgb(249, 191, 69);">4.3 故障转移</span></h3> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">故障转移分为两种:</p> <ul data-tool="mdnice编辑器" style="color: black;font-size: 16px;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);"> 完全对等节点的故障转移。节点都是同等性质的。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 不对等节点的故障转移。不对等就是主备节点都存在。 </section></li> </ul> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">对等节点的系统中,所有节点都承担读写流量,并且节点不保存状态,每个节点就是另外一个的镜像。如果某个节点宕机了,按照负载均衡的权重配置访问其他节点就可以了。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">不对等的系统中,有一个主节点,多个备用节点,可以是热备(备用节点也在提供在线服务),也可以是冷备(只是备份作用)。如果主节点宕机了,可以被系统检测到,立即进行主备切换。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">而如何检测主节点宕机,就需要用到分布式 Leader 选举的算法,常见的就有 Paxos 和 Raft 算法,详细的选举算法可以看这两篇:</p> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 20px;font-weight: bold;line-height: 1.4;padding-top: 10px;margin-top: 10px;margin-bottom: 5px;"><span style="color: rgb(81, 81, 81);font-size: 1em;padding-left: 20px;border-left: 3px solid rgb(249, 191, 69);">4.4 超时控制</span></h3> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">超时控制就是模块与模块之间的调用需要限制请求的时间,如果请求超时的设置得较长,比如 30 s,那么当遇到大量请求超时的时候,由于请求线程都阻塞在慢请求上,导致很多请求都没来得及处理,如果持续时间足够长,就会产生级联反应,形成<code style="font-size: 14px;border-radius: 4px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(155, 110, 35);background-color: rgb(255, 245, 227);padding: 3px;margin: 3px;">雪崩</code>。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">还是以我们最熟悉的下单场景为例:用户下单了一个商品,客户端调用订单服务来生成预付款订单,订单服务调用商品服务查看下单的哪款商品,商品服务调用库存服务判断这款商品是否有库存,如有库存,则可以生成预付款订单。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;"><strong style="color: black;">雪崩如何造成的?</strong></p> <figure data-tool="mdnice编辑器" style="color: black;font-size: 16px;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.1615798922800717" src="/upload/b4bebd0af4f3fa25098e3179516996d4.png" data-type="png" data-w="557" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <ul data-tool="mdnice编辑器" style="color: black;font-size: 16px;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);"> 第一次滚雪球:库存服务不可用(如响应超时等),库存服务收到的很多请求都未处理完,库存服务将无法处理更多请求。 </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> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 第四次滚雪球:因订单服务不可用,客户端将不能下单,更多客户将重试下单,将导致更多下单请求不可用。 </section></li> </ul> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">所以设置合理的超时时间非常重要。具体设置的地方:模块与模块之间、请求数据库、缓存处理、调用第三方服务。</p> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 20px;font-weight: bold;line-height: 1.4;padding-top: 10px;margin-top: 10px;margin-bottom: 5px;"><span style="color: rgb(81, 81, 81);font-size: 1em;padding-left: 20px;border-left: 3px solid rgb(249, 191, 69);">4.5 熔断</span></h3> <figure data-tool="mdnice编辑器" style="color: black;font-size: 16px;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.3877159309021113" src="/upload/48cba2ee888f23107c2fb2c2911e0fc4.png" data-type="png" data-w="521" style="margin-right: auto;margin-left: auto;width: 485px;border-radius: 5px;display: block;margin-bottom: 15px;height: 188px;"> </figure> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">关键字:<code style="font-size: 14px;border-radius: 4px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(155, 110, 35);background-color: rgb(255, 245, 227);padding: 3px;margin: 3px;">断路保护</code>。比如 A 服务调用 B 服务,由于网络问题或 B 服务宕机了或 B 服务的处理时间长,导致请求的时间超长,如果在一定时间内多次出现这种情况,就可以直接将 B 断路了(A 不再请求B)。而调用 B 服务的请求直接返回降级数据,不必等待 B 服务的执行。因此 B 服务的问题,不会级联影响到 A 服务。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">熔断的详细原理还是可以上面提到的:<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzAwMjI0ODk0NA==&mid=2451948148&idx=1&sn=78eb2bd5fadf2680eabce0324b003f02&chksm=8d1c3bebba6bb2fd87608b6deaebbbfecd07a29658d0dcc3b36086fd342bfe93c52a0e489880&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" style="text-decoration: underline;" data-linktype="2">东汉末年,他们把「服务雪崩」玩到了极致(干货)</a></p> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 20px;font-weight: bold;line-height: 1.4;padding-top: 10px;margin-top: 10px;margin-bottom: 5px;"><span style="color: rgb(81, 81, 81);font-size: 1em;padding-left: 20px;border-left: 3px solid rgb(249, 191, 69);">4.6 降级</span></h3> <figure data-tool="mdnice编辑器" style="color: black;font-size: 16px;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.5980769230769231" src="/upload/61d260299dc77eed4c3313e957a5eb16.png" data-type="png" data-w="520" style="margin-right: auto;margin-left: auto;width: 397px;border-radius: 5px;display: block;margin-bottom: 15px;height: 237px;"> </figure> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">关键字:<code style="font-size: 14px;border-radius: 4px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(155, 110, 35);background-color: rgb(255, 245, 227);padding: 3px;margin: 3px;">返回降级数据</code>。网站处于流量高峰期,服务器压力剧增,根据当前业务情况及流量,对一些服务和页面进行有策略的降级(停止服务,所有的调用直接返回降级数据)。以此缓解服务器资源的压力,保证核心业务的正常运行,保持了客户和大部分客户得到正确的响应。降级数据可以简单理解为快速返回了一个 false,前端页面告诉用户“服务器当前正忙,请稍后再试。”</p> <ul data-tool="mdnice编辑器" style="color: black;font-size: 16px;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);"> <strong style="color: black;">熔断和降级的相同点?</strong> </section></li> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;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);"> 熔断和限流都是为了保证集群大部分服务的可用性和可靠性。防止核心服务崩溃。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 给终端用户的感受就是某个功能不可用。 </section></li> </ul> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <strong style="color: black;">熔断和降级的不同点?</strong> </section></li> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;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);"> 熔断是被调用方出现了故障,主动触发的操作。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 降级是基于全局考虑,停止某些正常服务,释放资源。 </section></li> </ul> </ul> <h2 data-tool="mdnice编辑器" style="color: black;font-size: 22px;font-weight: bold;line-height: 1.5em;margin-top: 2.2em;margin-bottom: 35px;"><span style="display: inline-block;background-image: linear-gradient(rgb(255, 255, 255) 60%, rgb(255, 177, 27) 40%);background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;color: rgb(81, 81, 81);padding: 2px 13px;margin-right: 3px;height: 50%;">五、异地多活</span></h2> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 20px;font-weight: bold;line-height: 1.4;padding-top: 10px;margin-top: 10px;margin-bottom: 5px;"><span style="color: rgb(81, 81, 81);font-size: 1em;padding-left: 20px;border-left: 3px solid rgb(249, 191, 69);">5.1 多机房部署</span></h3> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;"><strong style="color: black;">含义</strong>:在不同地域的数据中心(IDC)部署了多套服务,而这些服务又是共享同一份业务数据的,而且他们都可以处理用户的流量。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">某个服务挂了,其他服务随时切换到其他地域的机房中。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">现在服务是多套的,那数据库是不是也要多套,无非就两种方案:共用数据库或不共用。</p> <ul data-tool="mdnice编辑器" style="color: black;font-size: 16px;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);"> 共用一套机房的数据库。 </section></li> </ul> <figure data-tool="mdnice编辑器" style="color: black;font-size: 16px;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.6879194630872483" src="/upload/15da69607d906983186c9c797e6e30fa.png" data-type="png" data-w="596" style="margin-right: auto;margin-left: auto;width: 431px;border-radius: 5px;display: block;margin-bottom: 15px;height: 296px;"> </figure> <ul data-tool="mdnice编辑器" style="color: black;font-size: 16px;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);"> <p style="margin-bottom: 20px;line-height: 1.8em;color: rgb(58, 58, 58);">不共用数据库。每个机房都有自己的数据库,数据库之间做同步。实现起来这个方案更复杂。</p> <figure style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.6657183499288762" src="/upload/7b8e68b181af510d82b672f6440fee36.png" data-type="png" data-w="703" style="margin-right: auto;margin-left: auto;width: 439px;border-radius: 5px;display: block;margin-bottom: 15px;height: 292px;"> </figure> </section></li> </ul> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">不论使用哪种方式,都涉及到跨机房数据传输延迟的问题。</p> <ul data-tool="mdnice编辑器" style="color: black;font-size: 16px;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);"> 同地多机房专线,延迟 1ms~3 ms。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 异地多机房专线,延迟 50 ms 左右。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 跨国多机房,延迟 200 ms 左右。 </section></li> </ul> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 20px;font-weight: bold;line-height: 1.4;padding-top: 10px;margin-top: 10px;margin-bottom: 5px;"><span style="color: rgb(81, 81, 81);font-size: 1em;padding-left: 20px;border-left: 3px solid rgb(249, 191, 69);">5.2 同城双活</span></h3> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">高性能的同城双活,核心思想就是避免跨机房调用:</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">保证同机房服务调用:不同的 PRC(远程调用) 服务,向注册中心注册不同的服务组,而 RPC 服务只订阅同机房的 RPC 服务组,RPC 调用只存在于本机房。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">保证同机房缓存调用:查询缓存发生在本机房,如果没有,则从数据库加载。缓存也是采用主备的方式,数据更新采用多机房更新的方式。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">保证同机房数据库查询:和缓存一样,读取本机房的数据库,同样采用主备方式。</p> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 20px;font-weight: bold;line-height: 1.4;padding-top: 10px;margin-top: 10px;margin-bottom: 5px;"><span style="color: rgb(81, 81, 81);font-size: 1em;padding-left: 20px;border-left: 3px solid rgb(249, 191, 69);">5.3 异地多活</span></h3> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">同城双活无法做到城市级别的容灾。所以需要考虑异地多活。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">比如上海的服务器宕机了,还有重庆的服务器可以顶上来。但两地距离不要太近,因为发生自然灾害时有可能会被另外一地波及到。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">和同城双活的核心思想一样,避免跨机房调用。但是因为异地方案中的调用延迟远大于同机房的方案,所以数据同步是一个非常值得探讨的点。提供两种方案:</p> <ul data-tool="mdnice编辑器" style="color: black;font-size: 16px;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);"> 基于存储系统的主从复制,MySQL 和 Redis 天生就具备。但是数据量很大的情况下,性能是较差的。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 异步复制的方式。基于消息队列,将数据操作作为一个消息放到消息队列,另外的机房消费这条消息,操作存储组件。 </section></li> </ul> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 20px;font-weight: bold;line-height: 1.4;padding-top: 10px;margin-top: 10px;margin-bottom: 5px;"><span style="color: rgb(81, 81, 81);font-size: 1em;padding-left: 20px;border-left: 3px solid rgb(249, 191, 69);">5.4 两地三中心</span></h3> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">这个概念也被业界提到过很多次。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">两地:本地和异地。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">三中心:本地数据中心、同城数据中心、异地数据中心。</p> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;">这两个概念也就是我上面说的同城双活和异地多活的方式,只是针对的是数据中心。原理如下图所示:</p> <figure data-tool="mdnice编辑器" style="color: black;font-size: 16px;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.3173228346456693" src="/upload/5526486cbe422132d41e42db14477a.png" data-type="png" data-w="1270" style="margin-right: auto;margin-left: auto;width: 100%;border-radius: 5px;display: block;margin-bottom: 15px;"> </figure> <p data-tool="mdnice编辑器" style="color: rgb(58, 58, 58);font-size: 16px;margin-bottom: 20px;line-height: 1.8em;"></p>
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="white-space: normal;"> <blockquote data-tool="mdnice编辑器"> <p style="text-align: left;"><span style="font-family: Arial, Helvetica, sans-serif;">SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。</span></p> </blockquote> <p data-tool="mdnice编辑器" style="text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">本文主要是特性 & 用法介绍,不涉及源码解析(源码都很简单,相信你一定一看就懂)</span></p> <p data-tool="mdnice编辑器" style="text-align: left;"><br></p> </section> <h2 data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><strong><span style="font-family: Arial, Helvetica, sans-serif;"># SPI 有什么用?</span></strong></h2> <p style="white-space: normal;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">举个栗子,现在我们设计了一款全新的日志框架:super-logger。默认以XML文件作为我们这款日志的配置文件,并设计了一个配置文件解析的接口:</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="java"><code><span class="code-snippet_outer">package com.github.kongwu.spisamples;</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">public interface SuperLoggerConfiguration {</span></code><code><span class="code-snippet_outer"> void configure(String configFile);</span></code><code><span class="code-snippet_outer">}</span></code></pre> </section> <pre data-tool="mdnice编辑器" style="text-align: left;"><br></pre> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">然后来一个默认的XML实现:</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="java"><code><span class="code-snippet_outer">package com.github.kongwu.spisamples;</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">public class XMLConfiguration implements SuperLoggerConfiguration{</span></code><code><span class="code-snippet_outer"> public void configure(String configFile){</span></code><code><span class="code-snippet_outer"> ......</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">}</span></code></pre> </section> <pre data-tool="mdnice编辑器" style="text-align: left;"><br></pre> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">那么我们在初始化,解析配置时,只需要调用这个XMLConfiguration来解析XML配置文件即可。</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="java"><code><span class="code-snippet_outer">package com.github.kongwu.spisamples;</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">public class LoggerFactory {</span></code><code><span class="code-snippet_outer"> static {</span></code><code><span class="code-snippet_outer"> SuperLoggerConfiguration configuration = new XMLConfiguration();</span></code><code><span class="code-snippet_outer"> configuration.configure(configFile);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"> public static getLogger(Class clazz){</span></code><code><span class="code-snippet_outer"> ......</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">}</span></code></pre> </section> <pre data-tool="mdnice编辑器" style="text-align: left;"><br></pre> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;">这样就完成了一个基础的模型,看起来也没什么问题。不过扩展性不太好,因为如果想定制/扩展/重写解析功能的话,我还得重新定义入口的代码,LoggerFactory 也得重写,不够灵活,侵入性太强了。<span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;color: rgb(0, 0, 0);"></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">比如现在用户/使用方想增加一个 yml 文件的方式,作为日志配置文件,那么只需要新建一个YAMLConfiguration,实现 SuperLoggerConfiguration 就可以。但是……怎么注入呢,怎么让 LoggerFactory中使用新建的这个 YAMLConfiguration ?难不成连 LoggerFactory 也重写了?</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">如果借助SPI机制的话,这个事情就很简单了,可以很方便的完成这个入口的扩展功能。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">下面就先来看看,利用JDK 的 SPI 机制怎么解决上面的扩展性问题。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <h2 data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><strong><span style="font-family: Arial, Helvetica, sans-serif;">JDK SPI</span></strong><span style="font-family: Arial, Helvetica, sans-serif;"></span></h2> <p style="white-space: normal;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;color: rgb(0, 0, 0);">JDK 中 提供了一个 SPI 的功能,核心类是 java.util.ServiceLoader。其作用就是,可以通过类名获取在"META-INF/services/"下的多个配置实现文件。</span><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">为了解决上面的扩展问题,现在我们在META-INF/services/下创建一个com.github.kongwu.spisamples.SuperLoggerConfiguration文件(没有后缀)。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">文件中只有一行代码,那就是我们默认的com.github.kongwu.spisamples.XMLConfiguration(注意,一个文件里也可以写多个实现,回车分隔)</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="makefile"><code><span class="code-snippet_outer"><span class="code-snippet__section">META-INF/services/com.github.kongwu.spisamples.SuperLoggerConfiguration:</span></span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">com.github.kongwu.spisamples.XMLConfiguration</span></code></pre> </section> <pre data-tool="mdnice编辑器" style="text-align: left;"><br></pre> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">然后通过 ServiceLoader 获取我们的 SPI 机制配置的实现类:</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="swift"><code><span class="code-snippet_outer">ServiceLoader<SuperLoggerConfiguration> serviceLoader = ServiceLoader.load(SuperLoggerConfiguration.class);</span></code><code><span class="code-snippet_outer">Iterator<SuperLoggerConfiguration> iterator = serviceLoader.iterator();</span></code><code><span class="code-snippet_outer">SuperLoggerConfiguration configuration;</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">while(iterator.hasNext()) {</span></code><code><span class="code-snippet_outer"> //加载并初始化实现类</span></code><code><span class="code-snippet_outer"> configuration = iterator.next();</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">//对最后一个configuration类调用configure方法</span></code><code><span class="code-snippet_outer">configuration.configure(configFile);</span></code></pre> </section> <pre data-tool="mdnice编辑器" style="text-align: left;"><br></pre> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">最后在调整LoggerFactory中初始化配置的方式为现在的SPI方式:</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="java"><code><span class="code-snippet_outer">package com.github.kongwu.spisamples;</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">public class LoggerFactory {</span></code><code><span class="code-snippet_outer"> static {</span></code><code><span class="code-snippet_outer"> ServiceLoader<SuperLoggerConfiguration> serviceLoader = ServiceLoader.load(SuperLoggerConfiguration.class);</span></code><code><span class="code-snippet_outer"> Iterator<SuperLoggerConfiguration> iterator = serviceLoader.iterator();</span></code><code><span class="code-snippet_outer"> SuperLoggerConfiguration configuration;</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"> while(iterator.hasNext()) {</span></code><code><span class="code-snippet_outer"> configuration = iterator.next();//加载并初始化实现类</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> configuration.configure(configFile);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"> public static getLogger(Class clazz){</span></code><code><span class="code-snippet_outer"> ......</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">}</span></code></pre> </section> <pre data-tool="mdnice编辑器" style="text-align: left;"><br></pre> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">等等,这里为什么是用 iterator ? 而不是get之类的只获取一个实例的方法?</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">试想一下,如果是一个固定的get方法,那么get到的是一个固定的实例,SPI 还有什么意义呢?</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">SPI 的目的,就是增强扩展性。将固定的配置提取出来,通过 SPI 机制来配置。那既然如此,一般都会有一个默认的配置,然后通过 SPI 的文件配置不同的实现,这样就会存在一个接口多个实现的问题。要是找到多个实现的话,用哪个实现作为最后的实例呢?</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">所以这里使用iterator来获取所有的实现类配置。刚才已经在我们这个 super-logger 包里增加了默认的SuperLoggerConfiguration 实现。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">为了支持 YAML 配置,现在在使用方/用户的代码里,增加一个YAMLConfiguration的 SPI 配置:</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="makefile"><code><span class="code-snippet_outer">META-INF/services/com.github.kongwu.spisamples.SuperLoggerConfiguration:</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">com.github.kongwu.spisamples.ext.YAMLConfiguration</span></code></pre> </section> <pre data-tool="mdnice编辑器" style="text-align: left;"><br></pre> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">此时通过iterator方法,就会获取到默认的XMLConfiguration和我们扩展的这个YAMLConfiguration两个配置实现类了。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">在上面那段加载的代码里,我们遍历iterator,遍历到最后,我们**使用最后一个实现配置作为最终的实例。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;background-color: rgb(255, 218, 169);">再等等?最后一个?怎么算最后一个?</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">使用方/用户自定义的的这个 YAMLConfiguration 一定是最后一个吗?</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">这个真的不一定,取决于我们运行时的 ClassPath 配置,在前面加载的jar自然在前,最后的jar里的自然当然也在后面。所以</span><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;background-color: rgb(255, 218, 169);">如果用户的包在ClassPath中的顺序比super-logger的包更靠后,才会处于最后一个位置;如果用户的包位置在前,那么所谓的最后一个仍然是默认的XMLConfiguration。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;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="css"><code><span class="code-snippet_outer">java -cp super-logger.jar:a.jar:b.jar:main.jar example.Main</span></code></pre> </section> <pre data-tool="mdnice编辑器" style="text-align: left;"><br></pre> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">默认的XMLConfiguration SPI配置在super-logger.jar,扩展的YAMLConfiguration SPI配置文件在main.jar,那么iterator获取的最后一个元素一定为YAMLConfiguration。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">但这个classpath顺序如果反了呢?main.jar 在前,super-logger.jar 在后</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="css"><code><span class="code-snippet_outer">java -cp main.jar:super-logger.jar:a.jar:b.jar example.Main</span></code></pre> </section> <pre data-tool="mdnice编辑器" style="text-align: left;"><br></pre> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">这样一来,iterator 获取的最后一个元素又变成了默认的XMLConfiguration,我们使用 JDK SPI 没啥意义了,获取的又是第一个,还是默认的XMLConfiguration。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">由于这个加载顺序(classpath)是由用户指定的,所以无论我们加载第一个还是最后一个,都有可能会导致加载不到用户自定义的那个配置。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;background-color: rgb(255, 218, 169);">所以这也是JDK SPI机制的一个劣势,无法确认具体加载哪一个实现,也无法加载某个指定的实现,仅靠ClassPath的顺序是一个非常不严谨的方式。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <h2 data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><strong><span style="font-family: Arial, Helvetica, sans-serif;"># Dubbo SPI</span></strong><span style="font-family: Arial, Helvetica, sans-serif;"></span></h2> <blockquote data-tool="mdnice编辑器" style="white-space: normal;"> <p style="text-align: left;"><span style="font-family: Arial, Helvetica, sans-serif;">Dubbo 就是通过 SPI 机制加载所有的组件。不过,Dubbo 并未使用 Java 原生的 SPI 机制,而是对其进行了增强,使其能够更好的满足需求。在 Dubbo 中,SPI 是一个非常重要的模块。基于 SPI,我们可以很容易的对 Dubbo 进行拓展。如果大家想要学习 Dubbo 的源码,SPI 机制务必弄懂。接下来,我们先来了解一下 Java SPI 与 Dubbo SPI 的用法,然后再来分析 Dubbo SPI 的源码。</span></p> </blockquote> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">Dubbo 中实现了一套新的 SPI 机制,功能更强大,也更复杂一些。相关逻辑被封装在了 ExtensionLoader 类中,通过 ExtensionLoader,我们可以加载指定的实现类。Dubbo SPI 所需的配置文件需放置在 META-INF/dubbo 路径下,配置内容如下(以下demo来自dubbo官方文档)。</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="ini"><code><span class="code-snippet_outer">optimusPrime = org.apache.spi.OptimusPrime</span></code><code><span class="code-snippet_outer">bumblebee = org.apache.spi.Bumblebee</span></code></pre> </section> <pre data-tool="mdnice编辑器" style="text-align: left;"><br></pre> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">与 Java SPI 实现类配置不同,Dubbo SPI 是通过键值对的方式进行配置,这样我们可以按需加载指定的实现类。另外在使用时还需要在接口上标注 @SPI 注解。下面来演示 Dubbo SPI 的用法:</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="java"><code><span class="code-snippet_outer">@SPI</span></code><code><span class="code-snippet_outer">public interface Robot {</span></code><code><span class="code-snippet_outer"> void sayHello();</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">public class OptimusPrime implements Robot {</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"> @Override</span></code><code><span class="code-snippet_outer"> public void sayHello() {</span></code><code><span class="code-snippet_outer"> System.out.println("Hello, I am Optimus Prime.");</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">public class Bumblebee implements Robot {</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"> @Override</span></code><code><span class="code-snippet_outer"> public void sayHello() {</span></code><code><span class="code-snippet_outer"> System.out.println("Hello, I am Bumblebee.");</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">public class DubboSPITest {</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"> @Test</span></code><code><span class="code-snippet_outer"> public void sayHello() throws Exception {</span></code><code><span class="code-snippet_outer"> ExtensionLoader<Robot> extensionLoader = </span></code><code><span class="code-snippet_outer"> ExtensionLoader.getExtensionLoader(Robot.class);</span></code><code><span class="code-snippet_outer"> Robot optimusPrime = extensionLoader.getExtension("optimusPrime");</span></code><code><span class="code-snippet_outer"> optimusPrime.sayHello();</span></code><code><span class="code-snippet_outer"> Robot bumblebee = extensionLoader.getExtension("bumblebee");</span></code><code><span class="code-snippet_outer"> bumblebee.sayHello();</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">}</span></code></pre> </section> <pre data-tool="mdnice编辑器" style="text-align: left;"><br></pre> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;background-color: rgb(255, 218, 169);">Dubbo SPI 和 JDK SPI 最大的区别就在于支持“别名”</span><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">,可以通过某个扩展点的别名来获取固定的扩展点。就像上面的例子中,我可以获取 Robot 多个 SPI 实现中别名为“optimusPrime”的实现,也可以获取别名为“bumblebee”的实现,这个功能非常有用!</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;">通过 @SPI 注解的 value 属性,还可以默认一个“别名”的实现。比如在Dubbo 中,默认的是Dubbo 私有协议:dubbo protocol - dubbo://** 来看看Dubbo中协议的接口:<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="kotlin"><code><span class="code-snippet_outer">@SPI("dubbo")</span></code><code><span class="code-snippet_outer">public interface Protocol {</span></code><code><span class="code-snippet_outer"> ......</span></code><code><span class="code-snippet_outer">}</span></code></pre> </section> <pre data-tool="mdnice编辑器" style="text-align: left;"><br></pre> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">在 Protocol 接口上,增加了一个 @SPI 注解,而注解的 value 值为 Dubbo ,通过 SPI 获取实现时就会获取 Protocol SPI 配置中别名为dubbo的那个实现,com.alibaba.dubbo.rpc.Protocol文件如下:</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="makefile"><code><span class="code-snippet_outer">filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper</span></code><code><span class="code-snippet_outer">listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper</span></code><code><span class="code-snippet_outer">mock=com.alibaba.dubbo.rpc.support.MockProtocol</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol</span></code><code><span class="code-snippet_outer">rmi=com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol</span></code><code><span class="code-snippet_outer">hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol</span></code><code><span class="code-snippet_outer">com.alibaba.dubbo.rpc.protocol.http.HttpProtocol</span></code><code><span class="code-snippet_outer">com.alibaba.dubbo.rpc.protocol.webservice.WebServiceProtocol</span></code><code><span class="code-snippet_outer">thrift=com.alibaba.dubbo.rpc.protocol.thrift.ThriftProtocol</span></code><code><span class="code-snippet_outer">memcached=com.alibaba.dubbo.rpc.protocol.memcached.MemcachedProtocol</span></code><code><span class="code-snippet_outer">redis=com.alibaba.dubbo.rpc.protocol.redis.RedisProtocol</span></code><code><span class="code-snippet_outer">rest=com.alibaba.dubbo.rpc.protocol.rest.RestProtocol</span></code><code><span class="code-snippet_outer">registry=com.alibaba.dubbo.registry.integration.RegistryProtocol</span></code><code><span class="code-snippet_outer">qos=com.alibaba.dubbo.qos.protocol.QosProtocolWrapper</span></code></pre> </section> <pre data-tool="mdnice编辑器" style="text-align: left;"><br></pre> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">然后只需要通过getDefaultExtension,就可以获取到 @SPI 注解上value对应的那个扩展实现了</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="ruby"><code><span class="code-snippet_outer">Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getDefaultExtension();</span></code><code><span class="code-snippet_outer">//protocol: DubboProtocol</span></code></pre> </section> <pre data-tool="mdnice编辑器" style="text-align: left;"><br></pre> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;">还有一个 Adaptive 的机制,虽然非常灵活,但……用法并不是很“优雅”,这里就不介绍了<span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">Dubbo 的 SPI 中还有一个“加载优先级”,优先加载内置(internal)的,然后加载外部的(external),按优先级顺序加载,</span><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;background-color: rgb(255, 218, 169);">如果遇到重复就跳过不会加载了</span><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">所以如果想靠classpath加载顺序去覆盖内置的扩展,也是个不太理智的做法,原因同上 - 加载顺序不严谨。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <h2 data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><strong><span style="font-family: Arial, Helvetica, sans-serif;"># Spring SPI</span></strong></h2> <p style="white-space: normal;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">Spring 的 SPI 配置文件是一个固定的文件 - META-INF/spring.factories,功能上和 JDK 的类似,每个接口可以有多个扩展实现,使用起来非常简单:</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="php"><code><span class="code-snippet_outer">//获取所有factories文件中配置的LoggingSystemFactory</span></code><code><span class="code-snippet_outer">List<LoggingSystemFactory>> factories = </span></code><code><span class="code-snippet_outer"> SpringFactoriesLoader.loadFactories(LoggingSystemFactory.class, classLoader);</span></code></pre> </section> <pre data-tool="mdnice编辑器" style="text-align: left;"><br></pre> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">下面是一段 Spring Boot 中 spring.factories 的配置</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"># Logging Systems</span></code><code><span class="code-snippet_outer">org.springframework.boot.logging.LoggingSystemFactory=\</span></code><code><span class="code-snippet_outer">org.springframework.boot.logging.logback.LogbackLoggingSystem.Factory,\</span></code><code><span class="code-snippet_outer">org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.Factory,\</span></code><code><span class="code-snippet_outer">org.springframework.boot.logging.java.JavaLoggingSystem.Factory</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"># PropertySource Loaders</span></code><code><span class="code-snippet_outer">org.springframework.boot.env.PropertySourceLoader=\</span></code><code><span class="code-snippet_outer">org.springframework.boot.env.PropertiesPropertySourceLoader,\</span></code><code><span class="code-snippet_outer">org.springframework.boot.env.YamlPropertySourceLoader</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"># ConfigData Location Resolvers</span></code><code><span class="code-snippet_outer">org.springframework.boot.context.config.ConfigDataLocationResolver=\</span></code><code><span class="code-snippet_outer">org.springframework.boot.context.config.ConfigTreeConfigDataLocationResolver,\</span></code><code><span class="code-snippet_outer">org.springframework.boot.context.config.StandardConfigDataLocationResolver</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">......</span></code></pre> </section> <pre data-tool="mdnice编辑器" style="text-align: left;"><br></pre> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;">Spring SPI 中,将所有的配置放到一个固定的文件中,省去了配置一大堆文件的麻烦。至于多个接口的扩展配置,是用一个文件好,还是每个单独一个文件好这个,这个问题就见仁见智了(个人喜欢 Spring 这种,干净利落)。<span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">Spring的SPI 虽然属于spring-framework(core),但是目前主要用在spring boot中……</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">和前面两种 SPI 机制一样,Spring 也是支持 ClassPath 中存在多个 spring.factories 文件的,加载时会按照 classpath 的顺序依次加载这些 spring.factories 文件,添加到一个 ArrayList 中。由于没有别名,所以也没有去重的概念,有多少就添加多少。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;color: rgb(0, 0, 0);">但由于 Spring 的 SPI 主要用在 Spring Boot 中,而 Spring Boot 中的 ClassLoader 会优先加载项目中的文件,而不是依赖包中的文件。所以如果在你的项目中定义个spring.factories文件,那么你项目中的文件会被第一个加载,得到的Factories中,项目中spring.factories里配置的那个实现类也会排在第一个。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">如果我们要扩展某个接口的话,只需要在你的项目(spring boot)里新建一个META-INF/spring.factories文件,只添加你要的那个配置,不要完整的复制一遍 Spring Boot 的 spring.factories 文件然后修改** 比如我只想添加一个新的 LoggingSystemFactory 实现,那么我只需要新建一个META-INF/spring.factories文件,而不是完整的复制+修改:</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">org.springframework.boot.logging.LoggingSystemFactory=\</span></code><code><span class="code-snippet_outer">com.example.log4j2demo.Log4J2LoggingSystem.Factory</span></code></pre> </section> <pre data-tool="mdnice编辑器" style="text-align: left;"><br></pre> <h2 data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><strong><span style="font-family: Arial, Helvetica, sans-serif;"># 对比</span></strong></h2> <p style="white-space: normal;"><br></p> <figure data-tool="mdnice编辑器" style="white-space: normal;"> <img data-ratio="0.8990267639902676" src="/upload/d17b5f0397cfcbde0d61dddc35618e55.png" data-type="png" data-w="822" height="690" style="margin-right: auto;margin-left: auto;display: block;" width="822"> </figure> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;">三种 SPI 机制对比之下,JDK 内置的机制是最弱鸡的,但是由于是 JDK 内置,所以还是有一定应用场景,毕竟不用额外的依赖;Dubbo 的功能最丰富,但机制有点复杂了,而且只能配合 Dubbo 使用,不能完全算是一个独立的模块;Spring 的功能和JDK的相差无几,最大的区别是所有扩展点写在一个 spring.factories 文件中,也算是一个改进,并且 IDEA 完美支持语法提示。<span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;">各位看官们大佬们,你们觉得 JDK/Dubbo/Spring 三种 SPI 的机制,哪个更好呢?欢迎评论区留言!</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <h2 data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><span style="font-family: Arial, Helvetica, sans-serif;">参考</span></h2> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="width: 577.417px;white-space: normal;"> <li style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"> <section> <p style="text-align: left;">Introduction to the Service Provider Interfaces - Oracle</p> </section></li> <li style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"> <section> <p style="text-align: left;">Dubbo SPI - Apache Dubbo</p> </section></li> <li style="font-size: 16px;font-family: Arial, Helvetica, sans-serif;"> <section> <p style="text-align: left;">Creating Your Own Auto-configuration - Spring</p> </section></li> </ul> <p><br></p> <p><strong style="white-space: normal;color: rgb(0, 0, 0);text-align: left;widows: 1;background-color: rgb(255, 255, 255);font-size: 13px;font-family: PingFangSC-light;letter-spacing: 2px;caret-color: rgb(0, 0, 0);word-spacing: 2px;">版权申明:内容来源网络,版权归原创者所有。除非无法确认,都会标明作者及出处,如有侵权烦请告知,我们会立即删除并致歉。谢谢!</strong></p> <pre style="font-size: 16px;text-align: left;font-weight: 700;letter-spacing: 0.544px;widows: 1;caret-color: rgb(51, 51, 51);background-color: rgb(255, 255, 255);color: rgb(0, 0, 0);orphans: 4;word-spacing: 2px;"><h4 style="font-family: -apple-system-font, system-ui, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;letter-spacing: 0.544px;caret-color: rgb(255, 0, 0);white-space: pre-wrap;line-height: 1.2;"><pre style="letter-spacing: 0.544px;font-weight: 700;text-align: center;"><pre style="letter-spacing: 0.544px;text-align: left;word-spacing: 1px;font-size: 15px;text-size-adjust: auto;line-height: inherit;"><p style="margin-top: 10px;margin-bottom: 10px;font-family: -apple-system-font, system-ui, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;letter-spacing: 0.544px;text-indent: 0em;white-space: normal;word-spacing: 2px;color: rgb(62, 62, 62);"><span style="font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"><strong style="letter-spacing: 0.544px;caret-color: rgb(51, 51, 51);font-size: 14px;"><br></strong></span></p><pre style="color: rgb(0, 0, 0);font-size: 15px;font-weight: 700;letter-spacing: 0.544px;orphans: 4;text-align: left;widows: 1;word-spacing: 1px;caret-color: rgb(255, 0, 0);background-color: rgb(255, 255, 255);"><h4><pre> <section> <section style="margin-bottom: 15px;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"><strong><span style="font-size: 15px;color: rgb(255, 76, 65);">往期热门文章:</span></strong><br></span> </section> </section></pre></h4><h4 style="letter-spacing: 0.544px;white-space: normal;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 15px;margin-bottom: 15px;font-size: inherit;font-variant-numeric: normal;font-variant-east-asian: normal;letter-spacing: 0.544px;color: rgb(62, 62, 62);line-height: 1.75em;text-indent: 0em;"> <span style="font-size: 14px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">1、</span> <a href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&mid=2247489898&idx=1&sn=5c5e41a0f695649046de3db1b1810df5&chksm=e9c5e0dbdeb269cd52f798c675f58295ad0f632283c0fb07db4140949c72796deed7dfe7d434&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2" hasload="1" style="color: var(--weui-LINK);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-size: 14px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">《</a> <a href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&mid=2247489402&idx=2&sn=af5c3bb38717e828d92ed48874f03fe8&chksm=e9c5eecbdeb267dd3d05c159bdb9c611f24c4ca7fb7dafa12daf459becb0ef4fab7bcc2d1a67&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2" hasload="1" style="color: var(--weui-LINK);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-size: 14px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">历史文章分类导读列表!精选优秀博文都在这里了!》</a> </section></h4><h4 style="letter-spacing: 0.544px;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: pre-wrap;line-height: 1.2;"> <section style="margin-top: 15px;margin-bottom: 15px;letter-spacing: 0.544px;color: rgb(62, 62, 62);caret-color: rgb(51, 51, 51);line-height: 1.5em;"> <span style="letter-spacing: 0.544px;font-size: 14px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">2、<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&mid=2247496637&idx=1&sn=fff1a9a55b39f1617934cbdb40b2e62b&chksm=e9c60a0cdeb1831a8539d951d9cf98a295470b7924475efbb0eb9f084b60c43538cec8f25206&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;">Java必会的工具库,让你的代码量减少90%</a></span> </section> <section style="margin-top: 15px;margin-bottom: 15px;letter-spacing: 0.544px;color: rgb(62, 62, 62);caret-color: rgb(51, 51, 51);line-height: 1.5em;"> <span style="letter-spacing: 0.544px;font-size: 14px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">3、<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&mid=2247496460&idx=1&sn=db2f3f3d23fd65f683cfa7321c2ee4e1&chksm=e9c60abddeb183ab65f4c8d1f9668e3d1618dd09cd098f093e6e663222fd4d0754deabfcca44&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;">腾讯最大股东收购了 Stack Overflow,以后“抄代码”都要付费了么?</a></span> </section> <section style="margin-top: 15px;margin-bottom: 15px;letter-spacing: 0.544px;color: rgb(62, 62, 62);caret-color: rgb(51, 51, 51);line-height: 1.5em;"> <span style="font-size: 14px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">4、</span> <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&mid=2247496422&idx=1&sn=3a11bda6ecbb9c09f2ee389926bfd695&chksm=e9c60b57deb182413b6f617e3d3a7ff320bc37876afe2d4b2bbaa7920cfa8c1b23ae9a3dda89&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-size: 14px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">灵隐寺招聘:没有KPI,佛系上班……</a> </section> <section style="margin-top: 15px;margin-bottom: 15px;letter-spacing: 0.544px;color: rgb(62, 62, 62);caret-color: rgb(51, 51, 51);line-height: 1.5em;"> <span style="font-size: 14px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">5、</span> <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&mid=2247496418&idx=1&sn=2095e5e5e9e8b5d322a2329a003edddb&chksm=e9c60b53deb18245cc39ab84520082ea34f08dd5147a331f529405491c80a1206acfea6fc4ca&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-size: 14px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">如何优雅处理重复请求/并发请求?</a> </section> <section style="margin-top: 15px;margin-bottom: 15px;letter-spacing: 0.544px;color: rgb(62, 62, 62);caret-color: rgb(51, 51, 51);line-height: 1.5em;"> <span style="font-size: 14px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">6、</span> <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&mid=2247496413&idx=1&sn=2a15b2734dac08d0e18a33f9987dced7&chksm=e9c60b6cdeb1827a51b714c1e5b55c041db651a43e573e0acd19fdbcef765ee3f75719a59abe&scene=21#wechat_redirect" data-itemshowtype="11" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-size: 14px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">不用到2038年,MySQL的TIMESTAMP就能把我们系统搞崩!</a> </section> <section style="margin-top: 15px;margin-bottom: 15px;letter-spacing: 0.544px;color: rgb(62, 62, 62);caret-color: rgb(51, 51, 51);line-height: 1.5em;"> <span style="font-size: 14px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">7、</span> <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&mid=2247496364&idx=1&sn=7e74a865d142e2c13f2ee92b22cef396&chksm=e9c60b1ddeb1820bc587e93a854f8946b73dea4c4ec6627d7f7a227e1e01ab1235cdbb2b90a8&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-size: 14px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">翻车!在项目中用了Arrays.asList、ArrayList的subList,被公开批评</a> </section> <section style="margin-top: 15px;margin-bottom: 15px;letter-spacing: 0.544px;color: rgb(62, 62, 62);caret-color: rgb(51, 51, 51);line-height: 1.5em;"> <span style="font-size: 14px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">8、</span> <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&mid=2247496360&idx=1&sn=b0287346c03bc901acf539d3053c3025&chksm=e9c60b19deb1820fad8011037ca12a4248b9c5c8ae37de8a37ee41b5127fbd0a135a210a7be8&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-size: 14px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">想接私活时薪再翻一倍,建议根据这几个开源的Spring Boot项目改改~</a> </section> <section style="margin-top: 15px;margin-bottom: 15px;letter-spacing: 0.544px;color: rgb(62, 62, 62);caret-color: rgb(51, 51, 51);line-height: 1.5em;"> <span style="font-size: 14px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">9、</span> <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&mid=2247496350&idx=1&sn=b3486a14bbb315df1039b8c29c70c419&chksm=e9c60b2fdeb1823960d7812831d24d5c47a947f44a102b00ca8e1509917d84dde189f7da7451&scene=21#wechat_redirect" data-itemshowtype="11" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-size: 14px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">细数ThreadLocal三大坑,内存泄露仅是小儿科</a> </section> <section style="margin-top: 15px;margin-bottom: 15px;letter-spacing: 0.544px;color: rgb(62, 62, 62);caret-color: rgb(51, 51, 51);line-height: 1.5em;"> <span style="font-size: 14px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">10、</span> <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&mid=2247496335&idx=1&sn=40246299850b84d5c4bb677ddd377cfc&chksm=e9c60b3edeb18228de1588c663846c709233467d03423e0390f798faac7fb4a6ccc9e76d58f5&scene=21#wechat_redirect" data-itemshowtype="11" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-size: 14px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">Redis与MySQL双写一致性如何保证?</a> </section> <section style="margin-top: 15px;margin-bottom: 15px;letter-spacing: 0.544px;color: rgb(62, 62, 62);caret-color: rgb(51, 51, 51);line-height: 1.5em;"> <img data-backh="287" data-backw="574" data-before-oversubscription-url="https://mmbiz.qpic.cn/mmbiz_png/UtWdDgynLda8TWB0rCPDRObrOHuCRKNtndsCNwbsMejpEvEwWTibrcy9xhLbFwbOCarFP5NrZTOZtzGFjeLh6yg/640?" data-copyright="0" data-ratio="0.5" data-s="300,640" src="/upload/e46fc654dfcccf189c5d81430fa2708f.jpg" data-type="jpeg" data-w="800" style="letter-spacing: 0.544px;font-size: inherit;font-weight: 700;text-indent: 0em;font-family: -apple-system, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;white-space: normal;display: inline;box-sizing: border-box !important;visibility: visible !important;width: auto !important;" width="auto"> </section></h4></pre></pre></pre></h4></pre>
作者:微信小助手
作者:微信小助手
<h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVL4uBtJu1Yn8C33yibEaasrN042tWiaMXibvaR4w2WabpKnscEOEcGicicpA/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">前言</span><br></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">最近 mq 越来越火,很多公司在用,很多人在用,其重要性不言而喻。但是如果我让你回答下面的这些问题:</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;line-height: 26px;color: rgb(1, 1, 1);"> 我们为什么要用 mq? </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 引入 mq 会有哪些问题? </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-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">你心中是否有答案了呢?本文将会一一为你解答,这些看似平常却很有意义的问题。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVL4uBtJu1Yn8C33yibEaasrN042tWiaMXibvaR4w2WabpKnscEOEcGicicpA/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">1 传统模式有哪些痛点?</span></h2> <h3 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;margin-top: 1.2em;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVq5SbKjZZ1a2icaBzXlNwdwtyb7GPx3Z5oTgGQGaPEicJ1ewBibGRd2IMg/640?wx_fmt=png");background-size: 100% 100%;background-repeat: no-repeat;display: inline-block;width: 16px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 17px;display: inline-block;margin-left: 8px;color: rgb(72, 179, 120);">1.1 痛点1</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">有些复杂的业务系统,一次用户请求可能会同步调用N个系统的接口,需要等待所有的接口都返回了,才能真正的获取执行结果。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><img data-ratio="0.16551724137931034" src="/upload/a8d11f8533d3562281283ae32e08b982.png" data-type="png" data-w="1450" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;">这种同步接口调用的方式<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">总耗时比较长</code>,非常影响用户的体验,特别是在网络不稳定的情况下,极容易出现接口超时问题。</p> <h3 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;margin-top: 1.2em;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVq5SbKjZZ1a2icaBzXlNwdwtyb7GPx3Z5oTgGQGaPEicJ1ewBibGRd2IMg/640?wx_fmt=png");background-size: 100% 100%;background-repeat: no-repeat;display: inline-block;width: 16px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 17px;display: inline-block;margin-left: 8px;color: rgb(72, 179, 120);">1.2 痛点2</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">很多复杂的业务系统,一般都会拆分成多个子系统。我们在这里以用户下单为例,请求会先通过订单系统,然后分别调用:支付系统、库存系统、积分系统 和 物流系统。<img data-ratio="0.7027777777777777" src="/upload/4a9d001a94cf25a4d7e4ce2bc0db4af9.png" data-type="png" data-w="720" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;">系统之间<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">耦合性太高</code>,如果调用的任何一个子系统出现异常,整个请求都会异常,对系统的稳定性非常不利。</p> <h3 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;margin-top: 1.2em;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVq5SbKjZZ1a2icaBzXlNwdwtyb7GPx3Z5oTgGQGaPEicJ1ewBibGRd2IMg/640?wx_fmt=png");background-size: 100% 100%;background-repeat: no-repeat;display: inline-block;width: 16px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 17px;display: inline-block;margin-left: 8px;color: rgb(72, 179, 120);">1.3 痛点3</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">有时候为了吸引用户,我们会搞一些活动,比如秒杀等。<img data-ratio="0.2603305785123967" src="/upload/c464f5ecaab20bc352047d8f581f227b.png" data-type="png" data-w="968" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;">如果用户少还好,不会影响系统的稳定性。但如果用户突增,一时间所有的请求都到数据库,可能会导致数据库无法承受这么大的压力,响应变慢或者直接挂掉。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><img data-ratio="0.5041152263374485" src="/upload/1e4d46b0b024c0f526acb5505b3de0c5.png" data-type="png" data-w="972" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;">对于这种突然出现的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">请求峰值</code>,无法保证系统的稳定性。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVL4uBtJu1Yn8C33yibEaasrN042tWiaMXibvaR4w2WabpKnscEOEcGicicpA/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">2 为什么要用mq?</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">对于上面传统模式的三类问题,我们使用mq就能轻松解决。</p> <h3 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;margin-top: 1.2em;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVq5SbKjZZ1a2icaBzXlNwdwtyb7GPx3Z5oTgGQGaPEicJ1ewBibGRd2IMg/640?wx_fmt=png");background-size: 100% 100%;background-repeat: no-repeat;display: inline-block;width: 16px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 17px;display: inline-block;margin-left: 8px;color: rgb(72, 179, 120);">2.1 异步</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">对于痛点1:同步接口调用导致响应时间长的问题,使用mq之后,将同步调用改成异步,能够显著减少系统响应时间。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><img data-ratio="0.365814696485623" src="/upload/c70c10a7f356a6ff1c1f882163386bff.png" data-type="png" data-w="1252" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;">系统A作为消息的生产者,在完成本职工作后,就能直接返回结果了。而无需等待消息消费者的返回,它们最终会独立完成所有的业务功能。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这样能避免<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">总耗时比较长</code>,从而影响用户的体验的问题。</p> <h3 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;margin-top: 1.2em;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVq5SbKjZZ1a2icaBzXlNwdwtyb7GPx3Z5oTgGQGaPEicJ1ewBibGRd2IMg/640?wx_fmt=png");background-size: 100% 100%;background-repeat: no-repeat;display: inline-block;width: 16px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 17px;display: inline-block;margin-left: 8px;color: rgb(72, 179, 120);">2.2 解耦</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">对于痛点2:子系统间耦合性太大的问题,使用mq之后,我们只需要依赖于mq,避免了各个子系统间的强依赖问题。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.489010989010989" src="/upload/e1c7eaa954243ccebc6737256d32c093.png" data-type="png" data-w="1092" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">订单系统作为消息生产者,保证它自己没有异常即可,不会受到支付系统等业务子系统的异常影响,并且各个消费者业务子系统之间,也互不影响。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这样就把之前复杂的业务子系统的依赖关系,转换为只依赖于mq的简单依赖,从而显著的降低了系统间的耦合度。</p> <h3 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;margin-top: 1.2em;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVq5SbKjZZ1a2icaBzXlNwdwtyb7GPx3Z5oTgGQGaPEicJ1ewBibGRd2IMg/640?wx_fmt=png");background-size: 100% 100%;background-repeat: no-repeat;display: inline-block;width: 16px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 17px;display: inline-block;margin-left: 8px;color: rgb(72, 179, 120);">2.3 消峰</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">对于痛点3:由于突然出现的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">请求峰值</code>,导致系统不稳定的问题。使用mq后,能够起到消峰的作用。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><img data-ratio="0.3611898016997167" src="/upload/6e4618f5c7389078481433dcd06d329f.png" data-type="png" data-w="1412" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;">订单系统接收到用户请求之后,将请求直接发送到mq,然后订单消费者从mq中消费消息,做写库操作。如果出现<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">请求峰值</code>的情况,由于消费者的消费能力有限,会按照自己的节奏来消费消息,多的请求不处理,保留在mq的队列中,不会对系统的稳定性造成影响。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVL4uBtJu1Yn8C33yibEaasrN042tWiaMXibvaR4w2WabpKnscEOEcGicicpA/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">3 引入mq会多哪些问题?</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">引入mq后让我们子系统间耦合性降低了,异步处理机制减少了系统的响应时间,同时能够有效的应对<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">请求峰值</code>问题,提升系统的稳定性。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">但是,引入mq同时也会带来一些问题。</p> <h3 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;margin-top: 1.2em;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVq5SbKjZZ1a2icaBzXlNwdwtyb7GPx3Z5oTgGQGaPEicJ1ewBibGRd2IMg/640?wx_fmt=png");background-size: 100% 100%;background-repeat: no-repeat;display: inline-block;width: 16px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 17px;display: inline-block;margin-left: 8px;color: rgb(72, 179, 120);">3.1 重复消息问题</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">重复消费问题可以说是mq中普遍存在的问题,不管你用哪种mq都无法避免。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">有哪些场景会出现重复的消息呢?</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;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);"> kafka和rocketmq的offset被回调了 </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> <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-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><img data-ratio="0.18217821782178217" src="/upload/3424b338627d755beb91d0c0434bb5a4.png" data-type="png" data-w="1010" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;">如果重复消息不做正确的处理,会对业务造成很大的影响,产生重复的数据,或者导致数据异常,比如会员系统多开通了一个月的会员。</p> <h3 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;margin-top: 1.2em;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVq5SbKjZZ1a2icaBzXlNwdwtyb7GPx3Z5oTgGQGaPEicJ1ewBibGRd2IMg/640?wx_fmt=png");background-size: 100% 100%;background-repeat: no-repeat;display: inline-block;width: 16px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 17px;display: inline-block;margin-left: 8px;color: rgb(72, 179, 120);">3.2 数据一致性问题</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">很多时候,如果mq的消费者业务处理异常的话,就会出现数据一致性问题。比如:一个完整的业务流程是,下单成功之后,送100个积分。下单写库了,但是消息消费者在送积分的时候失败了,就会造成<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">数据不一致</code>的情况,即该业务流程的部分数据写库了,另外一部分没有写库。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><img data-ratio="0.1693548387096774" src="/upload/218f4ef9bcf5278df2914c28bd14e897.png" data-type="png" data-w="992" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;">如果下单和送积分在同一个事务中,要么同时成功,要么同时失败,是不会出现数据一致性问题的。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">但由于跨系统调用,为了性能考虑,一般不会使用强一致性的方案,而改成达成最终一致性即可。</p> <h3 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;margin-top: 1.2em;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVq5SbKjZZ1a2icaBzXlNwdwtyb7GPx3Z5oTgGQGaPEicJ1ewBibGRd2IMg/640?wx_fmt=png");background-size: 100% 100%;background-repeat: no-repeat;display: inline-block;width: 16px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 17px;display: inline-block;margin-left: 8px;color: rgb(72, 179, 120);">3.3 消息丢失问题</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">同样消息丢失问题,也是mq中普遍存在的问题,不管你用哪种mq都无法避免。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">有哪些场景会出现消息丢失问题呢?</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;line-height: 26px;color: rgb(1, 1, 1);"> 消息生产者发生消息时,由于网络原因,发生到mq失败了。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> mq服务器持久化时,磁盘出现异常 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> kafka和rocketmq的offset被回调时,略过了很多消息。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 消息消费者刚读取消息,已经ack确认了,但业务还没处理完,服务就被重启了。 </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">导致消息丢失问题的原因挺多的,<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">生产者</code>、<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">mq服务器</code>、<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">消费者</code> 都有可能产生问题,我在这里就不一一列举了。最终的结果会导致消费者无法正确的处理消息,而导致数据不一致的情况。</p> <h3 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;margin-top: 1.2em;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVq5SbKjZZ1a2icaBzXlNwdwtyb7GPx3Z5oTgGQGaPEicJ1ewBibGRd2IMg/640?wx_fmt=png");background-size: 100% 100%;background-repeat: no-repeat;display: inline-block;width: 16px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 17px;display: inline-block;margin-left: 8px;color: rgb(72, 179, 120);">3.4 消息顺序问题</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">有些业务数据是有状态的,比如订单有:下单、支付、完成、退货等状态,如果订单数据作为消息体,就会涉及顺序问题了。如果消费者收到同一个订单的两条消息,第一条消息的状态是下单,第二条消息的状态是支付,这是没问题的。但如果第一条消息的状态是支付,第二条消息的状态是下单就会有问题了,没有下单就先支付了?<img data-ratio="0.30303030303030304" src="/upload/a50a5dc53de32884cc7e332bbefffbd0.png" data-type="png" data-w="726" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;">消息顺序问题是一个非常棘手的问题,比如:</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;line-height: 26px;color: rgb(1, 1, 1);"> <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">kafka</code>同一个 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">partition</code>中能保证顺序,但是不同的 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">partition</code>无法保证顺序。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">rabbitmq</code>的同一个 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">queue</code>能够保证顺序,但是如果多个消费者同一个 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">queue</code>也会有顺序问题。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果消费者使用多线程消费消息,也无法保证顺序。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果消费消息时同一个订单的多条消息中,中间的一条消息出现异常情况,顺序将会被打乱。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">还有如果生产者发送到mq中的路由规则,跟消费者不一样,也无法保证顺序。</p> <h3 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;margin-top: 1.2em;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVq5SbKjZZ1a2icaBzXlNwdwtyb7GPx3Z5oTgGQGaPEicJ1ewBibGRd2IMg/640?wx_fmt=png");background-size: 100% 100%;background-repeat: no-repeat;display: inline-block;width: 16px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 17px;display: inline-block;margin-left: 8px;color: rgb(72, 179, 120);">3.5 消息堆积</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果消息消费者读取消息的速度,能够跟上消息生产者的节奏,那么整套mq机制就能发挥最大作用。但是很多时候,由于某些批处理,或者其他原因,导致消息消费的速度小于生产的速度。这样会直接导致消息堆积问题,从而影响业务功能。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><img data-ratio="0.23076923076923078" src="/upload/dc1e2b215276ceff69260bbf2858504a.png" data-type="png" data-w="1014" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;">这里以下单开通会员为例,如果消息出现堆积,会导致用户下单之后,很久之后才能变成会员,这种情况肯定会引起大量用户投诉。</p> <h3 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;margin-top: 1.2em;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVq5SbKjZZ1a2icaBzXlNwdwtyb7GPx3Z5oTgGQGaPEicJ1ewBibGRd2IMg/640?wx_fmt=png");background-size: 100% 100%;background-repeat: no-repeat;display: inline-block;width: 16px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 17px;display: inline-block;margin-left: 8px;color: rgb(72, 179, 120);">3.6 系统复杂度提升</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这里说的系统复杂度和系统耦合性是不一样的,比如以前只有:系统A、系统B和系统C 这三个系统,现在引入mq之后,你除了需要关注前面三个系统之外,还需要关注mq服务,需要关注的点越多,系统的复杂度越高。<img data-ratio="0.5303030303030303" src="/upload/f867f4479e85ccfe6cbedd1f7b7cf2f8.png" data-type="png" data-w="1056" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;">mq的机制需要:生产者、mq服务器、消费者。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">有一定的学习成本,需要额外部署mq服务器,而且有些mq比如:rocketmq,功能非常强大,用法有点复杂,如果使用不好,会出现很多问题。有些问题,不像接口调用那么容易排查,从而导致系统的复杂度提升了。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVL4uBtJu1Yn8C33yibEaasrN042tWiaMXibvaR4w2WabpKnscEOEcGicicpA/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">4 如何解决这些问题?</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">mq是一种趋势,总体来说对我们的系统是利大于弊的,难道因为它会出现一些问题,我们就不用它了?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">那么我们要如何解决这些问题呢?</p> <h3 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;margin-top: 1.2em;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVq5SbKjZZ1a2icaBzXlNwdwtyb7GPx3Z5oTgGQGaPEicJ1ewBibGRd2IMg/640?wx_fmt=png");background-size: 100% 100%;background-repeat: no-repeat;display: inline-block;width: 16px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 17px;display: inline-block;margin-left: 8px;color: rgb(72, 179, 120);">4.1 重复消息问题</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">不管是由于生产者产生的重复消息,还是由于消费者导致的重复消息,我们都可以在消费者中这个问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这就要求消费者在做业务处理时,要做幂等设计,如果有不知道如何设计的朋友,可以参考《<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzUxODkzNTQ3Nw==&mid=2247486496&idx=1&sn=063f248ea9ae0b594194bcfc9c7449d8&chksm=f9800afacef783ec3034bbf1b065be01d92225c5442039388cf7a1dad16d0120f93034075b69&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2">高并发下如何保证接口的幂等性?</a>》,里面介绍得非常详情。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">在这里我推荐增加一张消费消息表,来解决mq的这类问题。消费消息表中,使用<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">messageId</code>做<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">唯一索引</code>,在处理业务逻辑之前,先根据messageId查询一下该消息有没有处理过,如果已经处理过了则直接返回成功,如果没有处理过,则继续做业务处理。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.514388489208633" src="/upload/30525f96168c7c15a54b6ba82f1f46ce.png" data-type="png" data-w="556" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <h3 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;margin-top: 1.2em;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVq5SbKjZZ1a2icaBzXlNwdwtyb7GPx3Z5oTgGQGaPEicJ1ewBibGRd2IMg/640?wx_fmt=png");background-size: 100% 100%;background-repeat: no-repeat;display: inline-block;width: 16px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 17px;display: inline-block;margin-left: 8px;color: rgb(72, 179, 120);">4.2 数据一致性问题</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们都知道数据一致性分为:</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;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> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 最终一致性 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">而mq为了性能考虑使用的是<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">最终一致性</code>,那么必定会出现数据不一致的问题。这类问题大概率是因为消费者读取消息后,业务逻辑处理失败导致的,这时候可以增加<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">重试机制</code>。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">重试分为:<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">同步重试</code> 和 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">异步重试</code>。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">有些消息量比较小的业务场景,可以采用同步重试,在消费消息时如果处理失败,立刻重试3-5次,如何还是失败,则写入到<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">记录表</code>中。但如果消息量比较大,则不建议使用这种方式,因为如果出现网络异常,可能会导致大量的消息不断重试,影响消息读取速度,造成<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">消息堆积</code>。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.6778947368421052" src="/upload/acb8c348a8f061b518355102d2e1dbd5.png" data-type="png" data-w="950" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">而消息量比较大的业务场景,建议采用异步重试,在消费者处理失败之后,立刻写入<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">重试表</code>,有个<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">job</code>专门定时重试。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">还有一种做法是,如果消费失败,自己给同一个topic发一条消息,在后面的某个时间点,自己又会消费到那条消息,起到了重试的效果。如果对消息顺序要求不高的场景,可以使用这种方式。</p> <h3 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;margin-top: 1.2em;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVq5SbKjZZ1a2icaBzXlNwdwtyb7GPx3Z5oTgGQGaPEicJ1ewBibGRd2IMg/640?wx_fmt=png");background-size: 100% 100%;background-repeat: no-repeat;display: inline-block;width: 16px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 17px;display: inline-block;margin-left: 8px;color: rgb(72, 179, 120);">4.3 消息丢失问题</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">不管你是否承认有时候消息真的会丢,即使这种概率非常小,也会对业务有影响。生产者、mq服务器、消费者都有可能会导致消息丢失的问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">为了解决这个问题,我们可以增加一张<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">消息发送表</code>,当生产者发完消息之后,会往该表中写入一条数据,状态status标记为待确认。如果消费者读取消息之后,调用生产者的api更新该消息的status为已确认。有个job,每隔一段时间检查一次消息发送表,如果5分钟(这个时间可以根据实际情况来定)后还有状态是待确认的消息,则认为该消息已经丢失了,重新发条消息。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><img data-ratio="0.6858475894245724" src="/upload/db5cc16ed5c960143593da5ebca36bd3.png" data-type="png" data-w="1286" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;">这样不管是由于生产者、mq服务器、还是消费者导致的消息丢失问题,job都会重新发消息。</p> <h3 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;margin-top: 1.2em;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVq5SbKjZZ1a2icaBzXlNwdwtyb7GPx3Z5oTgGQGaPEicJ1ewBibGRd2IMg/640?wx_fmt=png");background-size: 100% 100%;background-repeat: no-repeat;display: inline-block;width: 16px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 17px;display: inline-block;margin-left: 8px;color: rgb(72, 179, 120);">4.4 消息顺序问题</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">消息顺序问题是我们非常常见的问题,我们以<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">kafka</code>消费订单消息为例。订单有:下单、支付、完成、退货等状态,这些状态是有先后顺序的,如果顺序错了会导致业务异常。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">解决这类问题之前,我们先确认一下,消费者是否真的需要知道中间状态,只知道最终状态行不行?</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.6059479553903345" src="/upload/95f3d8f47b8e1e2332dcc5611052417a.png" data-type="png" data-w="538" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">其实很多时候,我真的需要知道的是最终状态,这时可以把流程优化一下:</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><img data-ratio="0.7949640287769785" src="/upload/ecce06b3f381217eef85e0c511bf990b.png" data-type="png" data-w="556" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;">这种方式可以解决大部分的消息顺序问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">但如果真的有需要保证消息顺序的需求。订单号路由到不同的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">partition</code>,同一个订单号的消息,每次到发到同一个<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">partition</code>。<img data-ratio="0.38596491228070173" src="/upload/f1eebfe700cdbc35fd27004f75d484a6.png" data-type="png" data-w="1368" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"></p> <h3 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;margin-top: 1.2em;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/uL371281oDEEegWyONc7CZpoOuRp6GbVq5SbKjZZ1a2icaBzXlNwdwtyb7GPx3Z5oTgGQGaPEicJ1ewBibGRd2IMg/640?wx_fmt=png");background-size: 100% 100%;background-repeat: no-repeat;display: inline-block;width: 16px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 17px;display: inline-block;margin-left: 8px;color: rgb(72, 179, 120);">4.5 消息堆积</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果消费者消费消息的速度小于生产者生产消息的速度,将会出现消息堆积问题。其实这类问题产生的原因很多,如果你想进一步了解,可以看看我的另一篇文章《<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzUxODkzNTQ3Nw==&mid=2247486202&idx=1&sn=23f249d3796eb53aff9cf41de6a41761&chksm=f9800c20cef785361afc55298d26e8dc799751a472be48eae6c02b508b7cb8c62ba3ac4eb99b&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2">我用kafka两年踩过的一些非比寻常的坑</a>》。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">那么消息堆积问题该如何解决呢?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这个要看消息是否需要保证顺序。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果不需要保证顺序,可以读取消息之后用多线程处理业务逻辑。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.4405940594059405" src="/upload/76a271c6334893dbb3139ccc88e3e3bc.png" data-type="png" data-w="404" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这样就能增加业务逻辑处理速度,解决消息堆积问题。但是线程池的核心线程数和最大线程数需要合理配置,不然可能会浪费系统资源。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如果需要保证顺序,可以读取消息之后,将消息按照一定的规则分发到多个队列中,然后在队列中用单线程处理。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.38341158059467917" src="/upload/69d39a8482143807bd9bdb396bb6e549.png" data-type="png" data-w="1278" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> 好了,今天先分享到这来,下期再见。我在这里只是抛砖引玉,其实mq相关的内容还有很多,比如:定时发送、延迟发送、私信队列、事务问题等等,有兴趣的朋友可以找我私聊。 <br> </figure> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="margin-top: -10px;padding-right: 10px;padding-left: 10px;max-width: 100%;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;line-height: 1.6;letter-spacing: 0.034em;color: rgb(63, 63, 63);font-size: 16px;box-sizing: border-box !important;"> <h3 data-tool="mdnice编辑器" style="margin-top: 50px;margin-bottom: 20px;font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;font-weight: bold;color: black;max-width: 100%;white-space: normal;letter-spacing: 2px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="padding-bottom: 2px;max-width: 100%;border-bottom: 2px solid rgba(79, 177, 249, 0.65);color: rgb(89, 89, 89);box-sizing: border-box !important;overflow-wrap: break-word !important;">最后说一句(求关注,别白嫖我)</span></h3> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;max-width: 100%;min-height: 1em;white-space: normal;color: rgb(89, 89, 89);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;background-color: rgb(255, 255, 255);line-height: 26px;font-size: 14px;word-spacing: 2px;box-sizing: border-box !important;overflow-wrap: break-word !important;">如果这篇文章对您有所帮助,或者有所启发的话,帮忙扫描下发二维码关注一下,您的支持是我坚持写作最大的动力。</p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;max-width: 100%;min-height: 1em;white-space: normal;color: rgb(89, 89, 89);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;background-color: rgb(255, 255, 255);line-height: 26px;font-size: 14px;word-spacing: 2px;box-sizing: border-box !important;overflow-wrap: break-word !important;">求一键三连:点赞、转发、在看。</p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;max-width: 100%;min-height: 1em;white-space: normal;color: rgb(89, 89, 89);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;background-color: rgb(255, 255, 255);line-height: 26px;font-size: 14px;word-spacing: 2px;box-sizing: border-box !important;overflow-wrap: break-word !important;">关注公众号:【苏三说技术】,在公众号中回复:面试、代码神器、开发手册、时间管理有超赞的粉丝福利,另外回复:加群,可以跟很多BAT大厂的前辈交流和学习。</p> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzUxODkzNTQ3Nw==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/uL371281oDFEpxenPicr7Kh8c9P0I97yVWw9JfljjPy5gVBLDiaIrWkzAGHGib09Kib4TQtyw8jpLicW3yoyibMY8yMA/0?wx_fmt=png" data-nickname="苏三说技术" data-alias="susanSayJava" data-signature="「苏三说技术」 维护者目前就职于某知名互联网公司,从事开发、架构和部分管理工作。实战经验丰富,对jdk、spring、springboot、springcloud、mybatis等开源框架源码有一定研究,欢迎关注,和我一起交流。" data-from="0"></mpprofile> </section> </section> <p style="max-width: 100%;min-height: 1em;text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p>
作者:微信小助手
<blockquote data-tool="mdnice编辑器"> 掌握一套架构方法论,掌握规范的设计方法,设计出更好、更稳定的架构设计。 <span style="float: right;color: RGBA(64, 184, 250, .5);"><br></span> </blockquote> <h1 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 25px;"><span style="color: rgb(64, 184, 250);display: none;"></span><span style="display: inline-block;color: rgb(64, 184, 250);">概念解析</span><span style="display: inline-block;color: rgb(64, 184, 250);"></span></h1> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">在文章开始之前需要先理解几个概念:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;font-size: 15px;color: #595959;list-style-type: circle;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 什么是方法论? <br>我们拿到一个输入,然后根据这个输入预期一个输出,把中间这个过程描述出来就是方法论。所以我们本篇讲的架构师方法论就是架构师先拿到经过需求分析出来的输入,然后完成架构设计,这个过程就是架构设计方法论。 </section> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> <br> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 什么是设计? <br>设计是实现意图的书面表现形式,而非口头的东西; <br>设计是要让实现者能理解设计者的意图,是给别人看而非自己看; <br>设计是要让不同的实现者做出来的东西差不多; <br>设计是严肃的,后续实现者不能随意偏离设计 </section> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> <br> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 什么是系统架构师 <br>作为系统架构师你需要跳出代码层面的设计,站在更加宏观的角度进行把握。 <br>你关注的是整个系统而不是其中的一两个查询模块,你看到的元素更多的是 :人 、硬件 、软件 、网络。 </section></li> </ul> <h1 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 25px;"><span style="color: rgb(64, 184, 250);display: none;"></span><span style="display: inline-block;color: rgb(64, 184, 250);">业务分析</span><span style="display: inline-block;color: rgb(64, 184, 250);"></span></h1> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 22px;border-bottom: 4px solid rgb(64, 184, 250);"><span style="display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDce6hAKTbaYbLnoroXoK3NnLBEuWbJhkuff3aMZwp2qbXXVjCkdhOPPQ/640?wx_fmt=png");margin-bottom: -22px;"></span><span style="display: flex;color: #40B8FA;font-size: 20px;margin-left: 25px;">业务分析概述</span><span style="display: flex;box-sizing: border-box;width: 200px;height: 10px;border-top-left-radius: 20px;background: RGBA(64, 184, 250, .5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;margin-top: -10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></h2> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;font-size: 15px;color: #595959;list-style-type: circle;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 业务分析是在系统开发之前对系统要解决的业务领域的研究过程,目的是搞清楚该业务领域的概念以及业务的运转过程 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 开发系统的目的一般是为了优化业务流程,使业务运转得更加高效、经济 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 系统的价值主要在于实施后能够帮助客户带来多少业务价值 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 不管有无系统,业务通常是不变的 </section></li> </ul> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 22px;border-bottom: 4px solid rgb(64, 184, 250);"><span style="display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDce6hAKTbaYbLnoroXoK3NnLBEuWbJhkuff3aMZwp2qbXXVjCkdhOPPQ/640?wx_fmt=png");margin-bottom: -22px;"></span><span style="display: flex;color: #40B8FA;font-size: 20px;margin-left: 25px;">业务分析阶段活动模型</span><span style="display: flex;box-sizing: border-box;width: 200px;height: 10px;border-top-left-radius: 20px;background: RGBA(64, 184, 250, .5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;margin-top: -10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></h2> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.415061295971979" src="/upload/48dfd160f307ac808613ee66b09ca9e0.png" data-type="png" data-w="1142" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;"><br>业务分析阶段是由业务分析师 基于自身的业务知识和类似产品的参考,再结合客户、领域专家的咨询和指导输出业务分析阶段的成果,主要包括 <strong style="color: rgb(53, 148, 247);">领域模型</strong> 和 <strong style="color: rgb(53, 148, 247);">业务模型</strong></p> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 17px;font-weight: bold;text-align: center;margin-top: 20px;margin-bottom: 20px;"><span style="display: none;"></span><span style="border-bottom: 2px solid RGBA(79, 177, 249, .65);color: #2b2b2b;padding-bottom: 2px;"><span style="width: 30px;height: 30px;display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDc9UibFpTYDRBNFbwQyKkaeaFIC97TY1dcic9JBekBxgyY5YIheuM8VZUw/640?wx_fmt=png");background-position: center center;background-size: 30px;margin: auto auto -8px;opacity: 1;background-repeat: no-repeat;"></span>领域模型</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">领域模型是对领域内的概念类或现实世界中对象的可视化表示。又称概念模型、领域对象模型、分析对象模型。它专注于分析问题领域本 身,发掘重要的业务领域概念,并建立业务领域概念之间的关系。概念比较深奥,其实说白了就是我们把基于对业务的理解画成一个类图,并画出这些类之间的关系(面向对象),下面我们看一个实际的领域模型然后加深一下理解。<img data-ratio="0.6428571428571429" src="/upload/84385403de5ddb72a5058de8028b1409.png" data-type="png" data-w="1316" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">在领域模型绘制时经常会出现下面两种典型错误:</p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;"><strong style="color: rgb(53, 148, 247);">将待开发系统也放在领域模型里面</strong><br>待开发系统要不要出现在领域模型中取决于你的业务离开待开发的系统能不能玩的转。举个例子:如果开发的是共享单车的信息系统,共享单车离开信息系统肯定玩不转,所以这时候信息系统需要出现在领域模型。</p></li> <li><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;"><strong style="color: rgb(53, 148, 247);">概念划分不清,关系没有画到位</strong><br>比如属性画成了类,继承关系搞错</p></li> </ul> <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="height: 16px;line-height: 16px;font-size: 16px;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDcw1XXjrZBp7cFyP0mRefyrJibFqWIhUuRyylyjwqyH12mo9aLbxRQ3uw/640?wx_fmt=png");display: inline-block;background-size: 100%;background-position: left bottom;background-repeat: no-repeat;width: 16px;height: 15px;line-height: 15px;margin-right: 6px;margin-bottom: -2px;"></span>领域模型的作用</span><span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">领域模型可以整理业务中的概念以及关系,帮助团队中的成员对业务的理解保持一致;往后可以指导数据库设计、系统功能设计、指导开发。<img data-ratio="0.7164750957854407" src="/upload/e25911c1d98e94feb2c01664b86a3d79.png" data-type="png" data-w="1044" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">现在有种开发模式是基于UI指导开发,根据UI进行数据库数据库设计、代码编写,我们称之为 “<strong style="color: rgb(53, 148, 247);">急功近利式</strong>” 开发模式。因为UI是系统表面性的东西,是异变的,不稳定的,这种模式下UI变化后我们的的设计可能也需要跟着变。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">而右边是基于领域驱动开发,在开发前先去思考业务的本质,先把领域层分析出来,再根据分析出来的领域层进行界面设计、架构设计、代码开发,这是由内而外的设计,这样做出来的系统就会比较稳定。</p> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 17px;font-weight: bold;text-align: center;margin-top: 20px;margin-bottom: 20px;"><span style="display: none;"></span><span style="border-bottom: 2px solid RGBA(79, 177, 249, .65);color: #2b2b2b;padding-bottom: 2px;"><span style="width: 30px;height: 30px;display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDc9UibFpTYDRBNFbwQyKkaeaFIC97TY1dcic9JBekBxgyY5YIheuM8VZUw/640?wx_fmt=png");background-position: center center;background-size: 30px;margin: auto auto -8px;opacity: 1;background-repeat: no-repeat;"></span>业务模型</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">前面讲的领域模型是基于静态的关系,要理解业务其实更多的需要从动态的角度来了解业务运转的过程,所以这时候就需要做业务模型。理解业务模型需要先理解以下几个概念:</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="height: 16px;line-height: 16px;font-size: 16px;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDcw1XXjrZBp7cFyP0mRefyrJibFqWIhUuRyylyjwqyH12mo9aLbxRQ3uw/640?wx_fmt=png");display: inline-block;background-size: 100%;background-position: left bottom;background-repeat: no-repeat;width: 16px;height: 15px;line-height: 15px;margin-right: 6px;margin-bottom: -2px;"></span>业务对象</span><span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">业务主体主要有 业务执行者、业务工人、业务实体。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">要理解这三个对象,我们首先需要知道什么是业务,<strong style="color: rgb(53, 148, 247);">业务是指一个组织可以向组织外的人提供服务。<br></strong><strong style="color: rgb(53, 148, 247);">业务执行者(Business Actor)</strong> :<strong style="color: rgb(53, 148, 247);">组织外</strong>的人,来享受这个服务的人就称为业务执行者;<br><strong style="color: rgb(53, 148, 247);">业务工人(Business worker)</strong> :提供业务的 <strong style="color: rgb(53, 148, 247);">组织的内部支撑人员<br></strong><strong style="color: rgb(53, 148, 247);">业务实体(Business Entity)</strong> :提供业务的 <strong style="color: rgb(53, 148, 247);">组织的内部信息系统</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">理解这几个概念还是有点拗口,我们来看看下面一张图,一个餐厅对象的业务建模<img data-ratio="0.7438692098092643" src="/upload/c71b12d864d9d0c37a975dc470d73722.png" data-type="png" data-w="1101" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">右边大的黑框是提供业务的组织,是餐厅;图的左边是个业务执行者,是顾客;而在餐厅内部又分为业务工人(领位员、点餐员、厨师等),业务实体(餐厅点餐管理系统)</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;"><strong style="color: rgb(53, 148, 247);">我们在做业务建模时需要注意,所有业务对象在圆圈的右下方需要有个斜线,这是一个建模规范。</strong></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="height: 16px;line-height: 16px;font-size: 16px;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDcw1XXjrZBp7cFyP0mRefyrJibFqWIhUuRyylyjwqyH12mo9aLbxRQ3uw/640?wx_fmt=png");display: inline-block;background-size: 100%;background-position: left bottom;background-repeat: no-repeat;width: 16px;height: 15px;line-height: 15px;margin-right: 6px;margin-bottom: -2px;"></span>业务用例</span><span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">概念:组织对外提供的业务服务<img data-ratio="1.1146025878003696" src="/upload/27fbe1ff3a13ba064e77b789060e2a94.png" data-type="png" data-w="541" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">一个银行是一个提供业务的组织,这就是业务用例,考察的对象是银行这个组织而不是系统。</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="height: 16px;line-height: 16px;font-size: 16px;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDcw1XXjrZBp7cFyP0mRefyrJibFqWIhUuRyylyjwqyH12mo9aLbxRQ3uw/640?wx_fmt=png");display: inline-block;background-size: 100%;background-position: left bottom;background-repeat: no-repeat;width: 16px;height: 15px;line-height: 15px;margin-right: 6px;margin-bottom: -2px;"></span>业务流程</span><span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">业务用例是最基本的定义,而要分析业务动态的过程就需要业务流程,我们一般用时序图来表示。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">餐厅现状的业务流程<img data-ratio="0.6298482293423272" src="/upload/3a298f231b10a8d77489bc4cb749bc53.png" data-type="png" data-w="1186" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;">这时候所有的动作步骤全部靠人参与</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">建设系统后的业务流程<img data-ratio="0.529839883551674" src="/upload/878804de0d8fd476592aae3568ea0bc7.png" data-type="png" data-w="1374" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">有了系统后系统可以承担一部分工作,有了系统能改善业务流程、提高价值、降低成本,这就是 <strong style="color: rgb(53, 148, 247);">建设系统的价值以及意义</strong> ,否则就没必要做系统建设了。</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="height: 16px;line-height: 16px;font-size: 16px;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDcw1XXjrZBp7cFyP0mRefyrJibFqWIhUuRyylyjwqyH12mo9aLbxRQ3uw/640?wx_fmt=png");display: inline-block;background-size: 100%;background-position: left bottom;background-repeat: no-repeat;width: 16px;height: 15px;line-height: 15px;margin-right: 6px;margin-bottom: -2px;"></span>业务流程分析的作用</span><span style="display: none;"></span></h4> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;font-size: 15px;color: #595959;list-style-type: circle;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 动态表达业务运转的过程 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 只有很好的理解了业务流程,才能设计出更好的支持该业务的系统 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 通过对比系统实施前后的流程变化,分析优化点,评估系统价值 </section></li> </ul> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 22px;border-bottom: 4px solid rgb(64, 184, 250);"><span style="display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDce6hAKTbaYbLnoroXoK3NnLBEuWbJhkuff3aMZwp2qbXXVjCkdhOPPQ/640?wx_fmt=png");margin-bottom: -22px;"></span><span style="display: flex;color: #40B8FA;font-size: 20px;margin-left: 25px;">小结</span><span style="display: flex;box-sizing: border-box;width: 200px;height: 10px;border-top-left-radius: 20px;background: RGBA(64, 184, 250, .5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;margin-top: -10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">在准备做一个系统之前需要先分析业务,将业务理解清楚。理解业务既有静态的理解(领域模型)又有动态的理解(业务流程),只有将业务理解清楚才能做出良好的系统。</p> <h1 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 25px;"><span style="color: rgb(64, 184, 250);display: none;"></span><span style="display: inline-block;color: rgb(64, 184, 250);">需求分析</span><span style="display: inline-block;color: rgb(64, 184, 250);"></span></h1> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">需求分析是需求工程的环节,整个需求工程分为两大块<img data-ratio="0.5290598290598291" src="/upload/8a0c89960f792e24a65c44d995fe65c0.png" data-type="png" data-w="1170" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">前期主要是做需求开发,包括需求调研、需求分析、需求定义;后期需要做需求管理,包括需求确认、需求跟踪、需求变更控制。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">咱们架构师主要聚焦在 <strong style="color: rgb(53, 148, 247);">需求分析</strong> 和 <strong style="color: rgb(53, 148, 247);">需求定义</strong> 两个环节。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 22px;border-bottom: 4px solid rgb(64, 184, 250);"><span style="display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDce6hAKTbaYbLnoroXoK3NnLBEuWbJhkuff3aMZwp2qbXXVjCkdhOPPQ/640?wx_fmt=png");margin-bottom: -22px;"></span><span style="display: flex;color: #40B8FA;font-size: 20px;margin-left: 25px;">需求分析阶段活动模型</span><span style="display: flex;box-sizing: border-box;width: 200px;height: 10px;border-top-left-radius: 20px;background: RGBA(64, 184, 250, .5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;margin-top: -10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></h2> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.2840646651270208" src="/upload/84f83bf8779db7f7376fb7fbdd32d7ba.png" data-type="png" data-w="1299" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;"><br>需求分析阶段是由系统分析师 基于业务分析师输出的领域模型、业务模型 再结合 需求调研成果 输出需求分析阶段的成果,主要包括 <strong style="color: rgb(53, 148, 247);">系统上下文</strong>, <strong style="color: rgb(53, 148, 247);">功能型需求(用例模型)</strong> 和 <strong style="color: rgb(53, 148, 247);">非功能性需求(性能等)</strong>。</p> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 17px;font-weight: bold;text-align: center;margin-top: 20px;margin-bottom: 20px;"><span style="display: none;"></span><span style="border-bottom: 2px solid RGBA(79, 177, 249, .65);color: #2b2b2b;padding-bottom: 2px;"><span style="width: 30px;height: 30px;display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDc9UibFpTYDRBNFbwQyKkaeaFIC97TY1dcic9JBekBxgyY5YIheuM8VZUw/640?wx_fmt=png");background-position: center center;background-size: 30px;margin: auto auto -8px;opacity: 1;background-repeat: no-repeat;"></span>系统上下文</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">系统上下文是指系统与周边用户和其它系统之间的关系<img data-ratio="0.7773109243697479" src="/upload/3cc3ca0600b44248fe05d33f160ea0ec.png" data-type="png" data-w="952" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">系统上下文的绘制很简单,就是将准备开发的系统画在中间,把用户和对接的周边系统画出来这就叫系统上下文。</p> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 17px;font-weight: bold;text-align: center;margin-top: 20px;margin-bottom: 20px;"><span style="display: none;"></span><span style="border-bottom: 2px solid RGBA(79, 177, 249, .65);color: #2b2b2b;padding-bottom: 2px;"><span style="width: 30px;height: 30px;display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDc9UibFpTYDRBNFbwQyKkaeaFIC97TY1dcic9JBekBxgyY5YIheuM8VZUw/640?wx_fmt=png");background-position: center center;background-size: 30px;margin: auto auto -8px;opacity: 1;background-repeat: no-repeat;"></span>系统用例</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">系统用例是指系统被使用的案例,主要是从业务流程中推导出来,系统用例的命名规范主要是使用动词短语,如:添加用户、查询话费等短语。<img data-ratio="0.7659327925840093" src="/upload/ecf4d48a9bf276e39b84f63db9cd4d25.png" data-type="png" data-w="863" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">我们可以对系统用例细化,如上对登记入座信息这一用例进行细化<img data-ratio="0.6211340206185567" src="/upload/f66375d59fb325faa5285961c2fcec8d.png" data-type="png" data-w="1164" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"></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="height: 16px;line-height: 16px;font-size: 16px;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDcw1XXjrZBp7cFyP0mRefyrJibFqWIhUuRyylyjwqyH12mo9aLbxRQ3uw/640?wx_fmt=png");display: inline-block;background-size: 100%;background-position: left bottom;background-repeat: no-repeat;width: 16px;height: 15px;line-height: 15px;margin-right: 6px;margin-bottom: -2px;"></span>系统用例与业务用例的区别与联系</span><span style="display: none;"></span></h4> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;font-size: 15px;color: #595959;list-style-type: circle;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 业务用例是描述组织对外提供的能力,系统用例是描述系统对外提供的能力,两者考察的对象不一样 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 系统用例是业务用例相应流程中对系统的一个操作 </section></li> </ul> <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="height: 16px;line-height: 16px;font-size: 16px;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDcw1XXjrZBp7cFyP0mRefyrJibFqWIhUuRyylyjwqyH12mo9aLbxRQ3uw/640?wx_fmt=png");display: inline-block;background-size: 100%;background-position: left bottom;background-repeat: no-repeat;width: 16px;height: 15px;line-height: 15px;margin-right: 6px;margin-bottom: -2px;"></span>功能与用例的区别和联系</span><span style="display: none;"></span></h4> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;font-size: 15px;color: #595959;list-style-type: circle;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 用例是需求分析的产物,描述的是某种用户使用系统完成什么业务 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 功能是设计阶段的产物,是根据系统用例和架构中的组件推导出来的 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 功能是静态的,用例是动态的 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 从语法角度来说,用例是动词短语,功能是名词短语 例:用例命名(查询空位),功能命名(空位查询) </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 常见的错误:描述需求时拿出一份功能清单 </section></li> </ul> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 17px;font-weight: bold;text-align: center;margin-top: 20px;margin-bottom: 20px;"><span style="display: none;"></span><span style="border-bottom: 2px solid RGBA(79, 177, 249, .65);color: #2b2b2b;padding-bottom: 2px;"><span style="width: 30px;height: 30px;display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDc9UibFpTYDRBNFbwQyKkaeaFIC97TY1dcic9JBekBxgyY5YIheuM8VZUw/640?wx_fmt=png");background-position: center center;background-size: 30px;margin: auto auto -8px;opacity: 1;background-repeat: no-repeat;"></span>非功能性需求</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">主要是确定一些非功能性需求,比如:<strong style="color: rgb(53, 148, 247);">可用性</strong>、 <strong style="color: rgb(53, 148, 247);">性能</strong>、 <strong style="color: rgb(53, 148, 247);">安全性</strong>、 经济性、可扩展性、 可伸缩性、可移植性等等 可用性、性能、安全性是需要重点关注的内容,我们后期专门拿出来讲。</p> <h1 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 25px;"><span style="color: rgb(64, 184, 250);display: none;"></span><span style="display: inline-block;color: rgb(64, 184, 250);">架构设计(重点)</span><span style="display: inline-block;color: rgb(64, 184, 250);"></span></h1> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">前面的业务分析与需求分析一般是由其他专人来做,那么这一块的内容则是架构师的工作,需要重点关注。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">在系统简单时我们需要从 <strong style="color: rgb(53, 148, 247);">功能角度</strong> 对系统进行分解和拆分,这个时候我们只要做下概要设计和详细设计就可以。<br><br>在复杂系统时我们需要从 <strong style="color: rgb(53, 148, 247);">组件角度</strong> 对系统进行分解和拆分,这个时候我们就需要做架构设计与概要设计。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 22px;border-bottom: 4px solid rgb(64, 184, 250);"><span style="display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDce6hAKTbaYbLnoroXoK3NnLBEuWbJhkuff3aMZwp2qbXXVjCkdhOPPQ/640?wx_fmt=png");margin-bottom: -22px;"></span><span style="display: flex;color: #40B8FA;font-size: 20px;margin-left: 25px;">组件、功能、模块</span><span style="display: flex;box-sizing: border-box;width: 200px;height: 10px;border-top-left-radius: 20px;background: RGBA(64, 184, 250, .5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;margin-top: -10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">组件是架构设计阶段考虑的单元(进程级别),功能、模块是概要设计、详细设计考虑的单元;一个组件可包含多个模块,涉及多个功能;一个功能的实现可能需要多个组件中的相应模块来协作完成。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.48119122257053293" src="/upload/609c7f5234792668ca92931fc107f75d.png" data-type="png" data-w="1276" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;"><br>我们用一张图来理解他们三者之间的关系 </p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">前后端分离的一个项目从进程角度划分出三个组件,分别是web前端、后端接口服务、后台服务, 为了实现用户查询这个功能必须要在相应组件里都需要有相应的模块</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">一个组件里可以有多个不同的模块,各个组件里的模块相互协作完成某一个功能</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 22px;border-bottom: 4px solid rgb(64, 184, 250);"><span style="display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDce6hAKTbaYbLnoroXoK3NnLBEuWbJhkuff3aMZwp2qbXXVjCkdhOPPQ/640?wx_fmt=png");margin-bottom: -22px;"></span><span style="display: flex;color: #40B8FA;font-size: 20px;margin-left: 25px;">架构</span><span style="display: flex;box-sizing: border-box;width: 200px;height: 10px;border-top-left-radius: 20px;background: RGBA(64, 184, 250, .5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;margin-top: -10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">如果用一句话来描述什么是架构,那应该可以这样定义:架构是系统的内部结构(组件以及它们之间的关系)还要包含系统的技术要素。做架构设计其实就是干这两件事。<img data-ratio="0.5539714867617108" src="/upload/6ab137ffccf520508bd6ba6ea6a60fc8.png" data-type="png" data-w="491" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">架构设计有两个目标:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;font-size: 15px;color: #595959;list-style-type: circle;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 满足功能性需求 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 满足非功能性需求 </section></li> </ul> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 22px;border-bottom: 4px solid rgb(64, 184, 250);"><span style="display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDce6hAKTbaYbLnoroXoK3NnLBEuWbJhkuff3aMZwp2qbXXVjCkdhOPPQ/640?wx_fmt=png");margin-bottom: -22px;"></span><span style="display: flex;color: #40B8FA;font-size: 20px;margin-left: 25px;">架构设计阶段活动模型</span><span style="display: flex;box-sizing: border-box;width: 200px;height: 10px;border-top-left-radius: 20px;background: RGBA(64, 184, 250, .5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;margin-top: -10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></h2> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.49244712990936557" src="/upload/55d5606fdfea9fc41e12ab8c7c4161a0.png" data-type="png" data-w="1324" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;"><br>架构设计阶段是由系统架构师 参照需求分析的产物(SRS),再通过对系统分析师、项目经理的咨询输出架构设计阶段的成果,主要包括 <strong style="color: rgb(53, 148, 247);">架构工作计划</strong> 、 <strong style="color: rgb(53, 148, 247);">逻辑架构</strong>、<strong style="color: rgb(53, 148, 247);">物理架构</strong>、<strong style="color: rgb(53, 148, 247);">开发组件一览表</strong>、<strong style="color: rgb(53, 148, 247);">部署组件一览表</strong>、<strong style="color: rgb(53, 148, 247);">技术选型一览表</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">那如何来衡量一个架构的设计好坏呢?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">在设计完成时我们可以通过设计资料的规范性以及设计思路、方案决策、技术选型的合理性来校验;<br>在系统实现后可以通过功能性和非功能性需求的满足程度来校验。</p> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 17px;font-weight: bold;text-align: center;margin-top: 20px;margin-bottom: 20px;"><span style="display: none;"></span><span style="border-bottom: 2px solid RGBA(79, 177, 249, .65);color: #2b2b2b;padding-bottom: 2px;"><span style="width: 30px;height: 30px;display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDc9UibFpTYDRBNFbwQyKkaeaFIC97TY1dcic9JBekBxgyY5YIheuM8VZUw/640?wx_fmt=png");background-position: center center;background-size: 30px;margin: auto auto -8px;opacity: 1;background-repeat: no-repeat;"></span>逻辑架构设计(非技术型)</span><span style="display: none;"></span></h3> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;font-size: 15px;color: #595959;list-style-type: circle;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 将系统从非技术角度分解成若干逻辑组件,并建立它们之间的关系,以满足系统需求。关系分静态和动态,其中静态关系用组件图表示,动态关系用序 列图表示。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 逻辑架构中,组件名称使用母语以便理解 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 逻辑架构不涉及技术元素,只是纯概念上的表述 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 逻辑架构的读者可以是非技术人员 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 逻辑架构设计完成后应和系统分析师、产品经理等人员一起确认,检查是否满足需求 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">我们看一个典型的逻辑架构<img data-ratio="0.9842857142857143" src="/upload/63fc5b340757dd3dfe6747339bf0557f.png" data-type="png" data-w="700" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"></p> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 17px;font-weight: bold;text-align: center;margin-top: 20px;margin-bottom: 20px;"><span style="display: none;"></span><span style="border-bottom: 2px solid RGBA(79, 177, 249, .65);color: #2b2b2b;padding-bottom: 2px;"><span style="width: 30px;height: 30px;display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDc9UibFpTYDRBNFbwQyKkaeaFIC97TY1dcic9JBekBxgyY5YIheuM8VZUw/640?wx_fmt=png");background-position: center center;background-size: 30px;margin: auto auto -8px;opacity: 1;background-repeat: no-repeat;"></span>物理架构设计(技术型)</span><span style="display: none;"></span></h3> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;font-size: 15px;color: #595959;list-style-type: circle;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 将逻辑架构中的组件转换为技术性的物理组件,名称使用英文,在实现时应遵循这些命名 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 物理组件粒度有大有小,可表现为子系统、进程、对象等多种形式 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 物理架构还需要解决非功能性需求 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 物理架构还要和后续设计和实现进行衔接 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 非技术人员可能难以理解 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">我们看一个典型的物理架构<img data-ratio="1.1628571428571428" src="/upload/fd015a5cab3574390a32fe40abdf47c7.png" data-type="png" data-w="700" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"></p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 22px;border-bottom: 4px solid rgb(64, 184, 250);"><span style="display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDce6hAKTbaYbLnoroXoK3NnLBEuWbJhkuff3aMZwp2qbXXVjCkdhOPPQ/640?wx_fmt=png");margin-bottom: -22px;"></span><span style="display: flex;color: #40B8FA;font-size: 20px;margin-left: 25px;">小结</span><span style="display: flex;box-sizing: border-box;width: 200px;height: 10px;border-top-left-radius: 20px;background: RGBA(64, 184, 250, .5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;margin-top: -10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">架构设计为系统的总体设计,决定了系统的组件划分、关键技术方案决策、技术选型 架构设计上接需求,下接进一步的设计和实现,是决定系统实现的质量、效率、成本的关键阶段</p> <h1 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 25px;"><span style="color: rgb(64, 184, 250);display: none;"></span><span style="display: inline-block;color: rgb(64, 184, 250);">概要设计与详细设计</span><span style="display: inline-block;color: rgb(64, 184, 250);"></span></h1> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 22px;border-bottom: 4px solid rgb(64, 184, 250);"><span style="display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDce6hAKTbaYbLnoroXoK3NnLBEuWbJhkuff3aMZwp2qbXXVjCkdhOPPQ/640?wx_fmt=png");margin-bottom: -22px;"></span><span style="display: flex;color: #40B8FA;font-size: 20px;margin-left: 25px;">概要设计</span><span style="display: flex;box-sizing: border-box;width: 200px;height: 10px;border-top-left-radius: 20px;background: RGBA(64, 184, 250, .5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;margin-top: -10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">概要设计阶段的主要内容是进行功能模块划分以及接口定义(接口名称、功能概要、参数、返回值)</p> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 17px;font-weight: bold;text-align: center;margin-top: 20px;margin-bottom: 20px;"><span style="display: none;"></span><span style="border-bottom: 2px solid RGBA(79, 177, 249, .65);color: #2b2b2b;padding-bottom: 2px;"><span style="width: 30px;height: 30px;display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDc9UibFpTYDRBNFbwQyKkaeaFIC97TY1dcic9JBekBxgyY5YIheuM8VZUw/640?wx_fmt=png");background-position: center center;background-size: 30px;margin: auto auto -8px;opacity: 1;background-repeat: no-repeat;"></span>概要设计阶段活动模型</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 data-ratio="0.4110985277463194" src="/upload/f0b748c0997663a4214275af39b099da.png" data-type="png" data-w="883" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">概要设计阶段是由开发组长 基于系统用例、开发组件一览表 再结合对架构师和系统分析师的咨询输出概要设计阶段成果,主要包括 <strong style="color: rgb(53, 148, 247);">功能一览表</strong> , <strong style="color: rgb(53, 148, 247);">接口说明书</strong>。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 22px;border-bottom: 4px solid rgb(64, 184, 250);"><span style="display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDce6hAKTbaYbLnoroXoK3NnLBEuWbJhkuff3aMZwp2qbXXVjCkdhOPPQ/640?wx_fmt=png");margin-bottom: -22px;"></span><span style="display: flex;color: #40B8FA;font-size: 20px;margin-left: 25px;">详细设计</span><span style="display: flex;box-sizing: border-box;width: 200px;height: 10px;border-top-left-radius: 20px;background: RGBA(64, 184, 250, .5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;margin-top: -10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">详细设计阶段的主要内容是描述内部模块实现、界面设计以及数据库设计</p> <h3 data-tool="mdnice编辑器" style="color: black;font-size: 17px;font-weight: bold;text-align: center;margin-top: 20px;margin-bottom: 20px;"><span style="display: none;"></span><span style="border-bottom: 2px solid RGBA(79, 177, 249, .65);color: #2b2b2b;padding-bottom: 2px;"><span style="width: 30px;height: 30px;display: block;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDc9UibFpTYDRBNFbwQyKkaeaFIC97TY1dcic9JBekBxgyY5YIheuM8VZUw/640?wx_fmt=png");background-position: center center;background-size: 30px;margin: auto auto -8px;opacity: 1;background-repeat: no-repeat;"></span>详细设计阶段活动模型</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 data-ratio="0.5979614949037373" src="/upload/2eece86b5b6668d26f6f94564b469bbd.png" data-type="png" data-w="883" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;"><br>详细设计阶段可能会根据工作内容进行分工,主要结合之前的产出输出详细设计阶段的成果,主要包括 <strong style="color: rgb(53, 148, 247);">界面设计</strong> , <strong style="color: rgb(53, 148, 247);">模块内部设计</strong> , <strong style="color: rgb(53, 148, 247);">数据库设计</strong>。</p> <h1 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 25px;"><span style="color: rgb(64, 184, 250);display: none;"></span><span style="display: inline-block;color: rgb(64, 184, 250);">后续工程阶段</span><span style="display: inline-block;color: rgb(64, 184, 250);"></span></h1> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 22px;border-bottom: 4px solid rgb(64, 184, 250);"><span style="display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDce6hAKTbaYbLnoroXoK3NnLBEuWbJhkuff3aMZwp2qbXXVjCkdhOPPQ/640?wx_fmt=png");margin-bottom: -22px;"></span><span style="display: flex;color: #40B8FA;font-size: 20px;margin-left: 25px;">开发阶段活动模型</span><span style="display: flex;box-sizing: border-box;width: 200px;height: 10px;border-top-left-radius: 20px;background: RGBA(64, 184, 250, .5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;margin-top: -10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></h2> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.6519274376417233" src="/upload/6cbbc13f63de4ce96a68683b9a1a8d34.png" data-type="png" data-w="882" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;"><br>开发阶段主要是由开发人员结合架构设计的产物以及详细设计的产物编写相应代码。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 22px;border-bottom: 4px solid rgb(64, 184, 250);"><span style="display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDce6hAKTbaYbLnoroXoK3NnLBEuWbJhkuff3aMZwp2qbXXVjCkdhOPPQ/640?wx_fmt=png");margin-bottom: -22px;"></span><span style="display: flex;color: #40B8FA;font-size: 20px;margin-left: 25px;">测试阶段活动模型</span><span style="display: flex;box-sizing: border-box;width: 200px;height: 10px;border-top-left-radius: 20px;background: RGBA(64, 184, 250, .5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;margin-top: -10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></h2> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.5761363636363637" src="/upload/c3739076d9c7367a8e2a53457738b48b.png" data-type="png" data-w="880" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;"><br>测试阶段主要是由测试人员结合架构设计的产物以及详细设计的产物进行功能测试,包括功能性需求以及非功能性需求,需要对外输出 <strong style="color: rgb(53, 148, 247);">测试计划</strong>,<strong style="color: rgb(53, 148, 247);">测试用例</strong> 以及 <strong style="color: rgb(53, 148, 247);">测试报告</strong>。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 22px;border-bottom: 4px solid rgb(64, 184, 250);"><span style="display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDce6hAKTbaYbLnoroXoK3NnLBEuWbJhkuff3aMZwp2qbXXVjCkdhOPPQ/640?wx_fmt=png");margin-bottom: -22px;"></span><span style="display: flex;color: #40B8FA;font-size: 20px;margin-left: 25px;">部署阶段活动模型</span><span style="display: flex;box-sizing: border-box;width: 200px;height: 10px;border-top-left-radius: 20px;background: RGBA(64, 184, 250, .5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;margin-top: -10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></h2> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.4557823129251701" src="/upload/e7dc04731661018fa35dde9469b9dd7a.png" data-type="png" data-w="882" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;"><br>部署阶段主要是由部署人员结合架构设计的产物以及跟开发人员的咨询进行组件部署,这一阶段需要输出<strong style="color: rgb(53, 148, 247);">部署计划</strong>、<strong style="color: rgb(53, 148, 247);">部署方案</strong>、<strong style="color: rgb(53, 148, 247);">部署手册</strong>、<strong style="color: rgb(53, 148, 247);">部署脚本</strong>、<strong style="color: rgb(53, 148, 247);">部署实施</strong> 等</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 22px;border-bottom: 4px solid rgb(64, 184, 250);"><span style="display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4iaF4Sq4uibNXyKOU18aCoyDce6hAKTbaYbLnoroXoK3NnLBEuWbJhkuff3aMZwp2qbXXVjCkdhOPPQ/640?wx_fmt=png");margin-bottom: -22px;"></span><span style="display: flex;color: #40B8FA;font-size: 20px;margin-left: 25px;">运维阶段活动模型</span><span style="display: flex;box-sizing: border-box;width: 200px;height: 10px;border-top-left-radius: 20px;background: RGBA(64, 184, 250, .5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;margin-top: -10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></h2> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.4343891402714932" src="/upload/d20305e4dbbb05b3af761a11e48ceb94.png" data-type="png" data-w="884" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;"><br>运维阶段主要是由运维人员结合架构设计的产物进行系统运维,需要输出<strong style="color: rgb(53, 148, 247);">运维计划</strong>、<strong style="color: rgb(53, 148, 247);">运维方案</strong>、<strong style="color: rgb(53, 148, 247);">运维手册</strong>、<strong style="color: rgb(53, 148, 247);">运维脚本</strong>、<strong style="color: rgb(53, 148, 247);">运维报告</strong> 等。</p>