文章列表

OpenFeign的9个坑

作者:微信小助手

<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;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;margin-bottom: 0px;" data-mpa-powered-by="yiban.io"> <section> <mp-common-profile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-weui-theme="light" data-id="Mzg4NjYyODc4OA==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/J4jTHmo8Xh6qM32ASOtVbXNoiaegrI26qLRw6r6FTI7dZw6TMT7vecvnjd1O8xSsM5MiajIuQZicxSC6KFK8TMpbg/0?wx_fmt=png" data-nickname="java突击队" data-alias="" data-signature="技术经验分享" data-from="0" data-is_biz_ban="0"></mp-common-profile> </section> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">OpenFeign是SpringCloud中的重要组件,它是一种声明式的HTTP客户端。</span><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">使用OpenFeign调用远程服务就像调用本地方法一样,但是如果使用不当,很容易踩到坑。</span><br></p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: none;"></span><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">坑一:用对Http Client</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>1.1 feign中http client<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">如果不做特殊配置,OpenFeign默认使用jdk自带的HttpURLConnection,我们知道HttpURLConnection没有连接池、性能和效率比较低,如果采用默认,很可能会遇到性能问题导致系统故障。</span></p> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">可以采用Apache HttpClient,properties文件中增加下面配置:</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRDJlxGCxehEB5vnwN6a5YF9gq4JubgUI5OXqaUBFMiagw67iaaJaqkc6oI8KG0q9bNLhfW9dokXv7UlbXDXAVkt6s/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">feign.httpclient.enabled=<span style="color: #56b6c2;line-height: 26px;">true</span><br></code></pre> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">pom文件中增加依赖:</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRDJlxGCxehEB5vnwN6a5YF9gq4JubgUI5OXqaUBFMiagw67iaaJaqkc6oI8KG0q9bNLhfW9dokXv7UlbXDXAVkt6s/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">&lt;dependency&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;groupId&gt;io.github.openfeign&lt;/groupId&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;artifactId&gt;feign-httpclient&lt;/artifactId&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;version&gt;9.3.1&lt;/version&gt;<br>&lt;/dependency&gt;<br></code></pre> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">也可以采用OkHttpClient,properties文件中增加下面配置:</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRDJlxGCxehEB5vnwN6a5YF9gq4JubgUI5OXqaUBFMiagw67iaaJaqkc6oI8KG0q9bNLhfW9dokXv7UlbXDXAVkt6s/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">feign.okhttp.enabled=<span style="color: #56b6c2;line-height: 26px;">true</span><br></code></pre> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">pom文件中增加依赖:</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRDJlxGCxehEB5vnwN6a5YF9gq4JubgUI5OXqaUBFMiagw67iaaJaqkc6oI8KG0q9bNLhfW9dokXv7UlbXDXAVkt6s/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">&lt;dependency&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;groupId&gt;io.github.openfeign&lt;/groupId&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;artifactId&gt;feign-okhttp&lt;/artifactId&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;version&gt;10.2.0&lt;/version&gt;<br>&lt;/dependency&gt;<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>1.2 ribbon中的Http Client<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">通过OpenFeign作为注册中心的客户端时,默认使用Ribbon做负载均衡,Ribbon默认也是用jdk自带的HttpURLConnection,需要给Ribbon也设置一个Http client,比如使用okhttp,在properties文件中增加下面配置:</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRDJlxGCxehEB5vnwN6a5YF9gq4JubgUI5OXqaUBFMiagw67iaaJaqkc6oI8KG0q9bNLhfW9dokXv7UlbXDXAVkt6s/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">ribbon.okhttp.enabled=<span style="color: #56b6c2;line-height: 26px;">true</span><br></code></pre> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: none;"></span><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">坑二:全局超时时间</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">OpenFeign可以设置超时时间,简单粗暴,设置一个全局的超时时间,如下:</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRDJlxGCxehEB5vnwN6a5YF9gq4JubgUI5OXqaUBFMiagw67iaaJaqkc6oI8KG0q9bNLhfW9dokXv7UlbXDXAVkt6s/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">feign.client.config.default.connectTimeout=2000<br>feign.client.config.default.readTimeout=60000<br></code></pre> <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(239, 112, 96);background: rgb(255, 249, 249);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">如果不配置超时时间,默认是连接超时10s,读超时60s,在源码feign.Request的内部类Options中定义。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">这个接口设置了最大的readTimeout是60s,这个时间必须大于调用的所有外部接口的readTimeout,否则处理时间大于readTimeout的接口就会调用失败。</span></p> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">如下图,在一个系统中使用OpenFeign调用外部三个服务,每个服务提供两个接口,其中serviceC的一个接口需要60才能返回,那上面的readTimeout必须设置成60s。</span></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.6004228329809725" src="/upload/57af255567d091db81379f622fce57fa.png" data-type="png" data-w="473" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;"> </figure> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">但是如果serviceA出故障了,表现是接口1超过60s才能返回,这样OpenFeign只能等到读超时,如果调用这个接口的并发量很高,会大量占用连接资源直到资源耗尽系统奔溃。要防止这样的故障发生,就必须保证接口1能fail-fast。最好的做法就是给serviceC单独设置超时时间。</span></p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: none;"></span><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">坑三:单服务设置超时时间</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">从上一节的讲解我们看到,需要对serviceC单独设置一个超时时间,代码如下:</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRDJlxGCxehEB5vnwN6a5YF9gq4JubgUI5OXqaUBFMiagw67iaaJaqkc6oI8KG0q9bNLhfW9dokXv7UlbXDXAVkt6s/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">feign.client.config.serviceC.connectTimeout=2000<br>feign.client.config.serviceC.readTimeout=60000<br></code></pre> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">这个时间会覆盖第一节中默认的超时时间。但是问题又来了,serviceC中又掉了serviceD,因为serviceD的故障导致接口6发生了读超时的情况,为了不让系统奔溃,不得不对serviceC的接口5单独设置超时时间。如下图:</span></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5664335664335665" src="/upload/b06e8dd4ea54606329b1ab4f04ed19cf.png" data-type="png" data-w="572" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;"> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: none;"></span><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">坑四:熔断超时时间</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">怎样给单个接口设置超时时间,查看网上资料,必须开启熔断,配置如下:</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRDJlxGCxehEB5vnwN6a5YF9gq4JubgUI5OXqaUBFMiagw67iaaJaqkc6oI8KG0q9bNLhfW9dokXv7UlbXDXAVkt6s/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">feign.hystrix.enabled=<span style="color: #56b6c2;line-height: 26px;">true</span><br></code></pre> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">开启熔断后,就可以给单个接口配置超时了。如果调用serviceC的接口5的声明如下:</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRDJlxGCxehEB5vnwN6a5YF9gq4JubgUI5OXqaUBFMiagw67iaaJaqkc6oI8KG0q9bNLhfW9dokXv7UlbXDXAVkt6s/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">@FeignClient(value&nbsp;=&nbsp;<span style="color: #98c379;line-height: 26px;">"serviceC"</span>configuration&nbsp;=&nbsp;FeignMultipartSupportConfig.class)<br>public&nbsp;interface&nbsp;ServiceCClient&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;@GetMapping(<span style="color: #98c379;line-height: 26px;">"/interface5"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;interface5(String&nbsp;param);<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">根据上面interface5接口的声明,在properties文件中增加如下配置:</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRDJlxGCxehEB5vnwN6a5YF9gq4JubgUI5OXqaUBFMiagw67iaaJaqkc6oI8KG0q9bNLhfW9dokXv7UlbXDXAVkt6s/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">hystrix.command.ServiceCClient<span style="color: #5c6370;font-style: italic;line-height: 26px;">#interface5(param).execution.isolation.thread.timeoutInMilliseconds=60000</span><br></code></pre> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">网上资料说的并不准确,这个超时时间并没有起作用。为什么不生效呢?</span></p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>4.1 使用feign超时<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">最终使用的超时时间来自于Options类。如果我们配置了feign的超时时间,会选择使用feign超时时间,下面代码在FeignClientFactoryBean类的configureUsingProperties方法:</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRDJlxGCxehEB5vnwN6a5YF9gq4JubgUI5OXqaUBFMiagw67iaaJaqkc6oI8KG0q9bNLhfW9dokXv7UlbXDXAVkt6s/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #c678dd;line-height: 26px;">if</span>&nbsp;(config.getConnectTimeout()&nbsp;!=&nbsp;null&nbsp;&amp;&amp;&nbsp;config.getReadTimeout()&nbsp;!=&nbsp;null)&nbsp;{<br>&nbsp;builder.options(new&nbsp;Request.Options(config.getConnectTimeout(),&nbsp;config.getReadTimeout()));<br>}<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>4.2 使用ribbon超时<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">如果没有配置feign,但是配置了ribbon的超时时间,会使用ribbon的超时时间。我们看下这段源代码,FeignLoadBalancer里面的execute方法,</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRDJlxGCxehEB5vnwN6a5YF9gq4JubgUI5OXqaUBFMiagw67iaaJaqkc6oI8KG0q9bNLhfW9dokXv7UlbXDXAVkt6s/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">public&nbsp;RibbonResponse&nbsp;execute(RibbonRequest&nbsp;request,&nbsp;IClientConfig&nbsp;configOverride)<br>&nbsp;&nbsp;throws&nbsp;IOException&nbsp;{<br>&nbsp;Request.Options&nbsp;options;<br>&nbsp;<span style="color: #c678dd;line-height: 26px;">if</span>&nbsp;(configOverride&nbsp;!=&nbsp;null)&nbsp;{<br>&nbsp;&nbsp;RibbonProperties&nbsp;override&nbsp;=&nbsp;RibbonProperties.from(configOverride);<br>&nbsp;&nbsp;options&nbsp;=&nbsp;new&nbsp;Request.Options(<br>&nbsp;&nbsp;&nbsp;&nbsp;override.connectTimeout(this.connectTimeout),<br>&nbsp;&nbsp;&nbsp;&nbsp;override.readTimeout(this.readTimeout));<br>&nbsp;}<br>&nbsp;<span style="color: #c678dd;line-height: 26px;">else</span>&nbsp;{<br>&nbsp;&nbsp;options&nbsp;=&nbsp;new&nbsp;Request.Options(this.connectTimeout,&nbsp;this.readTimeout);<br>&nbsp;}<br>&nbsp;//这个request里面的client就是OkHttpClient<br>&nbsp;Response&nbsp;response&nbsp;=&nbsp;request.client().execute(request.toRequest(),&nbsp;options);<br>&nbsp;<span style="color: #e6c07b;line-height: 26px;">return</span>&nbsp;new&nbsp;RibbonResponse(request.getUri(),&nbsp;response);<br>}<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>4.3 使用自定义Options<span style="display: none;"></span></h3> <p style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">对于单个接口怎么配置超时时间,我这里给出一个方案,如果你有其他方案,欢迎探讨。我的方案是使用RestTemplate来调这个接口,单独配置超时时间,配置代码如下,这里使用OkHttpClient:</span></p> <pre style="box-sizing: border-box;font-size: 16px;font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(0, 0, 0);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-size: 12px;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;">public&nbsp;class&nbsp;RestTemplateConfiguration&nbsp;{<br style="box-sizing: border-box;">&nbsp;<br style="box-sizing: border-box;">&nbsp;&nbsp;&nbsp;&nbsp;@Bean<br style="box-sizing: border-box;">&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;OkHttp3ClientHttpRequestFactory&nbsp;<span style="box-sizing: border-box;line-height: 26px;"><span style="box-sizing: border-box;color: rgb(97, 174, 238);line-height: 26px;">okHttp3RequestFactory</span></span>(){<br style="box-sizing: border-box;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OkHttp3ClientHttpRequestFactory&nbsp;requestFactory&nbsp;=&nbsp;new&nbsp;OkHttp3ClientHttpRequestFactory();<br style="box-sizing: border-box;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;requestFactory.setConnectTimeout(2000);<br style="box-sizing: border-box;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;requestFactory.setReadTimeout(60000);<br style="box-sizing: border-box;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="box-sizing: border-box;color: rgb(230, 192, 123);line-height: 26px;">return</span>&nbsp;requestFactory;<br style="box-sizing: border-box;">&nbsp;&nbsp;&nbsp;&nbsp;}<br style="box-sizing: border-box;">&nbsp;<br style="box-sizing: border-box;">&nbsp;&nbsp;&nbsp;&nbsp;@Bean<br style="box-sizing: border-box;">&nbsp;&nbsp;&nbsp;&nbsp;@LoadBalanced<br style="box-sizing: border-box;">&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;RestTemplate&nbsp;restTemplate(OkHttp3ClientHttpRequestFactory&nbsp;okHttp3RequestFactory){<br style="box-sizing: border-box;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="box-sizing: border-box;color: rgb(230, 192, 123);line-height: 26px;">return</span>&nbsp;new&nbsp;RestTemplate(okHttp3RequestFactory);<br style="box-sizing: border-box;">&nbsp;&nbsp;&nbsp;&nbsp;}<br style="box-sizing: border-box;">}<br style="box-sizing: border-box;"></code></pre> <blockquote style="box-sizing: border-box;margin-top: 20px;margin-bottom: 20px;border-top: none;border-right: none;border-bottom: none;border-left-color: rgb(239, 112, 96);font-size: 0.9em;overflow: auto;background: rgb(255, 249, 249);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;text-align: left;white-space: normal;"> <p style="box-sizing: border-box;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;">为了使用ribbon负载均衡,上面加了@LoadBalanced</p> </blockquote> <p style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">如果使用RestTemplate,就会使用OkHttp3ClientHttpRequestFactory中配置的时间。</span></p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: none;"></span><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">坑五:ribbon超时时间</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">作为负载均衡,ribbon超时时间也是可以配置的,可以在properties增加下面配置:</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRDJlxGCxehEB5vnwN6a5YF9gq4JubgUI5OXqaUBFMiagw67iaaJaqkc6oI8KG0q9bNLhfW9dokXv7UlbXDXAVkt6s/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">ribbon.ConnectTimeout=2000<br>ribbon.ReadTimeout=11000<br></code></pre> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">有文章讲ribbon配置的超时时间必须要满足接口响应时间,其实不然,配置feign的超时时间就足够了,因为它可以覆盖掉ribbon的超时时间。</span></p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: none;"></span><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">坑六:重试默认不开启</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">OpenFeign默认是不支持重试的,可以在源代码FeignClientsConfiguration中feignRetryer中看出。</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRDJlxGCxehEB5vnwN6a5YF9gq4JubgUI5OXqaUBFMiagw67iaaJaqkc6oI8KG0q9bNLhfW9dokXv7UlbXDXAVkt6s/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><br>@Bean<br>@ConditionalOnMissingBean<br>public&nbsp;Retryer&nbsp;<span style="line-height: 26px;"><span style="color: #61aeee;line-height: 26px;">feignRetryer</span></span>()&nbsp;{<br>&nbsp;<span style="color: #e6c07b;line-height: 26px;">return</span>&nbsp;Retryer.NEVER_RETRY;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">要开启重试,我们可以自定义Retryer,比如下面这行代码:</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRDJlxGCxehEB5vnwN6a5YF9gq4JubgUI5OXqaUBFMiagw67iaaJaqkc6oI8KG0q9bNLhfW9dokXv7UlbXDXAVkt6s/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">Retryer&nbsp;retryer&nbsp;=&nbsp;new&nbsp;Retryer.Default(100,&nbsp;1000,&nbsp;2);<br></code></pre> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">表示每间隔100ms,最大间隔1000ms重试一次,最大重试次数是1,因为第三个参数包含了第一次请求。</span></p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: none;"></span><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">坑七:Ribbon重试</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>7.1 拉取服务列表<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">Ribbon默认从服务端拉取列表的时间间隔是30s,这个对优雅发布很不友好,一般我们会把这个时间改短,如下改成3s:</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRDJlxGCxehEB5vnwN6a5YF9gq4JubgUI5OXqaUBFMiagw67iaaJaqkc6oI8KG0q9bNLhfW9dokXv7UlbXDXAVkt6s/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="white-space:pre-wrap;color: rgb(171, 178, 191);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;font-size: 12px;text-align: left;background-color: rgb(40, 44, 52);">serviceC</span>.ribbon.ServerListRefreshInterval=3<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>7.2 重试<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">Ribbon重试有不少需要注意的地方,这里分享4个。</span></p> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">1.同一实例最大重试次数,不包括首次调用,配置如下:</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRDJlxGCxehEB5vnwN6a5YF9gq4JubgUI5OXqaUBFMiagw67iaaJaqkc6oI8KG0q9bNLhfW9dokXv7UlbXDXAVkt6s/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">serviceC.ribbon.MaxAutoRetries=1<br></code></pre> <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(239, 112, 96);background: rgb(255, 249, 249);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">这个次数不包括首次调用,配置了1,重试策略会先尝试在失败的实例上重试一次,如果失败,请求下一个实例。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">2.同一个服务其他实例的最大重试次数,这里不包括第一次调用的实例。默认值为1:</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRDJlxGCxehEB5vnwN6a5YF9gq4JubgUI5OXqaUBFMiagw67iaaJaqkc6oI8KG0q9bNLhfW9dokXv7UlbXDXAVkt6s/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">serviceC.ribbon.MaxAutoRetriesNextServer=1<br></code></pre> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">3.是否对所有操作都重试,如果改为true,则对所有操作请求都进行重试,包括post,建议采用默认配置false。</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRDJlxGCxehEB5vnwN6a5YF9gq4JubgUI5OXqaUBFMiagw67iaaJaqkc6oI8KG0q9bNLhfW9dokXv7UlbXDXAVkt6s/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">serviceC.ribbon.OkToRetryOnAllOperations=<span style="color: #56b6c2;line-height: 26px;">false</span><br></code></pre> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">4.对指定的http状态码进行重试</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRDJlxGCxehEB5vnwN6a5YF9gq4JubgUI5OXqaUBFMiagw67iaaJaqkc6oI8KG0q9bNLhfW9dokXv7UlbXDXAVkt6s/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">serviceC.retryableStatusCodes=404,408,502,500<br></code></pre> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: none;"></span><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">坑八:hystrix超时</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">如下图:</span></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.2746710526315789" src="/upload/ab243d8232d544d34c414eac498c768b.png" data-type="png" data-w="608" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;"> </figure> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">hystrix默认不开启,但是如果开启了hystrix,因为hystrix是在Ribbon外面,所以超时时间需要符合下面规则:hystrix超时 &gt;= (MaxAutoRetries + 1) * (ribbon ConnectTimeout + ribbon ReadTimeout)</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(239, 112, 96);background: rgb(255, 249, 249);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">如果Ribbon不重试,MaxAutoRetries=0</p> </blockquote> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">根据上面公式,假如我们配置熔断超时时间如下:</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRDJlxGCxehEB5vnwN6a5YF9gq4JubgUI5OXqaUBFMiagw67iaaJaqkc6oI8KG0q9bNLhfW9dokXv7UlbXDXAVkt6s/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">hystrix.command.ServiceCClient<span style="color: #5c6370;font-style: italic;line-height: 26px;">#interface5(param).execution.isolation.thread.timeoutInMilliseconds=15000</span><br>ribbon.ReadTimeout=8000<br></code></pre> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">这个配置是不会重试一次的。serviceA调用serviceB时,hystrix会等待Ribbon返回的结果,如果Ribbon配置了重试,hystrix会一直等待直到超时。上面的配置,因为第一次请求已经耗去了8s,剩下时间7s不够请求一次了,所以是不会进行重试的。</span></p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: none;"></span><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">坑九:使用OpenFeign做http客户端</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span></h2> <p data-tool="mdnice编辑器" style="padding: 8px 10px;line-height: 26px;font-size: 16px;color: black;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">即使不用注册中心,使用OpenFeign做普通http客户端也是很方便的,但是有三点需要注意:</span></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li><p><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">不用配置ribbon相关参数</span></p></li> <li><p><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">使用RestTemplate调用时,不考虑负载均衡</span></p></li> <li><p><span style="background-color: rgb(255, 255, 255);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;">使用过程中OpenFeign要组装出自己的一套请求,跟直接使用http客户端比,会有一定开销</span></p></li> </ul> <blockquote style="box-sizing: border-box;margin-top: 20px;margin-bottom: 20px;border-top: none;border-right: none;border-bottom: none;border-left-color: rgb(239, 112, 96);font-size: 0.9em;overflow: auto;background: rgb(255, 249, 249);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;text-align: left;white-space: normal;"></blockquote> </section>

SpringCloud 微服务随机掉线排查过程

作者:微信小助手

<section style="font-size: 16px;line-height: 1.6;letter-spacing: 0px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;" data-mpa-powered-by="yiban.io"> <p style="text-align: center;margin-bottom: 0px;"><span style="color: rgb(255, 41, 65);font-size: 14px;letter-spacing: 0.544px;text-align: center;max-width: 100%;line-height: 22.4px;"></span></p> <p style="color: rgb(62, 62, 62);margin-bottom: 0px;"><strong style="letter-spacing: 0px;color: rgb(171, 25, 66);"><span style="font-size: 15px;text-align: left;">背景</span></strong><br></p> <p style="color: rgb(62, 62, 62);text-align: left;margin-bottom: 0px;font-size: 16px;line-height: 1.6;letter-spacing: 0px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><br></p> <p style="color: rgb(62, 62, 62);text-align: left;margin-bottom: 0px;font-size: 16px;line-height: 1.6;letter-spacing: 0px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><span style="font-size: 15px;">我们的业务共使用 11 台(阿里云)服务器,使用 SpringcloudAlibaba 构建微服务集群, 共计 60 个微服务, 全部注册在同一个 Nacos 集群。</span></p> <p style="color: rgb(62, 62, 62);text-align: left;margin-bottom: 0px;font-size: 16px;line-height: 1.6;letter-spacing: 0px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><span style="font-size: 15px;"><br></span></p> <p style="color: rgb(62, 62, 62);text-align: left;margin-bottom: 0px;font-size: 16px;line-height: 1.6;letter-spacing: 0px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><span style="font-size: 15px;">流量转发路径:nginx -&gt; spring-gateway -&gt; 业务微服务。</span></p> <p style="color: rgb(62, 62, 62);text-align: left;margin-bottom: 0px;font-size: 16px;line-height: 1.6;letter-spacing: 0px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><span style="font-size: 15px;"><br></span></p> <p style="color: rgb(62, 62, 62);text-align: left;margin-bottom: 0px;font-size: 16px;line-height: 1.6;letter-spacing: 0px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><span style="font-size: 15px;">使用的版本如下:</span></p> <p style="color: rgb(62, 62, 62);text-align: left;margin-bottom: 0px;font-size: 16px;line-height: 1.6;letter-spacing: 0px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><span style="font-size: 15px;letter-spacing: 0px;"><br></span></p> <ul class="list-paddingleft-1" style="color: rgb(62, 62, 62);list-style-type: disc;"> <li><p style="text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;letter-spacing: 0px;">spring-boot.version:</span><span style="font-size: 15px;letter-spacing: 0px;">2.2.5.RELEASE</span></p></li> <li><p style="text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;letter-spacing: 0px;">s</span><span style="font-size: 15px;letter-spacing: 0px;">pring-cloud.version:</span><span style="font-size: 15px;letter-spacing: 0px;">Hoxton.SR3</span></p></li> <li><p style="text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;letter-spacing: 0px;">spring-cloud-alibaba.version:</span><span style="font-size: 15px;letter-spacing: 0px;">2.2.1.RELEASE</span></p></li> <li><p style="text-align: left;margin-bottom: 0px;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;"><span style="letter-spacing: 0px;">ja</span><span style="letter-spacing: 0px;font-size: 15px;">va</span><span style="letter-spacing: 0px;font-size: 15px;">.version:1.8</span></p><p style="text-align: left;margin-bottom: 0px;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;"><span style="letter-spacing: 0px;font-size: 15px;"></span></p><p style="text-align: left;margin-bottom: 0px;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;"><span style="letter-spacing: 0px;font-size: 15px;"></span></p></li> </ul> <p style="color: rgb(62, 62, 62);text-align: left;margin-bottom: 0px;font-size: 16px;line-height: 1.6;letter-spacing: 0px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"><br></p> <strong style="color: rgb(171, 25, 66);letter-spacing: 0px;"><span style="font-size: 15px;">案发</span></strong> </section> <section style="text-align: left;margin-bottom: 0px;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;"> <span style="font-size: 15px;">春节放假期间收到反馈,网页报错服务未找到(gateway 找不到服务的报错提示)。</span> <span style="font-size: 15px;letter-spacing: 0px;">查看 na</span> <span style="font-size: 15px;letter-spacing: 0px;">cos 集群列表</span> <span style="font-size: 15px;letter-spacing: 0px;">,</span> <span style="font-size: 15px;letter-spacing: 0px;">发现个别服务丢失 (下线)</span> <span style="font-size: 15px;letter-spacing: 0px;">。</span> </section> <section> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><br></span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">这个问题每几天出现一次, 出现时间不固定, 每次掉线的服务像是随机选的几个。</span><span style="font-size: 15px;letter-spacing: 0px;">服务手动 kill+restart 后能稳定运行 2-3 天。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><br></span></p> <p style="font-size: 16px;margin-bottom: 0px;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;text-align: left;"><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;text-align: left;">排查和解决</span></strong></span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><strong><span style="font-size: 15px;">怀疑对象一:服务器内存爆了</span></strong><span style="font-size: 15px;"></span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><br></span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">进阿里云控制台查看故障机器近期的各项指标,但是发现故障机器的指标有重要的几项丢失。内存使用率、CPU 使用率、系统负载均不显示。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="letter-spacing: 0px;"><br></span></p> <p style="font-size: 16px;text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.478125" data-s="300,640" data-type="jpeg" data-w="1280" style="height: auto !important;" src="/upload/4344fd09fac5b8ec4d211c1586ce463e.jpg"></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br style="color: rgb(62, 62, 62);font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;font-size: 16px;white-space: normal;"><span style="font-size: 15px;">控制台看不了只好进服务器内查看各指标,free -m 查看内存无异常。</span><span style="letter-spacing: 0px;font-size: 15px;">提交阿里工单。</span><span style="letter-spacing: 0px;font-size: 15px;">授权阿里工程师帮忙修复控制台显示问题</span><span style="letter-spacing: 0px;font-size: 15px;">,</span><span style="letter-spacing: 0px;font-size: 15px;">怀疑这个问题对业务有影响</span><span style="letter-spacing: 0px;font-size: 15px;">。</span></p> <p style="text-align: left;margin-bottom: 0px;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;"><span style="letter-spacing: 0px;font-size: 15px;"></span></p> <p style="text-align: left;margin-bottom: 0px;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;"><span style="letter-spacing: 0px;font-size: 15px;"><br></span></p> <figure style="font-size: 16px;text-align: left;margin-bottom: 0px;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="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.56171875" data-s="300,640" data-type="jpeg" data-w="1280" style="height: auto !important;" src="/upload/f78713ca8c0ba61848ce5896f7aba45e.jpg"></p> <figure style="text-align: left;margin-bottom: 0px;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;"> <br> </figure> </figure> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">控制台修复后掉线问题依然存在。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><br></span></p> <h5 style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><strong><span style="font-size: 15px;">怀疑对象二:CPU满载</span></strong><span style="font-size: 15px;"></span></h5> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">能感觉到执行命令很流畅,所以感觉不是这个原因。top 查看后很正常。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><br></span></p> <h5 style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><strong><span style="font-size: 15px;">怀疑对象三:磁盘满了</span></strong><span style="font-size: 15px;"></span></h5> <p><span style="font-size: 15px;"><br></span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">虽然概率很小,但是 du -sh * <span style="color: rgb(62, 62, 62);font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;font-size: 15px;text-align: left;">看一下,</span>发现磁盘容量还能用到公司倒闭。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><br></span></p> <h5 style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><strong><span style="font-size: 15px;">怀疑对象四:网络有问题</span></strong><span style="font-size: 15px;"></span></h5> <p><strong><span style="font-size: 15px;"><br></span></strong></p> <ul class="list-paddingleft-1" style="list-style-type: disc;"> <li style="font-size: 15px;"><p style="margin-bottom: 0px;color: rgb(62, 62, 62);font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;letter-spacing: 0px;text-align: left;white-space: normal;font-size: 16px;line-height: 1.6;"><span style="font-size: 15px;">服务器那三个基本故障暂时排除后,最大怀疑对象就是网络。毕竟服务掉线肯定是服务端一段时间内接收不到客户端心跳包,所以把客户端踢下线了。</span></p></li> <li style="font-size: 15px;"><p style="margin-bottom: 0px;color: rgb(62, 62, 62);font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;letter-spacing: 0px;text-align: left;white-space: normal;font-size: 16px;line-height: 1.6;"><span style="font-size: 15px;">通过 telnet,mtr -n *.*.*.*,netstat -nat |grep "TIME_WAIT" | wc -l 这些命令也只能看个大概。</span></p></li> <li style="font-size: 15px;"><p style="margin-bottom: 0px;color: rgb(62, 62, 62);font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;letter-spacing: 0px;text-align: left;white-space: normal;font-size: 16px;line-height: 1.6;"><span style="font-size: 15px;">echo "1" &gt; /proc/sys/net/ipv4/tcp_tw_reuse 修改内核参数,开启 TIME_WAIT socket 复用能力,提升实例的网络发送请求性能。</span></p></li> <li style="font-size: 15px;"><p style="margin-bottom: 0px;color: rgb(62, 62, 62);font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;letter-spacing: 0px;text-align: left;white-space: normal;font-size: 16px;line-height: 1.6;"><span style="font-size: 15px;">查看 nacos 客户端(微服务)的日志,在前面案发里提到没有日志记录。</span></p></li> </ul> <p style="text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"></span></p> <h5 style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><strong><span style="font-size: 15px;">怀疑对象五:Nacos 集群服务端故障</span></strong></h5> <h5 style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></h5> <ol class="list-paddingleft-1" style="list-style-type: decimal;"> <li style="font-size: 15px;"><h5 style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">查看 nacos 集群部署的那几台服务器, 查看服务器基础指标 (内存、CPU、磁盘等),未发现异常 (毕竟还有几十个微服务都很正常工作)。</span></h5></li> <li style="font-size: 15px;"><h5 style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"></h5><p><span style="font-size: 15px;">查看 nacos 服务端日<span style="font-size: 15px;letter-spacing: 0px;">志,发现确实有主动下线服务操作。那就奇怪了,这个机器上的有些服务还在正常工作,为什么会随机下线几个服务呢?</span></span></p></li> </ol> </section> <section style="text-align: left;margin-bottom: 0px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;"> <h5 style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><strong><span style="font-size: 15px;">怀疑对象六:微服务占用资源太多</span></strong><span style="font-size: 15px;"></span><span style="font-size: 15px;"></span></h5> <p style="text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><br></span></p> <p style="text-align: left;margin-bottom: 0px;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;"><span style="font-size: 14px;color: rgb(136, 136, 136);">后来仔细想想,这个怀疑对象是不是有点离谱了?<br>因为部署脚本都是同一个,而且负载均衡也是一样的。<br>但其他机器的这个服务都好好的。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">1. 调大每个微服务的内存占用。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">2. 添加堆栈打印。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.5835140997830802" data-s="300,640" data-type="jpeg" data-w="461" style="height: auto !important;" src="/upload/efeb3b7b9253df59fcccaca67a4ac04f.jpg"></p> <p style="text-align: center;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">3. 等待一段时间后,异常依然存在,并且没有堆栈打印?因为进程好好的并没退出。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">4. google 搜索 nacos 服务掉线,找到一篇看起来极其靠谱的文章。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.5140271493212669" data-s="300,640" data-type="jpeg" data-w="1105" style="height: auto !important;" src="/upload/cb57082c4c88b2227c8f5f8bb3226bb0.jpg"></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">5. 上文提到我使用的 springcloud 版本,恰好这个版本的 nacos-client 版本就是 1.4.1, 于是立马测试升级。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><br></span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.26270456503014644" data-s="300,640" data-type="jpeg" data-w="1161" style="color: rgb(62, 62, 62);font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: center;white-space: normal;height: auto !important;" src="/upload/f802f66327b511b5a1eae15071672582.jpg"></span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">6. 观察几天后,发现问题依旧,只能将探查方向继续转回微服务本身。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">7. 用 arthas 进行勘测各项指标,发现所有正常的服务各指标均正常。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">8. 想到服务掉线大概率是因为心跳包丢失,怀疑是心跳线程因为某些原因被杀死了。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">9. 翻看 nacos-client 源码,找到心跳函数(nacos2.x 不是这个),使用 arthas 监听心跳包,尝试能找到心跳丢失的证据,贴上当时的记录。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.29324699352451433" data-s="300,640" data-type="jpeg" data-w="1081" style="height: auto !important;" src="/upload/2395377d5ab480392aa442ab3b97e2c3.jpg"></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"></span></p> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.30398671096345514" data-s="300,640" data-type="jpeg" data-w="1204" style="height: auto !important;" src="/upload/b797dedd90138e87f09b0faebd50207d.jpg"></p> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.7250639386189258" data-s="300,640" data-type="jpeg" data-w="782" style="height: auto !important;" src="/upload/137c5e887757a7d756fbd176a38af38b.jpg"></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><br></span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">10. 当异常再次发生,arthas 监听卡死,无任何记录和响应。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">11. 无奈更换思路,写一个监听服务掉线的程序,期望可以在工作时间内及时获取到异常。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.44472361809045224" data-s="300,640" data-type="jpeg" data-w="1194" style="height: auto !important;" src="/upload/373fe7e96cfed6738fae345b202a0fc7.jpg"></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"></span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">12. 终于在工作时间捕获到异常,第一时间进入服务器内查看情况。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.0453125" data-s="300,640" data-type="jpeg" data-w="1280" style="height: auto !important;" src="/upload/4482342f5b044597297dad54bba1dd01.jpg"></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">13. 确认服务器基础项没问题后,使用 arthas 查看服务进程堆栈情况,但是 arthas 无法进入进程。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.21185064935064934" data-s="300,640" data-type="jpeg" data-w="1232" style="height: auto !important;" src="/upload/b62e19eb499351fd95570c2a07c78a05.jpg"></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">14. 用 jstat 查看 GC 情况,显示很正常。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.0484375" data-s="300,640" data-type="jpeg" data-w="1280" style="height: auto !important;" src="/upload/5a5f628161e64131fa8cda3ce006523d.jpg"></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"></span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">15. 用 jmap/jstack 输出堆栈 jstack -l 25944 &gt;heap.txt,但是提示无法进入进程。无奈使用添加 - F(这个参数的堆栈少了很多信息),jstack -F -l 25944 &gt;heap.txt</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">16. 查看堆栈文件上万行记录,眼都看花了但是没有死锁也没有发现异常。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">17. 此时发现监听程序提示服务上线了?检查后发现确实掉线的几个微服务自动恢复了,心想这就难排查了。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">18. 尝试复现 Bug,此时离第一次案发已经过去一周多,必须尽快处理好这个 Bug 否则可能得被迫离职了。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">19. 当第二次发生异常的时候,使用同样的方式 arthas 无法进入 -&gt;...-&gt;jstack 输出堆栈。奇迹发生了,服务又恢复正常了。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">20. 思考 / 猜测:因为 JVM 死了(假死),所以导致进程中的一切内容,包括心跳线程、日志等都 hold 住。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">21. Google 搜索关键词 JVM 停止(假死)排查,终于找到一个极其靠谱的回答。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><br></span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.63515625" data-s="300,640" data-type="jpeg" data-w="1280" style="color: rgb(62, 62, 62);font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: center;white-space: normal;height: auto !important;" src="/upload/9bcb246b0fb7ae9d8204ec736829bdde.jpg"></span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><br></span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><span style="color: rgb(62, 62, 62);font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;font-size: 15px;text-align: left;">22. 连忙查看对比使用的几个机器内核版本号 uname -r。</span></span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.15865384615384615" data-s="300,640" data-type="png" data-w="416" style="height: auto !important;" src="/upload/8d7907b7033fc8e808f154afd934bb7f.png"></p> <p style="text-align: center;"><br></p> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.15473441108545036" data-s="300,640" data-type="jpeg" data-w="433" style="height: auto !important;" src="/upload/32814a1599335f0d104bd664b054bfd9.jpg"></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">23. 那个低版本的就是故障机器,确认相关信息后,联系阿里云提交工单。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><br></span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.390625" data-s="300,640" data-type="jpeg" data-w="1280" style="color: rgb(62, 62, 62);font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: center;white-space: normal;height: auto !important;" src="/upload/d2c1b1262d579ec5dcdeecde17865496.jpg"></span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><br></span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><span style="color: rgb(62, 62, 62);font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;font-size: 15px;text-align: left;">24. 升级完内核并重启机器后,观察两天至今这个问题不存在了。谁能想到这个问题居然是因为 Linux 内核的 Bug 引起的?!不得不佩服第一个发现这个 Bug 的大佬。</span></span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"><span style="color: rgb(62, 62, 62);font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;font-size: 15px;text-align: left;"><br></span></span></p> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.46328125" data-s="300,640" data-type="jpeg" data-w="1280" style="height: auto !important;" src="/upload/fecdcbdb2aa83c7c4e51cc22bc51188e.jpg"></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;"></span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;letter-spacing: 0px;"></span><br></p> <h3 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;"><strong style="letter-spacing: 0px;color: rgb(171, 25, 66);"><span style="font-size: 15px;">完结感言</span></strong></h3> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><br></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">这个问题折磨了一周多,每日如鲠在喉!调试过程也是苦乐参半,乐的是突然有了调试思路,苦的是思路是一条死胡同,还好最终结果是满意的。</span></p> <p style="font-size: 16px;text-align: left;margin-bottom: 0px;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;"><span style="font-size: 15px;">作为一名程序员,还是要时刻保持一颗探索的心,学海无涯!</span></p> </section>

Spring Boot 可以同时处理多少请求?

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.6;word-break: break-word;text-align: left;padding: 5px;font-size: 16px;color: rgb(53, 53, 53);word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">我们都知道,SpringBoot默认的内嵌容器是Tomcat,也就是我们的程序实际上是运行在Tomcat里的。所以与其说SpringBoot可以处理多少请求,倒不如说Tomcat可以处理多少请求。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">关于Tomcat的默认配置,都在<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">spring-configuration-metadata.json</code>文件中,对应的配置类则是<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">org.springframework.boot.autoconfigure.web.ServerProperties</code>。</p> <p style="min-height: 24px;"><img class="rich_pages wxw-img" data-ratio="0.4698544698544699" src="/upload/c16f6ffd658d1e3107bc6d1bfb242254.png" data-type="png" data-w="962" width="962"><span style="letter-spacing: 0.8px;word-spacing: 0.8px;"></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;"></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 style="min-height: 24px;"><img class="rich_pages wxw-img" data-ratio="0.55" src="/upload/1bbba4a7fc119dcc58bcd7c5c159845b.png" data-type="png" data-w="1080" width="1080"></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;"></figure> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-1"> <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);">server.tomcat.threads.min-spare</span>:最少的工作线程数,默认大小是10。该参数相当于长期工,如果并发请求的数量达不到10,就会依次使用这几个线程去处理请求。 </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);">server.tomcat.threads.max</span>:最多的工作线程数,默认大小是200。该参数相当于临时工,如果并发请求的数量在10到200之间,就会使用这些临时工线程进行处理。 </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);">server.tomcat.max-connections</span>:最大连接数,默认大小是8192。表示Tomcat可以处理的最大请求数量,超过8192的请求就会被放入到等待队列。 </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);">server.tomcat.accept-count</span>:等待队列的长度,默认大小是100。 </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> <p style="min-height: 24px;"><img class="rich_pages wxw-img" data-ratio="0.7527777777777778" src="/upload/1632c6a87a87f909607c846c9fc354a5.png" data-type="png" data-w="1080" width="1080"></p> <p style="min-height: 24px;"><br></p> <p style="min-height: 24px;"><br></p> <p style="min-height: 24px;"><br></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;"></figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">如果把Tomcat比作一家饭店的话,那么一个请求其实就相当于一位客人。min-spare就是厨师(长期工);max是厨师总数(长期工+临时工);max-connections就是饭店里的座位数量;accept-count是门口小板凳的数量。来的客人优先坐到饭店里面,然后厨师开始忙活,如果长期工可以干得完,就让长期工干,如果长期工干不完,就再让临时工干。图中画的厨师一共15人,饭店里有30个座位,也就是说,如果现在来了20个客人,那么就会有5个人先在饭店里等着。如果现在来了35个人,饭店里坐不下,就会让5个人先到门口坐一下。如果来了50个人,那么饭店座位+门口小板凳一共40个,所以就会有10人离开。关注公z号:码猿技术专栏,回复关键词:1111 获取阿里内部性能调优手册</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">也就是说,SpringBoot同所能处理的最大请求数量是<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">max-connections+accept-count</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> <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;">创建一个SpringBoot的项目,在application.yml里配置一下这几个参数,因为默认的数量太大,不好测试,所以配小一点:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oezBibiciavS1eDQfvuuibhbfXqp1iaBSK1Desy0hGuoEAzWMqyMso2IG8xclJnQAbEM0q7qEvxqCFcs0lfwSQicXic2h7J/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(255, 255, 255);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: black;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fff;border-radius: 5px;"><span style="color: #836C28;line-height: 26px;">server:</span><br>&nbsp;&nbsp;<span style="color: #836C28;line-height: 26px;">tomcat:</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #836C28;line-height: 26px;">threads:</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #007400;line-height: 26px;">#&nbsp;最少线程数</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #836C28;line-height: 26px;">min-spare:</span>&nbsp;<span style="color: #1c00cf;line-height: 26px;">10</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #007400;line-height: 26px;">#&nbsp;最多线程数</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #836C28;line-height: 26px;">max:</span>&nbsp;<span style="color: #1c00cf;line-height: 26px;">15</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #007400;line-height: 26px;">#&nbsp;最大连接数</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #836C28;line-height: 26px;">max-connections:</span>&nbsp;<span style="color: #1c00cf;line-height: 26px;">30</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #007400;line-height: 26px;">#&nbsp;最大等待数</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #836C28;line-height: 26px;">accept-count:</span>&nbsp;<span style="color: #1c00cf;line-height: 26px;">10</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;">再来写一个简单的接口:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oezBibiciavS1eDQfvuuibhbfXqp1iaBSK1Desy0hGuoEAzWMqyMso2IG8xclJnQAbEM0q7qEvxqCFcs0lfwSQicXic2h7J/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(255, 255, 255);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: black;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fff;border-radius: 5px;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #643820;line-height: 26px;">@GetMapping</span>(<span style="color: #c41a16;line-height: 26px;">"/test"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #aa0d91;line-height: 26px;">public</span>&nbsp;Response&nbsp;<span style="color: #1c00cf;line-height: 26px;">test1</span><span style="color: #5c2699;line-height: 26px;">(HttpServletRequest&nbsp;request)</span>&nbsp;<span style="color: #aa0d91;line-height: 26px;">throws</span>&nbsp;Exception&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.info(<span style="color: #c41a16;line-height: 26px;">"ip:{},线程:{}"</span>,&nbsp;request.getRemoteAddr(),&nbsp;Thread.currentThread().getName());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(<span style="color: #1c00cf;line-height: 26px;">500</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #aa0d91;line-height: 26px;">return</span>&nbsp;Response.buildSuccess();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<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;">代码很简单,只是打印了一下线程名,然后休眠0.5秒,这样肯定会导致部分请求处理一次性处理不了而进入到等待队列。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">然后我用Apifox创建了一个测试用例,去模拟100个请求:</p> <p style="min-height: 24px;"><img class="rich_pages wxw-img" data-ratio="1.5232558139534884" src="/upload/64e9b8de23b8772f09d55ad3a9ab060.png" data-type="png" data-w="688" width="688"><span style="letter-spacing: 0.8px;word-spacing: 0.8px;"></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;"></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 style="min-height: 24px;"><img class="rich_pages wxw-img" data-ratio="0.22870370370370371" src="/upload/15d9a882c08c64a9c2f5bb0dbfb871ae.png" data-type="png" data-w="1080" width="1080"></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;"></figure> <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);">max-connections+accept-count</span> 的和是40,所以有60个请求会被丢弃,这和我们的预期是相符的。由于最大线程是15,也就是有25个请求会先等待,等前15个处理完了再处理15个,最后在处理10个,也就是将40个请求分成了15,15,10这样三批进行处理。</p> <p style="min-height: 24px;"><img class="rich_pages wxw-img" data-ratio="0.9847494553376906" src="/upload/8c5a46c723ac8e450fc7df5378b433.png" data-type="png" data-w="918" width="918"><span style="letter-spacing: 0.8px;word-spacing: 0.8px;"></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;"></figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">再从控制台的打印日志可以看到,线程的最大编号是15,这也印证了前面的想法。</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>:如果并发请求数量低于<span style="font-weight: 700;color: rgb(248, 57, 41);">server.tomcat.threads.max</span>,则会被立即处理,超过的部分会先进行等待,如果数量超过max-connections与accept-count之和,则多余的部分则会被直接丢弃。</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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">延伸:并发问题是如何产生的</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;">到目前为止,就已经搞明白了SpringBoot同时可以处理多少请求的问题。但是在这里我还想基于上面的例子再延伸一下,就是为什么并发场景下会出现一些值和我们预期的不一样?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">设想有以下场景:厨师们用一个账本记录一共做了多少道菜,每个厨师做完菜都记录一下,每次记录都是将账本上的数字先抄到草稿纸上,计算x+1等于多少,然后将计算的结果写回到账本上。</p> <p style="min-height: 24px;"><img class="rich_pages wxw-img" data-ratio="0.7277777777777777" src="/upload/d7270b7c0394e43c1058454205c8f0ac.png" data-type="png" data-w="1080" width="1080"><span style="letter-spacing: 0.8px;word-spacing: 0.8px;"></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;"></figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">Spring容器中的Bean默认是单例的,也就是说,处理请求的Controller、Service实例就只有一份。在并发场景下,将cookSum定义为全局变量,是所有线程共享的,当一个线程读到了cookSum=20,然后计算,写回前另一个线程也读到是20,两个线程都加1后写回,最终cookSum就变成了21,但是实际上应该是22,因为加了两次。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oezBibiciavS1eDQfvuuibhbfXqp1iaBSK1Desy0hGuoEAzWMqyMso2IG8xclJnQAbEM0q7qEvxqCFcs0lfwSQicXic2h7J/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(255, 255, 255);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: black;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fff;border-radius: 5px;"><span style="color: #aa0d91;line-height: 26px;">private</span>&nbsp;<span style="color: #aa0d91;line-height: 26px;">int</span>&nbsp;cookSum&nbsp;=&nbsp;<span style="color: #1c00cf;line-height: 26px;">0</span>;<br><br><span style="color: #643820;line-height: 26px;">@GetMapping</span>(<span style="color: #c41a16;line-height: 26px;">"/test"</span>)<br><span style="line-height: 26px;"><span style="color: #aa0d91;line-height: 26px;">public</span>&nbsp;Response&nbsp;<span style="color: #1c00cf;line-height: 26px;">test1</span><span style="color: #5c2699;line-height: 26px;">(HttpServletRequest&nbsp;request)</span>&nbsp;<span style="color: #aa0d91;line-height: 26px;">throws</span>&nbsp;Exception&nbsp;</span>{<br>&nbsp;<span style="color: #007400;line-height: 26px;">//&nbsp;做菜。。。。。。</span><br>&nbsp;cookSum&nbsp;+=&nbsp;<span style="color: #1c00cf;line-height: 26px;">1</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;log.info(<span style="color: #c41a16;line-height: 26px;">"做了{}道菜"</span>,&nbsp;cookSum);<br>&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(<span style="color: #1c00cf;line-height: 26px;">500</span>);<br>&nbsp;<span style="color: #aa0d91;line-height: 26px;">return</span>&nbsp;Response.buildSuccess();<br>}<br></code></pre> <p style="min-height: 24px;"><img class="rich_pages wxw-img" data-ratio="0.562962962962963" src="/upload/ed762ad27a11777b414c0d6d984c5072.png" data-type="png" data-w="1080" width="1080"></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> <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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">最后说一句(别白嫖,求关注)</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);">点赞</span>、<span style="font-weight: 700;color: rgb(248, 57, 41);">在看</span>、<span style="font-weight: 700;color: rgb(248, 57, 41);">转发</span>、<span style="font-weight: 700;color: rgb(248, 57, 41);">收藏</span>,你的支持就是我坚持下去的最大动力!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">另外陈某的<a href="https://mp.weixin.qq.com/s?__biz=MzU3MDAzNDg1MA==&amp;mid=2247518914&amp;idx=1&amp;sn=b3fdfd78c32b15077ac67535ccc10a00&amp;scene=21#wechat_redirect" style="color: rgb(248, 57, 41);border-bottom: 1px solid rgb(248, 57, 41);" data-linktype="2">知识星球</a>开通了,公众号回复关键词:<span style="font-weight: 700;color: rgb(248, 57, 41);">知识星球</span> 获取限量<span style="font-weight: 700;color: rgb(248, 57, 41);">30元</span>优惠券加入只需<span style="font-weight: 700;color: rgb(248, 57, 41);">89</span>元,一顿饭钱,但是星球回馈的价值却是巨大,目前更新了<span style="font-weight: 700;color: rgb(248, 57, 41);">Spring全家桶实战系列</span>、<span style="font-weight: 700;color: rgb(248, 57, 41);">亿级数据分库分表实战</span>、<span style="font-weight: 700;color: rgb(248, 57, 41);">DDD微服务实战专栏</span>、<span style="font-weight: 700;color: rgb(248, 57, 41);">我要进大厂、Spring,Mybatis等框架源码、架构实战22讲</span>等....每增加一个专栏价格将上涨20元</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 class="rich_pages wxw-img" data-ratio="1.0398148148148147" src="/upload/d2a99d6b013cd820afa10b391d4794be.png" data-w="1080" 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: 0.8em;margin-bottom: 0.8em;">关注公众号:【码猿技术专栏】,公众号内有超赞的粉丝福利,回复:加群,可以加入技术讨论群,和大家一起讨论技术,吹牛逼!</p> </section> <section> <mp-common-profile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-weui-theme="light" data-id="MzU3MDAzNDg1MA==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/19cc2hfD2rA07Je2pY1o0ic2KcPRn44icO8GVcKRdwiaYvrE6bNeTbWPicyV7c7jWmSyzsiaWASjjckzBcsJMJw06pA/0?wx_fmt=png" data-nickname="码猿技术专栏" data-alias="oneswholife" data-signature="前蚂蚁P8,纯粹的技术人,以专栏的形式分享java全栈技术:SSM、Spring全家桶、微服务、MySQL、分布式、中间件、Linux、偶尔讲点运维Jenkins、Nexus、Docker、ELK.." data-from="0" data-is_biz_ban="0"></mp-common-profile> </section>

网易二面:CPU狂飙900%,该怎么处理?

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.6;word-break: break-word;text-align: left;padding: 5px;font-size: 16px;color: rgb(53, 53, 53);word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;" data-mpa-powered-by="yiban.io"> <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> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><a href="https://mp.weixin.qq.com/s?__biz=MzU3MDAzNDg1MA==&amp;mid=2247523057&amp;idx=1&amp;sn=32b42c6b0ac41b48785b7c0d24ce344a&amp;chksm=fcf7453ccb80cc2a4a6cf38d5b9ab0354f09f270418bf4ff5eeb832b020aedabd561979b712d&amp;token=1260267649&amp;lang=zh_CN&amp;scene=21#wechat_redirect" style="letter-spacing: 0.8px;word-spacing: 0.8px;color: rgb(248, 57, 41);border-bottom: 1px solid rgb(248, 57, 41);" data-linktype="2">星球</a><span style="letter-spacing: 0.8px;word-spacing: 0.8px;">一位小伙伴面试了 网易,遇到了一个 性能类的面试题:CPU飙升900%,该怎么处理?</span><a target="_blank" href="https://mp.weixin.qq.com/s?__biz=MzU3MDAzNDg1MA==&amp;mid=2247523057&amp;idx=1&amp;sn=32b42c6b0ac41b48785b7c0d24ce344a&amp;chksm=fcf7453ccb80cc2a4a6cf38d5b9ab0354f09f270418bf4ff5eeb832b020aedabd561979b712d&amp;token=1260267649&amp;lang=zh_CN&amp;scene=21#wechat_redirect" textvalue="星球" linktype="text" imgurl="" imgdata="null" tab="innerlink" data-linktype="2"></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;">可惜的是,以上的问题,这个小伙没有回答理想。</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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">首先,说明一下问题:CPU飙升200% 以上是生产容易发生的场景</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;">场景:1:MySQL进程飙升900%</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;">大家在使用MySQL过程,想必都有遇到过CPU突然过高,或者达到200%以上的情况。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">数据库执行查询或数据修改操作时,系统需要消耗大量的CPU资源维护从存储系统、内存数据中的一致性。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">并发量大并且大量SQL性能低的情况下,比如字段是没有建立索引,则会导致快速CPU飙升,如果还开启了慢日志记录,会导致性能更加恶化。生产上有MYSQL 飙升900% 的恶劣情况。</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;">场景2:Java进程飙升900%</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;">一般来说Java 进程不做大量 CPU 运算,正常情况下,CPU 应该在 100~200% 之间,</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">但是,一旦高并发场景,要么走到了死循环,要么就是在做大量的 GC, &nbsp;容易出现这种 CPU 飙升的情况,CPU飙升900%,是完全有可能的。</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;">其他场景:其他的类似进程飙升900%的场景</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;">比如Redis、Nginx等等。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">陈某提示:大家介绍场景的时候,就说自己主要涉及了两个场景, Java进程飙升900%、MySQL进程飙升900%两种场景,其实,这两个场景就足够讲半天了, 其他的,使用规避技巧规避一下就行。</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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">场景一:MySQL进程CPU飙升到900%,怎么处理?</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);">定位过程:</span></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 使用top 命令观察,确定是mysqld导致还是其他原因。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 如果是mysqld导致的,show processlist,查看session情况,确定是不是有消耗资源的sql在运行。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 找出消耗高的 sql,看看执行计划是否准确, index 是否缺失,或者实在是数据量太大造成。 </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;"><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);" class="list-paddingleft-1"> <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: 0.8em;margin-bottom: 0.8em;">kill 掉这些线程(同时观察 cpu 使用率是否下降), 一般来说,肯定要 kill 掉这些线程(同时观察 cpu 使用率是否下降),等进行相应的调整(比如说加索引、改 sql、改内存参数)之后,再重新跑这些 SQL。</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: 0.8em;margin-bottom: 0.8em;">进行相应的调整(比如说加索引、改 sql、改内存参数)</p> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">index 是否缺失,如果是,则 &nbsp;建立索引。也有可能是每个 sql 消耗资源并不多,但是突然之间,有大量的 session 连进来导致 cpu 飙升,这种情况就需要跟应用一起来分析为何连接数会激增,再做出相应的调整,比如说限制连接数等;关注公z号:码猿技术专栏,回复关键词:1111 获取阿里内部Java性能调优手册</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: 0.8em;margin-bottom: 0.8em;">优化的过程,往往不是一步完成的,而是一步一步,执行一项优化措辞,再观察,再优化。</p> </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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">场景1的真实案例:MySQL数据库优化的真实案例</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;"><em style="color: rgb(248, 57, 41);">陈某提示:以下案例,来自互联网。大家参考一下,准备一个自己的案例。</em></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;">之前开发同事编写的SQL语句,就导致过线上CPU过高,MySQL的CPU使用率达到900%+,通过优化最后降低到70%~80%。下面说说个人在这个过程中的排查思路。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">首先,我们要对问题定位而不是盲目的开启什么 慢日志,在并发量大并且大量SQL性能低的情况下,开启慢日志无意是将MySQL推向崩溃的边缘。</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> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 经过询问,发现很多查询都是走MySQL,没有用到缓存。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 既然没有用到缓存,则是大量请求全部查询MySQL导致。通过下面的命令查看: </section></li> </ol> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oezBibiciavS1eDQYzT0Mm0KLwW3XxwEjZzyqMmd5ypial39jf9wd5K9kFPSfHSrqsMU9hYvfseAWkU6rnhQcKK7xuQ8/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(255, 255, 255);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: black;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fff;border-radius: 5px;"><span style="color: #aa0d91;line-height: 26px;">show</span>&nbsp;<span style="color: #aa0d91;line-height: 26px;">processlist</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;">发现类似很多相同的SQL语句,一直处于query状态中。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oezBibiciavS1eDQYzT0Mm0KLwW3XxwEjZzyqMmd5ypial39jf9wd5K9kFPSfHSrqsMU9hYvfseAWkU6rnhQcKK7xuQ8/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(255, 255, 255);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: black;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fff;border-radius: 5px;"><span style="color: #aa0d91;line-height: 26px;">select</span>&nbsp;<span style="color: #aa0d91;line-height: 26px;">id</span>&nbsp;<span style="color: #aa0d91;line-height: 26px;">form</span>&nbsp;<span style="color: #aa0d91;line-height: 26px;">user</span>&nbsp;<span style="color: #aa0d91;line-height: 26px;">where</span>&nbsp;user_code&nbsp;=&nbsp;<span style="color: #c41a16;line-height: 26px;">'xxxxx'</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;">初步分析可能是 user_code 字段没有索引导致。接着查询user表的索引情况:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oezBibiciavS1eDQYzT0Mm0KLwW3XxwEjZzyqMmd5ypial39jf9wd5K9kFPSfHSrqsMU9hYvfseAWkU6rnhQcKK7xuQ8/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(255, 255, 255);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: black;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fff;border-radius: 5px;"><span style="color: #aa0d91;line-height: 26px;">show</span>&nbsp;<span style="color: #aa0d91;line-height: 26px;">index</span>&nbsp;<span style="color: #aa0d91;line-height: 26px;">form</span>&nbsp;<span style="color: #aa0d91;line-height: 26px;">user</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;">发现这个字段是没有建立索引。增加索引之后,该条SQL查询能够正常执行。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">3、没隔一会,又发生大量的请求超时问题。接着进行分析,发现是开启了 慢日志查询。大量的SQL查询语句超过慢日志设置的阀值,于是将慢日志关闭之后,速度瞬间提升。CPU的使用率基本保持在300%左右。但还不是理想状态。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">4、紧接着将部分实时查询数据的SQL语句,都通过缓存(redis)读写实现。观察一段时间后,基本维持在了70%~80%。</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> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 不推荐在这种CPU使用过高的情况下进行慢日志的开启。因为大量的请求,如果真是慢日志问题会发生日志磁盘写入,性能贼低。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 直接通过MySQL show processlist命令查看,基本能清晰的定位出部分查询问题严重的SQL语句,在针对该SQL语句进行分析。一般可能就是索引、锁、查询大量字段、大表等问题导致。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 再则一定要使用缓存系统,降低对MySQL的查询频次。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 对于内存调优,也是一种解决方案。 </section></li> </ol> <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;">场景2展开:Java进程CPU飙升到900%,怎么处理?</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;"><span style="font-weight: 700;color: rgb(248, 57, 41);">定位过程:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">CPU飙升问题定位的一般步骤是:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 首先通过top指令查看当前占用CPU较高的进程PID; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 查看当前进程消耗资源的线程PID:top -Hp PID </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 通过print命令将线程PID转为16进制,根据该16进制值去打印的堆栈日志内查询,查看该线程所驻留的方法位置。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 通过jstack命令,查看栈信息,定位到线程对应的具体代码。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 分析代码解决问题。 </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;"><span style="font-weight: 700;color: rgb(248, 57, 41);">处理过程:</span></p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-1"> <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: 0.8em;margin-bottom: 0.8em;">如果是空循环,或者空自旋。</p> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">处理方式:可以使用Thread.sleep或者加锁,让线程适当的阻塞。</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: 0.8em;margin-bottom: 0.8em;">在循环的代码逻辑中,创建大量的新对象导致频繁GC。比如,从mysql查出了大量的数据,比如100W以上等等。</p> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">处理方式:可以减少对象的创建数量,或者,可以考虑使用 对象池。</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: 0.8em;margin-bottom: 0.8em;">其他的一些造成CPU飙升的场景,比如 &nbsp;selector空轮训导致CPU飙升 。</p> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">处理方式:参考Netty源码,无效的事件查询到了一定的次数,进行 selector 重建。</p> </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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">Java的CPU 飙升700%优化的真实案例</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;"><em style="color: rgb(248, 57, 41);">陈某提示:以下案例,来自互联网。大家参考一下,准备一个自己的案例。</em></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">最近负责的一个项目上线,运行一段时间后发现对应的进程竟然占用了700%的CPU,导致公司的物理服务器都不堪重负,频繁宕机。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">那么,针对这类java进程CPU飙升的问题,我们一般要怎么去定位解决呢?、</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;">采用top命令定位进程</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;">登录服务器,执行top命令,查看CPU占用情况,找到进程的pid</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oezBibiciavS1eDQYzT0Mm0KLwW3XxwEjZzyqMmd5ypial39jf9wd5K9kFPSfHSrqsMU9hYvfseAWkU6rnhQcKK7xuQ8/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(255, 255, 255);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: black;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fff;border-radius: 5px;">top<br></code></pre> <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 class="rich_pages wxw-img" data-ratio="0.2898148148148148" data-w="1080" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;height: auto !important;" src="/upload/14ad3cc142a85977879157b33de30ffd.png"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">很容易发现,PID为29706的java进程的CPU飙升到700%多,且一直降不下来,很显然出现了问题。</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;">使用top -Hp命令定位线程</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;">使用 top -Hp命令(为Java进程的id号)查看该Java进程内所有线程的资源占用情况(按shft+p按照cpu占用进行排序,按shift+m按照内存占用进行排序)</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">此处按照cpu排序:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oezBibiciavS1eDQYzT0Mm0KLwW3XxwEjZzyqMmd5ypial39jf9wd5K9kFPSfHSrqsMU9hYvfseAWkU6rnhQcKK7xuQ8/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(255, 255, 255);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: black;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fff;border-radius: 5px;">top&nbsp;-Hp&nbsp;23602<br></code></pre> <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 class="rich_pages wxw-img" data-ratio="0.26481481481481484" data-w="1080" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;height: auto !important;" src="/upload/fa41962e087cef2630e177fa6cf10ea0.png"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">很容易发现,多个线程的CPU占用达到了90%多。我们挑选线程号为30309的线程继续分析。</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;">使用jstack命令定位代码</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;"><span style="font-weight: 700;color: rgb(248, 57, 41);">1.线程号转换5为16进制</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">printf “%x\n” 命令(tid指线程的id号)将以上10进制的线程号转换为16进制:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oezBibiciavS1eDQYzT0Mm0KLwW3XxwEjZzyqMmd5ypial39jf9wd5K9kFPSfHSrqsMU9hYvfseAWkU6rnhQcKK7xuQ8/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(255, 255, 255);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: black;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fff;border-radius: 5px;">printf&nbsp;"%x\n"&nbsp;&nbsp;30309<br></code></pre> <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 class="rich_pages wxw-img" data-ratio="0.09907407407407408" data-w="1080" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;height: auto !important;" src="/upload/a34db6944aa5b9776b4c862c06614d7d.png"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">转换后的结果分别为7665,由于导出的线程快照中线程的nid是16进制的,而16进制以0x开头,所以对应的16进制的线程号nid为0x7665</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);">2.采用jstack命令导出线程快照</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">通过使用dk自带命令jstack获取该java进程的线程快照并输入到文件中:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oezBibiciavS1eDQYzT0Mm0KLwW3XxwEjZzyqMmd5ypial39jf9wd5K9kFPSfHSrqsMU9hYvfseAWkU6rnhQcKK7xuQ8/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(255, 255, 255);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: black;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fff;border-radius: 5px;">&nbsp;jstack&nbsp;-l&nbsp;进程ID&nbsp;&gt;&nbsp;./jstack_result.txt&nbsp;<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;">命令(为Java进程的id号)来获取线程快照结果并输入到指定文件。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oezBibiciavS1eDQYzT0Mm0KLwW3XxwEjZzyqMmd5ypial39jf9wd5K9kFPSfHSrqsMU9hYvfseAWkU6rnhQcKK7xuQ8/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(255, 255, 255);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: black;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fff;border-radius: 5px;">jstack&nbsp;-l&nbsp;29706&nbsp;&gt;&nbsp;./jstack_result.txt<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);">3.根据线程号定位具体代码</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">在jstack_result.txt 文件中根据线程好nid搜索对应的线程描述</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oezBibiciavS1eDQYzT0Mm0KLwW3XxwEjZzyqMmd5ypial39jf9wd5K9kFPSfHSrqsMU9hYvfseAWkU6rnhQcKK7xuQ8/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(255, 255, 255);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: black;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fff;border-radius: 5px;">cat&nbsp;jstack_result.txt&nbsp;|grep&nbsp;-A&nbsp;100&nbsp;&nbsp;7665<br></code></pre> <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 class="rich_pages wxw-img" data-ratio="0.18703703703703703" data-w="1080" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;height: auto !important;" src="/upload/1b2e44dc97e96984e2af714974a1f01b.png"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">根据搜索结果,判断应该是ImageConverter.run()方法中的代码出现问题</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> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oezBibiciavS1eDQYzT0Mm0KLwW3XxwEjZzyqMmd5ypial39jf9wd5K9kFPSfHSrqsMU9hYvfseAWkU6rnhQcKK7xuQ8/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(255, 255, 255);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: black;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fff;border-radius: 5px;">jstack&nbsp;&lt;pid&gt;&nbsp;|grep&nbsp;-A&nbsp;200&nbsp;&lt;nid&gt;<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;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oezBibiciavS1eDQYzT0Mm0KLwW3XxwEjZzyqMmd5ypial39jf9wd5K9kFPSfHSrqsMU9hYvfseAWkU6rnhQcKK7xuQ8/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(255, 255, 255);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: black;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fff;border-radius: 5px;">$jstack&nbsp;<span style="color: #1c00cf;line-height: 26px;">44529</span>&nbsp;|grep&nbsp;-A&nbsp;<span style="color: #1c00cf;line-height: 26px;">200</span>&nbsp;ae24<br><span style="color: #c41a16;line-height: 26px;">"System&nbsp;Clock"</span>&nbsp;#<span style="color: #1c00cf;line-height: 26px;">28</span>&nbsp;daemon&nbsp;prio=<span style="color: #1c00cf;line-height: 26px;">5</span>&nbsp;os_prio=<span style="color: #1c00cf;line-height: 26px;">0</span>&nbsp;tid=<span style="color: #1c00cf;line-height: 26px;">0x00007efc19e8e800</span>&nbsp;nid=<span style="color: #1c00cf;line-height: 26px;">0xae24</span>&nbsp;waiting&nbsp;on&nbsp;condition&nbsp;[<span style="color: #1c00cf;line-height: 26px;">0x00007efbe0d91000</span>]<br>&nbsp;&nbsp;&nbsp;java.lang.Thread.State:&nbsp;TIMED_WAITING&nbsp;(sleeping)<br>&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.Thread.sleep(Native&nbsp;Method)<br>&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.lang.Thread.sleep(Thread.java:<span style="color: #1c00cf;line-height: 26px;">340</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;java.util.concurrentC.TimeUnit.sleep(TimeUnit.java:<span style="color: #1c00cf;line-height: 26px;">386</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;com.*.order.Controller.OrderController.detail(OrderController.java:<span style="color: #1c00cf;line-height: 26px;">37</span>)&nbsp;&nbsp;<span style="color: #007400;line-height: 26px;">//业务代码阻塞点</span><br></code></pre> <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;">下面是ImageConverter.run()方法中的部分核心代码。</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> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oezBibiciavS1eDQYzT0Mm0KLwW3XxwEjZzyqMmd5ypial39jf9wd5K9kFPSfHSrqsMU9hYvfseAWkU6rnhQcKK7xuQ8/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(255, 255, 255);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: black;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fff;border-radius: 5px;">/存储minicap的socket连接返回的数据&nbsp;&nbsp;&nbsp;(改用消息队列存储读到的流数据)&nbsp;,设置阻塞队列长度,防止出现内存溢出<br><span style="color: #007400;line-height: 26px;">//全局变量</span><br><span style="color: #aa0d91;line-height: 26px;">private</span>&nbsp;BlockingQueue&lt;<span style="color: #aa0d91;line-height: 26px;">byte</span>[]&gt;&nbsp;dataQueue&nbsp;=&nbsp;<span style="color: #aa0d91;line-height: 26px;">new</span>&nbsp;LinkedBlockingQueue&lt;<span style="color: #aa0d91;line-height: 26px;">byte</span>[]&gt;(<span style="color: #1c00cf;line-height: 26px;">100000</span>);<br><span style="color: #007400;line-height: 26px;">//消费线程</span><br><span style="color: #643820;line-height: 26px;">@Override</span><br><span style="line-height: 26px;"><span style="color: #aa0d91;line-height: 26px;">public</span>&nbsp;<span style="color: #aa0d91;line-height: 26px;">void</span>&nbsp;<span style="color: #1c00cf;line-height: 26px;">run</span><span style="color: #5c2699;line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #007400;line-height: 26px;">//long&nbsp;start&nbsp;=&nbsp;System.currentTimeMillis();</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #aa0d91;line-height: 26px;">while</span>&nbsp;(isRunning)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #007400;line-height: 26px;">//分析这里从LinkedBlockingQueue</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #aa0d91;line-height: 26px;">if</span>&nbsp;(dataQueue.isEmpty())&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #aa0d91;line-height: 26px;">continue</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #aa0d91;line-height: 26px;">byte</span>[]&nbsp;buffer&nbsp;=&nbsp;device.getMinicap().dataQueue.poll();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #aa0d91;line-height: 26px;">int</span>&nbsp;len&nbsp;=&nbsp;buffer.length;<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;">在while循环中,不断读取堵塞队列dataQueue中的数据,如果数据为空,则执行continue进行下一次循环。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">如果不为空,则通过poll()方法读取数据,做相关逻辑处理。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">初看这段代码好像每什么问题,但是如果dataQueue对象长期为空的话,这里就会一直空循环,导致CPU飙升。</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;">分析LinkedBlockingQueue阻塞队列的API发现:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oezBibiciavS1eDQYzT0Mm0KLwW3XxwEjZzyqMmd5ypial39jf9wd5K9kFPSfHSrqsMU9hYvfseAWkU6rnhQcKK7xuQ8/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(255, 255, 255);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: black;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fff;border-radius: 5px;"><span style="color: #007400;line-height: 26px;">//取出队列中的头部元素,如果队列为空则调用此方法的线程被阻塞等待,直到有元素能被取出,如果等待过程被中断则抛出InterruptedException</span><br><span style="line-height: 26px;">E&nbsp;<span style="color: #1c00cf;line-height: 26px;">take</span><span style="color: #5c2699;line-height: 26px;">()</span>&nbsp;<span style="color: #aa0d91;line-height: 26px;">throws</span>&nbsp;InterruptedException</span>;<br><span style="color: #007400;line-height: 26px;">//取出队列中的头部元素,如果队列为空返回null</span><br><span style="line-height: 26px;">E&nbsp;<span style="color: #1c00cf;line-height: 26px;">poll</span><span style="color: #5c2699;line-height: 26px;">()</span></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;">这两种取值的API,显然take方法更时候这里的场景。</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> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oezBibiciavS1eDQYzT0Mm0KLwW3XxwEjZzyqMmd5ypial39jf9wd5K9kFPSfHSrqsMU9hYvfseAWkU6rnhQcKK7xuQ8/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(255, 255, 255);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: black;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fff;border-radius: 5px;"><span style="color: #aa0d91;line-height: 26px;">while</span>&nbsp;(isRunning)&nbsp;{<br>&nbsp;&nbsp;&nbsp;<span style="color: #007400;line-height: 26px;">/*&nbsp;if&nbsp;(device.getMinicap().dataQueue.isEmpty())&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;continue;<br>&nbsp;&nbsp;&nbsp;&nbsp;}*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #aa0d91;line-height: 26px;">byte</span>[]&nbsp;buffer&nbsp;=&nbsp;<span style="color: #aa0d91;line-height: 26px;">new</span>&nbsp;<span style="color: #aa0d91;line-height: 26px;">byte</span>[<span style="color: #1c00cf;line-height: 26px;">0</span>];<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #aa0d91;line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buffer&nbsp;=&nbsp;device.getMinicap().dataQueue.take();<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #aa0d91;line-height: 26px;">catch</span>&nbsp;(InterruptedException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<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;">重启项目后,测试发现项目运行稳定,对应项目进程的CPU消耗占比不到10%。</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 class="rich_pages wxw-img" data-ratio="0.287962962962963" data-w="1080" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;height: auto !important;" src="/upload/f3033261860e72ba47ba0a4f08a7dce0.png"> </figure> <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="color: rgb(0, 0, 0);">来源:技术自由圈</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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">最后说一句(别白嫖,求关注)</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);">点赞</span>、<span style="font-weight: 700;color: rgb(248, 57, 41);">在看</span>、<span style="font-weight: 700;color: rgb(248, 57, 41);">转发</span>、<span style="font-weight: 700;color: rgb(248, 57, 41);">收藏</span>,你的支持就是我坚持下去的最大动力!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">另外陈某的<a href="https://mp.weixin.qq.com/s?__biz=MzU3MDAzNDg1MA==&amp;mid=2247523057&amp;idx=1&amp;sn=32b42c6b0ac41b48785b7c0d24ce344a&amp;chksm=fcf7453ccb80cc2a4a6cf38d5b9ab0354f09f270418bf4ff5eeb832b020aedabd561979b712d&amp;token=1260267649&amp;lang=zh_CN&amp;scene=21#wechat_redirect" style="color: rgb(248, 57, 41);border-bottom: 1px solid rgb(248, 57, 41);" data-linktype="2">知识星球</a>开通了,公众号回复关键词:<span style="font-weight: 700;color: rgb(248, 57, 41);">知识星球</span> 获取限量<span style="font-weight: 700;color: rgb(248, 57, 41);">30元</span>优惠券加入只需<span style="font-weight: 700;color: rgb(248, 57, 41);">89</span>元,一顿饭钱,但是星球回馈的价值却是巨大,目前更新了<span style="font-weight: 700;color: rgb(248, 57, 41);">Spring全家桶实战系列</span>、<span style="font-weight: 700;color: rgb(248, 57, 41);">亿级数据分库分表实战</span>、<span style="font-weight: 700;color: rgb(248, 57, 41);">DDD微服务实战专栏</span>、<span style="font-weight: 700;color: rgb(248, 57, 41);">我要进大厂、Spring,Mybatis等框架源码、架构实战22讲</span>等....每增加一个专栏价格将上涨20元</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 class="rich_pages wxw-img" data-ratio="1.0398148148148147" data-w="1080" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;height: auto !important;" src="/upload/d2a99d6b013cd820afa10b391d4794be.png"> </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> </section> <section> <mp-common-profile class="custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-weuitheme="light" data-id="MzU3MDAzNDg1MA==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/19cc2hfD2rA07Je2pY1o0ic2KcPRn44icO8GVcKRdwiaYvrE6bNeTbWPicyV7c7jWmSyzsiaWASjjckzBcsJMJw06pA/0?wx_fmt=png" data-nickname="码猿技术专栏" data-alias="oneswholife" data-signature="前蚂蚁P8,纯粹的技术人,以专栏的形式分享java全栈技术:SSM、Spring全家桶、微服务、MySQL、分布式、中间件、Linux、偶尔讲点运维Jenkins、Nexus、Docker、ELK.." data-from="0" data-weui-theme="light"></mp-common-profile> </section> <p style="display: none;"> <mp-style-type data-value="3"></mp-style-type></p>

对账系统,这下彻底搞清楚了!

作者:微信小助手

<section style="margin: 0pt 0% 0pt 0pt;font-size: 10.5pt;font-family: Calibri;font-weight: normal;text-align: left;text-indent: 0pt;line-height: 2em;"> <span style="font-size: 16px;font-family: 宋体;text-align: justify;text-indent: 0pt;">想必大家对“对账”这个词都不陌生,单从字面意思就能略知一二;其实就是字面意思;“对”就是核对,“账”就是账目;“对账”就是核对账目;账目核算是财务工作的必要部分,随着线上交易体量越来越大或者说对财务自动化线上化的效率提升需求越来越高;为了提升核对效率以及准确性,势必要将核对业务系统化线上化自动化;那么如何构建设计一套不同业务场景下的对账系统呢?<br></span> </section> <section style="margin: 0pt 0% 0pt 0pt;font-size: 10.5pt;font-family: Calibri;font-weight: normal;text-align: left;text-indent: 0pt;line-height: 2em;"> <span style="font-family: 宋体;text-align: justify;text-indent: 0pt;color: rgb(136, 136, 136);font-size: 12px;">全文11046字,建议先收藏慢慢看</span> </section> <section style="margin-top: 0pt;margin-bottom: 0pt;text-align: justify;margin-left: 0pt;text-indent: 0pt;font-size: 10.5pt;font-family: Calibri;font-weight: normal;line-height: 2em;"> <span style="font-family: 宋体;font-size: 16px;"><br></span> </section> <section style="margin-top: 0pt;margin-bottom: 0pt;text-align: center;margin-left: 0pt;text-indent: 0pt;font-size: 10.5pt;font-family: Calibri;font-weight: normal;line-height: 2em;"> <span style="font-family: 宋体;font-weight: bold;font-size: 24px;color: rgb(255, 0, 0);">1</span> </section> <section style="margin-top: 0pt;margin-bottom: 0pt;text-align: center;margin-left: 0pt;text-indent: 0pt;font-size: 10.5pt;font-family: Calibri;font-weight: normal;line-height: 2em;"> <span style="font-size: 16px;font-family: 宋体;font-weight: bold;">对账概述</span> </section> <section style="margin-top: 0pt;margin-bottom: 0pt;text-align: justify;margin-left: 0pt;text-indent: 0pt;font-size: 10.5pt;font-family: Calibri;font-weight: normal;line-height: 2em;"> <span style="font-family: 宋体;font-size: 16px;"><br></span> </section> <section style="margin-top: 0pt;margin-bottom: 0pt;text-align: justify;margin-left: 0pt;text-indent: 0pt;font-size: 10.5pt;font-family: Calibri;font-weight: normal;line-height: 2em;"> <span style="font-size: 16px;font-family: 宋体;">日常生活中我们每天都在对账,比如去餐馆吃饭付款,会对老板说一声“老板,钱付过去了”;老板检查过收款或者听到语音播报后回复一声“好嘞,下次再来”;这就是最简单的一次对账。</span> </section> <section style="margin-top: 0pt;margin-bottom: 0pt;text-align: justify;margin-left: 0pt;text-indent: 0pt;font-size: 10.5pt;font-family: Calibri;font-weight: normal;line-height: 2em;"> <span style="font-family: 宋体;font-size: 16px;"><br></span> </section> <section style="margin-top: 0pt;margin-bottom: 0pt;text-align: justify;margin-left: 0pt;text-indent: 0pt;font-size: 10.5pt;font-family: Calibri;font-weight: normal;line-height: 2em;"> <span style="font-size: 16px;font-family: 宋体;">再比如你在淘宝开了一个店铺,每个月几千单的交易,发货;每次月末都拿着所有的订单明细和支付宝收款账户收款记录逐笔的做一次核对,保证发过货的订单都收到款了,这就是一次更复杂的核对了。</span> </section> <section style="margin-top: 0pt;margin-bottom: 0pt;text-align: justify;margin-left: 0pt;text-indent: 0pt;font-size: 10.5pt;font-family: Calibri;font-weight: normal;line-height: 2em;"> <span style="font-family: 宋体;font-size: 16px;"><br></span> </section> <section style="margin-top: 0pt;margin-bottom: 0pt;text-align: justify;margin-left: 0pt;text-indent: 0pt;font-size: 10.5pt;font-family: Calibri;font-weight: normal;line-height: 2em;"> <span style="font-size: 16px;font-family: 宋体;font-weight: bold;">1.1什么是对账</span> </section> <section style="margin-top: 0pt;margin-bottom: 0pt;text-align: justify;margin-left: 0pt;text-indent: 0pt;font-size: 10.5pt;font-family: Calibri;font-weight: normal;line-height: 2em;"> <span style="font-family: 宋体;font-size: 16px;"><br></span> </section> <section style="margin-top: 0pt;margin-bottom: 0pt;text-align: justify;margin-left: 0pt;text-indent: 0pt;font-size: 10.5pt;font-family: Calibri;font-weight: normal;line-height: 2em;"> <span style="font-family: 宋体;font-size: 16px;">对账概括来说就是“账证实”核对,“账”就是账目,“证”就是凭证,“实”就是实际资金</span> </section> <section style="margin-top: 0pt;margin-bottom: 0pt;text-align: justify;margin-left: 0pt;text-indent: 0pt;font-size: 10.5pt;font-family: Calibri;font-weight: normal;line-height: 2em;"> <span style="font-family: 宋体;font-size: 16px;"><br></span> </section> <section style="margin-top: 0pt;margin-bottom: 0pt;text-align: justify;margin-left: 0pt;text-indent: 0pt;font-size: 10.5pt;font-family: Calibri;font-weight: normal;line-height: 2em;"> <span style="font-family: 宋体;font-size: 16px;">核对模式有三种:账证核对,账账核对,账实核对;确保账证实两两的一致性;比如吃了一碗面点菜单就是原始凭“证”,付了10元钱是“账”,老板电脑点菜记录小票10元是“账”,老板看到账户中余额增加了10元是“实”</span> </section> <section style="margin-top: 0pt;margin-bottom: 0pt;text-align: justify;margin-left: 0pt;text-indent: 0pt;font-size: 10.5pt;font-family: Calibri;font-weight: normal;line-height: 2em;"> <span style="font-family: 宋体;font-size: 16px;"><br></span> </section> <section style="margin-top: 0pt;margin-bottom: 0pt;text-align: justify;margin-left: 0pt;text-indent: 0pt;font-size: 10.5pt;font-family: Calibri;font-weight: normal;line-height: 2em;"> <span style="font-family: 宋体;font-size: 16px;">财务范畴来看,证就是会计凭证,比如发票,小票,出货单,收据,交易系统的支付记录等都是原始凭证;而账呢就是财务的账目,账务系统的账务记账,金蝶的科目余额等都是不同的账目;而一笔交易会记录在很多的环节,比如账务系统,金蝶等</span> </section> <section style="margin-top: 0pt;margin-bottom: 0pt;text-align: justify;margin-left: 0pt;text-indent: 0pt;font-size: 10.5pt;font-family: Calibri;font-weight: normal;line-height: 2em;"> <span style="font-family: 宋体;font-size: 16px;"><br></span> </section> <section style="margin-top: 0pt;margin-bottom: 0pt;text-align: justify;margin-left: 0pt;text-indent: 0pt;font-size: 10.5pt;font-family: Calibri;font-weight: normal;line-height: 2em;"> <span style="font-family: 宋体;font-size: 16px;">另外脱离财务范畴来看,其实账目本身抽象出来就是数据,商品数据,用户数据,卡券数据等,那么账证实需要核对,很多时候很多非财务范畴数据也需要核对,比如今天应到10人实到8人,军训时的报数等其实也可以称为对账,我们暂且称为“广义的对账”</span> </section> <section style="margin-top: 0pt;margin-bottom: 0pt;text-align: justify;margin-left: 0pt;text-indent: 0pt;font-size: 10.5pt;font-family: Calibri;font-weight: normal;line-height: 2em;"> <span style="font-family: 宋体;font-size: 16px;"><br></span> </section> <section style="margin-top: 0pt;margin-bottom: 0pt;text-align: justify;margin-left: 0pt;text-indent: 0pt;font-size: 10.5pt;font-family: Calibri;font-weight: normal;line-height: 2em;"> <span style

SpringBoot 统一功能处理

作者:微信小助手

<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;margin-bottom: 0px;" data-mpa-powered-by="yiban.io"> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;color: rgb(0, 150, 136);padding-left: 10px;margin: 1em auto;border-left: 3px solid rgb(0, 150, 136);"><span style="display: none;"></span>前言</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">接下来是 Spring Boot 统⼀功能处理模块了,也是 AOP 的实战环节,要实现的课程⽬标有以下 3 个:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 统⼀⽤户登录权限验证 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 统⼀数据格式返回 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 统⼀异常处理 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">接下我们⼀个⼀个来看。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;color: rgb(0, 150, 136);padding-left: 10px;margin: 1em auto;border-left: 3px solid rgb(0, 150, 136);"><span style="display: none;"></span>一、用户登录权限效验</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">⽤户登录权限的发展从之前每个⽅法中⾃⼰验证⽤户登录权限,到现在统⼀的⽤户登录验证处理,它是⼀个逐渐完善和逐渐优化的过程。</p> <h3 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 20px;margin: 0.6em auto;padding-left: 10px;border-left: 2px solid rgb(0, 150, 136);"><span style="display: none;"></span>1.1 最初的用户登录验证<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">我们先来回顾⼀下最初⽤户登录验证的实现⽅法:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/wJibWkqN1bUMGV1UTPd8Fdn9R4ia0B4ykz1XUqDUtZjYLwjOq87ibe3GjIIjicYMWbqibODqF7aiaGPiasNJgdTrUBDibcghxpzWDLL9/640?wx_fmt=svg&amp;amp;random=0.6475989819200294&amp;random=0.4093034983326109&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #61aeee;line-height: 26px;">@RestController</span><br><span style="color: #61aeee;line-height: 26px;">@RequestMapping</span>(<span style="color: #98c379;line-height: 26px;">"/user"</span>)<br><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span>&nbsp;<span style="color: #e6c07b;line-height: 26px;">UserController</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">/**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;某⽅法&nbsp;1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@RequestMapping</span>(<span style="color: #98c379;line-height: 26px;">"/m1"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;Object&nbsp;<span style="color: #61aeee;line-height: 26px;">method</span><span style="line-height: 26px;">(HttpServletRequest&nbsp;request)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;有&nbsp;session&nbsp;就获取,没有不会创建</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpSession&nbsp;session&nbsp;=&nbsp;request.getSession(<span style="color: #c678dd;line-height: 26px;">false</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">if</span>&nbsp;(session&nbsp;!=&nbsp;<span style="color: #c678dd;line-height: 26px;">null</span>&nbsp;&amp;&amp;&nbsp;session.getAttribute(<span style="color: #98c379;line-height: 26px;">"userinfo"</span>)&nbsp;!=&nbsp;<span style="color: #c678dd;line-height: 26px;">null</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;说明已经登录,业务处理</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">true</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #c678dd;line-height: 26px;">else</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;未登录</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">false</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">/**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;某⽅法&nbsp;2<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@RequestMapping</span>(<span style="color: #98c379;line-height: 26px;">"/m2"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;Object&nbsp;<span style="color: #61aeee;line-height: 26px;">method2</span><span style="line-height: 26px;">(HttpServletRequest&nbsp;request)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;有&nbsp;session&nbsp;就获取,没有不会创建</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpSession&nbsp;session&nbsp;=&nbsp;request.getSession(<span style="color: #c678dd;line-height: 26px;">false</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">if</span>&nbsp;(session&nbsp;!=&nbsp;<span style="color: #c678dd;line-height: 26px;">null</span>&nbsp;&amp;&amp;&nbsp;session.getAttribute(<span style="color: #98c379;line-height: 26px;">"userinfo"</span>)&nbsp;!=&nbsp;<span style="color: #c678dd;line-height: 26px;">null</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;说明已经登录,业务处理</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">true</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #c678dd;line-height: 26px;">else</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;未登录</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">false</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;其他⽅法...</span><br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">从上述代码可以看出,每个⽅法中都有相同的⽤户登录验证权限,它的缺点是:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 每个⽅法中都要单独写⽤户登录验证的⽅法,即使封装成公共⽅法,也⼀样要传参调⽤和在⽅法中进⾏判断。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 添加控制器越多,调⽤⽤户登录验证的⽅法也越多,这样就增加了后期的修改成本和维护成本。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 这些⽤户登录验证的⽅法和接下来要实现的业务⼏何没有任何关联,但每个⽅法中都要写⼀遍。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">所以提供⼀个公共的 AOP ⽅法来进⾏统⼀的⽤户登录权限验证迫在眉睫。</p> <h3 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 20px;margin: 0.6em auto;padding-left: 10px;border-left: 2px solid rgb(0, 150, 136);"><span style="display: none;"></span>1.2 Spring AOP 用户统一登录验证的问题<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">说到统⼀的⽤户登录验证,我们想到的第⼀个实现⽅案是 Spring AOP 前置通知或环绕通知来实现,具体实现代码如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/wJibWkqN1bUMGV1UTPd8Fdn9R4ia0B4ykz1XUqDUtZjYLwjOq87ibe3GjIIjicYMWbqibODqF7aiaGPiasNJgdTrUBDibcghxpzWDLL9/640?wx_fmt=svg&amp;amp;random=0.7380037754955342&amp;random=0.13713972055731016&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;org.aspectj.lang.ProceedingJoinPoint;<br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;org.aspectj.lang.annotation.*;<br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;org.springframework.stereotype.Component;<br><span style="color: #61aeee;line-height: 26px;">@Aspect</span><br><span style="color: #61aeee;line-height: 26px;">@Component</span><br><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span>&nbsp;<span style="color: #e6c07b;line-height: 26px;">UserAspect</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;定义切点⽅法&nbsp;controller&nbsp;包下、⼦孙包下所有类的所有⽅法</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Pointcut</span>(<span style="color: #98c379;line-height: 26px;">"execution(*&nbsp;com.example.demo.controller..*.*(..))"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">void</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">pointcut</span><span style="line-height: 26px;">()</span></span>{&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;前置⽅法</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Before</span>(<span style="color: #98c379;line-height: 26px;">"pointcut()"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">void</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">doBefore</span><span style="line-height: 26px;">()</span></span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;环绕⽅法</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Around</span>(<span style="color: #98c379;line-height: 26px;">"pointcut()"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;Object&nbsp;<span style="color: #61aeee;line-height: 26px;">doAround</span><span style="line-height: 26px;">(ProceedingJoinPoint&nbsp;joinPoint)</span></span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;obj&nbsp;=&nbsp;<span style="color: #c678dd;line-height: 26px;">null</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span style="color: #98c379;line-height: 26px;">"Around&nbsp;⽅法开始执⾏"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;执⾏拦截⽅法</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj&nbsp;=&nbsp;joinPoint.proceed();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #c678dd;line-height: 26px;">catch</span>&nbsp;(Throwable&nbsp;throwable)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throwable.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span style="color: #98c379;line-height: 26px;">"Around&nbsp;⽅法结束执⾏"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;obj;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">如果要在以上 Spring AOP 的切⾯中实现⽤户登录权限效验的功能,有以下两个问题:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 没办法获取到 HttpSession 对象。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 我们要对⼀部分⽅法进⾏拦截,⽽另⼀部分⽅法不拦截,如注册⽅法和登录⽅法是不拦截的,这样的话排除⽅法的规则很难定义,甚⾄没办法定义。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">那这样如何解决呢?</p> <h3 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 20px;margin: 0.6em auto;padding-left: 10px;border-left: 2px solid rgb(0, 150, 136);"><span style="display: none;"></span>1.3 Spring 拦截器<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">对于以上问题 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(0, 150, 136);">HandlerInterceptor</code>,拦截器的实现分为以下两个步骤:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 创建⾃定义拦截器,实现 <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(0, 150, 136);">HandlerInterceptor</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(0, 150, 136);">preHandle</code>(执⾏具体⽅法之前的预处理)⽅法。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 将⾃定义拦截器加⼊ <code style="font-size: 14px;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(0, 150, 136);">WebMvcConfigurer</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(0, 150, 136);">addInterceptors</code> ⽅法中。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">具体实现如下。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">补充 过滤器:</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;font-size: 0.9em;overflow: auto;background: rgba(0, 0, 0, 0.05);padding: 10px 10px 10px 1em;margin-bottom: 20px;margin-top: 20px;border-left-width: 2px;border-left-color: rgb(136, 136, 136);border-right: 2px solid rgb(136, 136, 136);color: rgb(119, 119, 119);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;text-align: justify;color: black;line-height: 26px;">过滤器是Web容器提供的。触发的时机比拦截器更靠前,Spring 初始化前就执行了,所以并不能处理用户登录权限效验等问题。</p> </blockquote> <h5 data-tool="mdnice编辑器" style="font-weight: bold;margin: 0.6em auto;font-size: 1.1em;padding-left: 10px;border-left: 1px dashed rgb(0, 150, 136);"><span style="display: none;"></span>1.3.1 准备工作<span style="display: none;"></span></h5> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/wJibWkqN1bUMGV1UTPd8Fdn9R4ia0B4ykz1XUqDUtZjYLwjOq87ibe3GjIIjicYMWbqibODqF7aiaGPiasNJgdTrUBDibcghxpzWDLL9/640?wx_fmt=svg&amp;amp;random=0.8151015981833318&amp;random=0.35908997815383437&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #c678dd;line-height: 26px;">package</span>&nbsp;com.example.demo.controller;<br><br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;lombok.extern.slf4j.Slf4j;<br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;org.springframework.util.StringUtils;<br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;org.springframework.web.bind.annotation.RequestMapping;<br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;org.springframework.web.bind.annotation.RestController;<br><br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;javax.servlet.http.HttpServletRequest;<br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;javax.servlet.http.HttpSession;<br><br><span style="color: #61aeee;line-height: 26px;">@RestController</span><br><span style="color: #61aeee;line-height: 26px;">@RequestMapping</span>(<span style="color: #98c379;line-height: 26px;">"/user"</span>)<br><span style="color: #61aeee;line-height: 26px;">@Slf</span>4j<br><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span>&nbsp;<span style="color: #e6c07b;line-height: 26px;">UserController</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@RequestMapping</span>(<span style="color: #98c379;line-height: 26px;">"/login"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">boolean</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">login</span><span style="line-height: 26px;">(HttpServletRequest&nbsp;request,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;username,&nbsp;String&nbsp;password)</span>&nbsp;</span>{<br><span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;1.非空判断</span><br><span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(username&nbsp;!=&nbsp;null&nbsp;&amp;&amp;&nbsp;username&nbsp;!=&nbsp;""&nbsp;&amp;&amp;</span><br><span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;password&nbsp;!=&nbsp;null&nbsp;&amp;&amp;&nbsp;username&nbsp;!=&nbsp;"")&nbsp;{</span><br><span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;2.验证用户名和密码是否正确</span><br><span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;1.非空判断</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">if</span>&nbsp;(StringUtils.hasLength(username)&nbsp;&amp;&amp;&nbsp;StringUtils.hasLength(password))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;2.验证用户名和密码是否正确</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">if</span>&nbsp;(<span style="color: #98c379;line-height: 26px;">"admin"</span>.equals(username)&nbsp;&amp;&amp;&nbsp;<span style="color: #98c379;line-height: 26px;">"admin"</span>.equals(password))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;登录成功</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpSession&nbsp;session&nbsp;=&nbsp;request.getSession();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;session.setAttribute(<span style="color: #98c379;line-height: 26px;">"userinfo"</span>,&nbsp;<span style="color: #98c379;line-height: 26px;">"admin"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">true</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #c678dd;line-height: 26px;">else</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;用户名或密码输入错误</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">false</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">false</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@RequestMapping</span>(<span style="color: #98c379;line-height: 26px;">"/getinfo"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;String&nbsp;<span style="color: #61aeee;line-height: 26px;">getInfo</span><span style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.debug(<span style="color: #98c379;line-height: 26px;">"执行了&nbsp;getinfo&nbsp;方法"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;<span style="color: #98c379;line-height: 26px;">"执行了&nbsp;getinfo&nbsp;方法"</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@RequestMapping</span>(<span style="color: #98c379;line-height: 26px;">"/reg"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;String&nbsp;<span style="color: #61aeee;line-height: 26px;">reg</span><span style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.debug(<span style="color: #98c379;line-height: 26px;">"执行了&nbsp;reg&nbsp;方法"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;<span style="color: #98c379;line-height: 26px;">"执行了&nbsp;reg&nbsp;方法"</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></code></pre> <h5 data-tool="mdnice编辑器" style="font-weight: bold;margin: 0.6em auto;font-size: 1.1em;padding-left: 10px;border-left: 1px dashed rgb(0, 150, 136);"><span style="display: none;"></span>1.3.2 自定义拦截器<span style="display: none;"></span></h5> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">接下来使⽤代码来实现⼀个⽤户登录的权限效验,⾃定义拦截器是⼀个普通类,具体实现代码如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/wJibWkqN1bUMGV1UTPd8Fdn9R4ia0B4ykz1XUqDUtZjYLwjOq87ibe3GjIIjicYMWbqibODqF7aiaGPiasNJgdTrUBDibcghxpzWDLL9/640?wx_fmt=svg&amp;amp;random=0.19150344899713878&amp;random=0.8272254072143401&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #c678dd;line-height: 26px;">package</span>&nbsp;com.example.demo.config;<br><br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;lombok.extern.slf4j.Slf4j;<br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;org.springframework.stereotype.Component;<br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;org.springframework.web.servlet.HandlerInterceptor;<br><br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;javax.servlet.http.HttpServletRequest;<br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;javax.servlet.http.HttpServletResponse;<br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;javax.servlet.http.HttpSession;<br><br><span style="color: #5c6370;font-style: italic;line-height: 26px;">/**<br>&nbsp;*&nbsp;登录拦截器<br>&nbsp;*/</span><br><span style="color: #61aeee;line-height: 26px;">@Component</span><br><span style="color: #61aeee;line-height: 26px;">@Slf</span>4j<br><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span>&nbsp;<span style="color: #e6c07b;line-height: 26px;">LoginInterceptor</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">implements</span>&nbsp;<span style="color: #e6c07b;line-height: 26px;">HandlerInterceptor</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">boolean</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">preHandle</span><span style="line-height: 26px;">(HttpServletRequest&nbsp;request,&nbsp;HttpServletResponse&nbsp;response,&nbsp;Object&nbsp;handler)</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">throws</span>&nbsp;Exception&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;登录判断业务</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpSession&nbsp;session&nbsp;=&nbsp;request.getSession(<span style="color: #c678dd;line-height: 26px;">false</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">if</span>&nbsp;(session&nbsp;!=&nbsp;<span style="color: #c678dd;line-height: 26px;">null</span>&nbsp;&amp;&amp;&nbsp;session.getAttribute(<span style="color: #98c379;line-height: 26px;">"userinfo"</span>)&nbsp;!=&nbsp;<span style="color: #c678dd;line-height: 26px;">null</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">true</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.error(<span style="color: #98c379;line-height: 26px;">"当前用户没有访问权限"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.setStatus(<span style="color: #d19a66;line-height: 26px;">401</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">false</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">返回 boolean 类型。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">相当于一层安保:</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;font-size: 0.9em;overflow: auto;background: rgba(0, 0, 0, 0.05);padding: 10px 10px 10px 1em;margin-bottom: 20px;margin-top: 20px;border-left-width: 2px;border-left-color: rgb(136, 136, 136);border-right: 2px solid rgb(136, 136, 136);color: rgb(119, 119, 119);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;text-align: justify;color: black;line-height: 26px;">为 false 则不能继续往下执行;为 true 则可以。</p> </blockquote> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.3425925925925926" src="/upload/ca60ebc1b3d9dd2de5b460290a9dab07.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;"> </figure> <h5 data-tool="mdnice编辑器" style="font-weight: bold;margin: 0.6em auto;font-size: 1.1em;padding-left: 10px;border-left: 1px dashed rgb(0, 150, 136);"><span style="display: none;"></span>1.3.3 将自定义拦截器加入到系统配置<span style="display: none;"></span></h5> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">将上⼀步中的⾃定义拦截器加⼊到系统配置信息中,具体实现代码如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/wJibWkqN1bUMGV1UTPd8Fdn9R4ia0B4ykz1XUqDUtZjYLwjOq87ibe3GjIIjicYMWbqibODqF7aiaGPiasNJgdTrUBDibcghxpzWDLL9/640?wx_fmt=svg&amp;amp;random=0.20421081801830554&amp;random=0.1342407836914481&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #c678dd;line-height: 26px;">package</span>&nbsp;com.example.demo.config;<br><br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;org.springframework.beans.factory.annotation.Autowired;<br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;org.springframework.context.annotation.Configuration;<br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;org.springframework.web.servlet.config.annotation.InterceptorRegistry;<br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;<br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;org.springframework.web.servlet.config.annotation.WebMvcConfigurer;<br><br><span style="color: #61aeee;line-height: 26px;">@Configuration</span>&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;一定不要忘记</span><br><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span>&nbsp;<span style="color: #e6c07b;line-height: 26px;">MyConfig</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">implements</span>&nbsp;<span style="color: #e6c07b;line-height: 26px;">WebMvcConfigurer</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Autowired</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">private</span>&nbsp;LoginInterceptor&nbsp;loginInterceptor;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">void</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">addInterceptors</span><span style="line-height: 26px;">(InterceptorRegistry&nbsp;registry)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;registry.addInterceptor(loginInterceptor)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.addPathPatterns(<span style="color: #98c379;line-height: 26px;">"/**"</span>)&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;拦截所有请求</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.excludePathPatterns(<span style="color: #98c379;line-height: 26px;">"/user/login"</span>)&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;排除不拦截的&nbsp;url</span><br><span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.excludePathPatterns("/**/*.html")</span><br><span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.excludePathPatterns("/**/*.js")</span><br><span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.excludePathPatterns("/**/*.css")</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.excludePathPatterns(<span style="color: #98c379;line-height: 26px;">"/user/reg"</span>);&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;排除不拦截的&nbsp;url</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">或者:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/wJibWkqN1bUMGV1UTPd8Fdn9R4ia0B4ykz1XUqDUtZjYLwjOq87ibe3GjIIjicYMWbqibODqF7aiaGPiasNJgdTrUBDibcghxpzWDLL9/640?wx_fmt=svg&amp;amp;random=0.36616860925828565&amp;random=0.6703342457789503&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #c678dd;line-height: 26px;">package</span>&nbsp;com.example.demo.common;<br><br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;org.springframework.beans.factory.annotation.Autowired;<br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;org.springframework.context.annotation.Configuration;<br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;org.springframework.web.servlet.config.annotation.InterceptorRegistration;<br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;org.springframework.web.servlet.config.annotation.InterceptorRegistry;<br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;org.springframework.web.servlet.config.annotation.WebMvcConfigurer;<br><br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;java.util.ArrayList;<br><span style="color: #c678dd;line-height: 26px;">import</span>&nbsp;java.util.List;<br><br><span style="color: #61aeee;line-height: 26px;">@Configuration</span><br><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span>&nbsp;<span style="color: #e6c07b;line-height: 26px;">AppConfig</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">implements</span>&nbsp;<span style="color: #e6c07b;line-height: 26px;">WebMvcConfigurer</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;不拦截的&nbsp;url&nbsp;集合</span><br>&nbsp;&nbsp;&nbsp;&nbsp;List&lt;String&gt;&nbsp;excludes&nbsp;=&nbsp;<span style="color: #c678dd;line-height: 26px;">new</span>&nbsp;ArrayList&lt;String&gt;()&nbsp;{{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add("/**/*.html");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add("/js/**");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add("/editor.md/**");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add("/css/**");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add("/img/**");&nbsp;//&nbsp;放行&nbsp;static/img&nbsp;下的所有文件<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add("/user/login");&nbsp;//&nbsp;放行登录接口<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add("/user/reg");&nbsp;//&nbsp;放行注册接口<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add("/art/detail");&nbsp;//&nbsp;放行文章详情接口<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add("/art/list");&nbsp;//&nbsp;放行文章分页列表接口<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add("/art/totalpage");&nbsp;//&nbsp;放行文章分页总页数接口<br>&nbsp;&nbsp;&nbsp;&nbsp;}};<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Autowired</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">private</span>&nbsp;LoginInterceptor&nbsp;loginInterceptor;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">void</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">addInterceptors</span><span style="line-height: 26px;">(InterceptorRegistry&nbsp;registry)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;配置拦截器</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;InterceptorRegistration&nbsp;registration&nbsp;=<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;registry.addInterceptor(loginInterceptor);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;registration.addPathPatterns(<span style="color: #98c379;line-height: 26px;">"/**"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;registration.excludePathPatterns(excludes);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">如果不注入对象的话,<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(0, 150, 136);">addInterceptor()</code> 的参数也可以直接 new 一个对象:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/wJibWkqN1bUMGV1UTPd8Fdn9R4ia0B4ykz1XUqDUtZjYLwjOq87ibe3GjIIjicYMWbqibODqF7aiaGPiasNJgdTrUBDibcghxpzWDLL9/640?wx_fmt=svg&amp;amp;random=0.48212302596640244&amp;random=0.23585149163792574&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #61aeee;line-height: 26px;">@Configuration</span>&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;一定不要忘记</span><br><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span>&nbsp;<span style="color: #e6c07b;line-height: 26px;">MyConfig</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">implements</span>&nbsp;<span style="color: #e6c07b;line-height: 26px;">WebMvcConfigurer</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">void</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">addInterceptors</span><span style="line-height: 26px;">(InterceptorRegistry&nbsp;registry)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;registry.addInterceptor(<span style="color: #c678dd;line-height: 26px;">new</span>&nbsp;LoginInterceptor())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.addPathPatterns(<span style="color: #98c379;line-height: 26px;">"/**"</span>)&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;拦截所有请求</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.excludePathPatterns(<span style="color: #98c379;line-height: 26px;">"/user/login"</span>)&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;排除不拦截的&nbsp;url</span><br><span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.excludePathPatterns("/**/*.html")</span><br><span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.excludePathPatterns("/**/*.js")</span><br><span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.excludePathPatterns("/**/*.css")</span><br><span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.excludePathPatterns("/**/*.jpg")</span><br><span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.excludePathPatterns("/**/login")</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.excludePathPatterns(<span style="color: #98c379;line-height: 26px;">"/user/reg"</span>);&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;排除不拦截的&nbsp;url</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">其中:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <strong style="color: black;">addPathPatterns</strong>:表示需要拦截的 URL,“ <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(0, 150, 136);">**</code>”表示拦截任意⽅法(也就是所有⽅法)。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <strong style="color: black;">excludePathPatterns</strong>:表示需要排除的 URL。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">说明:以上拦截规则可以拦截此项⽬中的使⽤ URL,包括静态⽂件 (图⽚⽂件、JS 和 CSS 等⽂件)。</p> <h3 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 20px;margin: 0.6em auto;padding-left: 10px;border-left: 2px solid rgb(0, 150, 136);"><span style="display: none;"></span>1.4 拦截器实现原理<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">正常情况下的调⽤顺序:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.6248600223964166" src="/upload/6679a29a6d0f67deb000bcc229df9328.png" data-type="png" data-w="893" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">然⽽有了拦截器之后,会在调⽤ Controller 之前进⾏相应的业务处理,执⾏的流程如下图所示:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.6666666666666666" src="/upload/efa3cd60fdba12bdf95f8e5e96ca4e32.png" data-type="png" data-w="882" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;"> </figure> <h5 data-tool="mdnice编辑器" style="font-weight: bold;margin: 0.6em auto;font-size: 1.1em;padding-left: 10px;border-left: 1px dashed rgb(0, 150, 136);"><span style="display: none;"></span>1.4.1 实现原理源码分析<span style="display: none;"></span></h5> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">所有的 Controller 执⾏都会通过⼀个调度器 <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(0, 150, 136);">DispatcherServlet</code> 来实现,这⼀点可以从 Spring Boot 控制台的打印信息看出,如下图所示:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5212962962962963" src="/upload/d5a799bd6a841e84a618c915465b7e2d.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">⽽所有⽅法都会执⾏ <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(0, 150, 136);">DispatcherServlet</code> 中的 doDispatch 调度⽅法,doDispatch 源码如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/wJibWkqN1bUMGV1UTPd8Fdn9R4ia0B4ykz1XUqDUtZjYLwjOq87ibe3GjIIjicYMWbqibODqF7aiaGPiasNJgdTrUBDibcghxpzWDLL9/640?wx_fmt=svg&amp;amp;random=0.9394325552664382&amp;random=0.4959458197451809&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">protected</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">void</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">doDispatch</span><span style="line-height: 26px;">(HttpServletRequest&nbsp;request,&nbsp;HttpServletResponse<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response)</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">throws</span>&nbsp;Exception&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;HttpServletRequest&nbsp;processedRequest&nbsp;=&nbsp;request;<br>&nbsp;&nbsp;&nbsp;&nbsp;HandlerExecutionChain&nbsp;mappedHandler&nbsp;=&nbsp;<span style="color: #c678dd;line-height: 26px;">null</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">boolean</span>&nbsp;multipartRequestParsed&nbsp;=&nbsp;<span style="color: #c678dd;line-height: 26px;">false</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;WebAsyncManager&nbsp;asyncManager&nbsp;=&nbsp;WebAsyncUtils.getAsyncManager(request);<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ModelAndView&nbsp;mv&nbsp;=&nbsp;<span style="color: #c678dd;line-height: 26px;">null</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;dispatchException&nbsp;=&nbsp;<span style="color: #c678dd;line-height: 26px;">null</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;processedRequest&nbsp;=&nbsp;<span style="color: #c678dd;line-height: 26px;">this</span>.checkMultipart(request);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;multipartRequestParsed&nbsp;=&nbsp;processedRequest&nbsp;!=&nbsp;request;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mappedHandler&nbsp;=&nbsp;<span style="color: #c678dd;line-height: 26px;">this</span>.getHandler(processedRequest);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">if</span>&nbsp;(mappedHandler&nbsp;==&nbsp;<span style="color: #c678dd;line-height: 26px;">null</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">this</span>.noHandlerFound(processedRequest,&nbsp;response);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HandlerAdapter&nbsp;ha&nbsp;=&nbsp;<span style="color: #c678dd;line-height: 26px;">this</span>.getHandlerAdapter(mappedHandler.<span style="line-height: 26px;">g<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #61aeee;line-height: 26px;">etHandler</span><span style="line-height: 26px;">()</span>)</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;method&nbsp;=&nbsp;request.getMethod();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">boolean</span>&nbsp;isGet&nbsp;=&nbsp;HttpMethod.GET.matches(method);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">if</span>&nbsp;(isGet&nbsp;||&nbsp;HttpMethod.HEAD.matches(method))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">long</span>&nbsp;lastModified&nbsp;=&nbsp;ha.getLastModified(request,&nbsp;mapped<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Handler.getHandler());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">if</span>&nbsp;((<span style="color: #c678dd;line-height: 26px;">new</span>&nbsp;ServletWebRequest(request,&nbsp;response)).<span style="line-height: 26px;">checkNo<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

九种分布式ID解决方案,总有一款适合你!

作者:微信小助手

<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, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&amp;scene=21&amp;token=899450012&amp;lang=zh_CN#wechat_redirect" style="font-weight: bold;color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);font-family: STHeitiSC-Light;" data-linktype="2">背景</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&amp;scene=21&amp;token=899450012&amp;lang=zh_CN#wechat_redirect" style="font-weight: bold;color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);font-family: STHeitiSC-Light;" data-linktype="2">1、UUID</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&amp;scene=21&amp;token=899450012&amp;lang=zh_CN#wechat_redirect" style="font-weight: bold;color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);font-family: STHeitiSC-Light;" data-linktype="2">2、数据库自增ID</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&amp;scene=21&amp;token=899450012&amp;lang=zh_CN#wechat_redirect" style="font-weight: bold;color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);font-family: STHeitiSC-Light;" data-linktype="2">3、号段模式</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&amp;scene=21&amp;token=899450012&amp;lang=zh_CN#wechat_redirect" style="font-weight: bold;color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);font-family: STHeitiSC-Light;" data-linktype="2">4、Redis INCR</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&amp;scene=21&amp;token=899450012&amp;lang=zh_CN#wechat_redirect" style="font-weight: bold;color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);font-family: STHeitiSC-Light;" data-linktype="2">5、雪花算法</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&amp;scene=21&amp;token=899450012&amp;lang=zh_CN#wechat_redirect" style="font-weight: bold;color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);font-family: STHeitiSC-Light;" data-linktype="2">6、美团(Leaf)</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&amp;scene=21&amp;token=899450012&amp;lang=zh_CN#wechat_redirect" style="font-weight: bold;color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);font-family: STHeitiSC-Light;" data-linktype="2">7、百度(Uidgenerator)</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&amp;scene=21&amp;token=899450012&amp;lang=zh_CN#wechat_redirect" style="font-weight: bold;color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);font-family: STHeitiSC-Light;" data-linktype="2">8、滴滴(TinyID)</a> </section></li> </ul> <hr data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;height: 1px;border-width: initial;border-style: none;border-color: initial;text-align: center;background-image: linear-gradient(to right, rgba(248, 57, 41, 0), rgb(14, 136, 235), rgba(248, 57, 41, 0));"> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-family: STHeitiSC-Light;color: rgb(14, 136, 235);font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(14, 136, 235);"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);" data-linktype="2">背景</a></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">在复杂的分布式系统中,往往需要对大量的数据进行唯一标识,比如在对一个订单表进行了分库分表操作,这时候数据库的自增ID显然不能作为某个订单的唯一标识。除此之外还有其他分布式场景对分布式ID的一些要求:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <strong style="font-weight: border;color: #0e88eb;">趋势递增:</strong> 由于多数RDBMS使用B-tree的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <strong style="font-weight: border;color: #0e88eb;">单调递增:</strong> 保证下一个ID一定大于上一个ID,例如排序需求。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <strong style="font-weight: border;color: #0e88eb;">信息安全:</strong> 如果ID是连续的,恶意用户的扒取工作就非常容易做了;如果是订单号就更危险了,可以直接知道我们的单量。所以在一些应用场景下,会需要ID无规则、不规则。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">就不同的场景及要求,市面诞生了很多分布式ID解决方案。本文针对多个分布式ID解决方案进行介绍,包括其优缺点、使用场景及代码示例。</p> <blockquote data-tool="mdnice编辑器" style="border-width: initial;border-style: none;border-color: initial;font-size: 0.9em;overflow: auto;margin-bottom: 20px;margin-top: 20px;padding-top: 10px;padding-right: 10px;padding-bottom: 10px;line-height: 1.8;border-radius: 0px 0px 10px 10px;color: rgb(14, 136, 235);background: rgb(255, 255, 255);box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> <p style="padding-top: 8px;padding-bottom: 8px;letter-spacing: 0.2em;word-spacing: 0.1em;line-height: 26px;font-size: 15px;">基于 Spring Boot + MyBatis Plus + Vue &amp; Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能</p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 项目地址:https://github.com/YunaiV/ruoyi-vue-pro </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 视频教程:https://doc.iocoder.cn/video/ </section></li> </ul> </blockquote> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-family: STHeitiSC-Light;color: rgb(14, 136, 235);font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(14, 136, 235);"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);" data-linktype="2">1、UUID</a></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">UUID(<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">Universally Unique Identifier</code>)是基于当前时间、计数器(counter)和硬件标识(通常为无线网卡的MAC地址)等数据计算生成的。包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的36个字符,可以生成全球唯一的编码并且性能高效。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">JDK提供了UUID生成工具,代码如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG1xSmLWhia6IibAichUHCopIRW83hibzLweOcDW1u1icWibrGYI1rprwGiaXWicibGzZfzxH1R44s3vwQNlDCzrPkic6G6qJ1/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;"><span style="color: #569CD6;line-height: 26px;">import</span>&nbsp;java.util.UUID;<br><br><span style="color: #569CD6;line-height: 26px;">public</span>&nbsp;<span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span>&nbsp;<span style="color: #DCDCDC;line-height: 26px;">Test</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span>&nbsp;<span style="color: #569CD6;line-height: 26px;">static</span>&nbsp;<span style="color: #569CD6;line-height: 26px;">void</span>&nbsp;<span style="line-height: 26px;">main</span><span style="line-height: 26px;">(String[]&nbsp;args)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(UUID.randomUUID());<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">输出如下</p> <blockquote data-tool="mdnice编辑器" style="border-width: initial;border-style: none;border-color: initial;font-size: 0.9em;overflow: auto;margin-bottom: 20px;margin-top: 20px;padding-top: 10px;padding-right: 10px;padding-bottom: 10px;line-height: 1.8;border-radius: 0px 0px 10px 10px;color: rgb(14, 136, 235);background: rgb(255, 255, 255);box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> <p style="padding-top: 8px;padding-bottom: 8px;letter-spacing: 0.2em;word-spacing: 0.1em;line-height: 26px;font-size: 15px;">b0378f6a-eeb7-4779-bffe-2a9f3bc76380</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">UUID完全可以满足分布式唯一标识,但是在实际应用过程中一般不采用,有如下几个原因:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <strong style="font-weight: border;color: #0e88eb;">存储成本高:</strong> UUID太长,16字节128位,通常以36长度的字符串表示,很多场景不适用。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <strong style="font-weight: border;color: #0e88eb;">信息不安全:</strong> 基于MAC地址生成的UUID算法会暴露MAC地址,曾经梅丽莎病毒的制造者就是根据UUID寻找的。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <strong style="font-weight: border;color: #0e88eb;">不符合MySQL主键要求:</strong> MySQL官方有明确的建议主键要尽量越短越好,因为太长对MySQL索引不利:如果作为数据库主键,在InnoDB引擎下,UUID的无序性可能会引起数据位置频繁变动,严重影响性能。 </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: 10px;padding-right: 10px;padding-bottom: 10px;line-height: 1.8;border-radius: 0px 0px 10px 10px;color: rgb(14, 136, 235);background: rgb(255, 255, 255);box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> <p style="padding-top: 8px;padding-bottom: 8px;letter-spacing: 0.2em;word-spacing: 0.1em;line-height: 26px;font-size: 15px;">基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue &amp; Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能</p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 项目地址:https://github.com/YunaiV/yudao-cloud </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 视频教程:https://doc.iocoder.cn/video/ </section></li> </ul> </blockquote> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-family: STHeitiSC-Light;color: rgb(14, 136, 235);font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(14, 136, 235);"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);" data-linktype="2">2、数据库自增ID</a></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">利用Mysql的特性ID自增,可以达到数据唯一标识,但是分库分表后只能保证一个表中的ID的唯一,而不能保证整体的ID唯一。为了避免这种情况,我们有以下两种方式解决该问题。</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);font-family: STHeitiSC-Light;" data-linktype="2">2.1、主键表</a><span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">通过单独创建主键表维护唯一标识,作为ID的输出源可以保证整体ID的唯一。举个例子:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">创建一个主键表</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG1xSmLWhia6IibAichUHCopIRW83hibzLweOcDW1u1icWibrGYI1rprwGiaXWicibGzZfzxH1R44s3vwQNlDCzrPkic6G6qJ1/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;">CREATE&nbsp;TABLE&nbsp;`unique_id`&nbsp;&nbsp;(<br>&nbsp;&nbsp;`id`&nbsp;bigint&nbsp;NOT&nbsp;NULL&nbsp;AUTO_INCREMENT,<br>&nbsp;&nbsp;`biz`&nbsp;<span style="color: #569CD6;line-height: 26px;">char</span>(<span style="color: #B8D7A3;line-height: 26px;">1</span>)&nbsp;NOT&nbsp;NULL,<br>&nbsp;&nbsp;<span style="line-height: 26px;">PRIMARY&nbsp;<span style="line-height: 26px;">KEY</span>&nbsp;<span style="line-height: 26px;">(`id`)</span>,<br>&nbsp;UNIQUE&nbsp;KEY&nbsp;`biz`&nbsp;<span style="line-height: 26px;">(`biz`)</span><br>)&nbsp;ENGINE&nbsp;</span>=&nbsp;InnoDB&nbsp;AUTO_INCREMENT=<span style="color: #B8D7A3;line-height: 26px;">1</span>&nbsp;DEFAULT&nbsp;CHARSET&nbsp;=utf8;<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">业务通过更新操作来获取ID信息,然后添加到某个分表中。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG1xSmLWhia6IibAichUHCopIRW83hibzLweOcDW1u1icWibrGYI1rprwGiaXWicibGzZfzxH1R44s3vwQNlDCzrPkic6G6qJ1/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;">BEGIN;<br><br><span style="line-height: 26px;">REPLACE&nbsp;INTO&nbsp;<span style="line-height: 26px;">unique_id</span>&nbsp;<span style="line-height: 26px;">(biz)</span>&nbsp;<span style="line-height: 26px;">values</span>&nbsp;<span style="line-height: 26px;">(<span style="color: #D69D85;line-height: 26px;">'o'</span>)</span>&nbsp;</span>;<br><span style="line-height: 26px;">SELECT&nbsp;<span style="line-height: 26px;">LAST_INSERT_ID</span><span style="line-height: 26px;">()</span></span>;<br><br>COMMIT;<br></code></pre> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.44074074074074077" src="/upload/c8e1dcdce716bdb96f620cd040a8bf64.png" data-type="png" data-w="1080" style="border-radius: 0px 0px 5px 5px;display: block;margin: 20px auto;width: 85%;height: 100%;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);font-family: STHeitiSC-Light;" data-linktype="2">2.2、ID自增步长设置</a><span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">我们可以设置Mysql主键自增步长,让分布在不同实例的表数据ID做到不重复,保证整体的唯一。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">如下,可以设置Mysql实例1步长为1,实例1步长为2。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.47572815533980584" src="/upload/fd7ed2ac4c53af59789e16e3500c8119.png" data-type="png" data-w="412" style="border-radius: 0px 0px 5px 5px;display: block;margin: 20px auto;width: 85%;height: 100%;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">查看主键自增的属性</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG1xSmLWhia6IibAichUHCopIRW83hibzLweOcDW1u1icWibrGYI1rprwGiaXWicibGzZfzxH1R44s3vwQNlDCzrPkic6G6qJ1/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;">show&nbsp;variables&nbsp;like&nbsp;<span style="color: #D69D85;line-height: 26px;">'%increment%'</span><br></code></pre> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5275" src="/upload/605d013f1371b96e8bf93d4ee1cde8a0.png" data-type="png" data-w="400" style="border-radius: 0px 0px 5px 5px;display: block;margin: 20px auto;width: 85%;height: 100%;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">显然,这种方式在并发量比较高的情况下,如何保证扩展性其实会是一个问题。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-family: STHeitiSC-Light;color: rgb(14, 136, 235);font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(14, 136, 235);"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);" data-linktype="2">3、号段模式</a></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">号段模式是当下分布式ID生成器的主流实现方式之一。其原理如下:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 号段模式每次从数据库取出一个号段范围,加载到服务内存中。业务获取时ID直接在这个范围递增取值即可。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 等这批号段ID用完,再次向数据库申请新号段,对max_id字段做一次update操作,新的号段范围是( <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">max_id</code> , <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">max_id +step</code>]。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 由于多业务端可能同时操作,所以采用版本号version乐观锁方式更新。 </section></li> </ul> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5407407407407407" src="/upload/2b1e58d2054576e99a64b0495fc52897.png" d

别再分库分表了,试试TiDB!

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.6;word-break: break-word;text-align: left;padding: 5px;font-size: 16px;color: rgb(53, 53, 53);word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">TiDB 是一个分布式 NewSQL 数据库。它支持水平弹性扩展、ACID 事务、标准 SQL、MySQL 语法和 MySQL 协议,具有数据强一致的高可用特性,是一个不仅适合 OLTP 场景还适合 OLAP 场景的混合数据库。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">TiDB是 PingCAP公司自主设计、研发的开源分布式关系型数据库,是一款同时支持在线事务处理与在线分析处理 (Hybrid Transactional and Analytical Processing, HTAP)的融合型分布式数据库产品,具备水平扩容或者缩容、金融级高可用、实时 HTAP、云原生的分布式数据库、兼容 MySQL 5.7 协议和 MySQL 生态等重要特性。目标是为用户提供一站式 OLTP (Online Transactional Processing)、OLAP (Online Analytical Processing)、HTAP 解决方案。TiDB 适合高可用、强一致要求较高、数据规模较大等各种应用场景。</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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">什么是NewSQL</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;">数据库发展至今已经有3代了:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> SQL,传统关系型数据库,例如 MySQL </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> noSQL,例如 MongoDB,Redis </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> newSQL </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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">传统SQL的问题</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;">互联网在本世纪初开始迅速发展,互联网应用的用户规模、数据量都越来越大,并且要求7X24小时在线。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">传统关系型数据库在这种环境下成为了瓶颈,通常有2种解决方法:</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> <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> <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);border-left-width: 2px;padding: 8px 10px 8px 15px;background: rgb(255, 249, 249);border-left-color: rgb(239, 112, 96);margin-top: 0px;margin-bottom: 20px;letter-spacing: 0.5444px;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(53, 53, 53);font-size: 16px;margin-right: 10px;margin-left: 10px;">使用分布式集群结构</p> </blockquote> <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;">分片中间件做简单的数据操作时还好,但涉及到跨库join、跨库事务时就很头疼了,很多人干脆自己在业务层处理,复杂度较高。</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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">NoSQL 的问题</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;">后来 noSQL 出现了,放弃了传统SQL的强事务保证和关系模型,重点放在数据库的高可用性和可扩展性。</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> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-1"> <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> </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;">缺点</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);" class="list-paddingleft-1"> <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);"> 不支持 SQL 语句,兼容性是个大问题,不同的 NoSQL 数据库都有自己的 api 操作数据,比较复杂。 </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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">NewSQL 特性</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;">NewSQL 提供了与 noSQL 相同的可扩展性,而且仍基于关系模型,还保留了极其成熟的 SQL 作为查询语言,保证了ACID事务特性。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">简单来讲,NewSQL 就是在传统关系型数据库上集成了 NoSQL 强大的可扩展性。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">传统的SQL架构设计基因中是没有分布式的,而 NewSQL 生于云时代,天生就是分布式架构。</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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">NewSQL 的主要特性</span></h2> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> SQL 支持,支持复杂查询和大数据分析。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 支持 ACID 事务,支持隔离级别。 </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> </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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">三种SQL的对比</span></h2> <p><img class="rich_pages wxw-img" data-ratio="0.5407554671968191" src="/upload/ad2fb442e8844b2c5d568bf8718d6f5b.jpg" data-type="jpeg" data-w="1006"></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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">TiDB怎么来的</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;">著名的开源分布式缓存服务 Codis 的作者,PingCAP联合创始人&amp; CTO ,资深 infrastructure 工程师的黄东旭,擅长分布式存储系统的设计与实现,开源狂热分子的技术大神级别人物。即使在互联网如此繁荣的今天,在数据库这片边界模糊且不确定地带,他还在努力寻找确定性的实践方向。关注公z号:码猿技术专栏,回复关键词:1111 获取阿里内部Java性能优化手册!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">直到 2012 年底,他看到 Google 发布的两篇论文,如同棱镜般,折射出他自己内心微烁的光彩。这两篇论文描述了 Google 内部使用的一个海量关系型数据库 F1/Spanner ,解决了关系型数据库、弹性扩展以及全球分布的问题,并在生产中大规模使用。“如果这个能实现,对数据存储领域来说将是颠覆性的”,黄东旭为完美方案的出现而兴奋, PingCAP 的 TiDB 在此基础上诞生了。</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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">TiDB社区版和企业版</span></h2> <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);border-left-width: 2px;padding: 8px 10px 8px 15px;background: rgb(255, 249, 249);border-left-color: rgb(239, 112, 96);margin-top: 0px;margin-bottom: 20px;letter-spacing: 0.5444px;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(53, 53, 53);font-size: 16px;margin-right: 10px;margin-left: 10px;">TiDB分为社区版以及企业版,企业版收费提供服务以及安全性的支持</p> </blockquote> <p><img class="rich_pages wxw-img" data-ratio="0.4190106692531523" src="/upload/d9710bc026a6cde39fede0faedb332c1.png" data-type="png" data-w="1031"></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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">TIDB核心特性</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;">水平弹性扩展</span><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(106, 115, 125);border-left-width: 2px;padding: 8px 10px 8px 15px;background: rgb(255, 249, 249);border-left-color: rgb(239, 112, 96);margin-top: 0px;margin-bottom: 20px;letter-spacing: 0.5444px;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(53, 53, 53);font-size: 16px;margin-right: 10px;margin-left: 10px;">通过简单地增加新节点即可实现 TiDB 的水平扩展,按需扩展吞吐或存储,轻松应对高并发、海量数据场景</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">得益于 TiDB 存储计算分离的架构的设计,可按需对计算、存储分别进行在线扩容或者缩容,扩容或者缩容过程中对应用运维人员透明。</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;">TiDB 100% 支持标准的 ACID 事务</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> <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);border-left-width: 2px;padding: 8px 10px 8px 15px;background: rgb(255, 249, 249);border-left-color: rgb(239, 112, 96);margin-top: 0px;margin-bottom: 20px;letter-spacing: 0.5444px;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(53, 53, 53);font-size: 16px;margin-right: 10px;margin-left: 10px;">相比于传统主从 (M-S) 复制方案,基于 Raft 的多数派选举协议可以提供金融级的 100% 数据强一致性保证,且在不丢失大多数副本的前提下,可以实现故障的自动恢复 (auto-failover),无需人工介入</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">数据采用多副本存储,数据副本通过 Multi-Raft 协议同步事务日志,多数派写入成功事务才能提交,确保数据强一致性且少数副本发生故障时不影响数据的可用性。可按需配置副本地理位置、副本数量等策略满足不同容灾级别的要求。</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;">实时 HTAP</span><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(106, 115, 125);border-left-width: 2px;padding: 8px 10px 8px 15px;background: rgb(255, 249, 249);border-left-color: rgb(239, 112, 96);margin-top: 0px;margin-bottom: 20px;letter-spacing: 0.5444px;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(53, 53, 53);font-size: 16px;margin-right: 10px;margin-left: 10px;">TiDB 作为典型的 OLTP 行存数据库,同时兼具强大的 OLAP 性能,配合 TiSpark,可提供一站式 HTAP 解决方案,一份存储同时处理 OLTP &amp; OLAP 无需传统繁琐的 ETL 过程</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">提供行存储引擎 TiKV、列存储引擎 TiFlash 两款存储引擎,TiFlash 通过 Multi-Raft Learner 协议实时从 TiKV 复制数据,确保行存储引擎 TiKV 和列存储引擎 TiFlash 之间的数据强一致。TiKV、TiFlash 可按需部署在不同的机器,解决 HTAP 资源隔离的问题。</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;">TiDB 是为云而设计的数据库,同 Kubernetes 深度耦合,支持公有云、私有云和混合云,使部署、配置和维护变得十分简单。TiDB 的设计目标是 100% 的 OLTP 场景和 80% 的 OLAP 场景,更复杂的 OLAP 分析可以通过 TiSpark 项目来完成。TiDB 对业务没有任何侵入性,能优雅的替换传统的数据库中间件、数据库分库分表等 Sharding 方案。同时它也让开发运维人员不用关注数据库 Scale 的细节问题,专注于业务开发,极大的提升研发的生产力</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;">高度兼容 MySQL</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;">兼容 MySQL 5.7 协议、MySQL 常用的功能、MySQL 生态,应用无需或者修改少量代码即可从 MySQL 迁移到 TiDB。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">提供丰富的数据迁移工具帮助应用便捷完成数据迁移,大多数情况下,无需修改代码即可从 MySQL 轻松迁移至 TiDB,分库分表后的 MySQL 集群亦可通过 TiDB 工具进行实时迁移。</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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">OLTP&amp;OLAP(自学)</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;">OLTP(联机事务处理)</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;">OLTP(Online Transactional Processing) 即联机事务处理,OLTP 是传统的关系型数据库的主要应用,主要是基本的、日常的事务处理,记录即时的增、删、改、查,比如在银行存取一笔款,就是一个事务交易</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">联机事务处理是事务性非常高的系统,一般都是高可用的在线系统,以�

这破系统终于拆分重构了,压力山大!

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.6;word-break: break-word;text-align: left;padding: 5px;font-size: 16px;color: rgb(53, 53, 53);word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;" data-mpa-powered-by="yiban.io"> <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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">1 为什么要拆分?</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><img class="rich_pages wxw-img" data-ratio="0.498545101842871" src="/upload/5037cff578f0a811348c678f5ab0c570.png" data-type="png" data-w="1031"></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;">1) <span style="font-weight: 700;color: rgb(248, 57, 41);">应用间耦合严重</span>。系统内各个应用之间不通,同样一个功能在各个应用中都有实现,后果就是改一处功能,需要同时改系统中的所有应用。这种情况多存在于历史较长的系统,因各种原因,系统内的各个应用都形成了自己的业务小闭环;</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">2) <span style="font-weight: 700;color: rgb(248, 57, 41);">业务扩展性差</span>。数据模型从设计之初就只支持某一类的业务,来了新类型的业务后又得重新写代码实现,结果就是项目延期,大大影响业务的接入速度;</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">3) <span style="font-weight: 700;color: rgb(248, 57, 41);">代码老旧,难以维护</span>。各种随意的if else、写死逻辑散落在应用的各个角落,处处是坑,开发维护起来战战兢兢;</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">4) <span style="font-weight: 700;color: rgb(248, 57, 41);">系统扩展性差</span>。系统支撑现有业务已是颤颤巍巍,不论是应用还是DB都已经无法承受业务快速发展带来的压力;</p> <p style="text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.6675461741424802" src="/upload/b26d29ba7a9c40578aa84284bfcb5085.jpg" data-type="jpeg" data-w="379"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">5) <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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">2 拆前准备什么?</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;">2.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: 0.8em;margin-bottom: 0.8em;">一个老生常谈的问题,系统与业务的关系?</p> <p><img class="rich_pages wxw-img" data-ratio="0.507274490785645" src="/upload/2db425f10d208f3543e0ee3be94aae5a.png" data-type="png" data-w="1031"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">我们最期望的理想情况是第一种关系(车辆与人),业务觉得不合适,可以马上换一辆新的。但现实的情况是更像心脏起搏器与人之间的关系,不是说换就能换。一个系统接的业务越多,耦合越紧密。如果在没有真正把握住业务复杂度之前贸然行动,最终的结局就是把心脏带飞。关注公众号:码猿技术专栏,回复关键词:1111 获取阿里内部Java性能调优手册!</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;">一个是技术层面,通过与pd以及开发的讨论,熟悉现有各个应用的领域模型,以及优缺点,这种讨论只能让人有个大概,更多的细节如代码、架构等需要通过做需求、改造、优化这些实践来掌握。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">各个应用熟悉之后,需要从系统层面来构思,我们想打造平台型的产品,那么最重要也是最难的一点就是功能集中管控,打破各个应用的业务小闭环,统一收拢,这个决心更多的是开发、产品、业务方、各个团队之间达成的共识,可以参考《微服务(Microservice)那点事》一文,“按照业务或者客户需求组织资源”。</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;">2.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: 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><img class="rich_pages wxw-img" data-ratio="0.35984481086323955" src="/upload/cb2d79674f1db2e663366e916c5fbc24.png" data-type="png" data-w="1031"></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="color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 16px;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;">关注公众号:</span><span style="color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 16px;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;">码猿技术专栏,回复关键词:</span><span style="color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 16px;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;">1111 获取阿里内部Java性能调优手册!</span></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;">2.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: 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;">首先要确定的就是某一应用拆分后的目标。拆分优化是没有底的,可能越做越深,越做越没结果,继而又影响自己和团队的士气。比如说可以定这期的目标就是将db、应用分拆出去,数据模型的重新设计可以在第二期。</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;">2.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: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">动手前的思考成本远远低于动手后遇到问题的解决成本</span>。应用拆分最怕的是中途说“他*的,这块不能动,原来当时这样设计是有原因的,得想别的路子!”这时的压力可想而知,整个节奏不符合预期后,很可能会接二连三遇到同样的问题,这时不仅同事们士气下降,自己也会丧失信心,继而可能导致拆分失败。</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;">2.5给自己留个锦囊,“有备无患”</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> <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;">2.6 放松心情,缓解压力</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> <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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">3 实践</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 db拆分实践</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;">DB拆分在整个应用拆分环节里最复杂,分为垂直拆分和水平拆分两种场景,我们都遇到了。垂直拆分是将库里的各个表拆分到合适的数据库中。比如一个库中既有消息表,又有人员组织结构表,那么将这两个表拆分到独立的数据库中更合适。</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><img class="rich_pages wxw-img" data-ratio="0.41998060135790494" src="/upload/46132d68ea8764de81e8530f58cf9c16.png" data-type="png" data-w="1031"></p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 18px;"><span style="display: none;"></span>3.1.1 主键id接入全局id发生器<span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">DB拆分的第一件事情就是使用全局id发生器来生成各个表的主键id。为什么?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">举个例子,假如我们有一张表,两个字段id和token,id是自增主键生成,要以token维度来分库分表,这时继续使用自增主键会出现问题。</p> <p><img class="rich_pages wxw-img" data-ratio="0.25680933852140075" src="/upload/650a64681c088a9f538e519dd67e1253.png" data-type="png" data-w="771"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">正向迁移扩容中,通过自增的主键,到了新的分库分表里一定是唯一的,但是,我们要考虑迁移失败的场景,如下图所示,新的表里假设已经插入了一条新的记录,主键id也是2,这个时候假设开始回滚,需要将两张表的数据合并成一张表(逆向回流),就会产生主键冲突!</p> <p><img class="rich_pages wxw-img" data-ratio="0.3831231813773036" src="/upload/260b4e1378f00e3c54e7fd2e39baac03.png" data-type="png" data-w="1031"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">因此在迁移之前,先要用全局唯一id发生器生成的id来替代主键自增id。这里有几种全局唯一id生成方法可以选择。</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)snowflake:https://github.com/twitter/snowflake;(非全局递增)</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">2) mysql新建一张表用来专门生成全局唯一id(利用auto_increment功能)(全局递增);</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">3)有人说只有一张表怎么保证高可用?那两张表好了(在两个不同db),一张表产生奇数,一张表产生偶数。或者是n张表,每张表的负责的步长区间不同(非全局递增)</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">4)……</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">我们使用的是阿里巴巴内部的tddl-sequence(mysql+内存),保证全局唯一但非递增,在使用上遇到一些坑:</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)对按主键id排序的sql要提前改造。因为id已经不保证递增,可能会出现乱序场景,这时候可以改造为按gmt_create排序;</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">2)报主键冲突问题。这里往往是代码改造不彻底或者改错造成的,比如忘记给某一insert sql的id添加#{},导致继续使用自增,从而造成冲突;</p> <p><img class="rich_pages wxw-img" data-ratio="0.1148491879350348" src="/upload/3d4f060486c9177d74a1c66ea239e0f6.png" data-type="png" data-w="862"></p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 18px;"><span style="display: none;"></span>3.1.2 建新表&amp;迁移数据&amp;binlog同步<span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">1) 新表字符集建议是utf8mb4,支持表情符。<span style="font-weight: 700;color: rgb(248, 57, 41);">新表建好后索引不要漏掉</span>,否则可能会导致慢sql!从经验来看索引被漏掉时有发生,建议事先列计划的时候将这些要点记下,后面逐条检查;</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">2) 使用全量同步工具或者自己写job来进行全量迁移;全量数据迁移务必要在业务低峰期时操作,并根据系统情况调整并发数;</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">3) 增量同步。全量迁移完成后可使用binlog增量同步工具来追数据,比如阿里内部使用精卫,其它企业可能有自己的增量系统,或者使用阿里开源的cannal/otter:</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);border-left-width: 2px;padding: 8px 10px 8px 15px;background: rgb(255, 249, 249);border-left-color: rgb(239, 112, 96);margin-top: 0px;margin-bottom: 20px;letter-spacing: 0.5444px;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(53, 53, 53);font-size: 16px;margin-right: 10px;margin-left: 10px;">https://github.com/alibaba/canal?spm=5176.100239.blogcont11356.10.5eNr98</p> </blockquote> <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);border-left-width: 2px;padding: 8px 10px 8px 15px;background: rgb(255, 249, 249);border-left-color: rgb(239, 112, 96);margin-top: 0px;margin-bottom: 20px;letter-spacing: 0.5444px;"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(53, 53, 53);font-size: 16px;margin-right: 10px;margin-left: 10px;">https://github.com/alibaba/otter/wiki/QuickStart?spm=5176.100239.blogcont11356.21.UYMQ17</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">增量同步起始获取的binlog位点必须在全量迁移之前,否则会丢数据,比如我中午12点整开始全量同步,13点整全量迁移完毕,那么增量同步的binlog的位点一定要选在12点之前。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">位点在前会不会导致重复记录?不会!线上的MySQL binlog是row 模式,如一个delete语句删除了100条记录,binlog记录的不是一条delete的逻辑sql,而是会有100条binlog记录。insert语句插入一条记录,如果主键冲突,插入不进去。</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 18px;"><span style="display: none;"></span>3.1.3 联表查询sql改造<span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">现在主键已经接入全局唯一id,新的库表、索引已经建立,且数据也在实时追平,现在可以开始切库了吗?no!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">考虑以下非常简单的联表查询sql,如果将B表拆分到另一个库里的话,这个sql怎么办?毕竟跨库联表查询是不支持的!</p> <p><img class="rich_pages wxw-img" data-ratio="0.15088757396449703" src="/upload/cd9b4daef58cb8254faf31ae4f46529a.png" data-type="png" data-w="676"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">因此,在切库之前,需要将系统中上百个联表查询的sql改造完毕。</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);">1)</span> <span style="font-weight: 700;color: rgb(248, 57, 41);">业务避免</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">业务上松耦合后技术才能松耦合,继而避免联表sql。但短期内不现实,需要时间沉淀;</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);">2)</span> <span style="font-weight: 700;color: rgb(248, 57, 41);">全局表</span></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);">3)</span> <span style="font-weight: 700;color: rgb(248, 57, 41);">冗余字段</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">就像订单表一样,冗余商品id字段,但是我们需要冗余的字段太多,而且要考虑字段变更后数据更新问题;</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);">4)</span> <span style="font-weight: 700;color: rgb(248, 57, 41);">内存拼接</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">4.1)通过RPC调用来获取另一张表的数据,然后再内存拼接。1)适合job类的sql,或改造后RPC查询量较少的sql;2)不适合大数据量的实时查询sql。假设10000个ID,分页RPC查询,每次查100个,需要5ms,共需要500ms,rt太高。</p> <p style="text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.36147757255936674" src="/upload/75ee5362f945e48660a171386d8d68ee.png" data-type="png" data-w="379"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">4.2)本地缓存另一张表的数据</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">适合数据变化不大、数据量查询大、接口性能稳定性要求高的sql。</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 class="rich_pages wxw-img" data-ratio="0.6112224448897795" src="/upload/9a3413ea7f83804fd3b602c4652e2154.png" data-w="499" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 18px;"><span style="display: none;"></span>3.1.4切库方案设计与实现(两种方案)<span style="display: none;"></span></h4> <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);">a)DB停写方案</span></p> <p><img class="rich_pages wxw-img" data-ratio="0.510184287099903" src="/upload/430838b18c79f7df3253d18c9f6104e4.png" data-type="png" data-w="1031"></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> <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> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">1)如果要回滚得联系DBA执行线上停写操作,风险高,因为有可能在业务高峰期回滚;</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">2)只有一处地方校验,出问题的概率高,回滚的概率高</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;">sql联表查询改造不完全;</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">sql联表查询改错&amp;性能问题;</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;">字符集问题</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">此外,binlog逆向回流很可能发生字符集问题(utf8mb4到gbk),导致回流失败。这些binlog同步工具为了保证强最终一致性,一旦某条记录回流失败,就卡住不同步,继而导致新老表的数据不同步,继而无法回滚!</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);">b)双写方案</span></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);"><img class="rich_pages wxw-img" data-ratio="0.5489815712900097" src="/upload/272c41f1561faf4209e4fd2d4c1be925.png" data-type="png" data-w="1031"></span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">第2步“打开双写开关,先写老表A再写新表B”,这时候确保写B表时try catch住,异常要用很明确的标识打出来,方便排查问题。第2步双写持续短暂时间后(比如半分钟后),可以关闭binlog同步任务。</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> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">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;">2)线上不停服,回滚容易;</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">3)字符集问题影响小</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> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">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;">2)双写造成RT增加</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 18px;"><span style="display: none;"></span>3.1.5 开关要写好<span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">不管什么切库方案,开关少不了,这里开关的初始值一定要设置为null!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">如果随便设置一个默认值,比如”读老表A“,假设我们已经进行到读新表B的环节了。这时重启了应用,在应用启动的一瞬间,最新的“读新表B”的开关推送等可能没有推送过来,这个时候就可能使用默认值,继而造成脏数据!</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;">3.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: 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);">1)分布式事务</span></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);">2)消息机制补偿</span>(如何用消息系统避免分布式事务?)</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);">3)定时任务补偿</span></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;">3.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: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">一句话:怀疑第三方</span>,<span style="font-weight: 700;color: rgb(248, 57, 41);">防备使用方</span>,<span style="font-weight: 700;color: rgb(248, 57, 41);">做好自己!</span></p> <p><img class="rich_pages wxw-img" data-ratio="0.1234718826405868" src="/upload/724c12ec765ce17882e6518e591054c5.png" data-type="png" data-w="818"></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);">1</span>)怀疑第三方</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">a)防御式编程,制定好各种降级策略;</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-1"> <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;">b)遵循快速失败原则,一定要设置超时时间,并异常捕获;</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">c)强依赖转弱依赖,旁支逻辑异步化</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 我们对某一个核心应用的旁支逻辑异步化后,响应时间几乎缩短了1/3,且后面中间件、其它应用等都出现过抖动情况,而核心链路一切正常; </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;">d)适当保护第三方,慎重选择重试机制</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);">2</span>)防备使用方</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">a)设计一个好的接口,避免误用</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-1"> <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);"> 不要让使用方做接口可以做的事情;比如你只暴露一个getMsgById接口,别人如果想批量调用的话,可能就直接for循环rpc调用,如果提供getMsgListByIdList接口就不会出现这种情况了。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 避免长时间执行的接口;特别是一些老系统,一个接口背后对应的可能是for循环select DB的场景。 </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> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">b)容量限制</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-1"> <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);"> 业务容量控制。有些时候不仅仅是系统层面的限制,业务层面也需要限制。举个例子,对saas化的一些系统来说,”你这个租户最多1w人使用“。 </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;"><span style="font-weight: 700;color: rgb(248, 57, 41);">3</span>)做好自己</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">a)<span style="font-weight: 700;color: rgb(248, 57, 41);">单一职责</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">b)<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);" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 例:例如我们改造时候发现一年前留下的坑,去掉后整个集群cpu使用率下降1/3 </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;">c) <span style="font-weight: 700;color: rgb(248, 57, 41);">运维SOP化</span></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 说实话,线上出现问题,如果没有预案,再怎么处理都会超时。曾经遇到过一次DB故障导致脏数据问题,最终只能硬着头皮写代码来清理脏数据,但是时间很长,只能眼睁睁看着故障不断升级。经历过这个事情后,我们马上设想出现脏数据的各种场景,然后上线了三个清理脏数据的job,以防其它不可预知的产生脏数据的故障场景,以后只要遇到出现脏数据的故障,直接触发这三个清理job,先恢复再排查。 </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;">d)资源使用可预测</p> <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: 0.8em;margin-bottom: 0.8em;">应用的cpu、内存、网络、磁盘心中有数</p> </section> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-1"> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);list-style-type: square;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 正则匹配耗cpu </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 耗性能的job优化、降级、下线(循环调用rpc或sql) </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 慢sql优化、降级、限流 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> tair/redis、db调用量要可预测 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 例:tair、db </section></li> </ul> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">举个例子: 某一个接口类似于秒杀功能,qps非常高(如下图所示),请求先到tair,如果找不到会回源到DB,当请求突增时候,甚至会触发tair/redis这层缓存的限流,此外由于缓存在一开始是没数据的,请求会穿透到db,从而击垮db。</p> <p><img class="rich_pages wxw-img" data-ratio="0.3317167798254122" src="/upload/d2de68f5b6326e6a64d97994a3967eb8.png" data-type="png" data-w="1031"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">这里的核心问题就是tair/redis这层资源的使用不可预测,因为依赖于接口的qps,怎么让请求变得可预测呢?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">如果我们再增加一层本地缓存(guava,比如超时时间设置为1秒),保证单机对一个key只有一个请求回源,那样对tair/redis这层资源的使用就可以预知了。假设有500台client,对一个key来说,一瞬间最多500个请求穿透到Tair/redis,以此类推到db。</p> <p><img class="rich_pages wxw-img" data-ratio="0.24151309408341415" src="/upload/ba1d4c6a1db2d0bc1cdd10fe5502d83.png" data-type="png" data-w="1031"></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;">比如client有500台,对某key一瞬间最多有500个请求穿透到db,如果key有10个,那么请求最多可能有5000个到db,恰好这些sql的RT有些高,怎么保护DB的资源?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">可以通过一个定时程序不断将数据从db刷到缓存。这里就将不可控的5000个qps的db访问变为可控的个位数qps的db访问。</p> <p><img class="rich_pages wxw-img" data-ratio="0.43743937924345294" src="/upload/cb5cbddc5a02ef807e5385ee26ecf655.png" data-type="png" data-w="1031"></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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">4 &nbsp;总结</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);">1)</span>做好准备面对压力!</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);">2)复杂问题要拆解为多步骤,每一步可测试可回滚!</span></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);">3)墨菲定律:你所担心的事情一定会发生,而且会很快发生,所以准备好你的SOP</span>(标准化解决方案)!</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=0.00001%,互联网环境下,请求量足够大,小概率事件就真发生了。</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);">4)借假修真</span></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;"></figure> </section>

聊聊大厂都在用的 JavaAgent

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.6;word-break: break-word;text-align: left;padding: 5px;font-size: 16px;color: rgb(53, 53, 53);word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">熟悉Spring的小伙伴们应该都对aop比较了解,面向切面编程允许我们在目标方法的前后织入想要执行的逻辑,而今天要给大家介绍的Java Agent技术,在思想上与aop比较类似,翻译过来可以被称为<span style="font-weight: 700;color: rgb(248, 57, 41);">Java代理</span>、<span style="font-weight: 700;color: rgb(248, 57, 41);">Java探针</span>技术。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">Java Agent出现在JDK1.5版本以后,它允许程序员利用agent技术构建一个独立于应用程序的代理程序,用途也非常广泛,可以协助监测、运行、甚至替换其他JVM上的程序,先从下面这张图直观的看一下它都被应用在哪些场景:</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 class="rich_pages wxw-img" data-ratio="0.5509433962264151" data-w="530" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;height: auto !important;" src="/upload/78521b20de0c338ec42335807f9fc71f.png"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">看到这里你是不是也很好奇,究竟是什么神仙技术,能够应用在这么多场景下,那今天我们就来挖掘一下,看看神奇的Java Agent是如何工作在底层,默默支撑了这么多优秀的应用。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">回到文章开头的类比,我们还是用和aop比较的方式,来先对Java Agent有一个大致的了解:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 作用级别:aop运行于应用程序内的方法级别,而agent能够作用于虚拟机级别 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 组成部分:aop的实现需要目标方法和逻辑增强部分的方法,而Java Agent要生效需要两个工程,一个是agent代理,另一个是需要被代理的主程序 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 执行场合:aop可以运行在切面的前后或环绕等场合,而Java Agent的执行只有两种方式,jdk1.5提供的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">preMain</code>模式在主程序运行前执行,jdk1.6提供的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">agentMain</code>在主程序运行后执行 </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;">下面我们就分别看一下在两种模式下,如何动手实现一个agent代理程序。</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(234, 84, 41);letter-spacing: 0.5444px;padding-bottom: 10px;border-bottom: 2px solid rgb(234, 84, 41);visibility: visible;">Premain模式</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;">Premain模式允许在主程序执行前执行一个agent代理,实现起来非常简单,下面我们分别实现两个组成部分。</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;">agent</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(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oezBibiciavS1eDQfRdwPzwVqg2j7cZtkg3kayqDLhYfOJib3YBga5zt09Uk8ic5FcDhK2PCExMNMUAqORUdytoG6Jq9k/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(255, 255, 255);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: black;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: