作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.6;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;color: rgb(53, 53, 53);word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;" data-mpa-powered-by="yiban.io"> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">一、前言</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">上一篇我们讲解了客户端首次获取注册表时,需要从注册中心<span style="color: rgb(255, 0, 0);">全量拉取</span>注册表到本地存着。那后续如果有客户端注册、下线的话,注册表肯定就发生变化了,这个时候客户端就得更新本地注册表了,怎么更新呢?下面我会带着大家一起来看下客户端第二次(这里代表全量获取后的下一次)获取注册表的方式。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">题外话:之前写过一篇 Redis 主从同步的架构原理,里面也涉及到首次同步和第二次同步,其实原理也类似,但是 Redis 的主从同步原理要复杂些。强烈推荐配合着看一波:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;"><a href="http://mp.weixin.qq.com/s?__biz=MzAwMjI0ODk0NA==&mid=2451958462&idx=1&sn=5380b7967443f88ef180ccd9db11fcc2&chksm=8d1c1321ba6b9a374ceeb993775d57800c4f98c3b5d4a85c3d3bc5eab649a2b0ca96c3db6c12&scene=21#wechat_redirect" style="color: rgb(248, 57, 41);border-bottom: 1px solid rgb(248, 57, 41);" data-linktype="2">镜 | 5 个维度深度剖析「主从架构」原理</a></p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">二、增量获取引发的问题</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">上面我们说到,当第一次获取全量信息后,本地就有注册信息了。那如果 Server 的注册表有更新,比如有服务注册、下线,Client 必须要重新获取一次注册表信息才行。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(255, 177, 27);background: rgb(255, 245, 227);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: black;line-height: 26px;">那是否可以重新全量拉取一次呢?</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">可以是可以,但是,如果注册表信息很大呢?比如有几百个微服务都注册上去了,那一次拉取是非常耗时的,而且占用网络带宽,性能较差,这种方案是不靠谱的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">所以我们就需要用增量拉取注册信息表的方式,也就是说只拉取变化的数据,这样数据量就比较小了。如下图所示:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img data-fileid="304477068" data-ratio="0.5828220858895705" src="/upload/75dc5501d362d7cc44522b3a7446cb6b.png" data-type="png" data-w="815" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 12px;"> 增量获取注册表 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">从源码里面我们可以看到,Eureka Client 通过调用 getAndUpdateDelta 方法获取增量的变化的注册表数据,Eureka Server 将变化的数据返回给 Client。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">这里就有几个问题</span>:</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(255, 177, 27);background: rgb(255, 245, 227);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: black;line-height: 26px;">(1)Client 隔多久进行一次增量获取?</p> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: black;line-height: 26px;">(2)Server 将变化的数据存放在哪里?</p> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: black;line-height: 26px;">(3)Client 如何将变化的数据合并到本地注册表里面?</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">下面分别针对上面的几个问题进行解答。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">三、间隔多久同步一次?</span></h2> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">3.1 默认间隔时间</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">默认每隔 30 s 执行一次同步,如下图所示:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img data-fileid="304477067" data-ratio="0.4857142857142857" src="/upload/8b17cb33f93060a7f1f11297e225390.png" data-type="png" data-w="490" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 12px;"> 默认 30s 同步一次 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">这个 30 s 就是由变量 client.refresh.interval 定义的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">Eureka 每 30 s 会调用一个后台线程去拉取增量注册表,这个后台线程的名字叫做:cacheRefresh。如下所示:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img data-fileid="304477070" data-ratio="0.5266457680250783" src="/upload/69d3572d578fd3ad65bcb3a05c39fb1f.png" data-type="png" data-w="638" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 12px;"> 间隔时间的源码 </figcaption> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">3.2 Client 发送拉取注册表的请求</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">就是调用 getDelta 方法,发送 HTTP请求调用 jersey 的 restful 接口,然后 Server 端的 Jersey 框架就会去处理这个请求了。发送请求的方法 getDelta 如下所示:</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/ibKHP1TZZeXJcMdxaKL5xIqr9DpnY6trz4s5R2CQqYUEEQBU7DCabVJWONJGSp9WD9g5zwcBRXE2wnia0n7WHibCIfhEUW7zw5d/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;">eurekaTransport.queryClient.getDelta(remoteRegionsRef.get());<br>restful 接口的地址就长这样:<br>http:<span style="color: #a0a1a7;font-style: italic;line-height: 26px;">//localhost:8080/v2/apps/delta</span><br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">那么 Server 端如何过滤出增量的注册表信息呢?我们可以找到这个方法:getContainerDifferential。如下图所示:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img data-fileid="304477066" data-ratio="0.178335535006605" src="/upload/bbfe7509ac003c5b81ea965f8644d334.png" data-type="png" data-w="757" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">这个方法主要干的活就是去获取最近改变的数据。接下来我们看下最近改变的数据存放在哪。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">四、变化的数据存放在哪?</span></h2> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">4.1 数据结构</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">其实就是放在这个队列里面:recentlyChangedQueue。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">它的数据结构是一个并发安全的链表队列 ConcurrentLinkedQueue。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">链表里面存放的元素就是最近变化的注册信息 RecentlyChangedItem。</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/ibKHP1TZZeXJcMdxaKL5xIqr9DpnY6trz4s5R2CQqYUEEQBU7DCabVJWONJGSp9WD9g5zwcBRXE2wnia0n7WHibCIfhEUW7zw5d/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;">ConcurrentLinkedQueue<RecentlyChangedItem><br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">当有客户端注册的时候,这个链表里面的尾部就会追加一个对象。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">关于 ConcurrentLinkedQueue,还记得我之前写过的 18 种队列吗?不记得话看下这篇:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;"><a href="https://mp.weixin.qq.com/s?__biz=MzAwMjI0ODk0NA==&mid=2451945316&idx=1&sn=2394559adb6b0aec9a70feaccc18b6a4&scene=21#wechat_redirect" style="color: rgb(248, 57, 41);border-bottom: 1px solid rgb(248, 57, 41);" data-linktype="2">45张图庖丁解牛18种Queue,你知道几种?</a></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">ConcurrentLinkedQueue 是由链表结构组成的线程安全的先进先出无界队列。如下图所示:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img data-fileid="304477074" data-ratio="0.29554655870445345" src="/upload/ceaaf5790b1ebe199e03956d80a5ca38.png" data-type="png" data-w="1235" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 12px;"> ConcurrentLinkedQueue原理 </figcaption> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">4.2 内部构造</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">我觉得这个队列的构造还是非常值得我们学习的,我们来看下这个队列的构造,如下图所示:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img data-fileid="304477075" data-ratio="0.838475499092559" src="/upload/87381415d33d4db6f9b4958eadae832d.png" data-type="png" data-w="551" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 12px;"> 增量数据内部构造 </figcaption> </figure> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);font-size: 16px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 这个队列里面存放的对象是最近改变的对象 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">RecentlyChangedItem</code>。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> RecentlyChangedItem 存有三个元素:实例信息、操作类型和最后更新时间。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <span style="font-weight: 700;color: rgb(248, 57, 41);">实例信息</span>:使用 Lease <instanceinfo> 保存一个客户端的注册表信息,这个在第四篇讲解注册表结构已经介绍过。 </instanceinfo> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <span style="font-weight: 700;color: rgb(248, 57, 41);">操作类型</span>:当有客户端发起注册、更新注册表、下线时,会设置 actionType,对应三种枚举值:新增、更新、删除。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <span style="font-weight: 700;color: rgb(248, 57, 41);">最后更新时间</span>:客户端注册信息发生改变时,需要同时更新最后更新时间。 </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">4.3 最近的数据</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">既然上面说到是最近改变的数据才会放进去,那这个最近是多近呢?1 分钟?2分钟?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">通过源码我们找到了这个默认配置,<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">三分钟</code>刷新一次,也就是 180s 刷新一次。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img data-fileid="304477071" data-ratio="0.17205882352941176" src="/upload/77ef0f4dd11850f9a57888e2aa6d2514.png" data-type="png" data-w="680" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">那刷新了什么?刷新其实是会遍历这个队列:recentlyChangedQueue。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">将队列里面的所有元素都遍历一遍,比对每个对象的最后更新时间是否超过了三分钟,如果超过了,就移除这个元素。如下图所示:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img data-fileid="304477072" data-ratio="0.43112701252236135" src="/upload/fe2872e99363efb76414a5b09b6586ab.png" data-type="png" data-w="559" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 12px;"> 比较最后更新时间 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">当元素的最后更新时间超过 3 分钟未更新,则移除该元素。如下图所示:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img data-fileid="304477073" data-ratio="0.9373297002724795" src="/upload/5f035bfb775a1cbd98ddd3ef60be2d3a.png" data-type="png" data-w="367" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 12px;"> 移除元素 </figcaption> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">4.4 检查间隔</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">Server 端会将最近 3 分钟有更新的注册信息放入到队列中,超过 3 分钟未更新的数据将会被移除。那么多久会检查一次呢?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">通过源码我们找到,每隔 30s 就会调用一次检查任务。如下图所示:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img data-fileid="304477078" data-ratio="0.2163265306122449" src="/upload/54322c5c1fc239daedbaef8ab861edc4.png" data-type="png" data-w="735" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 12px;"> 检查间隔 </figcaption> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">4.5 小结</span><span style="display: none;"></span></h3> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);font-size: 16px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> Client 每隔 30 秒调用一次增量获取注册表的接口。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> Server 每隔 30 秒调用检查一次队列。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 如果队列中有元素在 3 分钟以内都没有更新过,则从队列中移除该元素。 </section></li> </ul> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">五、客户端注册表合并</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">这里有个问题:客户端首次拿到的全量注册表,存放本地了。第二次拿到的是增量的注册表,怎么将两次的数据合并在一起呢?如下图所示:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img data-fileid="304477079" data-ratio="0.5129958960328317" src="/upload/f8fad9c48033959f62cc7698a061912.png" data-type="png" data-w="731" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 12px;"> 注册表合并 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">下面我们来看看下客户端注册表合并的原理。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">当客户端调用获取增量注册表的请求后,注册表会返回增量信息,然后客户端就会调用本地合并的方法:updateDelta。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">合并注册表的原理图如下所示:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img data-fileid="304477080" data-ratio="0.9929328621908127" src="/upload/49b794a2ea6ada55a91744c6bcafb073.png" data-type="png" data-w="849" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 12px;"> 合并注册表的原理 </figcaption> </figure> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);font-size: 16px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;font-size: 15px;">首先就会遍历增量注册表,检查其中的每一项,不论 actionType 是新增、删除还是更新,如果本地本来就有,则执行后续的类型判断逻辑。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;font-size: 15px;">如果实例信息的名字在<span style="font-weight: 700;color: rgb(248, 57, 41);">本地不存在</span>则会先往本地注册表新增一个注册信息。然后本地肯定存在注册信息了,执行后续的判断逻辑。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;font-size: 15px;">当类型字段 actionType 等于新增或更新时,先删除后增加。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;font-size: 15px;">当类型字段 actionType 等于删除时,直接进行删除。</p> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">经过这一些列的逻辑之后,增量注册表和本地注册表就合并好了。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">六、比对注册表</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">经过重重判断 + 合并操作,客户端终于完成了本地注册表的刷新,理论上来说,这个时候客户端的注册表应该和注册中心的注册表一致了。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">但是如何确定是一致的呢?这里我们来考虑几种方案</span>:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);font-size: 16px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 再全量拉取一次注册表,和本地注册表进行比对。但是既然又要做一次全量拉取,那之前的增量拉取就没有必要了。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 拉取增量注册表,Server 返回全量注册表的实例 id,客户端比对每个实例 id 是否存在,以及检查本地是否有多余的,如果能匹配上,则认为是一致的。但是这里也有一个问题,对于新增和更新的注册实例,得把更新的实例信息的字段一一比对才能确定是否一致,这就太麻烦了。 <span style="font-weight: 700;color: rgb(248, 57, 41);">另外还有一个致命的问题</span>:如果客户端因为网络故障下线了,上一次最近 3 分钟的增量数据没有拉取到,那么相当于丢失了一次增量数据,这个时候,就不是完整的注册表信息了。 </section></li> </ul> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(255, 177, 27);background: rgb(255, 245, 227);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;color: black;line-height: 26px;">有没有既方便又准确的比对方式呢?</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">有的,那就是<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">哈希比对</code>。哈希比对的意思就是将两个对象经过哈希算法计算出两个 hash 值,如果两个 hash 值相等,则认为这两个对象相等。这种方式在代码中也非常常见,比如类的 hashcode() 方法。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">从源码中,我们看到 Eureka Server 返回注册表时,会返回一个 hash 值,是将全量注册表 hash 之后的值。调用的是这个方法:getReconcileHashCode()。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">如下图所示,获取增量注册表的接口,会返回增量注册表和 hashcode。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img data-fileid="304477077" data-ratio="0.5919117647058824" src="/upload/1e0a32bf29941359262d8bb220e167e6.png" data-type="png" data-w="816" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">然后本地注册表合并后,再计算出一个 hashcode,和 Server 返回的 hashcode 进行比对,如果一致,说明本地注册表和 Server 端一致。如果不一致,则会进行一次全量拉取。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">上面说的原理我们画一张原理图看下就清楚了:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img data-fileid="304477076" data-ratio="0.862144420131291" src="/upload/e7cbe44944d8d03456faa85ef26a6f53.png" data-type="png" data-w="457" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">七、总结</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;">本篇文章可以用一张图来做总结,直接上图:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img data-fileid="304477083" data-ratio="0.7178014766201805" src="/upload/54085d9b4ec5957c85dbceea00ac012f.png" data-type="png" data-w="1219" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 12px;"> 客户端注册表同步原理 </figcaption> </figure> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);font-size: 16px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;font-size: 15px;">客户端每隔 30s 获取一次增量数据,注册中心返回最近 3 分钟变化的注册信息,包含了新注册的、更新的和下线的服务实例。然后将增量注册表 + 全量注册表的 hash 值返回。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0em;margin-bottom: 0em;font-size: 15px;">客户端将本地注册表 + 增量注册表进行合并。合并完成后,计算一个 hash 值,和 Server 返回的 hash 值进行比对,如果相等,则说明客户端的注册表和注册中心的注册表一致,同步完成。如果不一致,则还需要全量拉取一次。</p> </section></li> </ul> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(255, 177, 27);background: rgb(255, 245, 227);"></blockquote> </section> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;white-space: normal;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;text-align: left;"></figure>
作者:微信小助手
<section data-mpa-powered-by="yiban.io"> <section style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">limits.conf{</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">ulimit</span> -SHn 65535 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 临时设置文件描述符大小 进程最大打开文件柄数 还有socket最大连接数, 等同配置 nofile</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">ulimit</span> -SHu 65535 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 临时设置用户最大进程数</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">ulimit</span> -a <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 查看</span><br><br> /etc/security/limits.conf<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 文件描述符大小 open files</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># lsof |wc -l 查看当前文件句柄数使用数量</span><br> * soft nofile 16384 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 设置太大,进程使用过多会把机器拖死</span><br> * hard nofile 32768<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 用户最大进程数 max user processes</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># echo $((`ps uxm |wc -l`-`ps ux |wc -l`)) 查看当前用户占用的进程数 [包括线程]</span><br> user soft nproc 16384<br> user hard nproc 32768<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 如果/etc/security/limits.d/有配置文件,将会覆盖/etc/security/limits.conf里的配置</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 即/etc/security/limits.d/的配置文件里就不要有同样的参量设置</span><br> /etc/security/limits.d/90-nproc.conf <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># centos6.3的默认这个文件会覆盖 limits.conf</span><br> user soft nproc 16384<br> user hard nproc 32768<br><br> sysctl -p <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 修改配置文件后让系统生效</span><br><br>}<br><br>随机分配端口范围{<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 本机连其它端口用的</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"10000 65535"</span> > /proc/sys/net/ipv4/ip_local_port_range<br><br>}<br><br>百万长链接设置{<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 内存消耗需要较大</span><br> vim /root/.bash_profile<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 添加如下2行,退出bash重新登陆</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 一个进程不能使用超过NR_OPEN文件描述符</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> 20000500 > /proc/sys/fs/nr_open<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 当前用户最大文件数</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">ulimit</span> -n 10000000<br><br>}<br><br>core崩溃文件查看{<br><br> gdb core.13844<br> bt <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 查看函数调用信息(堆栈)</span><br><br>}<br><br><br>libc.so故障修复{<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 由于升级glibc导致libc.so不稳定,突然报错,幸好还有未退出的终端</span><br> grep: error <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">while</span> loading shared libraries: /lib64/libc.so.6: ELF file OS ABI invalid<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 看看当前系统有多少版本 libc.so</span><br> ls /lib64/libc-[tab]<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 更改环境变量指向其他 libc.so 文件测试</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">export</span> LD_PRELOAD=/lib64/libc-2.7.so <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 如果不改变LD_PRELOAD变量,ln不能用,需要使用 /sbin/sln 命令做链接</span><br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 当前如果好使了,在执行下面强制替换软链接。如不好使,测试其他版本的libc.so文件</span><br> ln -f -s /lib64/libc-2.7.so /lib64/libc.so.6<br><br>}<br><br>无法分配内存 {<br><br> fork: Cannot allocate memory<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 报错不一定是内存不够用,进程数或者线程数满了也会报这个错误, 可以适当增加 kernel.pid_max 的值,</span><br> cat /proc/sys/kernel/pid_max <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 默认3.2w</span><br><br>}<br><br>sudo{<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> myPassword | sudo -S ls /tmp <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 直接输入sudo的密码非交互,从标准输入读取密码而不是终端设备</span><br> visudo <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># sudo命令权限添加 /etc/sudoers</span><br> 用户 别名(可用all)=NOPASSWD:命令1,命令2<br> user ALL=NOPASSWD:/bin/su <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 免root密码切换root身份</span><br> wangming linuxfan=NOPASSWD:/sbin/apache start,/sbin/apache restart<br> UserName ALL=(ALL) ALL<br> UserName ALL=(ALL) NOPASSWD: ALL<br> peterli ALL=(ALL) NOPASSWD:/sbin/service<br> Defaults requiretty <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># sudo不允许后台运行,注释此行既允许</span><br> Defaults !visiblepw <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># sudo不允许远程,去掉!既允许</span><br><br>}<br><br>grub开机启动项添加{<br><br> vim /etc/grub.conf<br> title ms-dos<br> rootnoverify (hd0,0)<br> chainloader +1<br><br>}<br><br>stty{<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#stty时一个用来改变并打印终端行设置的常用命令</span><br><br> stty iuclc <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 在命令行下禁止输出大写</span><br> stty -iuclc <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 恢复输出大写</span><br> stty olcuc <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 在命令行下禁止输出小写</span><br> stty -olcuc <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 恢复输出小写</span><br> stty size <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 打印出终端的行数和列数</span><br> stty eof <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"string"</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 改变系统默认ctrl+D来表示文件的结束</span><br> stty -<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 禁止回显</span><br> stty <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 打开回显</span><br> stty -<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span>;<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">read</span>;stty <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span>;<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">read</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 测试禁止回显</span><br> stty igncr <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 忽略回车符</span><br> stty -igncr <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 恢复回车符</span><br> stty erase <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">'#'</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 将#设置为退格字符</span><br> stty erase <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">'^?'</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 恢复退格字符</span><br><br> 定时输入{<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;"><span style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);overflow-wrap: inherit !important;word-break: inherit !important;">timeout_read</span></span>(){<br> timeout=<span style="font-size: inherit;line-height: inherit;color: rgb(98, 151, 85);overflow-wrap: inherit !important;word-break: inherit !important;">$1</span><br> old_stty_settings=`stty -g` <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># save current settings</span><br> stty -icanon min 0 time 100 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># set 10seconds,not 100seconds</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">eval</span> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">read</span> varname <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># =read $varname</span><br> stty <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"<span style="font-size: inherit;line-height: inherit;color: rgb(98, 151, 85);overflow-wrap: inherit !important;word-break: inherit !important;">$old_stty_settings</span>"</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># recover settings</span><br> }<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">read</span> -t 10 varname <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 更简单的方法就是利用read命令的-t选项</span><br><br> }<br><br> 检测用户按键{<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#!/bin/bash</span><br> old_tty_settings=$(stty -g) <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 保存老的设置(为什么?).</span><br> stty -icanon<br> Keypress=$(head -c1) <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 或者使用$(dd bs=1 count=1 2> /dev/null)</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"Key pressed was \""</span><span style="font-size: inherit;line-height: inherit;color: rgb(98, 151, 85);overflow-wrap: inherit !important;word-break: inherit !important;">$Keypress</span><span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"\"."</span><br> stty <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"<span style="font-size: inherit;line-height: inherit;color: rgb(98, 151, 85);overflow-wrap: inherit !important;word-break: inherit !important;">$old_tty_settings</span>"</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 恢复老的设置.</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">exit</span> 0<br><br> }<br><br>}<br><br>iptables{<br><br> 内建三个表:nat mangle 和 filter<br> filter预设规则表,有INPUT、FORWARD 和 OUTPUT 三个规则链<br> vi /etc/sysconfig/iptables <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 配置文件</span><br> INPUT <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 进入</span><br> FORWARD <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 转发</span><br> OUTPUT <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 出去</span><br> ACCEPT <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 将封包放行</span><br> REJECT <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 拦阻该封包</span><br> DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 丢弃封包不予处理</span><br> -A <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 在所选择的链(INPUT等)末添加一条或更多规则</span><br> -D <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 删除一条</span><br> -E <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 修改</span><br> -p <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># tcp、udp、icmp 0相当于所有all !取反</span><br> -P <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 设置缺省策略(与所有链都不匹配强制使用此策略)</span><br> -s <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># IP/掩码 (IP/24) 主机名、网络名和清楚的IP地址 !取反</span><br> -j <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 目标跳转,立即决定包的命运的专用内建目标</span><br> -i <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 进入的(网络)接口 [名称] eth0</span><br> -o <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 输出接口[名称]</span><br> -m <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 模块</span><br> --sport <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 源端口</span><br> --dport <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 目标端口</span><br><br> iptables -F <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 将防火墙中的规则条目清除掉 # 注意: iptables -P INPUT ACCEPT</span><br> iptables-restore < 规则文件 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 导入防火墙规则</span><br> /etc/init.d/iptables save <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 保存防火墙设置</span><br> /etc/init.d/iptables restart <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 重启防火墙服务</span><br> iptables -L -n <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 查看规则</span><br> iptables -t nat -nL <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 查看转发</span><br><br> iptables实例{<br><br> iptables -L INPUT <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 列出某规则链中的所有规则</span><br> iptables -X allowed <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 删除某个规则链 ,不加规则链,清除所有非内建的</span><br> iptables -Z INPUT <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 将封包计数器归零</span><br> iptables -N allowed <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 定义新的规则链</span><br> iptables -P INPUT DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 定义过滤政策</span><br> iptables -A INPUT -s 192.168.1.1 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 比对封包的来源IP # ! 192.168.0.0/24 ! 反向对比</span><br> iptables -A INPUT -d 192.168.1.1 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 比对封包的目的地IP</span><br> iptables -A INPUT -i eth0 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 比对封包是从哪片网卡进入</span><br> iptables -A FORWARD -o eth0 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 比对封包要从哪片网卡送出 eth+表示所有的网卡</span><br> iptables -A INPUT -p tcp <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># -p ! tcp 排除tcp以外的udp、icmp。-p all所有类型</span><br> iptables -D INPUT 8 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 从某个规则链中删除一条规则</span><br> iptables -D INPUT --dport 80 -j DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 从某个规则链中删除一条规则</span><br> iptables -R INPUT 8 -s 192.168.0.1 -j DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 取代现行规则</span><br> iptables -I INPUT 8 --dport 80 -j ACCEPT <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 插入一条规则</span><br> iptables -A INPUT -i eth0 -j DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 其它情况不允许</span><br> iptables -A INPUT -p tcp -s IP -j DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 禁止指定IP访问</span><br> iptables -A INPUT -p tcp -s IP --dport port -j DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 禁止指定IP访问端口</span><br> iptables -A INPUT -s IP -p tcp --dport port -j ACCEPT <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 允许在IP访问指定端口</span><br> iptables -A INPUT -p tcp --dport 22 -j DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 禁止使用某端口</span><br> iptables -A INPUT -i eth0 -p icmp -m icmp --icmp-type 8 -j DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 禁止icmp端口</span><br> iptables -A INPUT -i eth0 -p icmp -j DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 禁止icmp端口</span><br> iptables -t filter -A INPUT -i eth0 -p tcp --syn -j DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 阻止所有没有经过你系统授权的TCP连接</span><br> iptables -A INPUT -f -m <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">limit</span> --<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">limit</span> 100/s --<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">limit</span>-burst 100 -j ACCEPT <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># IP包流量限制</span><br> iptables -A INPUT -i eth0 -s 192.168.62.1/32 -p icmp -m icmp --icmp-type 8 -j ACCEPT <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 除192.168.62.1外,禁止其它人ping我的主机</span><br> iptables -A INPUT -p tcp -m tcp --dport 80 -m state --state NEW -m recent --update --seconds 5 --hitcount 20 --rttl --name WEB --rsource -j DROP <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 可防御cc攻击(未测试)</span><br><br> }<br><br> iptables配置实例文件{<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># Generated by iptables-save v1.2.11 on Fri Feb 9 12:10:37 2007</span><br> *filter<br> :INPUT ACCEPT [637:58967]<br> :FORWARD DROP [0:0]<br> :OUTPUT ACCEPT [5091:1301533]<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 允许的IP或IP段访问 建议多个</span><br> -A INPUT -s 127.0.0.1 -p tcp -j ACCEPT<br> -A INPUT -s 192.168.0.0/255.255.0.0 -p tcp -j ACCEPT<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 开放对外开放端口</span><br> -A INPUT -p tcp --dport 80 -j ACCEPT<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 指定某端口针对IP开放</span><br> -A INPUT -s 192.168.10.37 -p tcp --dport 22 -j ACCEPT<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 拒绝所有协议(INPUT允许)</span><br> -A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,URG RST -j DROP<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 允许已建立的或相关连的通行</span><br> -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 拒绝ping</span><br> -A INPUT -p tcp -m tcp -j REJECT --reject-with icmp-port-unreachable<br> COMMIT<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># Completed on Fri Feb 9 12:10:37 2007</span><br><br> }<br><br> iptables配置实例{<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 允许某段IP访问任何端口</span><br> iptables -A INPUT -s 192.168.0.3/24 -p tcp -j ACCEPT<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 设定预设规则 (拒绝所有的数据包,再允许需要的,如只做WEB服务器.还是推荐三个链都是DROP)</span><br> iptables -P INPUT DROP<br> iptables -P FORWARD DROP<br> iptables -P OUTPUT ACCEPT<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 注意: 直接设置这三条会掉线</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 开启22端口</span><br> iptables -A INPUT -p tcp --dport 22 -j ACCEPT<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 如果OUTPUT 设置成DROP的,要写上下面一条</span><br> iptables -A OUTPUT -p tcp --sport 22 -j ACCEPT<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 注:不写导致无法SSH.其他的端口一样,OUTPUT设置成DROP的话,也要添加一条链</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 如果开启了web服务器,OUTPUT设置�
作者:微信小助手
<section data-width="100%" data-opacity="1" data-rotate="0" data-mpa-powered-by="yiban.io"> <section> <section> <section> <section data-width="100%" data-opacity="1" data-rotate="0"> <section> <section> <section data-width="100%" data-opacity="1" data-rotate="0"> <section> <section> <section> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzAwMTk4NjM1MA==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4gcBzLSUNh2cgXUsuLIsvQYJE1lzZd74qpC3iciaM6gcYIfOVV0KjDDkeN4CTLTn4ETPtaHOAuTWSWA/0?wx_fmt=png" data-nickname="JAVA日知录" data-alias="javadaily" data-signature="写代码的架构师,做架构的程序员! 实战、源码、数据库、架构...只要你来,你想了解的这里都有!" data-from="0"></mpprofile> </section> </section> </section> </section> </section> </section> </section> </section> <p style="text-align: right;"><span style="font-size: 15px;"></span><span style="color: rgb(136, 136, 136);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 12px;letter-spacing: 0.544px;text-align: right;text-indent: 34px;background-color: rgb(255, 255, 255);">来源:https://blog.csdn.net/qq_41378597</span></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.6;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;padding: 5px;font-size: 16px;color: rgb(53, 53, 53);word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;"> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">一、问题描述</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">平时我们在完成某些数据的入库后,发布了一个事件,此时使用的是 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">@EventListener</code>,然后在这个事件中,又去对刚才入库的数据进行查询,从而完成后续的操作。例如(数据入库=>对入库数据进行查询审核),这时候会发现,查询不到刚才入库的数据,这是因为事务还没提交完成,在同一个事务当中,查询不到才存入的数据,那么就引出了下面的解决方式。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">为了解决上述问题,Spring为我们提供了两种方式:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">@TransactionalEventListener</code>注解。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 事务同步管理器 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionSynchronizationManager</code>。 </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">以便我们可以在事务提交后再触发某一事件来进行其他操作。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">二、使用场景</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">在项目当中,我们有时候需要在执行数据库操作之后,发送消息或事件来异步调用其他组件执行相应的操作,例如:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 数据完成导入之后,发布审核事件,对入库的数据进行审核。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 用户在完成注册后发送激活码。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 配置修改后,发送更新配置的事件。 </section></li> </ol> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">三、@TransactionalEventListener详解</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">我们可以从命名上直接看出,它就是个 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">EventListener</code>,在Spring4.2+,有一种叫做 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">@TransactionEventListener</code>的方式,能够实现在控制事务的同时,完成对对事件的处理。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">我们知道,Spring的事件监听机制(发布订阅模型)实际上并不是异步的(默认情况下),而是同步的来将代码进行解耦。而 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">@TransactionEventListener</code>仍是通过这种方式,但是加入了回调的方式来解决,这样就能够在事务进行<span style="font-weight: 700;color: rgb(248, 57, 41);">Commited</span>,<span style="font-weight: 700;color: rgb(248, 57, 41);">Rollback</span>…等时候才去进行<span style="font-weight: 700;color: rgb(248, 57, 41);">Event</span>的处理,来达到事务同步的目的。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// @since 4.2 注解的方式提供的相对较晚,其实API的方式在第一个版本就已经提供了。</span><br><span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 值得注意的是,在这个注解上面有一个注解:`@EventListener`,所以表明其实这个注解也是个事件监听器。 </span><br><span style="color: #4078f2;line-height: 26px;">@Target</span>({ElementType.METHOD, ElementType.ANNOTATION_TYPE})<br><span style="color: #4078f2;line-height: 26px;">@Retention</span>(RetentionPolicy.RUNTIME)<br><span style="color: #4078f2;line-height: 26px;">@Documented</span><br><span style="color: #4078f2;line-height: 26px;">@EventListener</span> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">//有类似于注解继承的效果</span><br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #4078f2;line-height: 26px;">@interface</span> TransactionalEventListener {<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 这个注解取值有:BEFORE_COMMIT、AFTER_COMMIT、AFTER_ROLLBACK、AFTER_COMPLETION</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 各个值都代表什么意思表达什么功能,非常清晰,下面解释了对应的枚举类~</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 需要注意的是:AFTER_COMMIT + AFTER_COMPLETION是可以同时生效的</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// AFTER_ROLLBACK + AFTER_COMPLETION是可以同时生效的</span><br> <span style="line-height: 26px;">TransactionPhase <span style="color: #4078f2;line-height: 26px;">phase</span><span style="line-height: 26px;">()</span> <span style="color: #a626a4;line-height: 26px;">default</span> TransactionPhase.AFTER_COMMIT</span>;<br> <br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 表明若没有事务的时候,对应的event是否需要执行,默认值为false表示,没事务就不执行了。</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">boolean</span> <span style="color: #4078f2;line-height: 26px;">fallbackExecution</span><span style="line-height: 26px;">()</span> <span style="color: #a626a4;line-height: 26px;">default</span> <span style="color: #a626a4;line-height: 26px;">false</span></span>;<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 这里巧妙的用到了@AliasFor的能力,放到了@EventListener身上</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 注意:一般建议都需要指定此值,否则默认可以处理所有类型的事件,范围太广了。</span><br> <span style="color: #4078f2;line-height: 26px;">@AliasFor</span>(annotation = EventListener<span style="line-height: 26px;">.<span style="color: #a626a4;line-height: 26px;">class</span>, <span style="color: #c18401;line-height: 26px;">attribute</span> </span>= <span style="color: #50a14f;line-height: 26px;">"classes"</span>)<br> Class<?>[] value() <span style="color: #a626a4;line-height: 26px;">default</span> {};<br> <span style="color: #4078f2;line-height: 26px;">@AliasFor</span>(annotation = EventListener<span style="line-height: 26px;">.<span style="color: #a626a4;line-height: 26px;">class</span>, <span style="color: #c18401;line-height: 26px;">attribute</span> </span>= <span style="color: #50a14f;line-height: 26px;">"classes"</span>)<br> Class<?>[] classes() <span style="color: #a626a4;line-height: 26px;">default</span> {};<br><br> <span style="line-height: 26px;">String <span style="color: #4078f2;line-height: 26px;">condition</span><span style="line-height: 26px;">()</span> <span style="color: #a626a4;line-height: 26px;">default</span> ""</span>;<br>}<br></code></pre> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">enum</span> TransactionPhase {<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 指定目标方法在事务commit之前执行</span><br> BEFORE_COMMIT,<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 指定目标方法在事务commit之后执行</span><br> AFTER_COMMIT,<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 指定目标方法在事务rollback之后执行</span><br> AFTER_ROLLBACK,<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 指定目标方法在事务完成时执行,这里的完成是指无论事务是成功提交还是事务回滚了</span><br> AFTER_COMPLETION<br>}<br></code></pre> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">四、代码示例</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">这里主要是为了讲解如何使用 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">@TransactionalEventListener</code>,所以就不列出所有代码了。</span></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #4078f2;line-height: 26px;">@Data</span><br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">User</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">private</span> <span style="color: #a626a4;line-height: 26px;">long</span> id;<br> <span style="color: #a626a4;line-height: 26px;">private</span> String name;<br> <span style="color: #a626a4;line-height: 26px;">private</span> Integer age;<br><br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">业务实现:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #4078f2;line-height: 26px;">@Service</span><br><span style="color: #4078f2;line-height: 26px;">@Slf</span>4j<br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">UserServiceImpl</span> <span style="color: #a626a4;line-height: 26px;">extends</span> <span style="color: #a626a4;line-height: 26px;">implements</span> <span style="color: #c18401;line-height: 26px;">UserService</span> </span>{<br><br> <span style="color: #4078f2;line-height: 26px;">@Autowired</span><br> UserMapper userMapper;<br> <br> <span style="color: #4078f2;line-height: 26px;">@Autowired</span><br> ApplicationEventPublisher eventPublisher;<br> <br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">userRegister</span><span style="line-height: 26px;">(User user)</span></span>{<br> userMapper.insertUser(user);<br> eventPublisher.publishEvent(<span style="color: #a626a4;line-height: 26px;">new</span> UserRegisterEvent(<span style="color: #a626a4;line-height: 26px;">new</span> Date()));<br> }<br><br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">自定义事件:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #4078f2;line-height: 26px;">@Getter</span><br><span style="color: #4078f2;line-height: 26px;">@Setter</span><br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">UserRegisterEvent</span> <span style="color: #a626a4;line-height: 26px;">extends</span> <span style="color: #c18401;line-height: 26px;">ApplicationEvent</span> </span>{<br><br> <span style="color: #a626a4;line-height: 26px;">private</span> Date registerDate;<br> <br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #4078f2;line-height: 26px;">UserRegisterEvent</span><span style="line-height: 26px;">(Date registerDate)</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">super</span>(registerDate);<br> <span style="color: #a626a4;line-height: 26px;">this</span>.registerDate = registerDate;<br> }<br><br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">事件监听器:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #4078f2;line-height: 26px;">@Slf</span>4j<br><span style="color: #4078f2;line-height: 26px;">@Component</span><br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">UserListener</span> </span>{<br><br> <span style="color: #4078f2;line-height: 26px;">@Autowired</span><br> UserService userService;<br> <br> <span style="color: #4078f2;line-height: 26px;">@Async</span><br> <span style="color: #4078f2;line-height: 26px;">@TransactionalEventListener</span>(phase = TransactionPhase.AFTER_COMMIT, classes = UserRegisterEvent<span style="line-height: 26px;">.<span style="color: #a626a4;line-height: 26px;">class</span>)<br> <span style="color: #c18401;line-height: 26px;">public</span> <span style="color: #c18401;line-height: 26px;">void</span> <span style="color: #c18401;line-height: 26px;">onUserRegisterEvent</span>(<span style="color: #c18401;line-height: 26px;">UserRegisterEvent</span> <span style="color: #c18401;line-height: 26px;">event</span>) </span>{<br> userService.sendActivationCode(event.getRegisterDate());<br> }<br><br>}<br></code></pre> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">五、实现原理</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">关于事务的实现原理,这里其实是比较简单的,Spring对事务监控的处理逻辑在 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionSynchronization</code>中,如下是该接口的声明:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">interface</span> <span style="color: #c18401;line-height: 26px;">TransactionSynchronization</span> <span style="color: #a626a4;line-height: 26px;">extends</span> <span style="color: #c18401;line-height: 26px;">Flushable</span> </span>{<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 在当前事务挂起时执行</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">default</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">suspend</span><span style="line-height: 26px;">()</span> </span>{<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 在当前事务重新加载时执行</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">default</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">resume</span><span style="line-height: 26px;">()</span> </span>{<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 在当前数据刷新到数据库时执行</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">default</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">flush</span><span style="line-height: 26px;">()</span> </span>{<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 在当前事务commit之前执行</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">default</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">beforeCommit</span><span style="line-height: 26px;">(<span style="color: #a626a4;line-height: 26px;">boolean</span> readOnly)</span> </span>{<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 在当前事务completion之前执行</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">default</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">beforeCompletion</span><span style="line-height: 26px;">()</span> </span>{<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 在当前事务commit之后实质性</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">default</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">afterCommit</span><span style="line-height: 26px;">()</span> </span>{<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 在当前事务completion之后执行</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">default</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">afterCompletion</span><span style="line-height: 26px;">(<span style="color: #a626a4;line-height: 26px;">int</span> status)</span> </span>{<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">很明显,这里的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionSynchronization</code>接口只是抽象了一些行为,用于事务事件发生时触发,这些行为在Spring事务中提供了内在支持,即在相应的事务事件时,其会获取当前所有注册的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionSynchronization</code>对象,然后调用其相应的方法。那么这里 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionSynchronization</code>对象的注册点对于我们了解事务事件触发有至关重要的作用了。这里我们首先回到事务标签的解析处,在前面讲解事务标签解析时,我们讲到Spring会注册一个 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionalEventListenerFactory</code>类型的bean到Spring容器中,这里关于标签的解析读者可以阅读本人前面的文章Spring事务用法示例与实现原理。这里注册的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionalEventListenerFactory</code>实现了 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">EventListenerFactory</code>接口,这个接口的主要作用是先判断目标方法是否是某个监听器的类型,然后为目标方法生成一个监听器,其会在某个bean初始化之后由Spring调用其方法用于生成监听器。如下是该类的实现:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">TransactionalEventListenerFactory</span> <span style="color: #a626a4;line-height: 26px;">implements</span> <span style="color: #c18401;line-height: 26px;">EventListenerFactory</span>, <span style="color: #c18401;line-height: 26px;">Ordered</span> </span>{<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 指定当前监听器的顺序</span><br> <span style="color: #a626a4;line-height: 26px;">private</span> <span style="color: #a626a4;line-height: 26px;">int</span> order = <span style="color: #986801;line-height: 26px;">50</span>;<br><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">setOrder</span><span style="line-height: 26px;">(<span style="color: #a626a4;line-height: 26px;">int</span> order)</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">this</span>.order = order;<br> }<br><br> <span style="color: #4078f2;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">int</span> <span style="color: #4078f2;line-height: 26px;">getOrder</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #a626a4;line-height: 26px;">this</span>.order;<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 指定目标方法是否是所支持的监听器的类型,这里的判断逻辑就是如果目标方法上包含有</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// TransactionalEventListener注解,则说明其是一个事务事件监听器</span><br> <span style="color: #4078f2;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">boolean</span> <span style="color: #4078f2;line-height: 26px;">supportsMethod</span><span style="line-height: 26px;">(Method method)</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">return</span> (AnnotationUtils.findAnnotation(method, TransactionalEventListener<span style="line-height: 26px;">.<span style="color: #a626a4;line-height: 26px;">class</span>) !</span>= <span style="color: #a626a4;line-height: 26px;">null</span>);<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 为目标方法生成一个事务事件监听器,这里ApplicationListenerMethodTransactionalAdapter实现了</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// ApplicationEvent接口</span><br> <span style="color: #4078f2;line-height: 26px;">@Override</span><br> <span style="color: #a626a4;line-height: 26px;">public</span> ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {<br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #a626a4;line-height: 26px;">new</span> ApplicationListenerMethodTransactionalAdapter(beanName, type, method);<br> }<br><br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">这里关于事务事件监听的逻辑其实已经比较清楚了。<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">ApplicationListenerMethodTransactionalAdapter</code>本质上是实现了 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">ApplicationListener</code>接口的,也就是说,其是Spring的一个事件监听器,这也就是为什么进行事务处理时需要使用 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">ApplicationEventPublisher.publish()</code>方法发布一下当前事务的事件。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">ApplicationListenerMethodTransactionalAdapter</code>在监听到发布的事件之后会生成一个 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionSynchronization</code>对象,并且将该对象注册到当前事务逻辑中,如下是监听事务事件的处理逻辑:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;">@Override<br>public void onApplicationEvent(ApplicationEvent event) {<br> // 如果当前TransactionManager已经配置开启事务事件监听,<br> // 此时才会注册TransactionSynchronization对象<br> <span style="color: #a626a4;line-height: 26px;">if</span> (TransactionSynchronizationManager.isSynchronizationActive()) {<br> // 通过当前事务事件发布的参数,创建一个TransactionSynchronization对象<br> TransactionSynchronization transactionSynchronization = <br> createTransactionSynchronization(event);<br> // 注册TransactionSynchronization对象到TransactionManager中<br> TransactionSynchronizationManager<br> .registerSynchronization(transactionSynchronization);<br> } <span style="color: #a626a4;line-height: 26px;">else</span> <span style="color: #a626a4;line-height: 26px;">if</span> (this.annotation.fallbackExecution()) {<br> // 如果当前TransactionManager没有开启事务事件处理,但是当前事务监听方法中配置了<br> // fallbackExecution属性为<span style="color: #0184bb;line-height: 26px;">true</span>,说明其需要对当前事务事件进行监听,无论其是否有事务<br> <span style="color: #a626a4;line-height: 26px;">if</span> (this.annotation.phase() == TransactionPhase.AFTER_ROLLBACK <br> && logger.isWarnEnabled()) {<br> logger.warn(<span style="color: #50a14f;line-height: 26px;">"Processing "</span> <br> + event + <span style="color: #50a14f;line-height: 26px;">" as a fallback execution on AFTER_ROLLBACK phase"</span>);<br> }<br> processEvent(event);<br> } <span style="color: #a626a4;line-height: 26px;">else</span> {<br> // 走到这里说明当前是不需要事务事件处理的,因而直接略过<br> <span style="color: #a626a4;line-height: 26px;">if</span> (logger.isDebugEnabled()) {<br> logger.debug(<span style="color: #50a14f;line-height: 26px;">"No transaction is active - skipping "</span> + event);<br> }<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">这里需要说明的是,上述annotation属性就是在事务监听方法上解析的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionalEventListener</code>注解中配置的属性。可以看到,对于事务事件的处理,这里创建了一个 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionSynchronization</code>对象,其实主要的处理逻辑就是在返回的这个对象中,而 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">createTransactionSynchronization()</code>方法内部只是创建了一个 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionSynchronizationEventAdapter</code>对象就返回了。这里我们直接看该对象的源码:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #a626a4;line-height: 26px;">private</span> <span style="color: #a626a4;line-height: 26px;">static</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">TransactionSynchronizationEventAdapter</span> <br> <span style="color: #a626a4;line-height: 26px;">extends</span> <span style="color: #c18401;line-height: 26px;">TransactionSynchronizationAdapter</span> </span>{<br><br> <span style="color: #a626a4;line-height: 26px;">private</span> <span style="color: #a626a4;line-height: 26px;">final</span> ApplicationListenerMethodAdapter listener;<br> <span style="color: #a626a4;line-height: 26px;">private</span> <span style="color: #a626a4;line-height: 26px;">final</span> ApplicationEvent event;<br> <span style="color: #a626a4;line-height: 26px;">private</span> <span style="color: #a626a4;line-height: 26px;">final</span> TransactionPhase phase;<br><br><span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #4078f2;line-height: 26px;">TransactionSynchronizationEventAdapter</span><span style="line-height: 26px;">(ApplicationListenerMethodAdapter <br> listener, ApplicationEvent event, TransactionPhase phase)</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">this</span>.listener = listener;<br> <span style="color: #a626a4;line-height: 26px;">this</span>.event = event;<br> <span style="color: #a626a4;line-height: 26px;">this</span>.phase = phase;<br>}<br><br> <span style="color: #4078f2;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">int</span> <span style="color: #4078f2;line-height: 26px;">getOrder</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #a626a4;line-height: 26px;">this</span>.listener.getOrder();<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 在目标方法配置的phase属性为BEFORE_COMMIT时,处理before commit事件</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">beforeCommit</span><span style="line-height: 26px;">(<span style="color: #a626a4;line-height: 26px;">boolean</span> readOnly)</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">if</span> (<span style="color: #a626a4;line-height: 26px;">this</span>.phase == TransactionPhase.BEFORE_COMMIT) {<br> processEvent();<br> }<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 这里对于after completion事件的处理,虽然分为了三个if分支,但是实际上都是执行的processEvent()</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 方法,因为after completion事件是事务事件中一定会执行的,因而这里对于commit,</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// rollback和completion事件都在当前方法中处理也是没问题的</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">afterCompletion</span><span style="line-height: 26px;">(<span style="color: #a626a4;line-height: 26px;">int</span> status)</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">if</span> (<span style="color: #a626a4;line-height: 26px;">this</span>.phase == TransactionPhase.AFTER_COMMIT && status == STATUS_COMMITTED) {<br> processEvent();<br> } <span style="color: #a626a4;line-height: 26px;">else</span> <span style="color: #a626a4;line-height: 26px;">if</span> (<span style="color: #a626a4;line-height: 26px;">this</span>.phase == TransactionPhase.AFTER_ROLLBACK <br> && status == STATUS_ROLLED_BACK) {<br> processEvent();<br> } <span style="color: #a626a4;line-height: 26px;">else</span> <span style="color: #a626a4;line-height: 26px;">if</span> (<span style="color: #a626a4;line-height: 26px;">this</span>.phase == TransactionPhase.AFTER_COMPLETION) {<br> processEvent();<br> }<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 执行事务事件</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">protected</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">processEvent</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">this</span>.listener.processEvent(<span style="color: #a626a4;line-height: 26px;">this</span>.event);<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">可以看到,对于事务事件的处理,最终都是委托给了 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">ApplicationListenerMethodAdapter.processEvent()</code>方法进行的。如下是该方法的源码:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #383a42;background: #fafafa;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">processEvent</span><span style="line-height: 26px;">(ApplicationEvent event)</span> </span>{<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 处理事务事件的相关参数,这里主要是判断TransactionalEventListener注解中是否配置了value</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 或classes属性,如果配置了,则将方法参数转换为该指定类型传给监听的方法;如果没有配置,则判断</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 目标方法是ApplicationEvent类型还是PayloadApplicationEvent类型,是则转换为该类型传入</span><br> Object[] args = resolveArguments(event);<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 这里主要是获取TransactionalEventListener注解中的condition属性,然后通过</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// Spring expression language将其与目标类和方法进行匹配</span><br> <span style="color: #a626a4;line-height: 26px;">if</span> (shouldHandle(event, args)) {<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 通过处理得到的参数借助于反射调用事务监听方法</span><br> Object result = doInvoke(args);<br> <span style="color: #a626a4;line-height: 26px;">if</span> (result != <span style="color: #a626a4;line-height: 26px;">null</span>) {<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 对方法的返回值进行处理</span><br> handleResult(result);<br> } <span style="color: #a626a4;line-height: 26px;">else</span> {<br> logger.trace(<span style="color: #50a14f;line-height: 26px;">"No result object given - no result to handle"</span>);<br> }<br> }<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 处理事务监听方法的参数</span><br> <span style="color: #a626a4;line-height: 26px;">protected</span> Object[] resolveArguments(ApplicationEvent event) {<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 获取发布事务事件时传入的参数类型</span><br> ResolvableType declaredEventType = getResolvableType(event);<br> <span style="color: #a626a4;line-height: 26px;">if</span> (declaredEventType == <span style="color: #a626a4;line-height: 26px;">null</span>) {<br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #a626a4;line-height: 26px;">null</span>;<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 如果事务监听方法的参数个数为0,则直接返回</span><br> <span style="color: #a626a4;line-height: 26px;">if</span> (<span style="color: #a626a4;line-height: 26px;">this</span>.method.getParameterCount() == <span style="color: #986801;line-height: 26px;">0</span>) {<br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #a626a4;line-height: 26px;">new</span> Object[<span style="color: #986801;line-height: 26px;">0</span>];<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 如果事务监听方法的参数不为ApplicationEvent或PayloadApplicationEvent,则直接将发布事务</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 事件时传入的参数当做事务监听方法的参数传入。从这里可以看出,如果事务监听方法的参数不是</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// ApplicationEvent或PayloadApplicationEvent类型,那么其参数必须只能有一个,并且这个</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 参数必须与发布事务事件时传入的参数一致</span><br> Class<?> eventClass = declaredEventType.getRawClass();<br> <span style="color: #a626a4;line-height: 26px;">if</span> ((eventClass == <span style="color: #a626a4;line-height: 26px;">null</span> || !ApplicationEvent<span style="line-height: 26px;">.<span style="color: #a626a4;line-height: 26px;">class</span>.<span style="color: #c18401;line-height: 26px;">isAssignableFrom</span>(<span style="color: #c18401;line-height: 26px;">eventClass</span>)) &&<br> <span style="color: #c18401;line-height: 26px;">event</span> <span style="color: #c18401;line-height: 26px;">instanceof</span> <span style="color: #c18401;line-height: 26px;">PayloadApplicationEvent</span>) </span>{<br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #a626a4;line-height: 26px;">new</span> Object[] {((PayloadApplicationEvent) event).getPayload()};<br> } <span style="color: #a626a4;line-height: 26px;">else</span> {<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 如果参数类型为ApplicationEvent或PayloadApplicationEvent,则直接将其传入事务事件方法</span><br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #a626a4;line-height: 26px;">new</span> Object[] {event};<br> }<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 判断事务事件方法方法是否需要进行事务事件处理</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">private</span> <span style="color: #a626a4;line-height: 26px;">boolean</span> <span style="color: #4078f2;line-height: 26px;">shouldHandle</span><span style="line-height: 26px;">(ApplicationEvent event, @Nullable Object[] args)</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">if</span> (args == <span style="color: #a626a4;line-height: 26px;">null</span>) {<br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #a626a4;line-height: 26px;">false</span>;<br> }<br> String condition = getCondition();<br> <span style="color: #a626a4;line-height: 26px;">if</span> (StringUtils.hasText(condition)) {<br> Assert.notNull(<span style="color: #a626a4;line-height: 26px;">this</span>.evaluator, <span style="color: #50a14f;line-height: 26px;">"EventExpressionEvaluator must no be null"</span>);<br> EvaluationContext evaluationContext = <span style="color: #a626a4;line-height: 26px;">this</span>.evaluator.createEvaluationContext(<br> event, <span style="color: #a626a4;line-height: 26px;">this</span>.targetClass, <span style="color: #a626a4;line-height: 26px;">this</span>.method, args, <span style="color: #a626a4;line-height: 26px;">this</span>.applicationContext);<br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #a626a4;line-height: 26px;">this</span>.evaluator.condition(condition, <span style="color: #a626a4;line-height: 26px;">this</span>.methodKey, evaluationContext);<br> }<br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #a626a4;line-height: 26px;">true</span>;<br> }<br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 对事务事件方法的返回值进行处理,这里的处理方式主要是将其作为一个事件继续发布出去,这样就可以在</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 一个统一的位置对事务事件的返回值进行处理</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">protected</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">handleResult</span><span style="line-height: 26px;">(Object result)</span> </span>{<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 如果返回值是数组类型,则对数组元素一个一个进行发布</span><br> <span style="color: #a626a4;line-height: 26px;">if</span> (result.getClass().isArray()) {<br> Object[] events = ObjectUtils.toObjectArray(result);<br> <span style="color: #a626a4;line-height: 26px;">for</span> (Object event : events) {<br> publishEvent(event);<br> }<br> } <span style="color: #a626a4;line-height: 26px;">else</span> <span style="color: #a626a4;line-height: 26px;">if</span> (result <span style="color: #a626a4;line-height: 26px;">instanceof</span> Collection<?>) {<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 如果返回值是集合类型,则对集合进行遍历,并且发布集合中的每个元素</span><br> Collection<?> events = (Collection<?>) result;<br> <span style="color: #a626a4;line-height: 26px;">for</span> (Object event : events) {<br> publishEvent(event);<br> }<br> } <span style="color: #a626a4;line-height: 26px;">else</span> {<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 如果返回值是一个对象,则直接将其进行发布</span><br> publishEvent(result);<br> }<br>}<br></code></pre> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">六、小结</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">对于事务事件的处理,总结而言,就是为每个事务事件监听方法创建了一个 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionSynchronizationEventAdapter</code>对象,通过该对象在发布事务事件的时候,会在当前线程中注册该对象,这样就可以保证每个线程每个监听器中只会对应一个 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">TransactionSynchronizationEventAdapter</code>对象。在Spring进行事务事件的时候会调用该对象对应的监听方法,从而达到对事务事件进行监听的目的。</p> </section> </section> </section> </section>
作者:微信小助手
<section style="font-size: 16px;" data-mpa-powered-by="yiban.io"> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.6;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;padding: 5px;font-size: 16px;color: rgb(53, 53, 53);word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;"> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.6;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;padding: 5px;font-size: 16px;color: rgb(53, 53, 53);word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">文章目录如下:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-fileid="100016833" data-ratio="0.21674140508221226" src="/upload/8790972537ebb048b1921239a5d10703.png" data-type="png" data-w="1338" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">网关如何限流?</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">Spring Cloud Gateway本身自带的限流实现,过滤器是<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">RequestRateLimiterGatewayFilterFactory</code>,不过这种上不了台面的就不再介绍了,有兴趣的可以实现下。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">今天的重点是集成阿里的<span style="font-weight: 700;color: rgb(248, 57, 41);">Sentinel</span>实现网关限流,sentinel有不懂的可以看陈某的文章:<a href="https://mp.weixin.qq.com/s?__biz=MzU3MDAzNDg1MA==&mid=2247498039&idx=1&sn=3a3caee655ff015b46249bd51aa4dc79&scene=21#wechat_redirect" style="color: rgb(248, 57, 41);border-bottom: 1px solid rgb(248, 57, 41);" data-linktype="2">阿里限流神器Sentinel夺命连环 17 问?</a></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">从1.6.0版本开始,Sentinel提供了SpringCloud Gateway的适配模块,可以提供两种资源维度的限流:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <span style="font-weight: 700;color: rgb(248, 57, 41);">route维度</span>:即在配置文件中配置的路由条目,资源名为对应的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">routeId</code>,这种属于粗粒度的限流,一般是对某个微服务进行限流。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <span style="font-weight: 700;color: rgb(248, 57, 41);">自定义API维度</span>:用户可以利用Sentinel提供的API来自定义一些API分组,这种属于细粒度的限流,针对某一类的uri进行匹配限流,可以跨多个微服务。 </section></li> </ul> <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: 15px;padding-right: 10px;padding-bottom: 15px;line-height: 1.75;border-radius: 13px;color: rgb(53, 53, 53);background: rgb(245, 245, 245);"> <span style="display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">“</span> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 16px;margin-right: 10px;margin-left: 10px;">sentinel官方文档:https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81</p> <span style="float: right;display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">”</span> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">Spring Cloud Gateway集成Sentinel实现很简单,这就是阿里的魅力,提供简单、易操作的工具,让程序员专注于业务。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">新建项目</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">新建一个<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">gateway-sentinel9026</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/A7sq8BD8oewJibQ3rHv2SQ8IrX6oXXiciaPmTIMiceSuiam7YoSysiccdJTCbfZDRyQCvbnAUPXSbdRxIAZMPO5wTlonFpFWHWJN6E/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #a0a1a7;font-style: italic;line-height: 26px;"><!--nacos注册中心--></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">dependency</span>></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">groupId</span>></span>com.alibaba.cloud<span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">groupId</span>></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">artifactId</span>></span>spring-cloud-starter-alibaba-nacos-discovery<span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">artifactId</span>></span><br> <span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">dependency</span>></span><br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;"><!--spring cloud gateway--></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">dependency</span>></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">groupId</span>></span>org.springframework.cloud<span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">groupId</span>></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">artifactId</span>></span>spring-cloud-starter-gateway<span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">artifactId</span>></span><br> <span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">dependency</span>></span><br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;"><!-- spring cloud gateway整合sentinel的依赖--></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">dependency</span>></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">groupId</span>></span>com.alibaba.cloud<span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">groupId</span>></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">artifactId</span>></span>spring-cloud-alibaba-sentinel-gateway<span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">artifactId</span>></span><br> <span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">dependency</span>></span><br><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;"><!-- sentinel的依赖--></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">dependency</span>></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">groupId</span>></span>com.alibaba.cloud<span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">groupId</span>></span><br> <span style="line-height: 26px;"><<span style="color: #e45649;line-height: 26px;">artifactId</span>></span>spring-cloud-starter-alibaba-sentinel<span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">artifactId</span>></span><br> <span style="line-height: 26px;"></<span style="color: #e45649;line-height: 26px;">dependency</span>></span><br></code></pre> <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: 15px;padding-right: 10px;padding-bottom: 15px;line-height: 1.75;border-radius: 13px;color: rgb(53, 53, 53);background: rgb(245, 245, 245);"> <span style="display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">“</span> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 16px;margin-right: 10px;margin-left: 10px;"><span style="font-weight: 700;color: rgb(248, 57, 41);">注意</span>:这依然是一个网关服务,不要添加WEB的依赖</p> <span style="float: right;display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">”</span> </blockquote> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">配置文件</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">配置文件中主要指定以下三种配置:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> nacos的地址 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> sentinel控制台的地址 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 网关路由的配置 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">配置如下:</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/A7sq8BD8oewJibQ3rHv2SQ8IrX6oXXiciaPmTIMiceSuiam7YoSysiccdJTCbfZDRyQCvbnAUPXSbdRxIAZMPO5wTlonFpFWHWJN6E/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #986801;line-height: 26px;">spring:</span><br> <span style="color: #986801;line-height: 26px;">cloud:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 整合sentinel,配置sentinel控制台的地址</span><br> <span style="color: #986801;line-height: 26px;">sentinel:</span><br> <span style="color: #986801;line-height: 26px;">transport:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 指定控制台的地址,默认端口8080</span><br> <span style="color: #986801;line-height: 26px;">dashboard:</span> <span style="color: #50a14f;line-height: 26px;">localhost:8080</span><br> <span style="color: #986801;line-height: 26px;">nacos:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 注册中心配置</span><br> <span style="color: #986801;line-height: 26px;">discovery:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;"># nacos的服务地址,nacos-server中IP地址:端口号</span><br> <span style="color: #986801;line-height: 26px;">server-addr:</span> <span style="color: #986801;line-height: 26px;">127.0</span><span style="color: #986801;line-height: 26px;">.0</span><span style="color: #986801;line-height: 26px;">.1</span><span style="color: #50a14f;line-height: 26px;">:8848</span><br> <span style="color: #986801;line-height: 26px;">gateway:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 路由</span><br> <span style="color: #986801;line-height: 26px;">routes:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## id只要唯一即可,名称任意</span><br> <span style="color: #4078f2;line-height: 26px;">-</span> <span style="color: #986801;line-height: 26px;">id:</span> <span style="color: #50a14f;line-height: 26px;">gateway-provider</span><br> <span style="color: #986801;line-height: 26px;">uri:</span> <span style="color: #50a14f;line-height: 26px;">lb://gateway-provider</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 配置断言</span><br> <span style="color: #986801;line-height: 26px;">predicates:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## Path Route Predicate Factory断言,满足/gateway/provider/**这个请求路径的都会被路由到http://localhost:9024这个uri中</span><br> <span style="color: #4078f2;line-height: 26px;">-</span> <span style="color: #50a14f;line-height: 26px;">Path=/gateway/provider/**</span><br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">上述配置中设置了一个路由<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">gateway-provider</code>,只要请求路径满足<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">/gateway/provider/**</code>都会被路由到<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">gateway-provider</code>这个服务中。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">限流配置</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">经过上述两个步骤其实已经整合好了Sentinel,此时访问一下接口:http://localhost:9026/gateway/provider/port</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">然后在sentinel控制台可以看到已经被监控了,监控的路由是<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">gateway-provider</code>,如下图:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-fileid="100016834" data-ratio="0.33877766069546894" src="/upload/4b96255c379b7f2b236f31bbb2ca0fd9.png" data-type="png" data-w="1898" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">此时我们可以为其新增一个route维度的限流,如下图:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-fileid="100016835" data-ratio="0.70261066969353" src="/upload/9e221768d24a8f5811d3248467b2fb8d.png" data-type="png" data-w="881" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">上图中对<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">gateway-provider</code>这个路由做出了限流,QPS阈值为1。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">此时快速访问:http://localhost:9026/gateway/provider/port,看到已经被限流了,如下图:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-fileid="100016836" data-ratio="0.4675419401896426" src="/upload/9b11ccf146c547f6a5e9899807f05626.png" data-type="png" data-w="1371" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">以上route维度的限流已经配置成功,小伙伴可以自己照着上述步骤尝试一下。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">API分组限流也很简单,首先需要定义一个分组,<span style="font-weight: 700;color: rgb(248, 57, 41);">API管理-> 新增API分组</span>,如下图:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-fileid="100016837" data-ratio="0.33125" src="/upload/67aeb63c65709b51e8d67acb04a777ac.png" data-type="png" data-w="1920" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">匹配模式选择了精确匹配(还有前缀匹配,正则匹配),因此只有这个uri:<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">http://xxxx/gateway/provider/port</code>会被限流。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">第二步需要对这个分组添加流控规则,<span style="font-weight: 700;color: rgb(248, 57, 41);">流控规则->新增网关流控</span>,如下图:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-fileid="100016841" data-ratio="0.4164484023048717" src="/upload/5cce17df4d6d895def63cb9d0e428c73.png" data-type="png" data-w="1909" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">API名称那里选择对应的分组即可,新增之后,限流规则就生效了。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">陈某不再测试了,小伙伴自己动手测试一下吧...............</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: 15px;padding-right: 10px;padding-bottom: 15px;line-height: 1.75;border-radius: 13px;color: rgb(53, 53, 53);background: rgb(245, 245, 245);"> <span style="display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">“</span> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 16px;margin-right: 10px;margin-left: 10px;">陈某这里只是简单的配置一下,至于限流规则持久化一些内容请看陈某的Sentinel文章,这里就不再过多的介绍了。</p> <span style="float: right;display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">”</span> </blockquote> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">如何自定义限流异常信息?</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">从上面的演示中可以看到默认的异常返回信息是:"Block.........",这种肯定是客户端不能接受的,因此需要定制自己的异常返回信息。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">下面介绍两种不同的方式定制异常返回信息,开发中自己选择其中一种。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">直接配置文件中定制</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">开发者可以直接在配置文件中直接修改返回信息,配置如下:</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/A7sq8BD8oewJibQ3rHv2SQ8IrX6oXXiciaPmTIMiceSuiam7YoSysiccdJTCbfZDRyQCvbnAUPXSbdRxIAZMPO5wTlonFpFWHWJN6E/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #986801;line-height: 26px;">spring:</span><br> <span style="color: #986801;line-height: 26px;">cloud:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 整合sentinel,配置sentinel控制台的地址</span><br> <span style="color: #986801;line-height: 26px;">sentinel:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">#配置限流之后,响应内容</span><br> <span style="color: #986801;line-height: 26px;">scg:</span><br> <span style="color: #986801;line-height: 26px;">fallback:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 两种模式,一种是response返回文字提示信息,</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 一种是redirect,重定向跳转,需要同时配置redirect(跳转的uri)</span><br> <span style="color: #986801;line-height: 26px;">mode:</span> <span style="color: #50a14f;line-height: 26px;">response</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 响应的状态</span><br> <span style="color: #986801;line-height: 26px;">response-status:</span> <span style="color: #986801;line-height: 26px;">200</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 响应体</span><br> <span style="color: #986801;line-height: 26px;">response-body:</span> <span style="color: #50a14f;line-height: 26px;">'{"code": 200,"message": "请求失败,稍后重试!"}'</span><br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">上述配置中<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">mode</code>配置的是<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">response</code>,一旦被限流了,将会返回<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">JSON</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/A7sq8BD8oewJibQ3rHv2SQ8IrX6oXXiciaPmTIMiceSuiam7YoSysiccdJTCbfZDRyQCvbnAUPXSbdRxIAZMPO5wTlonFpFWHWJN6E/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;">{<br> <span style="color: #986801;line-height: 26px;">"code"</span>: <span style="color: #986801;line-height: 26px;">200</span>,<br> <span style="color: #986801;line-height: 26px;">"message"</span>: <span style="color: #50a14f;line-height: 26px;">"请求失败,稍后重试!"</span><br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">重定向</span>的配置如下:</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/A7sq8BD8oewJibQ3rHv2SQ8IrX6oXXiciaPmTIMiceSuiam7YoSysiccdJTCbfZDRyQCvbnAUPXSbdRxIAZMPO5wTlonFpFWHWJN6E/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #986801;line-height: 26px;">spring:</span><br> <span style="color: #986801;line-height: 26px;">cloud:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 整合sentinel,配置sentinel控制台的地址</span><br> <span style="color: #986801;line-height: 26px;">sentinel:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">#配置限流之后,响应内容</span><br> <span style="color: #986801;line-height: 26px;">scg:</span><br> <span style="color: #986801;line-height: 26px;">fallback:</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 两种模式,一种是response返回文字提示信息,一种是redirect,重定向跳转,需要同时配置redirect(跳转的uri)</span><br> <span style="color: #986801;line-height: 26px;">mode:</span> <span style="color: #50a14f;line-height: 26px;">redirect</span><br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">## 跳转的URL</span><br> <span style="color: #986801;line-height: 26px;">redirect:</span> <span style="color: #50a14f;line-height: 26px;">http://www.baidu.com</span><br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">一旦被限流,将会直接跳转到:http://www.baidu.com</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">编码定制</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">这种就不太灵活了,通过硬编码的方式,完整代码如下:</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/A7sq8BD8oewJibQ3rHv2SQ8IrX6oXXiciaPmTIMiceSuiam7YoSysiccdJTCbfZDRyQCvbnAUPXSbdRxIAZMPO5wTlonFpFWHWJN6E/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@Configuration</span><br><span style="color: #a626a4;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">class</span> <span style="color: #c18401;line-height: 26px;">GatewayConfig</span> </span>{<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">/**<br> * 自定义限流处理器<br> */</span><br> <span style="color: #4078f2;line-height: 26px;">@PostConstruct</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> <span style="color: #a626a4;line-height: 26px;">void</span> <span style="color: #4078f2;line-height: 26px;">initBlockHandlers</span><span style="line-height: 26px;">()</span> </span>{<br> BlockRequestHandler blockHandler = (serverWebExchange, throwable) -> {<br> Map map = <span style="color: #a626a4;line-height: 26px;">new</span> HashMap();<br> map.put(<span style="color: #50a14f;line-height: 26px;">"code"</span>,<span style="color: #986801;line-height: 26px;">200</span>);<br> map.put(<span style="color: #50a14f;line-height: 26px;">"message"</span>,<span style="color: #50a14f;line-height: 26px;">"请求失败,稍后重试!"</span>);<br> <span style="color: #a626a4;line-height: 26px;">return</span> ServerResponse.status(HttpStatus.OK)<br> .contentType(MediaType.APPLICATION_JSON_UTF8)<br> .body(BodyInserters.fromObject(map));<br> };<br> GatewayCallbackManager.setBlockHandler(blockHandler);<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">两种方式介绍完了,根据业务需求自己选择适合的方式,当然陈某更喜欢第一种,理由:<span style="font-weight: 700;color: rgb(248, 57, 41);">约定>配置>编码</span>。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">网关限流了,服务就安全了吗?</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">很多人认为只要网关层面做了限流,躲在身后的服务就可以高枕无忧了,你是不是也有这种想法?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">很显然这种想法是错误的,复杂的微服务架构一个独立服务不仅仅被一方调用,往往是多方调用,如下图:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img data-fileid="100016839" data-ratio="0.5913838120104439" src="/upload/c32619339f86f42cdb366946869024dc.png" data-type="png" data-w="766" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">商品服务不仅仅被网关层调用,还被内部订单服务调用,这时候仅仅在网关层限流,那么商品服务还安全吗?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">一旦大量的请求订单服务,比如大促秒杀,商品服务不做限流会被瞬间击垮。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">因此需要根据公司业务场景对自己负责的服务也要进行限流兜底,最常见的方案:<span style="font-weight: 700;color: rgb(248, 57, 41);">网关层集群限流+内部服务的单机限流兜底</span>,这样才能保证不被流量冲垮。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">总结</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">文章介绍了Spring Cloud Gateway整合Sentinel对网关层进行限流,以及关于限流的一些思考。如有错误之处,欢迎留言指正。</p> </section> </section> </section>
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" data-mpa-powered-by="yiban.io" style="outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <h3 data-tool="mdnice编辑器" style="margin: 5px 16px 10px;outline: 0px;max-width: 100%;text-align: left;line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><strong style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">Postman 最被低估的功能,自动化接口测试效率简直无敌!</strong></span></h3> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">该篇文章针对已经掌握 Postman 基本用法的读者,即对接口相关概念有一定了解、已经会使用 Postman 进行模拟请求的操作。</span> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">当前环境:</span> </section> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin: 8px 16px;padding-left: 25px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <li style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">Window 7 - 64</span></p></li> <li style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">Postman 版本(免费版):Chrome App v5.5.3</span></p></li> </ul> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">不同版本页面 UI 和部分功能位置会有点不同,不过影响不大。</span> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">我们先思考一下,如果需要达到自动化接口测试的效果,那么我们在基本的模拟请求上还需要做哪些呢?</span> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">以下我粗略概括为 3 个问题(欢迎更多补充与建议):</span> </section> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin: 8px 16px;padding-left: 25px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <li style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">如何判断接口是否请求成功</span></p></li> <li style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">如何进行接口批量、定期测试</span></p></li> <li style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">如何处理依赖接口问题(比如商品下单的接口必须要求先登录)</span></p></li> </ul> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">所以,接下来就主要分为 3 个部分进行介绍,以分别解决这 3 个问题。</span> </section> <h3 data-tool="mdnice编辑器" style="margin: 10px 16px;outline: 0px;font-weight: bold;font-size: 20.4px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="color: rgb(123, 12, 0);"><strong style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="color: rgb(123, 12, 0);outline: 0px;max-width: 100%;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;font-size: 17px;box-sizing: border-box !important;overflow-wrap: break-word !important;">接口结果判断</span></strong></span></h3> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">首先,既然是自动化测试,那么我们肯定需要工具 (Postman) 或者代码能帮我们直接判断结果是否符合预期。那么在接口测试上,大体就两个思路:</span> </section> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin: 8px 16px;padding-left: 25px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <li style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">判断请求返回的 code 是否符合预期</span></p></li> <li style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">判断请求返回的内容中是否包含预期的内容(关键字)</span></p></li> </ul> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">接下来我们看看如何利用 Postman 来解决上述的问题:</span> </section> <h3 data-tool="mdnice编辑器" style="margin: 10px 16px;outline: 0px;font-weight: bold;font-size: 20.4px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">功能区</span></h3> <section style="margin-right: 16px;margin-left: 16px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;display: flex;flex-direction: column;justify-content: center;align-items: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <img data-fileid="504020082" data-ratio="0.39644444444444443" src="/upload/322e4a736bd2d5c24e7e0573c039845b.png" data-type="png" data-w="2250" style="margin-right: auto;margin-left: auto;outline: 0px;display: block;box-sizing: border-box !important;overflow-wrap: break-word !important;width: 556px !important;visibility: visible !important;"> </figure> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">在 Postman 中相关的功能在非常显眼的地方,Tests 功能的使用需要我们有一定的编程语言基础,目前支持的脚本语言即为 JavaScript 。但比较好的一点是,我们不需要再去考虑上下文问题以及运行环境的问题 ,也就是说我们只需要在这边完成结果逻辑判断的代码块即可。</span> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">而 Postman 还为我们提供了一些常用的代码模板,在 Tests 面板右边的 SNIPPETS 功能区中,所以对 JavaScript 不大了解问题也不大。代码编写相关将在下文进行具体介绍。</span> </section> <h3 data-tool="mdnice编辑器" style="margin: 10px 16px;outline: 0px;font-weight: bold;font-size: 20.4px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="color: rgb(123, 12, 0);"><strong style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="color: rgb(123, 12, 0);outline: 0px;max-width: 100%;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;font-size: 17px;box-sizing: border-box !important;overflow-wrap: break-word !important;">脚本相关</span></strong></span></h3> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">先看上图的代码部分,我们可以发现 responseCode 、 responseBody 和 tests 三个变量(可直接使用) :</span> </section> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin: 8px 16px;padding-left: 25px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <li style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><code style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">responseCode</span></code><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> :包含请求的返回的状态信息(如:code)</span></p></li> <li style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><code style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">responseBody</span></code><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">:为接口请求放回的数据内容(类型为字符串)</span></p></li> <li style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><code style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">tests</span></code><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> :为键值对形式,用于表示我们的测试结果是成功与否,最终展示在 Test Results 中。</span></p></li> <li style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><code style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">key</span></code><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> :(如:code 200)我们可以用来当做结果的一个描述</span></p></li> <li style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><code style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">value</span></code><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">:其值为布尔型,ture 表示测试通过, false 表示测试失败。</span></p></li> </ul> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">所以上述代码应该不难理解了,而有了返回结果的数据以及表示结果成功与否的方式,那么我们“接口结果判断”的问题也就基本解决了。</span> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">另外还有几个比较常用的:</span> </section> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin: 8px 16px;padding-left: 25px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <li style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">responseTime :请求所耗时长</span></p></li> <li style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">postman :可以做的比较多,比如</span></p></li> <ul class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;outline: 0px;max-width: 100%;color: black;list-style-type: square;overflow-wrap: break-word !important;"> <li style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">获取返回数据的头部信息:</span><code style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">postman.getResponseHeader("")</span></code></p></li> <li style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">设置全局变量:</span><code style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">postman.setGlobalVariable("variable_key", "variable_value");</span></code></p></li> </ul> </ul> <h3 data-tool="mdnice编辑器" style="margin: 10px 16px;outline: 0px;font-weight: bold;font-size: 20.4px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="color: rgb(123, 12, 0);"><strong style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="color: rgb(123, 12, 0);outline: 0px;max-width: 100%;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;font-size: 17px;box-sizing: border-box !important;overflow-wrap: break-word !important;">代码模板</span></strong></span></h3> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">Postman 在 SNIPPETS 功能区中为我们提供的代码模板已经能解决大部分情况了,以下先挑几个跟结果判断相关的进行讲解:</span> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">Status code : Code is 200</span> </section> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t" style="outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <pre style="outline: 0px;max-width: 100%;background: none;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin-right: 16px;margin-left: 16px;padding: 5.20625px;outline: 0px;max-width: 100%;border-radius: 4px;font-size: 0.85em;background: rgb(248, 248, 248);overflow-x: auto;white-space: nowrap;line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;font-size: 12px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;color: rgb(153, 153, 136);background: rgba(0, 0, 0, 0);width: 197px;text-decoration-style: solid;text-decoration-color: rgb(153, 153, 136);font-style: italic;box-sizing: border-box !important;overflow-wrap: break-word !important;">//根据返回的 Code 判断请求情况</span><br mpa-from-tpl="t" style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">tests[<span style="outline: 0px;max-width: 100%;color: rgb(221, 17, 68);background: rgba(0, 0, 0, 0);width: 145px;text-decoration-style: solid;text-decoration-color: rgb(221, 17, 68);box-sizing: border-box !important;overflow-wrap: break-word !important;">"Status code is 200"</span>] = responseCode.code === <span style="outline: 0px;max-width: 100%;color: rgb(0, 128, 128);background: rgba(0, 0, 0, 0);width: 22px;text-decoration-style: solid;text-decoration-color: rgb(0, 128, 128);box-sizing: border-box !important;overflow-wrap: break-word !important;">200</span>;</span> </section></pre> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">Response body: Contains string</span> </section> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t" style="outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <pre style="outline: 0px;max-width: 100%;background: none;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin-right: 16px;margin-left: 16px;padding: 5.20625px;outline: 0px;max-width: 100%;border-radius: 4px;font-size: 0.85em;background: rgb(248, 248, 248);overflow-x: auto;white-space: nowrap;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;font-size: 12px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;color: rgb(153, 153, 136);background: rgba(0, 0, 0, 0);width: 459px;text-decoration-style: solid;text-decoration-color: rgb(153, 153, 136);font-style: italic;box-sizing: border-box !important;overflow-wrap: break-word !important;">//判断返回的内容中是否存在“关键字”。(tests 的 key 可修改,将不再强调)</span><br mpa-from-tpl="t" style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">tests[<span style="outline: 0px;max-width: 100%;color: rgb(221, 17, 68);background: rgba(0, 0, 0, 0);width: 152px;text-decoration-style: solid;text-decoration-color: rgb(221, 17, 68);box-sizing: border-box !important;overflow-wrap: break-word !important;">"Body matches string"</span>] = responseBody.has(<span style="outline: 0px;max-width: 100%;color: rgb(221, 17, 68);background: rgba(0, 0, 0, 0);width: 206px;text-decoration-style: solid;text-decoration-color: rgb(221, 17, 68);box-sizing: border-box !important;overflow-wrap: break-word !important;">"这里可以改为你要判断的关键字内容"</span>);<br mpa-from-tpl="t" style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br mpa-from-tpl="t" style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;color: rgb(153, 153, 136);background: rgba(0, 0, 0, 0);width: 98px;text-decoration-style: solid;text-decoration-color: rgb(153, 153, 136);font-style: italic;box-sizing: border-box !important;overflow-wrap: break-word !important;">//如上文提到的:</span><br mpa-from-tpl="t" style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;color: rgb(153, 153, 136);background: rgba(0, 0, 0, 0);width: 267px;text-decoration-style: solid;text-decoration-color: rgb(153, 153, 136);font-style: italic;box-sizing: border-box !important;overflow-wrap: break-word !important;">// 判断结果中是否存在 access_token 关键字</span><br mpa-from-tpl="t" style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">tests[<span style="outline: 0px;max-width: 100%;color: rgb(221, 17, 68);background: rgba(0, 0, 0, 0);width: 130px;text-decoration-style: solid;text-decoration-color: rgb(221, 17, 68);box-sizing: border-box !important;overflow-wrap: break-word !important;">"has access_token"</span>] = responseBody.has(<span style="outline: 0px;max-width: 100%;color: rgb(221, 17, 68);background: rgba(0, 0, 0, 0);width: 101px;text-decoration-style: solid;text-decoration-color: rgb(221, 17, 68);box-sizing: border-box !important;overflow-wrap: break-word !important;">"access_token"</span>);</span> </section></pre> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">Response body: is equal to string</span> </section> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t" style="outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <pre style="outline: 0px;max-width: 100%;background: none;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin-right: 16px;margin-left: 16px;padding: 5.20625px;outline: 0px;max-width: 100%;border-radius: 4px;font-size: 0.85em;background: rgb(248, 248, 248);overflow-x: auto;white-space: nowrap;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;font-size: 12px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;color: rgb(153, 153, 136);background: rgba(0, 0, 0, 0);width: 206px;text-decoration-style: solid;text-decoration-color: rgb(153, 153, 136);font-style: italic;box-sizing: border-box !important;overflow-wrap: break-word !important;">//判断返回内容是否跟预期完全相等。</span><br mpa-from-tpl="t" style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">tests[<span style="outline: 0px;max-width: 100%;color: rgb(221, 17, 68);background: rgba(0, 0, 0, 0);width: 123px;text-decoration-style: solid;text-decoration-color: rgb(221, 17, 68);box-sizing: border-box !important;overflow-wrap: break-word !important;">"Body is correct"</span>] = responseBody === <span style="outline: 0px;max-width: 100%;color: rgb(221, 17, 68);background: rgba(0, 0, 0, 0);width: 158px;text-decoration-style: solid;text-decoration-color: rgb(221, 17, 68);box-sizing: border-box !important;overflow-wrap: break-word !important;">"这里可以改为你的预期内容"</span>;</span> </section></pre> </section> <section style="margin: 10px 16px;padding-top: 8px;padding-bottom: 8px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);color: rgb(63, 63, 63);line-height: 2em;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;box-sizing: border-box !important;overflow-wrap: break-word !important;">Response body: JSON value check</span> </section> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t" style="outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <pre style="outline: 0px;max-width: 100%;background: none;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin-right: 16px;margin-left: 16px;padding: 5.20625px;outline: 0px;max-width: 100%;border-radius: 4px;font-size: 0.85em;background: rgb(248, 248, 248);overflow-x: auto;white-space: nowrap;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="outline: 0px;max-width: 100%;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 1px;font-size: 12px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;color: rgb(153, 153, 136);background: rgba(0, 0, 0, 0);width: 367px;text-decoration-style: solid;text-decoration-color: rgb(153, 153, 136);font-style: italic;box-sizing: border-box !important;overflow-wrap: break-word !important;">//上文提到,responseBody 为字符串类型,支持转为 Json 格式</span><br mpa-from-tpl="t" style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;background: rgba(0, 0, 0, 0);width: 21px;text-decoration-style: solid;text-decoration-color: rgb(51, 51, 51);font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">var</span> jsonData = JSON.parse(re
作者:微信小助手
<section data-mpa-powered-by="yiban.io"> <p style="text-align: left;"><br></p> <section style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">{<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#!/bin/sh # 在脚本第一行脚本头 # sh为当前系统默认shell,可指定为bash等shell</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">shopt</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 显示和设置shell中的行为选项</span><br> sh -x <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 执行过程</span><br> sh -n <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 检查语法</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">set</span> -e <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 若指令传回值不等于0,则立即退出shell</span><br> (a=bbk) <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 括号创建子shell运行</span><br> basename /a/b/c <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 从全路径中保留最后一层文件名或目录</span><br> dirname <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 取路径</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(98, 151, 85);overflow-wrap: inherit !important;word-break: inherit !important;">$RANDOM</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 随机数</span><br> $$ <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 进程号</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">source</span> FileName <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 在当前bash环境下读取并执行FileName中的命令 # 等同 . FileName</span><br> sleep 5 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 间隔睡眠5秒</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">trap</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 在接收到信号后将要采取的行动</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">trap</span> <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">""</span> 2 3 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 禁止ctrl+c</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(98, 151, 85);overflow-wrap: inherit !important;word-break: inherit !important;">$PWD</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 当前目录</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(98, 151, 85);overflow-wrap: inherit !important;word-break: inherit !important;">$HOME</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 家目录</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(98, 151, 85);overflow-wrap: inherit !important;word-break: inherit !important;">$OLDPWD</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 之前一个目录的路径</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">cd</span> - <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 返回上一个目录路径</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">local</span> ret <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 局部变量</span><br> yes <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 重复打印</span><br> yes |rm -i * <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 自动回答y或者其他</span><br> ls -p /home <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 区分目录和文件夹</span><br> ls -d /home/ <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 查看匹配完整路径</span><br> time a.sh <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 测试程序执行时间</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> -n aa;<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> bb <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 不换行执行下一句话 将字符串原样输出</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> -e <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"s\tss\n\n\n"</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 使转义生效</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> <span style="font-size: inherit;line-height: inherit;color: rgb(98, 151, 85);overflow-wrap: inherit !important;word-break: inherit !important;">$a</span> | cut -c2-6 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 取字符串中字元</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> {a,b,c}{a,b,c}{a,b,c} <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 排列组合(括号内一个元素分别和其他括号内元素组合)</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> $((2<span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#11010)) # 二进制转10进制</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> aaa | tee file <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 打印同时写入文件 默认覆盖 -a追加</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> {1..10} <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 打印10个字符</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">printf</span> <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">'%10s\n'</span>|tr <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">" "</span> a <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 打印10个字符</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">pwd</span> | awk -F/ <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">'{ print $2 }'</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 返回目录名</span><br> tac file |sed 1,3d|tac <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 倒置读取文件 # 删除最后3行</span><br> tail -3 file <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 取最后3行</span><br> outtmp=/tmp/$$`date +%s%N`.outtmp <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 临时文件定义</span><br> :(){ :|:& };: <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># fork炸弹,系统执行海量的进程,直到系统僵死</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> -e <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"\e[32mcolour\e[0m"</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 打印颜色</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> -e <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"\033[32mcolour\033[m"</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 打印颜色</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">echo</span> -e <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"\033[0;31mL\033[0;32mO\033[0;33mV\033[0;34mE\t\033[0;35mY\033[0;36mO\033[0;32mU\e[m"</span> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 打印颜色</span><br><br> 正则表达式{<br><br> ^ <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 行首定位</span><br> $ <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 行尾定位</span><br> . <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配除换行符以外的任意字符</span><br> * <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配0或多个重复字符</span><br> + <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 重复一次或更多次</span><br> ? <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 重复零次或一次</span><br> ? <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 结束贪婪因子 .*? 表示最小匹配</span><br> [] <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配一组中任意一个字符</span><br> [^] <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配不在指定组内的字符</span><br> \ <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 用来转义元字符</span><br> < <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 词首定位符(支持vi和grep) <love</span><br> > <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 词尾定位符(支持vi和grep) love></span><br> x\{m\} <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 重复出现m次</span><br> x\{m,\} <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 重复出现至少m次</span><br> x\{m,n\} <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 重复出现至少m次不超过n次</span><br> X? <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配出现零次或一次的大写字母 X</span><br> X+ <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配一个或多个字母 X</span><br> () <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 括号内的字符为一组</span><br> (ab|de)+ <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配一连串的(最少一个) abc 或 def;abc 和 def 将匹配</span><br> [[:alpha:]] <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 代表所有字母不论大小写</span><br> [[:lower:]] <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 表示小写字母</span><br> [[:upper:]] <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 表示大写字母</span><br> [[:digit:]] <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 表示数字字符</span><br> [[:digit:][:lower:]] <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 表示数字字符加小写字母</span><br><br> 元字符{<br><br> \d <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配任意一位数字</span><br> \D <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配任意单个非数字字符</span><br> \w <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配任意单个字母数字下划线字符,同义词是 [:alnum:]</span><br> \W <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配非数字型的字符</span><br><br> }<br><br> 字符类:空白字符{<br><br> \s <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配任意的空白符</span><br> \S <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配非空白字符</span><br> \b <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配单词的开始或结束</span><br> \n <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配换行符</span><br> \r <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配回车符</span><br> \t <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配制表符</span><br> \b <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配退格符</span><br> \0 <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配空值字符</span><br><br> }<br><br> 字符类:锚定字符{<br><br> \b <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配字边界(不在[]中时)</span><br> \B <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># 匹配非字边界</span><br> \A <span style="font-size: inherit;line-h
作者:微信小助手
<section data-mpa-powered-by="yiban.io"> <p style="text-align: center;"><span style="background-color: rgb(255, 255, 255);font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;font-size: 15px;letter-spacing: 0.544px;text-align: start;caret-color: rgb(51, 51, 51);">WireShark是一个网络封包分析软件。</span><span style="background-color: rgb(255, 255, 255);font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;font-size: 15px;letter-spacing: 0.544px;text-align: start;caret-color: rgb(51, 51, 51);">网络封包分析软件的功能是撷取网络封包,并尽可能显示出最为详细的网络封包资料。</span><span style="background-color: rgb(255, 255, 255);font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;font-size: 15px;letter-spacing: 0.544px;text-align: start;caret-color: rgb(51, 51, 51);">Wireshark使用WinPCAP作为接口,直接与网卡进行数据报文交换。</span><span style="background-color: rgb(255, 255, 255);font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;font-size: 15px;letter-spacing: 0.544px;text-align: start;caret-color: rgb(51, 51, 51);">在网络封包和流量分析领域有着十分强大功能的工具,深受各类</span><strong style="font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;font-size: 15px;letter-spacing: 0.544px;text-align: start;caret-color: rgb(51, 51, 51);outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;color: rgb(0, 128, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">网络工程师</span></strong><span style="background-color: rgb(255, 255, 255);font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;font-size: 15px;letter-spacing: 0.544px;text-align: start;caret-color: rgb(51, 51, 51);">和网络分析师的喜爱。</span><br></p> <section> <p style="margin-bottom: 10px;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">本文主要内容包括:</p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;outline: 0px;max-width: 100%;caret-color: rgb(51, 51, 51);white-space: normal;text-size-adjust: auto;font-size: 16px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: -apple-system, system-ui, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <blockquote data-tool="mdnice编辑器" style="margin-top: 20px;margin-bottom: 20px;padding: 10px 10px 10px 20px;outline: 0px;border-top-style: none;border-right-style: none;border-bottom-style: none;border-left-color: rgb(178, 174, 197);color: rgb(106, 115, 125);font-size: 0.9em;max-width: 100%;overflow: auto;background-color: rgb(255, 249, 249);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <ul class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;outline: 0px;max-width: 100%;color: black;overflow-wrap: break-word !important;"> <li style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 1、Wireshark主界面介绍。 </section></li> <li style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 2、WireShark简单抓包示例。通过该例子学会怎么抓包以及如何简单查看分析数据包内容。 </section></li> <li style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;max-width: 100%;color: rgb(1, 1, 1);line-height: 2;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 3、Wireshark过滤器使用。通过过滤器可以筛选出想要分析的内容。包括按照协议过滤、端口和主机名过滤、数据包内容过滤。 </section></li> </ul> </blockquote> </section> <p style="margin-bottom: 10px;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">我们首先来介绍一下Wireshark这款软件。<img class="rich_pages wxw-img" data-fileid="100007154" data-ratio="1.0526315789473684" src="/upload/f56e0592a0517f5b2c9567ef4ee0a974.jpg" data-type="jpeg" data-w="76" style="margin: 15px auto 10px;outline: none;max-width: 635px;box-sizing: border-box;vertical-align: top;border-width: 0px;border-style: initial;border-color: initial;font-style: inherit;font-variant: inherit;font-weight: inherit;font-stretch: inherit;font-size: inherit;line-height: inherit;display: block;overflow-wrap: break-word !important;width: 76px !important;visibility: visible !important;"></p> <p style="margin-bottom: 10px;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">首先我们先认识一下这个软件的主界面是长这样的</p> <p style="margin-bottom: 10px;padding-right: 0.5em;padding-left: 0.5em;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-fileid="100007155" data-ratio="0.5379061371841155" src="/upload/efc5b2d9e61d3cd483b1d38c95685841.jpg" data-type="jpeg" data-w="554" style="margin: 15px auto 10px;outline: none;max-width: 635px;box-sizing: border-box;vertical-align: top;border-width: 0px;border-style: initial;border-color: initial;font-style: inherit;font-variant: inherit;font-weight: inherit;font-stretch: inherit;font-size: 17px;line-height: inherit;display: block;box-shadow: rgb(0, 0, 0) 0em 0em 0.5em 0px;overflow-wrap: break-word !important;width: 554px !important;visibility: visible !important;"></p> <p style="margin-bottom: 10px;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">在这个界面中为Wireshark的主界面</p> <p style="margin-bottom: 10px;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">选择菜单栏上Capture -> Option,勾选WLAN网卡(这里需要根据各自电脑网卡使用情况选择,简单的办法可以看使用的IP对应的网卡)。点击Start。启动抓包。</p> <p style="margin-bottom: 10px;padding-right: 0.5em;padding-left: 0.5em;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-fileid="100007156" data-ratio="0.4927797833935018" src="/upload/64ef7b5d25a761e3a7a243a4b59defcc.jpg" data-type="jpeg" data-w="554" style="margin: 15px auto 10px;outline: none;max-width: 635px;box-sizing: border-box;vertical-align: top;border-width: 0px;border-style: initial;border-color: initial;font-style: inherit;font-variant: inherit;font-weight: inherit;font-stretch: inherit;font-size: 17px;line-height: inherit;display: block;box-shadow: rgb(0, 0, 0) 0em 0em 0.5em 0px;overflow-wrap: break-word !important;width: 554px !important;visibility: visible !important;"></p> <p style="margin-bottom: 10px;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">wireshark启动后,wireshark处于抓包状态中。</p> <p style="margin-bottom: 10px;padding-right: 0.5em;padding-left: 0.5em;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-fileid="100007157" data-ratio="0.17443868739205526" data-type="jpeg" data-w="579" src="/upload/cc1ffa31ac54597a8196a7ca9cbc3fc7.jpg" style="margin: 15px auto 10px;outline: none;max-width: 635px;box-sizing: border-box;vertical-align: top;border-width: 0px;border-style: initial;border-color: initial;font-style: inherit;font-variant: inherit;font-weight: inherit;font-stretch: inherit;font-size: 17px;line-height: inherit;display: block;box-shadow: rgb(0, 0, 0) 0em 0em 0.5em 0px;overflow-wrap: break-word !important;width: 579px !important;visibility: visible !important;"></p> <p style="margin-bottom: 10px;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">1、执行需要抓包的操作,如ping www.baidu.com。</p> <p style="margin-bottom: 10px;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">2、操作完成后相关数据包就抓取到了。为避免其他无用的数据包影响分析,可以通过在过滤栏设置过滤条件进行数据包列表过滤,获取结果如下。说明:ip.addr == 119.75.217.26 and icmp 表示只显示ICPM协议且源主机IP或者目的主机IP为119.75.217.26的数据包。</p> <p style="margin-bottom: 10px;padding-right: 0.5em;padding-left: 0.5em;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-fileid="100007158" data-ratio="0.33934426229508197" src="/upload/476f699afd1f9ab86a6301ede1cbb9c4.jpg" data-type="jpeg" data-w="610" style="margin: 15px auto 10px;outline: none;max-width: 635px;box-sizing: border-box;border-width: 0px;border-style: initial;border-color: initial;vertical-align: top;font-style: inherit;font-variant: inherit;font-weight: inherit;font-stretch: inherit;font-size: 17px;line-height: inherit;display: block;box-shadow: rgb(0, 0, 0) 0em 0em 0.5em 0px;overflow-wrap: break-word !important;background-color: rgb(238, 237, 235) !important;background-size: 22px !important;height: 207px !important;width: 610px !important;background-position: center center !important;background-repeat: no-repeat !important;"></p> <p style="margin-bottom: 10px;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">3、wireshark抓包完成,就这么简单。关于wireshark过滤条件和如何查看数据包中的详细内容在后面介绍。</p> <h3 style="margin-top: 20px;margin-bottom: 20px;outline: none;font-weight: 600;max-width: 100%;letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;line-height: 26px;vertical-align: baseline;font-family: PingFangSC-Medium, "PingFang SC";word-break: break-word;color: rgba(0, 0, 0, 0.85);text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"></h3> <h3 style="margin-top: 20px;margin-bottom: 20px;outline: none;font-weight: 600;max-width: 100%;letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;line-height: 26px;vertical-align: baseline;font-family: PingFangSC-Medium, "PingFang SC";word-break: break-word;color: rgba(0, 0, 0, 0.85);text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: none;max-width: 100%;border-width: 0px;border-style: initial;border-color: initial;font-variant: inherit;font-stretch: inherit;font-size: inherit;line-height: inherit;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;box-sizing: border-box !important;overflow-wrap: break-word !important;">Wireshakr抓包界面</span></h3> <p style="margin-bottom: 10px;padding-right: 0.5em;padding-left: 0.5em;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-fileid="100007159" data-ratio="0.5351170568561873" src="/upload/11cf0cbf04959435574081ea2a60507d.jpg" data-type="jpeg" data-w="598" style="margin: 15px auto 10px;outline: none;max-width: 635px;box-sizing: border-box;border-width: 0px;border-style: initial;border-color: initial;vertical-align: top;font-style: inherit;font-variant: inherit;font-weight: inherit;font-stretch: inherit;font-size: 17px;line-height: inherit;display: block;box-shadow: rgb(0, 0, 0) 0em 0em 0.5em 0px;overflow-wrap: break-word !important;background-color: rgb(238, 237, 235) !important;background-size: 22px !important;height: 320px !important;width: 598px !important;background-position: center center !important;background-repeat: no-repeat !important;"></p> <p style="margin-bottom: 10px;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">说明:数据包列表区中不同的协议使用了不同的颜色区分。协议颜色标识定位在菜单栏View --> Coloring Rules。如下所示</p> <p style="margin-bottom: 10px;padding-right: 0.5em;padding-left: 0.5em;outline: none;max-width: 100%;caret-color: rgb(51, 51, 51);letter-spacing: 0.544px;white-space: normal;text-size-adjust: auto;border-width: 0px;border-style: initial;border-color: initial;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-stretch: inherit;font-size: 15px;line-height: 26px;vertical-align: baseline;font-family: 微软雅黑, "Microsoft YaHei", "WenQuanYi Micro Hei", PingFangSC;overflow: auto;word-break: break-word;text-align: start;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img class="rich_pages wxw-img" data-fileid="100007163" data-ratio="0.4278523489932886" src="/upload/1b4d6819
作者:微信小助手
<section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MjM5ODI5Njc2MA==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/MOwlO0INfQpzXtDQlnqOx4arg3nssiap6PXl3MYlficbxT9ML65AQrcp3HESLmibjjxOHoFKNKQBt3vHNmNXAsqnA/0?wx_fmt=png" data-nickname="51CTO技术栈" data-alias="blog51cto" data-signature="51CTO技术栈专注于IT技术领域,汇聚技术大咖为您分享开发架构、系统运维、大数据、人工智能等一线技术解析和实践案例等深度干货文章,愿我们一起悦享技术,成就CTO梦想!" data-from="0"></mpprofile> </section> <section style="box-sizing: border-box;font-size: 16px;"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;" powered-by="xiumi.us"> <section style="padding-top: 10px;padding-right: 10px;padding-left: 10px;background-color: rgb(239, 239, 239);box-sizing: border-box;"> <span style="display: inline-block;width: 5%;line-height: 0.8;font-weight: bolder;font-size: 48px;box-sizing: border-box;" title="" opera-tn-ra-cell="_$.pages:0.layers:0.comps:0.txt1"> <section style="box-sizing: border-box;"> “ </section></span> <section style="display: inline-block;vertical-align: top;float: right;width: 90%;line-height: 1.5;font-size: 15px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><span style="letter-spacing: 1px;">伴随着业务的快速的发展、越来越高的业务复杂度,几乎每个公司的系统都会从单体走向分布式,特别是转向微服务架构。</span></p> </section> <section style="clear: both;box-sizing: border-box;"> <section> <svg viewbox="0 0 1 1" style="float:left;line-height:0;width:0;vertical-align:top;"></svg> </section> </section> </section> </section> </section> <section style="line-height: normal;"> <br> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;"> <img class="rich_pages wxw-img custom_select_img" data-fileid="508370886" data-galleryid="" data-ratio="0.661432777232581" data-s="300,640" src="/upload/4bbc5f893e460ea5f6b83e5b77c7725b.png" data-type="png" data-w="1019" style=""> </section> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;"><em>图片来自 Pexels</em></span><br></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">随之而来就必然遇到分布式事务这个难题。而我的这篇文章总结了分布式事务的解决方案,希望给大家带来帮助。</span></p> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;font-size: 16px;"> <section style="border-bottom: 1px solid black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;border-bottom: 6px solid rgb(89, 89, 89);margin-bottom: -1px;border-top-color: rgb(89, 89, 89);border-right-color: rgb(89, 89, 89);border-left-color: rgb(89, 89, 89);font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">分布式事务基础</p> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">①到底什么是事务呢?</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">什么是事务?举个生活中的例子:你去小卖铺买东西,“一手交钱,一手交货”就是一个事务的例子,交钱和交货必须全部成功,事务才算成功,任一个活动失败,事务将撤销所有已成功的活动。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">明白上述例子,再来看事务的定义:事务可以看做是一次大的活动,它由不同的小活动组成,这些活动要么全部成功,要么全部失败。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">②先来回顾本地事务</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在计算机系统中,更多的是通过关系型数据库来控制事务,这是利用数据库本身的事务特性来实现的,因此叫数据库事务。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">由于应用主要靠关系数据库来控制事务,而数据库通常和应用在同一个服务器,所以基于关系型数据库的事务又被称为本地事务。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">回顾一下数据库事务的四大特性 ACID:</span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">A(Atomic):</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">原子性,构成事务的所有操作,要么都执行完成,要么全部不执行,不可能出现部分成功部分失败的情况。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">C(Consistency):</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">一致性,在事务执行前后,数据库的一致性约束没有被破坏。</span></p><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">比如:张三向李四转 100 元,转账前和转账后的数据是正确状态这叫一致性,如果出现张三转出 100 元,李四账户没有增加 100 元这就出现了数据错误,就没有达到一致性。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">I(Isolation):</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">隔离性,数据库中的事务一般都是并发的,隔离性是指并发的两个事务的执行互不干扰,一个事务不能看到其他事务运行过程的中间状态。通过配置事务隔离级别可以避脏读、重复读等问题。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">D(Durability):</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">持久性,事务完成之后,该事务对数据的更改会被持久化到数据库,且不会被回滚。</span></p></li> </ul> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">数据库事务在实现时会将一次事务涉及的所有操作全部纳入到一个不可分割的执行单元,该执行单元中的所有操作要么都成功,要么都失败,只要其中任一操作执行失败,都将导致整个事务的回滚。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">③分布式事务</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">银行跨行转账业务是一个典型分布式事务场景,假设 A 需要跨行转账给 B,那么就涉及两个银行的数据,无法通过一个数据库的本地事务保证转账的 ACID,只能够通过分布式事务来解决。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">分布式事务就是指事务的发起者、资源及资源管理器和事务协调者分别位于分布式系统的不同节点之上。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在上述转账的业务中,用户 A-100 操作和用户 B+100 操作不是位于同一个节点上。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">本质上来说,分布式事务就是为了保证在分布式场景下,数据操作的正确执行。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">分布式事务在分布式环境下,为了满足可用性、性能与降级服务的需要,降低一致性与隔离性的要求,一方面遵循 BASE 理论(BASE 相关理论,涉及内容非常多,感兴趣的程序员们,可以参考 BASE 理论)。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">BASE 理论:</span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">基本业务可用性(Basic Availability)</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">柔性状态(Soft state)</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">最终一致性(Eventual consistency)</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">同样的,分布式事务也部分遵循 ACID 规范:</span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">原子性:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">严格遵循</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">一致性:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">事务完成后的一致性严格遵循;事务中的一致性可适当放宽</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">隔离性:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">并行事务间不可影响;事务中间结果可见性允许安全放宽</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">持久性:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">严格遵循</span></p></li> </ul> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">④分布式事务场景</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">典型的场景就是微服务架构:</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">微服务之间通过远程调用完成事务操作。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">比如:订单微服务和库存微服务,下单的同时订单微服务请求库存微服务减库存。</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">简言之:跨 JVM 进程产生分布式事务。</span></p> <section style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <img class="rich_pages wxw-img" data-fileid="508370863" data-galleryid="" data-ratio="0.9218009478672986" data-s="300,640" src="/upload/c31a6d0a1279b75942604330fd24974f.png" data-type="png" data-w="422" style=""> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">单体系统访问多个数据库实例:</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">当单体系统需要访问多个数据库(实例)时就会产生分布式事务。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">比如:用户信息和订单信息分别在两个 MySQL 实例存储,用户管理系统删除用户信息,需要分别删除用户信息及用户的订单信息,由于数据分布在不同的数据实例,需要通过不同的数据库链接去操作数据,此时产生分布式事务。</span></p> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">简言之:跨数据库实例产生分布式事务。</span> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <img class="rich_pages wxw-img" data-fileid="508370864" data-galleryid="" data-ratio="0.8452380952380952" data-s="300,640" src="/upload/aa6083b30eb0795610cfbbc5d4efa454.png" data-type="png" data-w="504" style=""> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">多服务访问同一个数据库实例:</span> <span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">比如:订单微服务和库存微服务即使访问同一个数据库也会产生分布式事务,原因就是跨 JVM 进程,两个微服务持有了不同的数据库链接进行数据库操作,此时产生分布式事务。</span> </section> <section style="text-align: center;line-height: 1.75em;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"> <img class="rich_pages wxw-img" data-fileid="508370865" data-galleryid="" data-ratio="0.8034782608695652" data-s="300,640" src="/upload/c7fb7c3f5f30de5b8d1e2d356d156300.png" data-type="png" data-w="575" style=""> </section> <section style="box-sizing: border-box;font-size: 16px;"> <section style="border-bottom: 1px solid black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;border-bottom: 6px solid rgb(89, 89, 89);margin-bottom: -1px;border-top-color: rgb(89, 89, 89);border-right-color: rgb(89, 89, 89);border-left-color: rgb(89, 89, 89);font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">分布式事务的解决方案</p> </section> </section> </section> <p style="line-height: normal;"><br></p> <h2 style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">①2PC(两阶段提交)/XA</span></strong></h2> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">2PC(Two-phase commit protocol),中文叫二阶段提交。 </span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">二阶段提交是一种强一致性设计,2PC 引入一个事务协调者的角色来协调管理各参与者(也可称之为各本地资源)的提交和回滚,二阶段分别指的是准备(投票)和提交两个阶段。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">XA 是由 X/Open 组织提出的分布式事务的规范,XA 规范主要定义了(全局)事务管理器(TM)和(局部)资源管理器(RM)之间的接口。本地的数据库如 MySQL 在 XA 中扮演的是 RM 角色。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">XA 一共分为两阶段:</span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第一阶段(prepare):</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">即所有的参与者 RM 准备执行事务并锁住需要的资源。参与者 ready 时,向 TM 报告已准备就绪。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第二阶段 (commit/rollback):</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">当事务管理者(TM)确认所有参与者(RM)都 ready 后,向所有参与者发送 commit 命令。</span></p></li> </ul> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">目前主流的数据库基本都支持 XA 事务,包括 MySQL、Oracle、SQL Server、Postgre。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">XA 事务由一个或多个资源管理器(RM)、一个事务管理器(TM)和一个应用程序(ApplicationProgram)组成。</span></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <img class="rich_pages wxw-img" data-fileid="508370866" data-galleryid="" data-ratio="0.8602409638554217" data-s="300,640" src="/upload/5f063e06d51b922f1cca33dbbb3b35ae.png" data-type="png" data-w="830" style=""> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果有任何一个参与者 prepare 失败,那么 TM 会通知所有完成 prepare 的参与者进行回滚。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">XA 事务的特点是:</span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">简单易理解,开发较容易</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">对资源进行了长时间的锁定,并发度低</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">②三阶段提交(3PC)</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">三阶段提交又称 3PC,相对于 2PC 来说增加了 CanCommit 阶段和超时机制。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果段时间内没有收到协调者的 commit 请求,那么就会自动进行 commit,解决了 2PC 单点故障的问题。</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">但是性能问题和不一致问题仍然没有根本解决。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">下面我们还是一起看下三阶段流程的是什么样的?</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第一阶段:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">CanCommit 阶段。这个阶段所做的事很简单,就是协调者询问事务参与者,你是否有能力完成此次事务。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">如果都返回 yes,则进入第二阶段。有一个返回 no 或等待响应超时,则中断事务,并向所有参与者发送 abort 请求。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第二阶段:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">PreCommit 阶段。此时协调者会向所有的参与者发送 PreCommit 请求,参与者收到后开始执行事务操作,并将 Undo 和 Redo 信息记录到事务日志中。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">参与者执行完事务操作后(此时属于未提交事务的状态),就会向协调者反馈“Ack”表示我已经准备好提交了,并等待协调者的下一步指令。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第三阶段:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">DoCommit 阶段。在阶段二中如果所有的参与者节点都可以进行 PreCommit 提交,那么协调者就会从“预提交状态”转变为“提交状态”。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">然后向所有的参与者节点发送"doCommit"请求,参与者节点在收到提交请求后就会各自执行事务提交操作,并向协调者节点反馈“Ack”消息,协调者收到所有参与者的 Ack 消息后完成事务。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">相反,如果有一个参与者节点未完成 PreCommit 的反馈或者反馈超时,那么协调者都会向所有的参与者节点发送 abort 请求,从而中断事务。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">③SAGA</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Saga 是这一篇数据库论文 saga 提到的一个方案。其核心思想是将长事务拆分为多个本地短事务,由 Saga 事务协调器协调,如果正常结束那就正常完成,如果某个步骤失败,则根据相反顺序一次调用补偿操作。</span></p> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">把上面的转账作为例子,一个成功完成的 SAGA 事务时序图如下:</span> </section> <p style="text-align: center;line-height: 1.75em;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="rich_pages wxw-img" data-fileid="508370867" data-galleryid="" data-ratio="0.6077015643802648" data-s="300,640" src="/upload/4fbf34bfc1175d5012d5a82828f858a6.png" data-type="png" data-w="831" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">SAGA 事务的特点:</span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">并发度高,不用像 XA 事务那样长期锁定资源</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">需要定义正常操作以及补偿操作,开发量比 XA 大</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">一致性较弱,对于转账,可能发生 A 用户已扣款,最后转账又失败的情况</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">④TCC</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">2PC 是数据库层面的,而 TCC 是业务层面的分布式事务,就像我前面说的分布式事务不仅仅包括数据库的操作,还包括发送短信等,这时候 TCC 就派上用场了!</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">TCC 的 3 个阶段:</span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Try 阶段:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">尝试执行,完成所有业务检查(一致性), 预留必须业务资源(准隔离性)</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Confirm 阶段:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">确认执行真正执行业务,不作任何业务检查,只使用 Try 阶段预留的业务资源,Confirm 操作要求具备幂等设计,Confirm 失败后需要进行重试。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><strong>Cancel 阶段:</strong>取消执行,释放 Try 阶段预留的业务资源,也可以理解为可以理解为把预留阶段的动作撤销了。Cancel 阶段的异常和 Confirm 阶段异常处理方案基本上一致,要求满足幂等设计。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">把上面的转账作为例子,通常会在 Try 里面冻结金额,但不扣款,Confirm 里面扣款,Cancel 里面解冻金额。</span></p> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">一个成功完成的 TCC 事务时序图如下:</span> </section> <p style="text-align: center;line-height: 1.75em;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="rich_pages wxw-img" data-fileid="508370868" data-galleryid="" data-ratio="0.9048192771084337" data-s="300,640" src="/upload/392246c60bbee19539a2ec3c389386.png" data-type="png" data-w="830" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">TCC 特点如下:</span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">并发度较高,无长期资源锁定</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">开发量较大,需要提供 Try/Confirm/Cancel 接口</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">一致性较好,不会发生 SAGA 已扣款最后又转账失败的情况</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">TCC 适用于订单类业务,对中间状态有约束的业务</span></p></li> </ul> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">⑤本地消息表</span></strong></p> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">本地消息表其实就是利用了 各系统本地的事务来实现分布式事务。</span> </section> <p style="text-align: center;line-height: 1.75em;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="rich_pages wxw-img" data-fileid="508370869" data-galleryid="" data-ratio="0.5282791817087846" data-s="300,640" src="/upload/b741ef02254d2483110cbaad652b2ed4.png" data-type="png" data-w="831" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">本地消息表顾名思义就是会有一张存放本地消息的表,一般都是放在数据库中,然后在执行业务的时候将业务的执行和将消息放入消息表中的操作放在同一个事务中,这样就能保证消息放入本地表中业务肯定是执行成功的。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">然后再去调用下一个操作,如果下一个操作调用成功了好说,消息表的消息状态可以直接改成已成功。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果调用失败也没事,会有后台任务定时去读取本地消息表,筛选出还未成功的消息再调用对应的服务,服务更新成功了再变更消息的状态。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这时候有可能消息对应的操作不成功,因此也需要重试,重试就得保证对应服务的方法是幂等的,而且一般重试会有最大次数,超过最大次数可以记录下报警让人工处理。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">可以看到本地消息表其实实现的是最终一致性,容忍了数据暂时不一致的情况。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">⑤消息事务</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">RocketMQ 就很好的支持了消息事务,让我们来看一下如何通过消息实现事务。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">第一步先给 Broker 发送事务消息即半消息,半消息不是说一半消息,而是这个消息对消费者来说不可见,然后发送成功后发送方再执行本地事务。再根据本地事务的结果向 Broker 发送 Commit 或者 RollBack 命令。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">并且 RocketMQ 的发送方会提供一个反查事务状态接口,如果一段时间内半消息没有收到任何操作请求,那么 Broker 会通过反查接口得知发送方事务是否执行成功,然后执行 Commit 或者 RollBack 命令。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果是 Commit 那么订阅方就能收到这条消息,然后再做对应的操作,做完了之后再消费这条消息即可。</span></p> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果是 RollBack 那么订阅方收不到这条消息,等于事务就没执行过。可以看到通过 RocketMQ 还是比较容易实现的,RocketMQ 提供了事务消息的功能,我们只需要定义好事务反查接口即可。</span> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <img class="rich_pages wxw-img" data-fileid="508370870" data-galleryid="" data-ratio="0.53309265944645" data-s="300,640" src="/upload/93dcad6d9d27eaafe99af61a49f6a6d8.png" data-type="png" data-w="831" style=""> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">同时也可以看到消息事务实现的也是最终一致性。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">⑥最大努力通知</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">发起通知方通过一定的机制最大努力将业务处理结果通知到接收方。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">具体包括:</span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">有一定的消息重复通知机制。因为接收通知方可能没有接收到通知,此时要有一定的机制对消息重复通知。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">消息校对机制。如果尽最大努力也没有通知到接收方,或者接收方消费消息后要再次消费,此时可由接收方主动向通知方查询消息信息来满足需求。</span></p></li> </ul> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">前面介绍的的本地消息表和消息事务都属于可靠消息,与这里介绍的最大努力通知有什么不同?</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">可靠消息一致性,发起通知方需要保证将消息发出去,并且将消息发到接收通知方,消息的可靠性关键由发起通知方来保证。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">最大努力通知,发起通知方尽最大的努力将业务处理结果通知为接收通知方,但是可能消息接收不到,此时需要接收通知方主动调用发起通知方的接口查询业务处理结果,通知的可靠性关键在接收通知方。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">解决方案上,最大努力通知需要:</span></p> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">提供接口,让接受通知放能够通过接口查询业务处理结果</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">消息队列 ACK 机制,消息队列按照间隔 1min、5min、10min、30min、1h、2h、5h、10h 的方式,逐步拉大通知间隔 ,直到达到通知要求的时间窗口上限。之后不再通知</span></p></li> </ul> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">最大努力通知适用于业务通知类型,例如微信交易的结果,就是通过最大努力通知方式通知各个商户,既有回调通知,也有交易查询接口。</span> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <img class="rich_pages wxw-img" data-fileid="508370871" data-galleryid="" data-ratio="0.6469879518072289" data-s="300,640" src="/upload/4d40adabe6e1973f058618e8af4b7897.png" data-type="png" data-w="830" style=""> </section> <h3 style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">⑦AT 事务模式</span></strong></h3> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这是阿里开源项目 seata 中的一种事务模式,在蚂蚁金服也被称为 FMT。优点是该事务模式使用方式,类似 XA 模式,业务无需编写各类补偿操作,回滚由框架自动完成,缺点也类似 AT,存在较长时间的锁,不满足高并发的场景。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在 Seata 项目中,最早由阿里巴巴中间件开源出的 AT 模式(Automatic Transaction) 是一套创新的、业务无侵入的分布式事务解决方案。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">截止 Seata 的 GA 版本发布,AT 模式 已经在开源社区引起了广泛关注,很多家企业用户已经将 Seata 的 AT 模式应用于生产。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">AT 模式一阶段:</span></strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">首先,在 Seata 的组件中,如果你想开启分布式事务,那么就应该在你的业务入口或者事务发起入口加上 @GlobalTransactional 注解。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">如果你是 AT 模式就要做好数据源代理(seata1.0 后全面支持自动代理),并被 sqlsessionfactroy 使用(或者直接 jdbc 操作使用被代理数据源)。</span></p> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">可以发现比较关键的异步,与其他模式的区别便是代理数据源,而代理数据源又有什么奥秘呢?</span> </section> <p style="text-align: center;line-height: 1.75em;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="rich_pages wxw-img" data-fileid="508370872" data-galleryid="" data-ratio="0.5819277108433735" data-s="300,640" src="/upload/d07877fe119cdaa2a81bbd09fb050aee.png" data-type="png" data-w="830" style=""></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">AT 模式二阶段提交:</span></strong> <span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">二阶段如果是提交的话,因为“业务 SQL”在一阶段已经提交至数据库,所以 Seata 框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。</span> </section> <p style="text-align: center;line-height: 1.75em;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="rich_pages wxw-img" data-fileid="508370873" data-galleryid="" data-ratio="0.5939759036144578" data-s="300,640" src="/upload/35ddcdb985fbfababae4d59c1ea7eb8.png" data-type="png" data-w="830" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">AT 模式二阶段回滚:</span></strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;">二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的“业务 SQL”,还原业务数据。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">回滚方式便是用“before image”还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image”。</span></p> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写, 出现脏写就需要转人工处理。</span> </section> <section style="text-align: center;line-height: 1.75em;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"> <img class="rich_pages wxw-img" data-fileid="508370874" data-galleryid="" data-ratio="0.5679903730445247" data-s="300,640" src="/upload/6a3c9291b1548229b216c0de78f9a88e.png" data-type="png" data-w="831" style=""> </section> <section style="box-sizing: border-box;font-size: 16px;"> <section style="border-bottom: 1px solid black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;border-bottom: 6px solid rgb(89, 89, 89);margin-bottom: -1px;border-top-color: rgb(89, 89, 89);border-right-color: rgb(89, 89, 89);border-left-color: rgb(89, 89, 89);font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">小结</p> </section> </section> </section> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">本文介绍了分布式事务的一些基础理论,并对常用的分布式事务方案进行了讲解。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">分布式事务本身就是一个技术难题,业务中具体使用哪种方案还是需要不同的业务特点自行选择。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">但是我们也会发现,分布式事务会大大的提高流程的复杂度,会带来很多额外的开销工作,<strong>「代码量上去了,业务复杂了,性能下跌了」</strong>。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">所以,当我们真实开发的过程中,能不使用分布式事务就不使用。</span></p> </section>
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.5em;" data-mpa-powered-by="yiban.io"> <pre data-tool="mdnice编辑器" style="line-height: 1.5em;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;letter-spacing: 0.5px;">Lamda 表达式非常方便,在项目中一般在 stream 编程中用的比较多。</span><br></pre> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: PingFangSC-Light;font-size: 16px;padding: 10px;"> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;">List<Student> studentList = gen();<br>Map<String, Student> map = studentList .stream()<br> .collect(Collectors.toMap(Student::getId, a -> a, (a, b) -> a));<br></code></pre> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;">理解一个 Lamda 表达式就三步:</span> <br> </section> <section style="line-height: 1.5em;"> <span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;letter-spacing: 0.5px;"><br></span> </section> <p style="line-height: 2em;margin-left: 16px;margin-right: 16px;"><span style="font-size: 16px;letter-spacing: 0.5px;">1. 确认 Lamda 表达式的类型</span></p> <p style="line-height: 2em;margin-left: 16px;margin-right: 16px;"><span style="font-size: 16px;letter-spacing: 0.5px;">2. 找到要实现的方法</span></p> <p style="line-height: 2em;margin-left: 16px;margin-right: 16px;"><span style="font-size: 16px;letter-spacing: 0.5px;">3. 实现这个方法</span></p> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"> </span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;">就这三步,没其他的了。而每一步,都非常非常简单,以至于我分别展开讲一下,你就懂了。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"> </span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"><br></span> </section> <h2 data-foldable-wrapper=""> <section style="line-height: 1.5em;"> <span style="color: rgb(0, 122, 170);"><strong><span style="color: rgb(0, 122, 170);letter-spacing: 0.5px;font-size: 20px;">确认 Lamda 表达式的类型</span></strong></span> </section></h2> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"> </span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;">能用 Lamda 表达式来表示的类型,必须是一个<span style="font-weight: bold;">函数式接口</span>,而函数式接口,就是只有一个抽象方法的接口。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"> </span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;">我们看下非常熟悉的 Runnable 接口在 JDK 中的样子就明白了。</span> </section> <pre> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: PingFangSC-Light;font-size: 16px;padding: 10px;"> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #999;font-weight: bold;line-height: 26px;">@FunctionalInterface</span><br><span style="font-weight: bold;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">interface</span> <span style="color: #458;font-weight: bold;line-height: 26px;">Runnable</span> </span>{<br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">public</span> <span style="font-weight: bold;line-height: 26px;">abstract</span> <span style="font-weight: bold;line-height: 26px;">void</span> <span style="color: #900;font-weight: bold;line-height: 26px;">run</span><span style="line-height: 26px;">()</span></span>;<br>}<br></code></pre> </section> <section style="line-height: 1.5em;"> <span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;letter-spacing: 0.5px;">这就是一个标准的函数式接口。</span> </section> <section style="line-height: 1.5em;"> <span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;letter-spacing: 0.5px;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;letter-spacing: 0.5px;">因为只有一个抽象方法。而且这个接口上有个注解</span> <br> </section></pre> <section style="line-height: 1.5em;margin-bottom: 10px;margin-top: 10px;"> <span style="font-size: 16px;letter-spacing: 0.5px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;color: rgb(0, 122, 170);">@FunctionalInterface</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">这个仅仅是在<span style="font-weight: bold;">编译期</span>帮你检查你这个接口是否符合函数式接口的条件,比如你没有任何抽象方法,或者有多个抽象方法,编译是无法通过的。</span> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: PingFangSC-Light;font-size: 16px;padding: 10px;"> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #998;font-style: italic;line-height: 26px;">// 没有实现任何抽象方法的接口</span><br><span style="color: #999;font-weight: bold;line-height: 26px;">@FunctionalInterface</span><br><span style="font-weight: bold;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">interface</span> <span style="color: #458;font-weight: bold;line-height: 26px;">MyRunnable</span> </span>{}<br><br><span style="color: #998;font-style: italic;line-height: 26px;">// 编译后控制台显示如下信息</span><br><span style="color: rgb(255, 76, 0);">Error:(<span style="color: rgb(255, 76, 0);line-height: 26px;">3</span>, <span style="color: rgb(255, 76, 0);line-height: 26px;">1</span>) java: <br> 意外的 <span style="color: rgb(255, 76, 0);font-weight: bold;line-height: 26px;">@FunctionalInterface</span> 注释<br> MyRunnable 不是函数接口<br> 在 接口 MyRunnable 中找不到抽象方法</span><br></code></pre> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;">再稍稍复杂一点,Java 8 之后接口中是允许使用</span> <span style="font-size: 16px;letter-spacing: 0.5px;font-weight: bold;">默认方法</span> <span style="font-size: 16px;letter-spacing: 0.5px;">和</span> <span style="font-size: 16px;letter-spacing: 0.5px;font-weight: bold;">静态方法</span> <span style="font-size: 16px;letter-spacing: 0.5px;">的,而这些都不算抽象方法,所以也可以加在函数式接口里。</span> <br> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"> </span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;">看看你可能不太熟悉又有点似曾相识的一个接口。</span> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: PingFangSC-Light;font-size: 16px;padding: 10px;"> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #999;font-weight: bold;line-height: 26px;">@FunctionalInterface</span><br><span style="font-weight: bold;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">interface</span> <span style="color: #458;font-weight: bold;line-height: 26px;">Consumer</span><<span style="color: #458;font-weight: bold;line-height: 26px;">T</span>> </span>{<br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">void</span> <span style="color: #900;font-weight: bold;line-height: 26px;">accept</span><span style="line-height: 26px;">(T t)</span></span>;<br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">default</span> Consumer<T> <span style="color: #900;font-weight: bold;line-height: 26px;">andThen</span><span style="line-height: 26px;">(Consumer<? <span style="font-weight: bold;line-height: 26px;">super</span> T> after)</span> </span>{...}<br>}<br></code></pre> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;">看,只有一个抽象方法,还有一个默认方法(方法体的代码省略了),这个也不影响它是个函数式接口。再看一个更复杂的,多了静态方法,这同样也是个函数式接口,因为它仍然只有一个抽象方法。自行体会吧。</span> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: PingFangSC-Light;font-size: 16px;padding: 10px;"> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #999;font-weight: bold;line-height: 26px;">@FunctionalInterface</span><br><span style="font-weight: bold;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">interface</span> <span style="color: #458;font-weight: bold;line-height: 26px;">Predicate</span><<span style="color: #458;font-weight: bold;line-height: 26px;">T</span>> </span>{<br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">boolean</span> <span style="color: #900;font-weight: bold;line-height: 26px;">test</span><span style="line-height: 26px;">(T t)</span></span>;<br> <br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">default</span> Predicate<T> <span style="color: #900;font-weight: bold;line-height: 26px;">and</span><span style="line-height: 26px;">(Predicate<? <span style="font-weight: bold;line-height: 26px;">super</span> T> other)</span> </span>{...}<br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">default</span> Predicate<T> <span style="color: #900;font-weight: bold;line-height: 26px;">negate</span><span style="line-height: 26px;">()</span> </span>{...}<br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">default</span> Predicate<T> <span style="color: #900;font-weight: bold;line-height: 26px;">or</span><span style="line-height: 26px;">(Predicate<? <span style="font-weight: bold;line-height: 26px;">super</span> T> other)</span> </span>{...}<br> <br> <span style="font-weight: bold;line-height: 26px;">static</span> <T> <span style="line-height: 26px;">Predicate<T> <span style="color: #900;font-weight: bold;line-height: 26px;">isEqual</span><span style="line-height: 26px;">(Object targetRef)</span> </span>{...}<br> <span style="font-weight: bold;line-height: 26px;">static</span> <T> <span style="line-height: 26px;">Predicate<T> <span style="color: #900;font-weight: bold;line-height: 26px;">not</span><span style="line-height: 26px;">(Predicate<? <span style="font-weight: bold;line-height: 26px;">super</span> T> target)</span> </span>{...}<br>}<br></code></pre> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;">先不用管这些方法都是干嘛的,这些类在 Stream 设计的方法中比比皆是,我们就先记住这么一句话,</span> <span style="font-size: 16px;letter-spacing: 0.5px;font-weight: bold;">Lamda 表达式需要的类型为函数式接口,函数式接口里只有一个抽象方法</span> <span style="font-size: 16px;letter-spacing: 0.5px;">,就够了,以上三个例子都属于函数式接口。</span> <br> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"> </span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;">恭喜你,已经学会了 Lamda 表达式最难的部分,就是认识函数式接口。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"> </span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"><br></span> </section> <h2 data-foldable-wrapper=""> <section style="line-height: 1.5em;"> <span style="color: rgb(0, 122, 170);"><strong><span style="color: rgb(0, 122, 170);letter-spacing: 0.5px;font-size: 20px;">找到要实现的方法</span></strong></span> </section></h2> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"> </span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;">Lamda 表达式就是实现一个方法,什么方法呢?就是刚刚那些函数式接口中的<span style="font-weight: bold;">抽象方法</span>。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 16px;letter-spacing: 0.5px;"> </span> </section> <section style="line-height: 1.5em
作者:微信小助手
<p style="outline: 0px;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);" data-mpa-powered-by="yiban.io"><strong style="outline: 0px;letter-spacing: 0.544px;text-align: left;font-family: 微软雅黑;font-size: 16px;"><span style="outline: 0px;font-size: 15px;">点击关注公众号,实用技术文章</span></strong><span style="outline: 0px;letter-spacing: 0.544px;text-align: left;color: rgb(123, 12, 0);"><strong style="outline: 0px;font-family: 微软雅黑;font-size: 16px;"><span style="outline: 0px;font-size: 15px;">及时了解</span></strong></span><img data-fileid="100030893" data-ratio="1" data-type="png" data-w="64" src="/upload/29316807de8eec165a101cfe6173a39c.png" style="outline: 0px;letter-spacing: 0.544px;text-align: left;font-family: 微软雅黑;font-size: 16px;box-sizing: border-box !important;max-height: 20px !important;visibility: visible !important;width: 20px !important;"></p> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzI4Njc5NjM1NQ==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/eQPyBffYbueAgIuCqZdZnW3NW44AOD32W2BOe28vCWLC2XdcNqJufjmlCCI2YVbFh0fjL6qCxEoNjHN9jTBItQ/0?wx_fmt=png" data-nickname="Java知音" data-alias="Java_friends" data-signature="专注于java。分享java基础、原理性知识、JavaWeb实战、spring全家桶、设计模式及面试资料、开源项目,助力开发者成长!" data-from="0"></mpprofile> </section> <h3 data-tool="mdnice编辑器" style="outline: 0px;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);text-align: right;"><em style="outline: 0px;color: rgb(136, 136, 136);font-size: 12px;letter-spacing: 0.5px;">来源:blog.csdn.net/lsy0903/article/details/103949459</em></h3> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">基于 SpringCloud, 用户发起点赞、取消点赞后先存入 Redis 中,再每隔两小时从 Redis 读取点赞数据写入数据库中做持久化存储。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">点赞功能在很多系统中都有,但别看功能小,想要做好需要考虑的东西还挺多的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;"><strong style="color: rgb(255, 53, 2);line-height: 1.5;">点赞、取消点赞是高频次的操作,若每次都读写数据库,大量的操作会影响数据库性能,所以需要做缓存。</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">至于多久从 Redis 取一次数据存到数据库中,根据项目的实际情况定吧,我是暂时设了两个小时。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">项目需求需要查看都谁点赞了,所以要存储每个点赞的点赞人、被点赞人,不能简单的做计数。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">文章分四部分介绍:</p> <ul 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);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">Redis 缓存设计及实现</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">数据库设计</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">数据库操作</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">开启定时任务持久化存储到数据库</p> </section></li> </ul> <h2 data-tool="mdnice编辑器" style="margin: 80px 10px 40px;text-align: center;color: rgb(63, 63, 63);font-size: 140%;"><span style="display: none;"></span>一、Redis 缓存设计及实现</h2> <h3 data-tool="mdnice编辑器" style="margin-top: 40px;margin-bottom: 20px;font-weight: bold;line-height: 1.5;color: rgb(63, 63, 63);font-size: 120%;"><span style="display: none;"></span>1.1 Redis 安装及运行<span style="display: none;"></span></h3> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(91, 91, 91);border-left-color: rgb(158, 158, 158);background: rgba(158, 158, 158, 0.1);padding-top: 1px;padding-bottom: 1px;margin-top: 20px;margin-bottom: 20px;"> <p style="color: rgb(63, 63, 63);line-height: 1.5;font-size: 16px;margin: 10px;">Redis 安装请自行查阅相关教程。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">说下Docker 安装运行 Redis</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;">docker run -d -p 6379:6379 redis:4.0.8 <br><br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">如果已经安装了 Redis,打开命令行,输入启动 Redis 的命令</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;">redis-server <br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 40px;margin-bottom: 20px;font-weight: bold;line-height: 1.5;color: rgb(63, 63, 63);font-size: 120%;"><span style="display: none;"></span>1.2 Redis 与 SpringBoot 项目的整合<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">1.在 pom.xml 中引入依赖</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: rgb(0, 0, 128);line-height: 26px;"><<span style="line-height: 26px;">dependency</span>></span> <br> <span style="color: rgb(0, 0, 128);line-height: 26px;"><<span style="line-height: 26px;">groupId</span>></span>org.springframework.boot<span style="color: rgb(0, 0, 128);line-height: 26px;"></<span style="line-height: 26px;">groupId</span>></span> <br> <span style="color: rgb(0, 0, 128);line-height: 26px;"><<span style="line-height: 26px;">artifactId</span>></span>spring-boot-starter-data-redis<span style="color: rgb(0, 0, 128);line-height: 26px;"></<span style="line-height: 26px;">artifactId</span>></span> <br><span style="color: rgb(0, 0, 128);line-height: 26px;"></<span style="line-height: 26px;">dependency</span>></span> <br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">2.在启动类上添加注释 @EnableCaching</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #999;font-weight: bold;line-height: 26px;">@SpringBootApplication</span> <br><span style="color: #999;font-weight: bold;line-height: 26px;">@EnableDiscoveryClient</span> <br><span style="color: #999;font-weight: bold;line-height: 26px;">@EnableSwagger</span>2 <br><span style="color: #999;font-weight: bold;line-height: 26px;">@EnableFeignClients</span>(basePackages = <span style="color: #d14;line-height: 26px;">"com.solo.coderiver.project.client"</span>) <br><span style="color: #999;font-weight: bold;line-height: 26px;">@EnableCaching</span> <br><span style="font-weight: bold;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">class</span> <span style="color: #458;font-weight: bold;line-height: 26px;">UserApplication</span> </span>{ <br> <br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">public</span> <span style="font-weight: bold;line-height: 26px;">static</span> <span style="font-weight: bold;line-height: 26px;">void</span> <span style="color: #900;font-weight: bold;line-height: 26px;">main</span><span style="line-height: 26px;">(String[] args)</span> </span>{ <br> SpringApplication.run(UserApplication<span style="line-height: 26px;">.<span style="font-weight: bold;line-height: 26px;">class</span>, <span style="color: #458;font-weight: bold;line-height: 26px;">args</span>)</span>; <br> } <br>} <br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">3.编写 Redis 配置类 RedisConfig</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="font-weight: bold;line-height: 26px;">import</span> com.fasterxml.jackson.annotation.JsonAutoDetect; <br><span style="font-weight: bold;line-height: 26px;">import</span> com.fasterxml.jackson.annotation.PropertyAccessor; <br><span style="font-weight: bold;line-height: 26px;">import</span> com.fasterxml.jackson.databind.ObjectMapper; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.context.annotation.Bean; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.context.annotation.Configuration; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.data.redis.connection.RedisConnectionFactory; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.data.redis.core.RedisTemplate; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.data.redis.core.StringRedisTemplate; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; <br> <br><span style="font-weight: bold;line-height: 26px;">import</span> java.net.UnknownHostException; <br> <br> <br><span style="color: #999;font-weight: bold;line-height: 26px;">@Configuration</span> <br><span style="font-weight: bold;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">class</span> <span style="color: #458;font-weight: bold;line-height: 26px;">RedisConfig</span> </span>{ <br> <br> <span style="color: #999;font-weight: bold;line-height: 26px;">@Bean</span> <br> <span style="color: #999;font-weight: bold;line-height: 26px;">@ConditionalOnMissingBean</span>(name = <span style="color: #d14;line-height: 26px;">"redisTemplate"</span>) <br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">public</span> RedisTemplate<String, Object> <span style="color: #900;font-weight: bold;line-height: 26px;">redisTemplate</span><span style="line-height: 26px;">( <br> RedisConnectionFactory redisConnectionFactory)</span> <br> <span style="font-weight: bold;line-height: 26px;">throws</span> UnknownHostException </span>{ <br> <br> Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = <span style="font-weight: bold;line-height: 26px;">new</span> Jackson2JsonRedisSerializer<Object>(Object<span style="line-height: 26px;">.<span style="font-weight: bold;line-height: 26px;">class</span>)</span>; <br> ObjectMapper om = <span style="font-weight: bold;line-height: 26px;">new</span> ObjectMapper(); <br> om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); <br> om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); <br> jackson2JsonRedisSerializer.setObjectMapper(om); <br> <br> RedisTemplate<String, Object> template = <span style="font-weight: bold;line-height: 26px;">new</span> RedisTemplate<String, Object>(); <br> template.setConnectionFactory(redisConnectionFactory); <br> template.setKeySerializer(jackson2JsonRedisSerializer); <br> template.setValueSerializer(jackson2JsonRedisSerializer); <br> template.setHashKeySerializer(jackson2JsonRedisSerializer); <br> template.setHashValueSerializer(jackson2JsonRedisSerializer); <br> template.afterPropertiesSet(); <br> <span style="font-weight: bold;line-height: 26px;">return</span> template; <br> } <br> <br> <br> <span style="color: #999;font-weight: bold;line-height: 26px;">@Bean</span> <br> <span style="color: #999;font-weight: bold;line-height: 26px;">@ConditionalOnMissingBean</span>(StringRedisTemplate<span style="line-height: 26px;">.<span style="font-weight: bold;line-height: 26px;">class</span>) <br> <span style="color: #458;font-weight: bold;line-height: 26px;">public</span> <span style="color: #458;font-weight: bold;line-height: 26px;">StringRedisTemplate</span> <span style="color: #458;font-weight: bold;line-height: 26px;">stringRedisTemplate</span>( <br> <span style="color: #458;font-weight: bold;line-height: 26px;">RedisConnectionFactory</span> <span style="color: #458;font-weight: bold;line-height: 26px;">redisConnectionFactory</span>) <br> <span style="color: #458;font-weight: bold;line-height: 26px;">throws</span> <span style="color: #458;font-weight: bold;line-height: 26px;">UnknownHostException</span> </span>{ <br> StringRedisTemplate template = <span style="font-weight: bold;line-height: 26px;">new</span> StringRedisTemplate(); <br> template.setConnectionFactory(redisConnectionFactory); <br> <span style="font-weight: bold;line-height: 26px;">return</span> template; <br> } <br>} <br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">至此 Redis 在 SpringBoot 项目中的配置已经完成,可以愉快的使用了。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 40px;margin-bottom: 20px;font-weight: bold;line-height: 1.5;color: rgb(63, 63, 63);font-size: 120%;"><span style="display: none;"></span>1.3 Redis 的数据结构类型<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">Redis 可以存储键与5种不同数据结构类型之间的映射,这5种数据结构类型分别为String(字符串)、List(列表)、Set(集合)、Hash(散列)和 Zset(有序集合)。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">下面来对这5种数据结构类型作简单的介绍:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img src="/upload/525d7f3749f972f406c88eea960bb02e.png" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" class="rich_pages wxw-img" data-ratio="0.75920245398773" data-w="652" data-fileid="100030892"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> 图片 </figcaption> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 40px;margin-bottom: 20px;font-weight: bold;line-height: 1.5;color: rgb(63, 63, 63);font-size: 120%;"><span style="display: none;"></span>1.4 点赞数据在 Redis 中的存储格式<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">用 Redis 存储两种数据,一种是记录点赞人、被点赞人、点赞状态的数据,另一种是每个用户被点赞了多少次,做个简单的计数。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;"><strong style="color: rgb(255, 53, 2);line-height: 1.5;">由于需要记录点赞人和被点赞人,还有点赞状态(点赞、取消点赞),还要固定时间间隔取出 Redis 中所有点赞数据,分析了下 Redis 数据格式中 Hash 最合适。</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">因为 Hash 里的数据都是存在一个键里,可以通过这个键很方便的把所有的点赞数据都取出。这个键里面的数据还可以存成键值对的形式,方便存入点赞人、被点赞人和点赞状态。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">设点赞人的 id 为 likedPostId,被点赞人的 id 为 likedUserId ,点赞时状态为 1,取消点赞状态为 0。将点赞人 id 和被点赞人 id 作为键,两个 id 中间用 :: 隔开,点赞状态作为值。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">所以如果用户点赞,存储的键为:likedUserId::likedPostId,对应的值为 1 。取消点赞,存储的键为:likedUserId::likedPostId,对应的值为 0 。取数据时把键用 :: 切开就得到了两个id,也很方便。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">在可视化工具 RDM 中看到的是这样子</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img src="/upload/44949f005402d2a2324d8ef126a327c5.jpg" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-ratio="0.2574074074074074" data-w="1080" data-fileid="100030891"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> 图片 </figcaption> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img src="/upload/64cd6e400019ac0a82185f9f5a338d02.jpg" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" class="rich_pages wxw-img" data-ratio="0.23425925925925925" data-w="1080" data-fileid="100030890"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> 图片 </figcaption> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 40px;margin-bottom: 20px;font-weight: bold;line-height: 1.5;color: rgb(63, 63, 63);font-size: 120%;"><span style="display: none;"></span>1.5 操作 Redis<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">将具体操作方法封装到了 RedisService 接口里</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">RedisService.java</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="font-weight: bold;line-height: 26px;">import</span> com.solo.coderiver.user.dataobject.UserLike; <br><span style="font-weight: bold;line-height: 26px;">import</span> com.solo.coderiver.user.dto.LikedCountDTO; <br> <br><span style="font-weight: bold;line-height: 26px;">import</span> java.util.List; <br> <br><span style="font-weight: bold;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">interface</span> <span style="color: #458;font-weight: bold;line-height: 26px;">RedisService</span> </span>{ <br> <br> <span style="color: #998;font-style: italic;line-height: 26px;">/** <br> * 点赞。状态为1 <br> * <span style="color: #d14;line-height: 26px;">@param</span> likedUserId <br> * <span style="color: #d14;line-height: 26px;">@param</span> likedPostId <br> */</span> <br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">void</span> <span style="color: #900;font-weight: bold;line-height: 26px;">saveLiked2Redis</span><span style="line-height: 26px;">(String likedUserId, String likedPostId)</span></span>; <br> <br> <span style="color: #998;font-style: italic;line-height: 26px;">/** <br> * 取消点赞。将状态改变为0 <br> * <span style="color: #d14;line-height: 26px;">@param</span> likedUserId <br> * <span style="color: #d14;line-height: 26px;">@param</span> likedPostId <br> */</span> <br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">void</span> <span style="color: #900;font-weight: bold;line-height: 26px;">unlikeFromRedis</span><span style="line-height: 26px;">(String likedUserId, String likedPostId)</span></span>; <br> <br> <span style="color: #998;font-style: italic;line-height: 26px;">/** <br> * 从Redis中删除一条点赞数据 <br> * <span style="color: #d14;line-height: 26px;">@param</span> likedUserId <br> * <span style="color: #d14;line-height: 26px;">@param</span> likedPostId <br> */</span> <br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">void</span> <span style="color: #900;font-weight: bold;line-height: 26px;">deleteLikedFromRedis</span><span style="line-height: 26px;">(String likedUserId, String likedPostId)</span></span>; <br> <br> <span style="color: #998;font-style: italic;line-height: 26px;">/** <br> * 该用户的点赞数加1 <br> * <span style="color: #d14;line-height: 26px;">@param</span> likedUserId <br> */</span> <br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">void</span> <span style="color: #900;font-weight: bold;line-height: 26px;">incrementLikedCount</span><span style="line-height: 26px;">(String likedUserId)</span></span>; <br> <br> <span style="color: #998;font-style: italic;line-height: 26px;">/** <br> * 该用户的点赞数减1 <br> * <span style="color: #d14;line-height: 26px;">@param</span> likedUserId <br> */</span> <br> <span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">void</span> <span style="color: #900;font-weight: bold;line-height: 26px;">decrementLikedCount</span><span style="line-height: 26px;">(String likedUserId)</span></span>; <br> <br> <span style="color: #998;font-style: italic;line-height: 26px;">/** <br> * 获取Redis中存储的所有点赞数据 <br> * <span style="color: #d14;line-height: 26px;">@return</span> <br> */</span> <br> <span style="line-height: 26px;">List<UserLike> <span style="color: #900;font-weight: bold;line-height: 26px;">getLikedDataFromRedis</span><span style="line-height: 26px;">()</span></span>; <br> <br> <span style="color: #998;font-style: italic;line-height: 26px;">/** <br> * 获取Redis中存储的所有点赞数量 <br> * <span style="color: #d14;line-height: 26px;">@return</span> <br> */</span> <br> <span style="line-height: 26px;">List<LikedCountDTO> <span style="color: #900;font-weight: bold;line-height: 26px;">getLikedCountFromRedis</span><span style="line-height: 26px;">()</span></span>; <br> <br>} <br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.6;color: rgb(63, 63, 63);margin-top: 10px;margin-bottom: 10px;">实现类 RedisServiceImpl.java</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;color: #333;background: #f8f8f8;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="font-weight: bold;line-height: 26px;">import</span> com.solo.coderiver.user.dataobject.UserLike; <br><span style="font-weight: bold;line-height: 26px;">import</span> com.solo.coderiver.user.dto.LikedCountDTO; <br><span style="font-weight: bold;line-height: 26px;">import</span> com.solo.coderiver.user.enums.LikedStatusEnum; <br><span style="font-weight: bold;line-height: 26px;">import</span> com.solo.coderiver.user.service.LikedService; <br><span style="font-weight: bold;line-height: 26px;">import</span> com.solo.coderiver.user.service.RedisService; <br><span style="font-weight: bold;line-height: 26px;">import</span> com.solo.coderiver.user.utils.RedisKeyUtils; <br><span style="font-weight: bold;line-height: 26px;">import</span> lombok.extern.slf4j.Slf4j; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.beans.factory.annotation.Autowired; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.data.redis.core.Cursor; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.data.redis.core.RedisTemplate; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.data.redis.core.ScanOptions; <br><span style="font-weight: bold;line-height: 26px;">import</span> org.springframework.stereotype.Service; <br> <br><span style="font-weight: bold;line-height: 26px;">import</span> java.util.ArrayList; <br><span style="font-weight: bold;line-height: 26px;">import</span> java.util.List; <br><span style="font-weight: bold;line-height: 26px;">import</span> java.util.Map; <br> <br><span style="color: #999;font-weight: bold;line-height: 26px;">@Service</span> <br><span style="color: #999;font-weight: bold;line-height: 26px;">@Slf</span>4j <br><span style="font-weight: bold;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="font-