作者:じ☆ve不哭
# Java加密套件强度限制引起的SSL handshake_failure <p>HttpURLConnection的调用非常简单。</p> <pre><code>HttpURLConnection connection = (HttpURLConnection)m_url.openConnection(); connection.setRequestMethod("GET"); connection.setAllowUserInteraction(false); connection.setDefaultUseCaches(false); connection.setDoInput(true); connection.setDoOutput(false); connection.setInstanceFollowRedirects(true); connection.setUseCaches(false); connection.connect(); <----- handshake error </code></pre> <p>错误也很抽象。</p> <pre><code>javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure at sun.security.ssl.Alerts.getSSLException(Unknown Source) at sun.security.ssl.Alerts.getSSLException(Unknown Source) at sun.security.ssl.SSLSocketImpl.recvAlert(Unknown Source) at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source) at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source) at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source) at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source) at sun.net.www.protocol.https.HttpsClient.afterConnect(Unknown Source) at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source) at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(Unknown Source) at com.agile.common.HttpReader.getInputStream(HttpReader.java:76) </code></pre> <p>初步怀疑本地的JRE证书信任文件中没有包含对方服务器的根证书。</p> <pre><code>keytool -list -v -keystore "C:\Java\jdk1.8.0_152\jre\lib\security\cacerts" >store.txt </code></pre> <p>检查发现,根证书和中间证书都存在,信任链没有问题。</p> <pre><code>## 中间证书 Alias name: comodorsaca [jdk] Creation date: 25 Aug, 2016 Entry type: trustedCertEntry Owner: CN=COMODO RSA Certification Authority, O=COMODO CA Limited, L=Salford, ST=Greater Manchester, C=GB Issuer: CN=COMODO RSA Certification Authority, O=COMODO CA Limited, L=Salford, ST=Greater Manchester, C=GB Serial number: 4caaf9cadb636fe01ff74ed85b03869d Valid from: Tue Jan 19 05:30:00 IST 2010 until: Tue Jan 19 05:29:59 IST 2038 Certificate fingerprints: MD5: 1B:31:B0:71:40:36:CC:14:36:91:AD:C4:3E:FD:EC:18 SHA1: AF:E5:D2:44:A8:D1:19:42:30:FF:47:9F:E2:F8:97:BB:CD:7A:8C:B4 SHA256: 52:F0:E1:C4:E5:8E:C6:29:29:1B:60:31:7F:07:46:71:B8:5D:7E:A8:0D:5B:07:27:34:63:53:4B:32:B4:02:34 Signature algorithm name: SHA384withRSA Version: 3 ## 根证书 Alias name: addtrustqualifiedca [jdk] Creation date: 25 Aug, 2016 Entry type: trustedCertEntry Owner: CN=AddTrust Qualified CA Root, OU=AddTrust TTP Network, O=AddTrust AB, C=SE Issuer: CN=AddTrust Qualified CA Root, OU=AddTrust TTP Network, O=AddTrust AB, C=SE Serial number: 1 Valid from: Tue May 30 16:14:50 IST 2000 until: Sat May 30 16:14:50 IST 2020 Certificate fingerprints: MD5: 27:EC:39:47:CD:DA:5A:AF:E2:9A:01:65:21:A9:4C:BB SHA1: 4D:23:78:EC:91:95:39:B5:00:7F:75:8F:03:3B:21:1E:C5:4D:8B:CF SHA256: 80:95:21:08:05:DB:4B:BC:35:5E:44:28:D8:FD:6E:C2:CD:E3:AB:5F:B9:7A:99:42:98:8E:B8:F4:DC:D0:60:16 Signature algorithm name: SHA1withRSA Version: 3 </code></pre> <p>那就去抓SSL包吧。</p> <p>请求包:</p> <pre><code>Secure Sockets Layer TLSv1.2 Record Layer: Handshake Protocol: Client Hello Content Type: Handshake (22) Version: TLS 1.2 (0x0303) Length: 229 Handshake Protocol: Client Hello Handshake Type: Client Hello (1) Length: 225 Version: TLS 1.2 (0x0303) Random: 5af01897734438e606e3342398727fe8a539522a2ef0dfa6... GMT Unix Time: May 7, 2018 17:12:55.000000000 中国标准时间 Random Bytes: 734438e606e3342398727fe8a539522a2ef0dfa6b698a1eb... Session ID Length: 0 Cipher Suites Length: 58 Cipher Suites (29 suites) Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 (0xc023) Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (0xc027) Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA256 (0x003c) Cipher Suite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 (0xc025) Cipher Suite: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 (0xc029) Cipher Suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 (0x0067) Cipher Suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 (0x0040) Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009) Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013) Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f) Cipher Suite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA (0xc004) Cipher Suite: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA (0xc00e) Cipher Suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA (0x0033) Cipher Suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA (0x0032) Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b) Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f) Cipher Suite: TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c) Cipher Suite: TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02d) Cipher Suite: TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 (0xc031) Cipher Suite: TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (0x009e) Cipher Suite: TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 (0x00a2) Cipher Suite: TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA (0xc008) Cipher Suite: TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA (0xc012) Cipher Suite: TLS_RSA_WITH_3DES_EDE_CBC_SHA (0x000a) Cipher Suite: TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA (0xc003) Cipher Suite: TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA (0xc00d) Cipher Suite: TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA (0x0016) Cipher Suite: TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA (0x0013) Cipher Suite: TLS_EMPTY_RENEGOTIATION_INFO_SCSV (0x00ff) </code></pre> <p>响应包:</p> <pre><code>Secure Sockets Layer TLSv1.2 Record Layer: Alert (Level: Fatal, Description: Handshake Failure) Content Type: Alert (21) Version: TLS 1.2 (0x0303) Length: 2 Alert Message Level: Fatal (2) Description: Handshake Failure (40) </code></pre> <p>客户端发送了Hello,服务器翻了个白眼直接拒绝了。这个响应包,没有任何线索。但是能明确一点就是客户端和服务器都通过TLS 1.2协议协商。不用再怀疑协议版本问题了。</p> <p>联想到浏览器访问对方https系统是成功的,再次抓取浏览器请求和响应包。发现了一点有用的线索。</p> <p>浏览器的请求包同样采用TLS 1.2协议,但是加密套件 (Cipher Suites)和前面的差异很大,提供了17个可选加密套件。前面的包提供了29个可选列表。</p> <pre><code>Secure Sockets Layer TLSv1.2 Record Layer: Handshake Protocol: Client Hello Content Type: Handshake (22) Version: TLS 1.0 (0x0301) Length: 512 Handshake Protocol: Client Hello Handshake Type: Client Hello (1) Length: 508 Version: TLS 1.2 (0x0303) Random: 7179caea804a5281b411adca67c11883f616e13e13756d0d... GMT Unix Time: May 1, 2030 03:20:10.000000000 中国标准时间 Random Bytes: 804a5281b411adca67c11883f616e13e13756d0d5764d936... Session ID Length: 32 Session ID: 324885cac7ecf4dd09e38acdd3e45ceb2e0c5e248b31d267... Cipher Suites Length: 34 Cipher Suites (17 suites) Cipher Suite: Reserved (GREASE) (0xcaca) Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301) Cipher Suite: TLS_AES_256_GCM_SHA384 (0x1302) Cipher Suite: TLS_CHACHA20_POLY1305_SHA256 (0x1303) Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b) Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f) Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c) Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030) Cipher Suite: TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca9) Cipher Suite: TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8) Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013) Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014) Cipher Suite: TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c) Cipher Suite: TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009d) Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f) Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035) Cipher Suite: TLS_RSA_WITH_3DES_EDE_CBC_SHA (0x000a) </code></pre> <p>接着看浏览器接收到的tcp包Server Hello。服务器通过TLS 1.2协议协商,告诉浏览器它要使用 TLS_RSA_WITH_AES_256_GCM_SHA384 加密套件作为后续数据的加密算法。</p> <pre><code>Secure Sockets Layer TLSv1.2 Record Layer: Handshake Protocol: Server Hello Content Type: Handshake (22) Version: TLS 1.2 (0x0303) Length: 81 Handshake Protocol: Server Hello Handshake Type: Server Hello (2) Length: 77 Version: TLS 1.2 (0x0303) Random: f1649150aeb3366381e54392bfdb8f49ae8ead9f47dbcb1f... GMT Unix Time: May 3, 2098 04:10:56.000000000 中国标准时间 Random Bytes: aeb3366381e54392bfdb8f49ae8ead9f47dbcb1fc13f95a4... Session ID Length: 32 Session ID: 264c4906bbd0fd2b8f94f66ea2992433b82690fb7fb844d7... Cipher Suite: TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009d) Compression Method: null (0) Extensions Length: 5 Extension: renegotiation_info (len=1) Type: renegotiation_info (65281) Length: 1 Renegotiation Info extension Renegotiation info extension length: 0 </code></pre> <p>马上注意到在前面的请求包中,29个加密套件里都没有TLS_RSA_WITH_AES_256_GCM_SHA384。似乎问题就在这里,就是通过java代码访问https服务器时,候选的加密套件中没有服务器希望的TLS_RSA_WITH_AES_256_GCM_SHA384。</p> <p>真的是这样吗?为了弄清服务器和本地JRE是否存在加密套件不匹配,还得使用有足够说服力的诊断方法来验证。</p> <p>首先搬上OPENSSL来调试。</p> <pre><code>openssl s_client -connect server.mycompany.com:443 </code></pre> <p>诊断发现,服务器确实需要AES256-GCM-SHA384,这是一个很长长度的强加密,一般128位长度加密很牛逼了。</p> <pre><code>New, TLSv1.2, Cipher is AES256-GCM-SHA384 Server public key is 2048 bit Secure Renegotiation IS supported Compression: NONE Expansion: NONE No ALPN negotiated SSL-Session: Protocol : TLSv1.2 Cipher : AES256-GCM-SHA384 Session-ID: 5BFE44E0BE1248156266BE6947FA113C1035DDDC3A3BD1888940EF8257CAA18C </code></pre> <p>对Java代码也来一次调试,确认它所支持的所有加密套件。</p> <pre><code>-Dssl.debug=true -Djavax.net.debug=all </code></pre> <p>返回结果确实如此,根本就不存在TLS_RSA_WITH_AES_256_GCM_SHA384,也不存在基于AES256-GCM-SHA384的加密方法。</p> <pre><code>Cipher Suites: [ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV ] </code></pre> <p>问题找到了。解决方法看来现在只能回到JRE本身的加密授权问题上来了。</p> <p>Java支持所有的加密套件,但是对于发行的JDK版本,它默认做了很多加密长度限制的裁剪,就是只出口强度低的加密,这是美国政府对于安全软件的强制性规定。但Oracle允许下载强加密的未限制版本,其实就是几个授权属性文件,因为源代码都在发行的JDK中。</p> <p>当前问题发生在JDK1.8中,所以可以去官网下载一个压缩包叫做 “Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 8”。对于JDK1.8版本但是低于1.8.0_151版本的JDK,将下载的包里的两个文件直接覆盖到本地 Java\jre\lib\security\</p> <pre><code>local_policy.jar US_export_policy.jar </code></pre> <p>1.8.0_151和以后的版本,无需下载任何文件,只要修改Java\jre\lib\security\java.security文件,修改这一行注释并启用就可以了。</p> <pre><code>crypto.policy=unlimited </code></pre> <hr> <div class="tags"> Tags : <span class="tag"><a href="http://www.xwiz.cn/tag/SSL" rel="nofollow">ssl</a></span> </div> <div align="center"> <div> </div> </div>
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;padding-right: 10px;padding-left: 10px;word-break: break-word;word-wrap: break-word;text-align: left;margin-top: -10px;line-height: 1.25;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;background-image: linear-gradient(90deg, rgba(50, 0, 0, 0.04) 3%, rgba(0, 0, 0, 0) 3%), linear-gradient(360deg, rgba(50, 0, 0, 0.04) 3%, rgba(0, 0, 0, 0) 3%);background-size: 20px 20px;background-position: center center;"> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-cropselx1="0" data-cropselx2="558" data-cropsely1="0" data-cropsely2="227" data-ratio="0.625" src="/upload/313ec90f0c604aa831df5b25bb0df78e.jpg" data-type="jpeg" data-w="1280" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;width: 558px;height: 349px;"> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 22px;border-bottom: 4px solid rgb(64, 184, 250);"><span style="display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/IJUXwBNpKlhiaoic9iaaqXKVAaaLPcXhEosxKKLxeHaSfHBlNgChJKLSbEicR0HM7NdE4QW4CegmYtsVaSazVaov9Q/640?wx_fmt=png");margin-bottom: -22px;"></span><span style="display: flex;color: #40B8FA;font-size: 20px;margin-left: 25px;">概述</span><span style="display: flex;box-sizing: border-box;width: 200px;height: 10px;border-top-left-radius: 20px;background: RGBA(64, 184, 250, .5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;margin-top: -10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;"><code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">Redis</code>的文章,我之前写过一篇关于<strong style="color: rgb(53, 148, 247);">「Redis的缓存的三大问题」</strong>,累计阅读也快800了,对于还只有3k左右的粉丝量,能够达到这个阅读量,已经是比较难了。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">这说明那篇文章写的还过得去,收到很多人的阅读肯定,感兴趣的看一下[<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU1MzE4OTU0OQ==&mid=2247483977&idx=1&sn=e42c9aedb8bb1c914afa5f2f74324d1f&chksm=fbf7eb8bcc80629dac2c4074c393ca672915bab26864a277251883f1f6c2a6d96338dae3e5b0&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2">看完这篇Redis缓存三大问题,保你能和面试官互扯。</a>]。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;"><strong style="color: rgb(53, 148, 247);">「三大缓存问题」</strong>只是Redis的其中的一小部分的知识点,想要深入学习Redis还要学习比较多的知识点。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">那么今天就带来了一个面试常问的一个问题:<strong style="color: rgb(53, 148, 247);">「假如你的Redis内存满了怎么办?」</strong> 长期的把Redis作为缓存使用,总有一天会存满的时候对吧。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">这个面试题不慌呀,在Redis中有配置参数<code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">maxmemory</code>可以<strong style="color: rgb(53, 148, 247);">「设置Redis内存的大小」</strong>。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">在Redis的配置文件<code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">redis.conf</code>文件中,配置<code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">maxmemory</code>的大小参数如下所示:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.038845726970033294" src="/upload/29d27bc746771450e167385771922e2c.png" data-type="png" data-w="901" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">实际生产中肯定不是<code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">100mb</code>的大小哈,不要给误导了,这里我只是让大家认识这个参数,一般小的公司都是设置为<code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">3G</code>左右的大小。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">除了在配置文件中配置生效外,还可以通过命令行参数的形式,进行配置,具体的配置命令行如下所示:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/IJUXwBNpKlhiaoic9iaaqXKVAaaLPcXhEosbRsnTv301RBibN7XaicZ915JAv40JVuicpzTBOJw82qfLVwVb8U3XOvzQ/640?wx_fmt=png") 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;letter-spacing: 0px;padding-top: 15px;background: #1E1E1E;border-radius: 5px;">//获取maxmemory配置参数的大小<br>127.0.0.1:6379> config get maxmemory<br>//设置maxmemory参数为100mb<br>127.0.0.1:6379> config set maxmemory 100mb<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">倘若实际的存储中超出了Redis的配置参数的大小时,Redis中有<strong style="color: rgb(53, 148, 247);">「淘汰策略」</strong>,把<strong style="color: rgb(53, 148, 247);">「需要淘汰的key给淘汰掉,整理出干净的一块内存给新的key值使用」</strong>。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">接下来我们就详细的聊一聊Redis中的淘汰策略,并且深入的理解每个淘汰策略的原理和应用的场景。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 22px;border-bottom: 4px solid rgb(64, 184, 250);"><span style="display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/IJUXwBNpKlhiaoic9iaaqXKVAaaLPcXhEosxKKLxeHaSfHBlNgChJKLSbEicR0HM7NdE4QW4CegmYtsVaSazVaov9Q/640?wx_fmt=png");margin-bottom: -22px;"></span><span style="display: flex;color: #40B8FA;font-size: 20px;margin-left: 25px;">淘汰策略</span><span style="display: flex;box-sizing: border-box;width: 200px;height: 10px;border-top-left-radius: 20px;background: RGBA(64, 184, 250, .5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;margin-top: -10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">Redis提供了<strong style="color: rgb(53, 148, 247);">「6种的淘汰策略」</strong>,其中默认的是<code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">noeviction</code>,这6种淘汰策略如下:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;font-size: 15px;color: rgb(89, 89, 89);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> <code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">noeviction</code>( <strong style="color: rgb(53, 148, 247);">「默认策略」</strong>):若是内存的大小达到阀值的时候,所有申请内存的指令都会报错。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> <code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">allkeys-lru</code>:所有key都是使用 <strong style="color: rgb(53, 148, 247);">「LRU算法」</strong>进行淘汰。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> <code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">volatile-lru</code>:所有 <strong style="color: rgb(53, 148, 247);">「设置了过期时间的key使用LRU算法」</strong>进行淘汰。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> <code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">allkeys-random</code>:所有的key使用 <strong style="color: rgb(53, 148, 247);">「随机淘汰」</strong>的方式进行淘汰。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> <code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">volatile-random</code>:所有 <strong style="color: rgb(53, 148, 247);">「设置了过期时间的key使用随机淘汰」</strong>的方式进行淘汰。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> <code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">volatile-ttl</code>:所有设置了过期时间的key <strong style="color: rgb(53, 148, 247);">「根据过期时间进行淘汰,越早过期就越快被淘汰」</strong>。 </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">假如在Redis中的数据有<strong style="color: rgb(53, 148, 247);">「一部分是热点数据,而剩下的数据是冷门数据」</strong>,或者<strong style="color: rgb(53, 148, 247);">「我们不太清楚我们应用的缓存访问分布状况」</strong>,这时可以使用<code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">allkeys-lru</code>。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">假如所有的数据访问的频率大概一样,就可以使用<code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">allkeys-random</code>的淘汰策略。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">假如要配置具体的淘汰策略,可以在<code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">redis.conf</code>配置文件中配置,具体配置如下所示:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.0649746192893401" src="/upload/240222b396799f628f5982a734c30d58.png" data-type="png" data-w="985" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">这只需要把注释给打开就可以,并且配置指定的策略方式,另一种的配置方式就是命令的方式进行配置,具体的执行命令如下所示:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/IJUXwBNpKlhiaoic9iaaqXKVAaaLPcXhEosbRsnTv301RBibN7XaicZ915JAv40JVuicpzTBOJw82qfLVwVb8U3XOvzQ/640?wx_fmt=png") 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;letter-spacing: 0px;padding-top: 15px;background: #1E1E1E;border-radius: 5px;">// 获取maxmemory-policy配置<br>127.0.0.1:6379> config get maxmemory-policy<br>// 设置maxmemory-policy配置为allkeys-lru<br>127.0.0.1:6379> config set maxmemory-policy allkeys-lru<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">在介绍6种的淘汰策略方式的时候,说到了LRU算法,<strong style="color: rgb(53, 148, 247);">「那么什么是LRU算法呢?」</strong></p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 22px;border-bottom: 4px solid rgb(64, 184, 250);"><span style="display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/IJUXwBNpKlhiaoic9iaaqXKVAaaLPcXhEosxKKLxeHaSfHBlNgChJKLSbEicR0HM7NdE4QW4CegmYtsVaSazVaov9Q/640?wx_fmt=png");margin-bottom: -22px;"></span><span style="display: flex;color: #40B8FA;font-size: 20px;margin-left: 25px;">LRU算法</span><span style="display: flex;box-sizing: border-box;width: 200px;height: 10px;border-top-left-radius: 20px;background: RGBA(64, 184, 250, .5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;margin-top: -10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;"><code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">LRU(Least Recently Used)</code>即表示最近最少使用,也就是在最近的时间内最少被访问的key,算法根据数据的历史访问记录来进行淘汰数据。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">它的核心的思想就是:<strong style="color: rgb(53, 148, 247);">「假如一个key值在最近很少被使用到,那么在将来也很少会被访问」</strong>。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">实际上Redis实现的LRU并不是真正的LRU算法,也就是名义上我们使用LRU算法淘汰键,但是实际上被淘汰的键并不一定是真正的最久没用的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">Redis使用的是近似的LRU算法,<strong style="color: rgb(53, 148, 247);">「通过随机采集法淘汰key,每次都会随机选出5个key,然后淘汰里面最近最少使用的key」</strong>。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">这里的5个key只是默认的个数,具体的个数也可以在配置文件中进行配置,在配置文件中的配置如下图所示:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.08086785009861933" src="/upload/291950b8a7f52633d22baa6b16f0cae7.png" data-type="png" data-w="1014" style="border-radius: 6px;display: block;margin: 20px auto;object-fit: contain;box-shadow: rgb(153, 153, 153) 2px 4px 7px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">当近似LRU算法取值越大的时候就会越接近真实的LRU算法,可以这样理解,因为<strong style="color: rgb(53, 148, 247);">「取值越大那么获取的数据就越全,淘汰中的数据的就越接近最近最少使用的数据」</strong>。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">那么为了实现根据时间实现LRU算法,Redis必须为每个key中额外的增加一个内存空间用于存储每个key的时间,大小是3字节。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">在Redis 3.0中对近似的LRU算法做了一些优化,Redis中会维护大小是<code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">16</code>的一个候选池的内存。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">当第一次随机选取的采样数据,数据都会被放进候选池中,并且候选池中的数据会根据时间进行排序。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">当第二次以后选取的数据,只有<strong style="color: rgb(53, 148, 247);">「小于候选池内的最小时间」</strong>的才会被放进候选池中。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">当某一时刻候选池的数据满了,那么时间最大的key就会被挤出候选池。当执行淘汰时,直接从候选池中选取最近访问时间最小的key进行淘汰。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">这样做的目的就是选取出最近似符合最近最少被访问的key值,能够正确的淘汰key值,因为随机选取的样本中的最小时间可能不是真正意义上的最小时间。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">但是LRU算法有一个弊端:就是假如一个key值在以前都没有被访问到,然而最近一次被访问到了,那么就会认为它是热点数据,不会被淘汰。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">然而有些数据以前经常被访问到,只是最近的时间内没有被访问到,这样就导致这些数据很可能被淘汰掉,这样一来就会出现误判而淘汰热点数据。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">于是在Redis 4.0的时候除了LRU算法,新加了一种LFU算法,<strong style="color: rgb(53, 148, 247);">「那么什么是LFU算法算法呢?」</strong></p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 22px;border-bottom: 4px solid rgb(64, 184, 250);"><span style="display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/IJUXwBNpKlhiaoic9iaaqXKVAaaLPcXhEosxKKLxeHaSfHBlNgChJKLSbEicR0HM7NdE4QW4CegmYtsVaSazVaov9Q/640?wx_fmt=png");margin-bottom: -22px;"></span><span style="display: flex;color: #40B8FA;font-size: 20px;margin-left: 25px;">LFU算法</span><span style="display: flex;box-sizing: border-box;width: 200px;height: 10px;border-top-left-radius: 20px;background: RGBA(64, 184, 250, .5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;margin-top: -10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;"><code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">LFU(Least Frequently Used)</code>即表示最近频繁被使用,也就是最近的时间段内,频繁被访问的key,它以最近的时间段的被访问次数的频率作为一种判断标准。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">它的核心思想就是:根据key最近被访问的频率进行淘汰,比较少被访问的key优先淘汰,反之则优先保留。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">LFU算法反映了一个key的热度情况,不会因为LRU算法的偶尔一次被访问被认为是热点数据。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">在LFU算法中支持<code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">volatile-lfu</code>策略和<code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">allkeys-lfu</code>策略。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">以上介绍了Redis的6种淘汰策略,这6种淘汰策略旨在告诉我们怎么做,但是什么时候做?这个还没说,下面我们就来详细的了解Redis什么时候执行淘汰策略。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 22px;border-bottom: 4px solid rgb(64, 184, 250);"><span style="display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/IJUXwBNpKlhiaoic9iaaqXKVAaaLPcXhEosxKKLxeHaSfHBlNgChJKLSbEicR0HM7NdE4QW4CegmYtsVaSazVaov9Q/640?wx_fmt=png");margin-bottom: -22px;"></span><span style="display: flex;color: #40B8FA;font-size: 20px;margin-left: 25px;">删除过期键策略</span><span style="display: flex;box-sizing: border-box;width: 200px;height: 10px;border-top-left-radius: 20px;background: RGBA(64, 184, 250, .5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;margin-top: -10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">在Redis中有三种删除的操作此策略,分别是:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;font-size: 15px;color: rgb(89, 89, 89);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> <strong style="color: rgb(53, 148, 247);">「定时删除」</strong>:创建一个定时器,定时的执行对key的删除操作。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> <strong style="color: rgb(53, 148, 247);">「惰性删除」</strong>:每次只有再访问key的时候,才会检查key的过期时间,若是已经过期了就执行删除。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> <strong style="color: rgb(53, 148, 247);">「定期删除」</strong>:每隔一段时间,就会检查删除掉过期的key。 </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;"><strong style="color: rgb(53, 148, 247);">「定时删除」</strong>对于<strong style="color: rgb(53, 148, 247);">「内存来说是友好的」</strong>,定时清理出干净的空间,但是对于<strong style="color: rgb(53, 148, 247);">「cpu来说并不是友好的」</strong>,程序需要维护一个定时器,这就会占用cpu资源。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;"><strong style="color: rgb(53, 148, 247);">「惰性的删除」</strong>对于<strong style="color: rgb(53, 148, 247);">「cpu来说是友好的」</strong>,cpu不需要维护其它额外的操作,但是对于<strong style="color: rgb(53, 148, 247);">「内存来说是不友好的」</strong>,因为要是有些key一直没有被访问到,就会一直占用着内存。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">定期删除是上面两种方案的折中方案**,每隔一段时间删除过期的key,也就是根据具体的业务,合理的取一个时间定期的删除key**。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">通过<strong style="color: rgb(53, 148, 247);">「最合理控制删除的时间间隔」</strong>来删除key,减<strong style="color: rgb(53, 148, 247);">「少对cpu的资源的占用消耗」</strong>,使删除操作合理化。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 22px;border-bottom: 4px solid rgb(64, 184, 250);"><span style="display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/IJUXwBNpKlhiaoic9iaaqXKVAaaLPcXhEosxKKLxeHaSfHBlNgChJKLSbEicR0HM7NdE4QW4CegmYtsVaSazVaov9Q/640?wx_fmt=png");margin-bottom: -22px;"></span><span style="display: flex;color: #40B8FA;font-size: 20px;margin-left: 25px;">RDB和AOF 的淘汰处理</span><span style="display: flex;box-sizing: border-box;width: 200px;height: 10px;border-top-left-radius: 20px;background: RGBA(64, 184, 250, .5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;margin-top: -10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">在Redis中持久化的方式有两种<code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">RDB</code>和<code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">AOF</code>,具体这两种详细的持久化介绍,可以参考这一篇文章[<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU1MzE4OTU0OQ==&mid=2247484021&idx=1&sn=4c228c705cfd524db3cf27425fde1ba0&chksm=fbf7ebb7cc8062a138b9baad194548983095806f8f15a82e1a95f18f19999c76d750aa91b93b&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2">面试造飞机系列:面对Redis持久化连环Call,你还顶得住吗?</a>]。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">在RDB中是以快照的形式获取内存中某一时间点的数据副本,在创建RDB文件的时候可以通过<code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">save</code>和<code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">bgsave</code>命令执行创建RDB文件。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;"><strong style="color: rgb(53, 148, 247);">「这两个命令都不会把过期的key保存到RDB文件中」</strong>,这样也能达到删除过期key的效果。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">当在启动Redis载入RDB文件的时候,<code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">Master</code>不会把过期的key载入,而<code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">Slave</code>会把过期的key载入。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 10px;margin-bottom: 10px;font-size: 14px;word-spacing: 2px;">在AOF模式下,Redis提供了Rewite的优化措施,执行的命令分别是<code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">REWRITEAOF</code>和<code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);display: inline-block;padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">BGREWRITEAOF</code>,<strong style="color: rgb(53, 148, 247);">「这两个命令都不会把过期的key写入到AOF文件中,也能删除过期key」</strong>。</p> <pre ng-bind-html="message.MMActualContent" style="max-width: 100%;text-align: left;color: rgb(62, 62, 62);letter-spacing: 0.544px;background-color: rgb(255, 255, 255);font-size: 15px;word-spacing: 2px;white-space: pre-wrap;word-break: normal;box-sizing: border-box !important;word-wrap: break-word !important;"><p style="max-width: 100%;min-height: 1em;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;color: rgb(255, 76, 0);box-sizing: border-box !important;word-wrap: break-word !important;">特别推荐一个分享架构+算法的优质内容,还没关注的小伙伴,可以长按关注一下:</span></p><p style="max-width: 100%;min-height: 1em;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;text-align: center;box-sizing: border-box !important;word-wrap: break-word !important;"><img class="rich_pages" data-ratio="0.5555555555555556" data-s="300,640" data-type="jpeg" data-w="900" src="/upload/c86a3619353fb9ac25fa19df4ec53cb1.jpg" style="box-sizing: border-box !important;word-wrap: break-word !important;visibility: visible !important;width: 677px !important;"></p><pre ng-bind-html="message.MMActualContent" style="max-width: 100%;letter-spacing: 0.544px;font-size: 16px;white-space: pre-wrap;word-break: normal;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;"><p style="max-width: 100%;min-height: 1em;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;text-align: center;box-sizing: border-box !important;word-wrap: break-word !important;">长按订阅更多精彩▼</p><p style="max-width: 100%;min-height: 1em;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;text-align: center;box-sizing: border-box !important;word-wrap: break-word !important;"><img class="rich_pages" data-ratio="1" data-s="300,640" data-type="jpeg" data-w="258" width="205px" src="/upload/cff6d4b6b063076da067005708088611.jpg" style="box-shadow: rgb(170, 170, 170) 0px 0px 14px 0px;box-sizing: border-box !important;word-wrap: break-word !important;visibility: visible !important;width: 205px !important;"></p><p style="max-width: 100%;min-height: 1em;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;text-align: right;line-height: 2em;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;word-wrap: break-word !important;">如有收获,点个在看,诚挚感谢</span></p></pre></pre> </section>
作者:微信小助手
<p style="text-align: center;"><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;text-align: center;max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">(给</span><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;text-align: center;max-width: 100%;line-height: 22.4px;color: rgb(0, 128, 255);">ImportNew</span><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;text-align: center;max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">加星标,提高Java技能)</span></p> <blockquote> <p style="letter-spacing: 0.5440000295639038px;white-space: normal;background-color: rgb(255, 255, 255);max-width: 100%;min-height: 1em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">转自:掘金 作者:Vt</span></p> <p style="letter-spacing: 0.5440000295639038px;white-space: normal;background-color: rgb(255, 255, 255);max-width: 100%;min-height: 1em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="font-size: 14px;">https://juejin.im/post/5eb0e724e51d454d89440da2</span></p> </blockquote> <h1 data-tool="mdnice编辑器" data-id="heading-0" style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;letter-spacing: normal;">前言</span></strong></span></h1> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">Redis大家都不陌生,就算是没用过,也都听说过了。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">作为最广泛使用的KV内存数据库之一,在当今的大流量时代,单机模式略显单薄,免不了要有一些拓展的方案。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">笔者下文会对各种方案进行介绍,并且给出场景,实现 等等概述,还会提到一些新手常见的误区。</span> </section> <p data-tool="mdnice编辑器"><img data-height="324" data-ratio="0.5547945205479452" src="/upload/e4cf6d6adb0f188c35e1d474f09a7201.other" data-type="other" data-w="584" data-width="584" height="100px" 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;"></p> <h1 data-tool="mdnice编辑器" data-id="heading-1" style="line-height: 1.5em;"><br></h1> <h1 data-tool="mdnice编辑器" data-id="heading-1" style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;letter-spacing: normal;">正文</span></strong></span></h1> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">先从基础的拓展方式开始,这样更便于理解较高级的模式。</span> </section> <section style="line-height: 1.5em;"> <br> </section> <h2 data-tool="mdnice编辑器" data-id="heading-2" style="line-height: 1.5em;"><strong><span style="font-size: 15px;letter-spacing: normal;color: rgb(171, 25, 66);">分区</span></strong></h2> <p><strong><span style="font-size: 15px;letter-spacing: normal;color: rgb(171, 25, 66);"><br></span></strong></p> <h3 data-tool="mdnice编辑器" data-id="heading-3" style="line-height: 1.5em;"><span style="color: rgb(0, 0, 0);"><strong><span style="color: rgb(0, 0, 0);font-size: 15px;letter-spacing: normal;">概述</span></strong></span></h3> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">分区(Partitioning)是一种最为简单的拓展方式。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">在我们面临单机的存储空间瓶颈时,第一点就能想到像传统的关系型数据库一样,进行数据分区。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">或者假设手中有N台机器可以作为Redis服务器</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br>所有机器内存总和有256G, 而客户端正好也需要一个大内存的存储空间。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">我们除了可以把内存条都拆下来焊到一个机器上,也可以选择分区使用,这样又拓展了计算能力。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">单指分区来讲,即将全部数据分散在多个Redis实例中,每个实例不需要关联,可以是完全独立的。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <p><img data-height="813" data-ratio="0.6359375" src="/upload/3e2d9f21518cc17fe5cfc70daaeaa698.other" data-type="other" data-w="1280" data-width="1280" 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;"></p> <h3 data-tool="mdnice编辑器" data-id="heading-4" style="line-height: 1.5em;"><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></h3> <h3 data-tool="mdnice编辑器" data-id="heading-4" style="line-height: 1.5em;"><strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">使用方式</span></strong></h3> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-2"> <li> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">客户端处理</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br>和传统的数据库分库分表一样,可以从key入手,先进行计算,找到对应数据存储的实例在进行操作。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br>范围角度,比如orderId:1~orderId:1000放入实例1,orderId:1001~orderId:2000放入实例2...</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br>哈希计算,就像我们的hashmap一样,用hash函数加上位运算或者取模,高级玩法还有</span> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">一致性Hash</span> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">等操作,找到对应的实例进行操作</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"></span> </section></li> <li> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">使用代理中间件</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br>我们可以开发独立的代理中间件,屏蔽掉处理数据分片的逻辑,独立运行。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br>当然也有他人已经造好的轮子,Redis也有优秀的代理中间件,譬如</span> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">Twemproxy</span> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">,或者</span> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">codis</span> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">,可以结合场景选择是否使用。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"></span> </section></li> </ul> <h3 data-tool="mdnice编辑器" data-id="heading-5" style="line-height: 1.5em;"><br></h3> <h3 data-tool="mdnice编辑器" data-id="heading-5" style="line-height: 1.5em;"><strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">缺点</span></strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"></span></h3> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-2"> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">无缘多key操作,key都不一定在一个实例上,那么多key操作或者多key事务自然是不支持。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">维护成本,由于每个实例在物理和逻辑上,都属于单独的一个节点,缺乏统一管理。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"></span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">灵活性有限,范围分片还好,比如hash+MOD这种方式,如果想动态调整Redis实例的数量,就要考虑大量数据迁移,这就非常麻烦了。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"></span> </section></li> </ul> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">同为开发者,深知我们虽然总能“曲线救国”的完成一些当前环境不支持的功能,但是总归要麻烦一些。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <h2 data-tool="mdnice编辑器" data-id="heading-6" style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;letter-spacing: normal;">主从</span></strong></span></h2> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <h3 data-tool="mdnice编辑器" data-id="heading-7" style="line-height: 1.5em;"><strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">概述数据迁移</span></strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"></span></h3> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">常说的主从(Master-Slave),也就是复制(Replication)方式,怎么称呼都可以。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">同上面的分区一样,也是Redis高可用架构的基础,新手可能会误以为这类基础模式即是“高可用”,这并不是十分正确的。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">分区暂时能解决单点无法容纳的数据量问题,但是一个Key还是只在一个实例上,在大流量时代显得不那么可靠。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">主从就是另一个纬度的拓展,节点将数据同步到从节点,就像将实例“分身”了一样,可靠性又提高了不少。</span> </section> <figure data-tool="mdnice编辑器"> <img data-height="779" data-ratio="0.60859375" src="/upload/30e43f796c9db798d41b0769aef5615d.other" data-type="other" data-w="1280" data-width="1280" 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> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">图画的有些夸张了,主要还是想体现结构灵活,是一主一从,还是一主多从,还是一主多从多从... 看你心情</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">有了“实例分身”,自然就可以做读写分离,将读流量均摊在各个从节点。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <h3 data-tool="mdnice编辑器" data-id="heading-8" style="line-height: 1.5em;"><strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">使用方式</span></strong></h3> <figure data-tool="mdnice编辑器"> <img data-height="280" data-ratio="1.238938053097345" src="/upload/33b5d168ad62925a3676fc679a9b680.other" data-type="other" data-w="226" data-width="226" 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> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">高手云集的时代,聊天软件难免要备上这么一张表情包。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">这表情包和使用方式有什么关系呢?首先看看使用方式:</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <ol data-tool="mdnice编辑器" class="list-paddingleft-2"> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">作为主节点的Redis实例,并不要求配置任何参数,只需要正常启动</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">作为从节点的实例,使用配置文件或命令方式REPLICAOF 主节点Host 主节点port即可完成主从配置</span> </section></li> </ol> <section style="line-height: 1.5em;"> <br> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">是不是和表情包一样,“dalao”没动,我去“抱大腿”。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">这样一个主从最小配置就完成了,主从实例即可对外提供服务。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">命令里的“主节点”是相对的,slave也可以抱slave大腿,也就是上文提到的结构灵活。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <h3 data-tool="mdnice编辑器" data-id="heading-9" style="line-height: 1.5em;"><strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">缺点</span></strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"></span></h3> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-2"> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">slave节点都是只读的,如果写流量大的场景,就有些力不从心了。<br>那我把slave节点只读关掉不就行了?当然不行,数据复制是由主到从,从节点独有数据同步不到主节点,数据就不一致了。</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">故障转移不友好,主节点挂掉后,写处理就无处安放,需要手工的设定新的主节点,如使用REPLICAOF no one(谁大腿我都不抱了) 晋升为主节点,再梳理其他slave节点的新主配置,相对来说比较麻烦。</span> </section></li> </ul> <h2 data-tool="mdnice编辑器" data-id="heading-10" style="line-height: 1.5em;"><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></h2> <h2 data-tool="mdnice编辑器" data-id="heading-10" style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;letter-spacing: normal;">哨兵</span></strong></span></h2> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <h3 data-tool="mdnice编辑器" data-id="heading-11" style="line-height: 1.5em;"><strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">概述</span></strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"></span></h3> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">主从的手工故障转移,肯定让人很难接受,自然就出现了高可用方案-哨兵(Sentinel)。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">我们可以在主从架构不变的场景,直接加入Redis Sentinel,对节点进行监控,来完成自动的故障发现与转移。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">并且还能够充当配置提供者,提供主节点的信息,就算发生了故障转移,也能提供正确的地址。</span> </section> <figure data-tool="mdnice编辑器"> <img data-height="120" data-ratio="0.5454545454545454" src="/upload/5528ea96a88250f701bc7b6c45ee2d61.gif" data-type="gif" data-w="220" data-width="220" 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><br></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">哨兵本身也是Redis实例的一种,但不作为数据存储方使用,启动命令也是不一样的。</span> </section> <p><br></p> <figure data-tool="mdnice编辑器"> <img data-height="1280" data-ratio="1.046875" src="/upload/4b822da4801f7e05784465b6a32a6dc9.other" data-type="other" data-w="960" data-width="1222" 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><br></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">虽然图有些复杂,看起来像要召唤光能使者。</span> </section> <p><img data-height="247" data-ratio="0.7417417417417418" src="/upload/37ecdd895850277756449eb5493bbfa9.other" data-type="other" data-w="333" data-width="333" 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;"></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">其实实际使用起来是很便捷的。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <h3 data-tool="mdnice编辑器" data-id="heading-12" style="line-height: 1.5em;"><strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">使用方式</span></strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"></span></h3> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">Sentinel的最小配置,一行即可:</span> </section> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="xml"><code><span class="code-snippet_outer">sentinel monitor <span class="code-snippet__tag"><<span class="code-snippet__name">主节点别名</span>></span> <span class="code-snippet__tag"><<span class="code-snippet__name">主节点host</span>></span> <span class="code-snippet__tag"><<span class="code-snippet__name">主节点端口</span>></span> <span class="code-snippet__tag"><<span class="code-snippet__name">票数</span>></span></span></code></pre> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">只需要配置master即可,然后用redis-sentinel <配置文件> 命令即可启用。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">Redis官网</span> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">提到的“最小配置”是如下所示,除了上面提到的一行,还有其它的一些配置:</span> </section> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="properties"><code><span class="code-snippet_outer"><span class="code-snippet__attr">sentinel</span> <span class="code-snippet__string">monitor mymaster 127.0.0.1 6379 2</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">sentinel</span> <span class="code-snippet__string">down-after-milliseconds mymaster 60000</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">sentinel</span> <span class="code-snippet__string">failover-timeout mymaster 180000</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">sentinel</span> <span class="code-snippet__string">parallel-syncs mymaster 1</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">sentinel</span> <span class="code-snippet__string">monitor resque 192.168.1.3 6380 4</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">sentinel</span> <span class="code-snippet__string">down-after-milliseconds resque 10000</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">sentinel</span> <span class="code-snippet__string">failover-timeout resque 180000</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">sentinel</span> <span class="code-snippet__string">parallel-syncs resque 5</span></span></code></pre> </section> <p style="line-height: 1.5em;"><br></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">这是因为官网加了一个修饰词,是“典型的最小配置”,把重要参数和多主的例子都写出来了,照顾大家CV大法的时候,不要忘记重要参数,其实都是有默认值的。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">正如该例所示,设置主节点别名就是为了监控多主的时候,与其额外配置项能够与其对应, 以及sentinel一些命令,如SENTINEL get-master-addr-by-name就要用到别名了。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">哨兵数量建议在三个以上且为奇数,在</span> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">Redis官网</span> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">也提到了各种情况的“布阵”方式,非常值得参考。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <h3 data-tool="mdnice编辑器" data-id="heading-13" style="line-height: 1.5em;"><strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">更多</span></strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"></span></h3> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">既然是高可用方案,并非有严格意义上的“缺点”,还需配合使用场景进行考量。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <ul data-tool="mdnice编辑器" class="list-paddingleft-2"> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">故障转移期间短暂的不可用,但其实官网的例子也给出了parallel-syncs参数来指定并行的同步实例数量,以免全部实例都在同步出现整体不可用的情况,相对来说要比手工的故障转移更加方便。</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">分区逻辑需要自定义处理,虽然解决了主从下的高可用问题,但是Sentinel并没有提供分区解决方案,还需开发者考虑如何建设。</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">既然是还是主从,如果异常的写流量搞垮了主节点,那么自动的“故障转移”会不会变成自动“灾难传递”,即slave提升为Master之后挂掉,又进行提升又被挂掉。<br>不过最后这点也是笔者猜测,并没有听说过出现这种案例,可不必深究。</span> </section></li> </ul> <h2 data-tool="mdnice编辑器" data-id="heading-14" style="line-height: 1.5em;"><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></h2> <h2 data-tool="mdnice编辑器" data-id="heading-14" style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;letter-spacing: normal;">集群</span></strong></span></h2> <p><strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></strong></p> <h3 data-tool="mdnice编辑器" data-id="heading-15" style="line-height: 1.5em;"><strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">概述</span></strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"></span></h3> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">Redis Cluster是官方在3.0版本后推出的分布式方案。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">对开发者而言,“官方支持”一词是大概率非常美好的,小到issue,大到feature。自定义去解决问题,成本总是要高一些。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">有了官方的正式集群方案,从请求路由、故障转移、弹性伸缩几个纬度的使用上,将更为容易。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">Cluster不同于哨兵,是支持分区的。有说法Cluster是哨兵的升级,这是不严谨的。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">二者纬度不一样,如果因为Cluster也有故障转移的功能,就说它是哨兵的升级款,略显牵强。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">Cluster在分区管理上,使用了“哈希槽”(hash slot)这么一个概念,一共有16384个槽位,每个实例负责一部分槽,通过CRC16(key)&16383这样的公式,计算出来key所对应的槽位。</span> </section> <figure data-tool="mdnice编辑器"> <img data-height="350" data-ratio="0.6903353057199211" src="/upload/b6296bb8e2f26718c04f94f427a37208.gif" data-type="gif" data-w="507" data-width="507" 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><br></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">虽然在节点和key二者中又引入了槽的概念,看起来不易理解,实际上因为颗粒度更细了,减少了节点的扩容和收缩难度,相比传统策略还是很有优势。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">当然,“槽”是虚拟的概念,节点自身去维护“槽”的关系,并不是要真正下载启动个“槽服务”在跑。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <h3 data-tool="mdnice编辑器" data-id="heading-16" style="line-height: 1.5em;"><strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">使用方式</span></strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"></span></h3> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">Redis的各种玩法,都是从配置文件着手,集群也不例外。</span> </section> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="javascript"><code><span class="code-snippet_outer">cluster-enabled yes</span></code><code><span class="code-snippet_outer">cluster-config-file <span class="code-snippet__string">"redis-node.conf"</span></span></code></pre> </section> <section style="line-height: 1.5em;"> <br> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">关键配置简洁明了,有两步</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <ul data-tool="mdnice编辑器" class="list-paddingleft-2"> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">开启集群</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">指定集群配置文件</span> </section></li> </ul> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">集群配置文件(cluster-config-file)为内部使用,可以不去指定,Redis会帮助创建一个。启动还是普通的方式redis-server redis.conf</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">首先以集群方式启动了N台Redis实例,这当然还没完事。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">接下来的步骤笔者称为“牵线搭桥分配槽”,听起来还算顺口。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">“牵线搭桥分配槽”的方式也在不断升级,从直接用原始命令来处理,到使用脚本,以及现在的Redis-cli官方支持,使用哪种方式都可以。</span> </section> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="css"><code><span class="code-snippet_outer"><span class="code-snippet__selector-tag">redis-cli</span> <span class="code-snippet__selector-tag">--cluster</span> <span class="code-snippet__selector-tag">create</span> 127<span class="code-snippet__selector-class">.0</span><span class="code-snippet__selector-class">.0</span><span class="code-snippet__selector-class">.1</span><span class="code-snippet__selector-pseudo">:7000</span> 127<span class="code-snippet__selector-class">.0</span><span class="code-snippet__selector-class">.0</span><span class="code-snippet__selector-class">.1</span><span class="code-snippet__selector-pseudo">:7001</span> \</span></code><code><span class="code-snippet_outer">127<span class="code-snippet__selector-class">.0</span><span class="code-snippet__selector-class">.0</span><span class="code-snippet__selector-class">.1</span><span class="code-snippet__selector-pseudo">:7002</span> 127<span class="code-snippet__selector-class">.0</span><span class="code-snippet__selector-class">.0</span><span class="code-snippet__selector-class">.1</span><span class="code-snippet__selector-pseudo">:7003</span> 127<span class="code-snippet__selector-class">.0</span><span class="code-snippet__selector-class">.0</span><span class="code-snippet__selector-class">.1</span><span class="code-snippet__selector-pseudo">:7004</span> 127<span class="code-snippet__selector-class">.0</span><span class="code-snippet__selector-class">.0</span><span class="code-snippet__selector-class">.1</span><span class="code-snippet__selector-pseudo">:7005</span> \</span></code><code><span class="code-snippet_outer"><span class="code-snippet__selector-tag">--cluster-replicas</span> 1</span></code></pre> </section> <section style="line-height: 1.5em;"> <br> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">上方的命令即是Redis官网给出的redis-cli的方式用法,一行命令完成“三主三从”以及自动分配槽的操作。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">这样集群就搭建完成了,当然,使用官方提供的check命令检查一下,也是有必要的。</span> </section> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="css"><code><span class="code-snippet_outer"><span class="code-snippet__selector-tag">redis-cli</span> <span class="code-snippet__selector-tag">--cluster</span> <span class="code-snippet__selector-tag">check</span> 127<span class="code-snippet__selector-class">.0</span><span class="code-snippet__selector-class">.0</span><span class="code-snippet__selector-class">.1</span><span class="code-snippet__selector-pseudo">:7001</span></span></code></pre> </section> <p><br></p> <h3 data-tool="mdnice编辑器" data-id="heading-17" style="line-height: 1.5em;"><strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">更多</span></strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"></span></h3> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-2"> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><p style="line-height: 1.5em;"><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">虽然是对分区良好支持,但也有一些分区的老问题,譬如:如果不在同一个“槽”的数据,是没法使用类似mset的多键操作。</span></p><p><br></p></li> <li><p style="line-height: 1.5em;"><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">在</span><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">select命令页</span><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">有提到, 集群模式下只能使用一个库,虽然平时一般也是这么用的,但是要了解一下。</span></p><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><p style="line-height: 1.5em;"><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">运维上也要谨慎,俗话说得好,“使用越简单底层越复杂”,启动搭建是很方便,使用时面对带宽消耗,数据倾斜等等具体问题时,还需人工介入,或者研究合适的配置参数。</span></p></li> </ul> <h1 data-tool="mdnice编辑器" data-id="heading-18" style="line-height: 1.5em;"><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></h1> <h1 data-tool="mdnice编辑器" data-id="heading-18" style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;letter-spacing: normal;">结尾</span></strong></span></h1> <p><strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></strong></p> <h2 data-tool="mdnice编辑器" data-id="heading-19" style="line-height: 1.5em;"><strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">趣谈</span></strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"></span></h2> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">在写“主从”方案的时候,发现有一个有趣的事情:</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">笔者开始是记得主从的关键命令是SLAVEOF,后来查阅官方的时候,发现命令已经更改为REPLICAOF,虽然SLAVEOF还能用。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">官网的一些描述词汇,有的地方还是Slave,也有些是用Replication。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">好奇的笔者查了一下相关的资料,并看了些Redis作者antirez的有关此时博客,发现已经是两年前的事情了。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">其实就是“Slave”这个变量名给了一些人机会,借此“喷”了一波作者,作者也做出了一部分妥协。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">有兴趣的盆友可以自己搜搜看,技术外的东西就不做评价了,看个乐呵就行。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">笔者的主要目的还是:看官方文档的时候,别让不同的“词汇”迷惑了。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <h2 data-tool="mdnice编辑器" data-id="heading-20" style="line-height: 1.5em;"><strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">END</span></strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"></span></h2> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">本文对Redis这些拓展方案都作出了大致描述。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">具体使用上,还需留意详细配置,以及客户端支持等综合情况来考量。</span> </section> <section style="line-height: 1.5em;"> <br> </section> <section donone="shifuMouseDownCard('shifu_c_030')" label="Copyright Reserved by PLAYHUDONG." style="text-align: start;white-space: normal;margin-top: 1em;margin-bottom: 1em;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);border-width: 0px;border-style: initial;border-color: initial;"> <section style="margin-left: 1em;line-height: 1.4;"> <span style="padding: 3px 8px;border-top-left-radius: 4px;border-top-right-radius: 4px;border-bottom-right-radius: 4px;border-bottom-left-radius: 4px;color: rgb(255, 255, 255);background-color: rgb(255, 105, 31);font-family: inherit;text-align: inherit;text-decoration: inherit;font-size: 16px;">推荐阅读</span> <span style="margin-left: 4px;padding: 3px 8px;border-top-left-radius: 1.2em;border-top-right-radius: 1.2em;border-bottom-right-radius: 1.2em;border-bottom-left-radius: 1.2em;color: rgb(255, 255, 255);line-height: 1.2;background-color: rgb(204, 204, 204);font-family: inherit;text-align: inherit;text-decoration: inherit;border-color: rgb(249, 110, 87);font-size: 12px;">点击标题可跳转</span> </section> <section style="margin-top: -11px;padding: 22px 16px 16px;border-width: 1px;border-style: solid;border-color: rgb(255, 105, 31);color: rgb(51, 51, 51);font-size: 1em;font-family: inherit;text-align: inherit;text-decoration: inherit;"> <p><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&mid=2651486557&idx=1&sn=28f9682c8bbaf4696bf122403bcc4168&chksm=bd2515228a529c347cc04cd540695803031039e996f60c82a52224d2b3f4902bfe0374c6c117&scene=21#wechat_redirect" data-itemshowtype="11" tab="innerlink" style="font-size: 12px;text-decoration: underline;" data-linktype="2"><span style="font-size: 12px;">为什么 Redis 选择单线程模型</span></a><br></p> <p><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&mid=2651486720&idx=1&sn=878b78b200d07271940a4af638f094f8&chksm=bd25167f8a529f698ed47607203af5c4189e027d8add66c6f79d9e3b861b3d84a90dc909583b&scene=21#wechat_redirect" data-itemshowtype="11" tab="innerlink" style="font-size: 12px;text-decoration: underline;" data-linktype="2"><span style="font-size: 12px;">当 Redis 发生高延迟时,到底发生了什么</span></a><br></p> <p><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&mid=2651486646&idx=2&sn=8927618dfb883a5fa133bb4fc815720c&chksm=bd2515c98a529cdf64f20be0b677e2027a0266ba6141cef2091e0aeecc4845b00d6fd506a648&scene=21#wechat_redirect" data-itemshowtype="11" tab="innerlink" style="font-size: 12px;text-decoration: underline;" data-linktype="2"><span style="font-size: 12px;">Mysql百万量级数据高效导入Redis</span></a><br></p> <p><span style="font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 12px;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);"></span></p> </section> </section> <p style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);text-align: start;white-space: normal;"><br></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;color: rgb(255, 169, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;">看完本文有收获?请转发分享给更多人</span></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><strong style="max-width: 100%;color: rgb(255, 169, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;">关注「ImportNew」,提升Java技能</strong></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img data-ratio="0.9166666666666666" data-s="300,640" data-type="jpeg" data-w="600" width="auto" src="/upload/899866149276fa5fddb73c61ae04be64.jpg" style="box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 600px !important;"></p> <p style="text-align: right;"><span style="font-size: 14px;text-align: right;">好文章,我</span><span style="font-size: 14px;text-align: right;color: rgb(255, 41, 65);">在看</span><span style="font-size: 14px;text-align: right;">❤️</span></p>
作者:じ☆ve不哭
> 随着这些年短视频行业的发展,人们的生活越来越离不开抖音,快手之类的短视频平台。看到好的视频想要保存,无奈有些版本会禁止下载,有的虽然可以下载但是会有水印。本教程使用java下载无水印的抖音,快手小视频。 首先感谢大家对本小程序的支持。中间断更很长一段时间(工作太忙,实在抱歉)。最近有些时间就讲程序更新了一下。现已支持:抖音单个视频下载,抖音热门推荐(该功能需要更新小程序,已经提交待审核),快手的无水印下载。后续会继续补充完善。本着一直免费,且不限制下载数量,大家低调使用哈。 #### 第一步:搜索《Java学习者》微信小程序 {width=240 height=480} #### 第二步:进入首页后根据左上角提示,点击logo {width=240 height=480} #### 第三步:选择“短视频下载” {width=240 height=480} #### 四步:输入下载的 抖音或者快手地址 {width=240 height=480} #### 第五步:点击 “下载无水印视频” {width=240 height=480} #### 最后奉上本站小程序《Java学习者》 {width=240 height=240}
作者:微信小助手
<p style="text-align: center;"><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;text-align: center;max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">(给</span><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;text-align: center;max-width: 100%;line-height: 22.4px;color: rgb(0, 128, 255);">ImportNew</span><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;text-align: center;max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">加星标,提高Java技能)</span></p> <blockquote> <p style="letter-spacing: 0.5440000295639038px;white-space: normal;background-color: rgb(255, 255, 255);max-width: 100%;min-height: 1em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">作者:向海</span></p> <p style="letter-spacing: 0.5440000295639038px;white-space: normal;background-color: rgb(255, 255, 255);max-width: 100%;min-height: 1em;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="font-size: 14px;">www.cnblogs.com/haixiang/p/10905189.html</span></p> </blockquote> <h2 style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;letter-spacing: normal;">消费端限流</span></strong></span></h2> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <h3 style="line-height: 1.5em;"><span style="color: rgb(0, 0, 0);"><strong><span style="color: rgb(0, 0, 0);font-size: 15px;letter-spacing: normal;">1. 为什么要对消费端限流</span></strong></span></h3> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">假设一个场景,首先,我们 Rabbitmq 服务器积压了有上万条未处理的消息,我们随便打开一个消费者客户端,会出现这样情况: 巨量的消息瞬间全部推送过来,但是我们单个客户端无法同时处理这么多数据!</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">当数据量特别大的时候,我们对生产端限流肯定是不科学的,因为有时候并发量就是特别大,有时候并发量又特别少,我们无法约束生产端,这是用户的行为。所以我们应该对消费端限流,用于保持消费端的稳定,当消息数量激增的时候很有可能造成资源耗尽,以及影响服务的性能,导致系统的卡顿甚至直接崩溃。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <h3 style="line-height: 1.5em;"><span style="color: rgb(0, 0, 0);"><strong><span style="color: rgb(0, 0, 0);font-size: 15px;letter-spacing: normal;">2.限流的 api 讲解</span></strong></span></h3> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">RabbitMQ 提供了一种 qos (服务质量保证)功能,即在非自动确认消息的前提下,如果一定数目的消息(通过基于 consume 或者 channel 设置 Qos 的值)未被确认前,不进行消费新的消息。</span> </section> <pre style="overflow-x: auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;display: block;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;min-width: 400px;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);font-weight: 400;" data-wx-hl-code="/**&lt;br/&gt;* Request specific "quality of service" settings.&lt;br/&gt;* These settings impose limits on the amount of data the server&lt;br/&gt;* will deliver to consumers before requiring acknowledgements.&lt;br/&gt;* Thus they provide a means of consumer-initiated flow control.&lt;br/&gt;* @param prefetchSize maximum amount of content (measured in&lt;br/&gt;* octets) that the server will deliver, 0 if unlimited&lt;br/&gt;* @param prefetchCount maximum number of messages that the server&lt;br/&gt;* will deliver, 0 if unlimited&lt;br/&gt;* @param global true if the settings should be applied to the&lt;br/&gt;* entire channel rather than each consumer&lt;br/&gt;* @throws java.io.IOException if an error is encountered&lt;br/&gt;*/&lt;br/&gt;void basicQos(int prefetchSize, int prefetchCount, boolean global) throws IOException;" data-wx-hl-lang="Java" data-wx-hl-style="atom-one-dark"><span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">/**<br>* Request specific "quality of service" settings.<br>* These settings impose limits on the amount of data the server<br>* will deliver to consumers before requiring acknowledgements.<br>* Thus they provide a means of consumer-initiated flow control.<br>* <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: italic;">@param</span> prefetchSize maximum amount of content (measured in<br>* octets) that the server will deliver, 0 if unlimited<br>* <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: italic;">@param</span> prefetchCount maximum number of messages that the server<br>* will deliver, 0 if unlimited<br>* <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: italic;">@param</span> global true if the settings should be applied to the<br>* entire channel rather than each consumer<br>* <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: italic;">@throws</span> java.io.IOException if an error is encountered<br>*/</span><br><span style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">void</span> <span style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">basicQos</span><span style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;">(<span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">int</span> prefetchSize, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">int</span> prefetchCount, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">boolean</span> global)</span> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">throws</span> IOException</span>;</code></pre> <p><br></p> <ul style="margin-left: 30px;word-break: break-all;color: rgb(49, 70, 89);font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif;font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">prefetchSize:0,单条消息大小限制,0代表不限制</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">prefetchCount:一次性消费的消息数量。会告诉 RabbitMQ 不要同时给一个消费者推送多于 N 个消息,即一旦有 N 个消息还没有 ack,则该 consumer 将 block 掉,直到有消息 ack。</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">global:true、false 是否将上面设置应用于 channel,简单点说,就是上面限制是 channel 级别的还是 consumer 级别。当我们设置为 false 的时候生效,设置为 true 的时候没有了限流功能,因为 channel 级别尚未实现。</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">注意:prefetchSize 和 global 这两项,rabbitmq 没有实现,暂且不研究。特别注意一点,prefetchCount 在 no_ask=false 的情况下才生效,即在自动应答的情况下这两个值是不生效的。</span> </section></li> </ul> <h3 style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;letter-spacing: normal;"><br></span></strong></span></h3> <h3 style="line-height: 1.5em;"><span style="color: rgb(0, 0, 0);"><strong><span style="color: rgb(0, 0, 0);font-size: 15px;letter-spacing: normal;">3.如何对消费端进行限流</span></strong></span></h3> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <ul class="list-paddingleft-2"> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">首先第一步,我们既然要使用消费端限流,我们需要关闭自动 ack,将 autoAck 设置为 falsechannel.basicConsume(queueName, false, consumer);</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">第二步我们来设置具体的限流大小以及数量。channel.basicQos(0, 15, false);</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">第三步在消费者的 handleDelivery 消费方法中手动 ack,并且设置批量处理 ack 回应为 truechannel.basicAck(envelope.getDeliveryTag(), true);</span> </section></li> </ul> <section style="line-height: 1.5em;"> <br> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">这是生产端代码,与前几章的生产端代码没有做任何改变,主要的操作集中在消费端。</span> </section> <pre style="overflow-x: auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;display: block;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;min-width: 400px;font-weight: 400;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);" data-wx-hl-code="import com.rabbitmq.client.Channel;&lt;br/&gt;import com.rabbitmq.client.Connection;&lt;br/&gt;import com.rabbitmq.client.ConnectionFactory;&lt;br/&gt;&lt;br/&gt;public class QosProducer {&lt;br/&gt; public static void main(String[] args) throws Exception {&lt;br/&gt; //1. 创建一个 ConnectionFactory 并进行设置&lt;br/&gt; ConnectionFactory factory = new ConnectionFactory();&lt;br/&gt; factory.setHost("localhost");&lt;br/&gt; factory.setVirtualHost("/");&lt;br/&gt; factory.setUsername("guest");&lt;br/&gt; factory.setPassword("guest");&lt;br/&gt;&lt;br/&gt; //2. 通过连接工厂来创建连接&lt;br/&gt; Connection connection = factory.newConnection();&lt;br/&gt;&lt;br/&gt; //3. 通过 Connection 来创建 Channel&lt;br/&gt; Channel channel = connection.createChannel();&lt;br/&gt;&lt;br/&gt; //4. 声明&lt;br/&gt; String exchangeName = "test_qos_exchange";&lt;br/&gt; String routingKey = "item.add";&lt;br/&gt;&lt;br/&gt; //5. 发送&lt;br/&gt; String msg = "this is qos msg";&lt;br/&gt; for (int i = 0; i < 10; i++) {&lt;br/&gt; String tem = msg + " : " + i;&lt;br/&gt; channel.basicPublish(exchangeName, routingKey, null, tem.getBytes());&lt;br/&gt; System.out.println("Send message : " + tem);&lt;br/&gt; }&lt;br/&gt;&lt;br/&gt; //6. 关闭连接&lt;br/&gt; channel.close();&lt;br/&gt; connection.close();&lt;br/&gt; }&lt;br/&gt;}" data-wx-hl-lang="Java" data-wx-hl-style="atom-one-dark"><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">import</span> com.rabbitmq.client.Channel;<br><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">import</span> com.rabbitmq.client.Connection;<br><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">import</span> com.rabbitmq.client.ConnectionFactory;<br><br><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> <span style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">class</span> <span style="color: rgb(230, 192, 123);font-weight: 400;font-style: normal;">QosProducer</span> </span>{<br> <span style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">static</span> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">void</span> <span style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">main</span><span style="font-weight: 400;font-style: normal;">(String[] args)</span> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">throws</span> Exception </span>{<br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//1. 创建一个 ConnectionFactory 并进行设置</span><br> ConnectionFactory factory = <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">new</span> ConnectionFactory();<br> factory.setHost(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"localhost"</span>);<br> factory.setVirtualHost(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"/"</span>);<br> factory.setUsername(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"guest"</span>);<br> factory.setPassword(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"guest"</span>);<br><br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//2. 通过连接工厂来创建连接</span><br> Connection connection = factory.newConnection();<br><br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//3. 通过 Connection 来创建 Channel</span><br> Channel channel = connection.createChannel();<br><br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//4. 声明</span><br> String exchangeName = <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"test_qos_exchange"</span>;<br> String routingKey = <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"item.add"</span>;<br><br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//5. 发送</span><br> String msg = <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"this is qos msg"</span>;<br> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">for</span> (<span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">int</span> i = <span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">0</span>; i < <span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">10</span>; i++) {<br> String tem = msg + <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">" : "</span> + i;<br> channel.basicPublish(exchangeName, routingKey, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">null</span>, tem.getBytes());<br> System.out.println(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"Send message : "</span> + tem);<br> }<br><br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//6. 关闭连接</span><br> channel.close();<br> connection.close();<br> }<br>}</code></pre> <p><br></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">这里我们创建一个消费者,通过以下代码来验证限流效果以及 global 参数设置为 true 时不起作用.。我们通过Thread.sleep(5000); 来让 ack 即处理消息的过程慢一些,这样我们就可以从后台管理工具中清晰观察到限流情况。</span> </section> <pre><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;display: block;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;min-width: 400px;font-weight: 400;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);" data-wx-hl-code="import com.rabbitmq.client.*;&lt;br/&gt;import java.io.IOException;&lt;br/&gt;public class QosConsumer {&lt;br/&gt; public static void main(String[] args) throws Exception {&lt;br/&gt; //1. 创建一个 ConnectionFactory 并进行设置&lt;br/&gt; ConnectionFactory factory = new ConnectionFactory();&lt;br/&gt; factory.setHost("localhost");&lt;br/&gt; factory.setVirtualHost("/");&lt;br/&gt; factory.setUsername("guest");&lt;br/&gt; factory.setPassword("guest");&lt;br/&gt; factory.setAutomaticRecoveryEnabled(true);&lt;br/&gt; factory.setNetworkRecoveryInterval(3000);&lt;br/&gt;&lt;br/&gt; //2. 通过连接工厂来创建连接&lt;br/&gt; Connection connection = factory.newConnection();&lt;br/&gt;&lt;br/&gt; //3. 通过 Connection 来创建 Channel&lt;br/&gt; final Channel channel = connection.createChannel();&lt;br/&gt;&lt;br/&gt; //4. 声明&lt;br/&gt; String exchangeName = "test_qos_exchange";&lt;br/&gt; String queueName = "test_qos_queue";&lt;br/&gt; String routingKey = "item.#";&lt;br/&gt; channel.exchangeDeclare(exchangeName, "topic", true, false, null);&lt;br/&gt; channel.queueDeclare(queueName, true, false, false, null);&lt;br/&gt;&lt;br/&gt; channel.basicQos(0, 3, false);&lt;br/&gt;&lt;br/&gt; //一般不用代码绑定,在管理界面手动绑定&lt;br/&gt; channel.queueBind(queueName, exchangeName, routingKey);&lt;br/&gt;&lt;br/&gt; //5. 创建消费者并接收消息&lt;br/&gt; Consumer consumer = new DefaultConsumer(channel) {&lt;br/&gt; @Override&lt;br/&gt; public void handleDelivery(String consumerTag, Envelope envelope,&lt;br/&gt; AMQP.BasicProperties properties, byte[] body)&lt;br/&gt; throws IOException {&lt;br/&gt; try {&lt;br/&gt; Thread.sleep(5000);&lt;br/&gt; } catch (InterruptedException e) {&lt;br/&gt; e.printStackTrace();&lt;br/&gt; }&lt;br/&gt; String message = new String(body, "UTF-8");&lt;br/&gt; System.out.println("[x] Received '" + message + "'");&lt;br/&gt;&lt;br/&gt; channel.basicAck(envelope.getDeliveryTag(), true);&lt;br/&gt; }&lt;br/&gt; };&lt;br/&gt; //6. 设置 Channel 消费者绑定队列&lt;br/&gt; channel.basicConsume(queueName, false, consumer);&lt;br/&gt; channel.basicConsume(queueName, false, consumer1);&lt;br/&gt; }&lt;br/&gt;}" data-wx-hl-lang="Java" data-wx-hl-style="atom-one-dark"><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">import</span> com.rabbitmq.client.*;<br><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">import</span> java.io.IOException;<br><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> <span style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">class</span> <span style="color: rgb(230, 192, 123);font-weight: 400;font-style: normal;">QosConsumer</span> </span>{<br> <span style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">static</span> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">void</span> <span style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">main</span><span style="font-weight: 400;font-style: normal;">(String[] args)</span> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">throws</span> Exception </span>{<br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//1. 创建一个 ConnectionFactory 并进行设置</span><br> ConnectionFactory factory = <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">new</span> ConnectionFactory();<br> factory.setHost(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"localhost"</span>);<br> factory.setVirtualHost(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"/"</span>);<br> factory.setUsername(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"guest"</span>);<br> factory.setPassword(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"guest"</span>);<br> factory.setAutomaticRecoveryEnabled(<span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">true</span>);<br> factory.setNetworkRecoveryInterval(<span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">3000</span>);<br><br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//2. 通过连接工厂来创建连接</span><br> Connection connection = factory.newConnection();<br><br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//3. 通过 Connection 来创建 Channel</span><br> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">final</span> Channel channel = connection.createChannel();<br><br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//4. 声明</span><br> String exchangeName = <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"test_qos_exchange"</span>;<br> String queueName = <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"test_qos_queue"</span>;<br> String routingKey = <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"item.#"</span>;<br> channel.exchangeDeclare(exchangeName, <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"topic"</span>, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">true</span>, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">false</span>, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">null</span>);<br> channel.queueDeclare(queueName, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">true</span>, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">false</span>, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">false</span>, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">null</span>);<br><br> channel.basicQos(<span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">0</span>, <span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">3</span>, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">false</span>);<br><br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//一般不用代码绑定,在管理界面手动绑定</span><br> channel.queueBind(queueName, exchangeName, routingKey);<br><br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//5. 创建消费者并接收消息</span><br> Consumer consumer = <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">new</span> DefaultConsumer(channel) {<br> <span style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">@Override</span><br> <span style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">void</span> <span style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">handleDelivery</span><span style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;">(String consumerTag, Envelope envelope,<br> AMQP.BasicProperties properties, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">byte</span>[] body)</span><br> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">throws</span> IOException </span>{<br> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">try</span> {<br> Thread.sleep(<span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">5000</span>);<br> } <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">catch</span> (InterruptedException e) {<br> e.printStackTrace();<br> }<br> String message = <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">new</span> String(body, <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"UTF-8"</span>);<br> System.out.println(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"[x] Received '"</span> + message + <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"'"</span>);<br><br> channel.basicAck(envelope.getDeliveryTag(), <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">true</span>);<br> }<br> };<br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//6. 设置 Channel 消费者绑定队列</span><br> channel.basicConsume(queueName, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">false</span>, consumer);<br> channel.basicConsume(queueName, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">false</span>, consumer1);<br> }<br>}</code></pre> <p><br></p> <section style="line-height: 1.5em;"> <span style="color: rgb(0, 0, 0);letter-spacing: normal;font-size: 15px;">我们从下图中发现 Unacked值一直都是 3 ,每过 5 秒 消费一条消息即 Ready 和 Total 都减少 3,而 Unacked的值在这里代表消费者正在处理的消息,通过我们的实验发现了消费者一次性最多处理 3 条消息,达到了消费者限流的预期功能。</span> </section> <p><img data-backh="111" data-backw="578" data-ratio="0.19097744360902255" src="/upload/9a637224532c93bd70796ad2d18cc7bc.png" data-type="png" data-w="1330" style="margin: 30px auto;border-width: 0px;border-style: initial;border-color: initial;display: block;border-radius: 3px;width: 100%;height: auto;box-shadow: rgb(35, 32, 32) 3px 3px 30px !important;"></p> <section style="line-height: 1.5em;"> <span style="color: rgb(0, 0, 0);letter-spacing: normal;font-size: 15px;">当我们将void basicQos(int prefetchSize, int prefetchCount, boolean global)中的 global 设置为 true的时候我们发现并没有了限流的作用。</span> </section> <section style="line-height: 1.5em;"> <span style="color: rgb(0, 0, 0);letter-spacing: normal;font-size: 15px;"><br></span> </section> <h2 style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);letter-spacing: normal;font-size: 15px;">TTL</span></strong></span></h2> <p><span style="color: rgb(0, 0, 0);letter-spacing: normal;font-size: 15px;"><br></span></p> <section style="line-height: 1.5em;"> <span style="color: rgb(0, 0, 0);letter-spacing: normal;font-size: 15px;">TTL是Time To Live的缩写,也就是生存时间。RabbitMQ支持消息的过期时间,在消息发送时可以进行指定。</span> </section> <section style="line-height: 1.5em;"> <span style="color: rgb(0, 0, 0);letter-spacing: normal;font-size: 15px;"><br>RabbitMQ支持队列的过期时间,从消息入队列开始计算,只要超过了队列的超时时间配置,那么消息会自动的清除。</span> </section> <section style="line-height: 1.5em;"> <span style="color: rgb(0, 0, 0);letter-spacing: normal;font-size: 15px;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="color: rgb(0, 0, 0);letter-spacing: normal;font-size: 15px;">这与 Redis 中的过期时间概念类似。我们应该合理使用 TTL 技术,可以有效的处理过期垃圾消息,从而降低服务器的负载,最大化的发挥服务器的性能。</span> </section> <section style="line-height: 1.5em;"> <span style="color: rgb(154, 154, 154);font-size: 15px;"><br></span> </section> <section style="color: rgb(154, 154, 154);font-size: 15px;white-space: normal;line-height: 1.5em;"> <span style="color: rgb(171, 25, 66);">RabbitMQ allows you to set TTL (time to live) for both messages and queues. This can be done using optional queue arguments or policies (the latter option is recommended). Message TTL can be enforced for a single queue, a group of queues or applied for individual messages.</span> </section> <section style="color: rgb(154, 154, 154);font-size: 15px;white-space: normal;line-height: 1.5em;"> <br> </section> <section style="color: rgb(154, 154, 154);font-size: 15px;white-space: normal;line-height: 1.5em;"> <br> </section> <section style="color: rgb(154, 154, 154);font-size: 15px;white-space: normal;line-height: 1.5em;"> <span style="color: rgb(171, 25, 66);">RabbitMQ允许您为消息和队列设置TTL(生存时间)。 这可以使用可选的队列参数或策略来完成(建议使用后一个选项)。 可以对单个队列,一组队列强制执行消息TTL,也可以为单个消息应用消息TTL。</span> </section> <section style="color: rgb(154, 154, 154);font-size: 15px;white-space: normal;line-height: 1.5em;"> <br> </section> <section style="color: rgb(154, 154, 154);font-size: 15px;white-space: normal;line-height: 1.5em;"> <br> </section> <section style="color: rgb(154, 154, 154);font-size: 15px;white-space: normal;line-height: 1.5em;"> <span style="color: rgb(171, 25, 66);">——摘自 RabbitMQ 官方文档</span> </section> <h3><br></h3> <h3 style="line-height: 1.5em;"><strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">1.消息的 TTL</span></strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"></span></h3> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">我们在生产端发送消息的时候可以在 properties 中指定 expiration属性来对消息过期时间进行设置,单位为毫秒(ms)。</span> </section> <section> <span style="color: rgb(154, 154, 154);font-size: 15px;"><pre style="overflow-x:auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;display: block;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;min-width: 400px;font-weight: 400;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);" data-wx-hl-code=" /**&lt;br/&gt; * deliverMode 设置为 2 的时候代表持久化消息&lt;br/&gt; * expiration 意思是设置消息的有效期,超过10秒没有被消费者接收后会被自动删除&lt;br/&gt; * headers 自定义的一些属性&lt;br/&gt; * */&lt;br/&gt; //5. 发送&lt;br/&gt; Map<String, Object> headers = new HashMap<String, Object>();&lt;br/&gt; headers.put("myhead1", "111");&lt;br/&gt; headers.put("myhead2", "222");&lt;br/&gt;&lt;br/&gt; AMQP.BasicProperties properties = new AMQP.BasicProperties().builder()&lt;br/&gt; .deliveryMode(2)&lt;br/&gt; .contentEncoding("UTF-8")&lt;br/&gt; .expiration("100000")&lt;br/&gt; .headers(headers)&lt;br/&gt; .build();&lt;br/&gt; String msg = "test message";&lt;br/&gt; channel.basicPublish("", queueName, properties, msg.getBytes());" data-wx-hl-lang="Java" data-wx-hl-style="atom-one-dark"><span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">/**<br> * deliverMode 设置为 2 的时候代表持久化消息<br> * expiration 意思是设置消息的有效期,超过10秒没有被消费者接收后会被自动删除<br> * headers 自定义的一些属性<br> * */</span><br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//5. 发送</span><br> Map<String, Object> headers = <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">new</span> HashMap<String, Object>();<br> headers.put(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"myhead1"</span>, <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"111"</span>);<br> headers.put(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"myhead2"</span>, <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"222"</span>);<br><br> AMQP.BasicProperties properties = <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">new</span> AMQP.BasicProperties().builder()<br> .deliveryMode(<span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">2</span>)<br> .contentEncoding(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"UTF-8"</span>)<br> .expiration(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"100000"</span>)<br> .headers(headers)<br> .build();<br> String msg = <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"test message"</span>;<br> channel.basicPublish(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">""</span>, queueName, properties, msg.getBytes());</code></pre></span> </section> <section style="line-height: 1.5em;"> <br> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">我们也可以后台管理页面中进入 Exchange 发送消息指定expiration</span> </section> <p><img data-backh="321" data-backw="578" data-ratio="0.5550964187327824" src="/upload/ad4d8b70cd131bcd0e7adeba34e7e588.png" data-type="png" data-w="1452" style="margin: 30px auto;border-width: 0px;border-style: initial;border-color: initial;display: block;border-radius: 3px;box-shadow: rgb(35, 32, 32) 3px 3px 30px !important;width: 100%;height: auto;"></p> <h3 style="line-height: 1.5em;"><strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">2.队列的 TTL</span></strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"></span></h3> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">我们也可以在后台管理界面中新增一个 queue,创建时可以设置 ttl,对于队列中超过该时间的消息将会被移除。<br></span> </section> <p><img data-backh="174" data-backw="578" data-ratio="0.3009153318077803" src="/upload/1d1cf4326f0180fc9cfc33a3ae2bd63e.png" data-type="png" data-w="1748" style="margin: 30px auto;border-width: 0px;border-style: initial;border-color: initial;display: block;border-radius: 3px;box-shadow: rgb(35, 32, 32) 3px 3px 30px !important;width: 100%;height: auto;"></p> <h2 style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;letter-spacing: normal;">死信队列</span></strong></span></h2> <p><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;letter-spacing: normal;"><br></span></strong></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">死信队列:没有被及时消费的消息存放的队列</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">消息没有被及时消费的原因:</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <ul class="list-paddingleft-2"> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">a.消息被拒绝(basic.reject/ basic.nack)并且不再重新投递 requeue=false</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">b.TTL(time-to-live) 消息超时未消费</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">c.达到最大队列长度</span> </section></li> </ul> <h3 style="line-height: 1.5em;"><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></h3> <h3 style="line-height: 1.5em;"><strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">实现死信队列步骤</span></strong><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"></span></h3> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <ul class="list-paddingleft-2"> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">首先需要设置死信队列的 exchange 和 queue,然后进行绑定:</span> </section></li> </ul> <section style="line-height: 1.5em;"> <br> </section> <section> <pre><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;display: block;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;min-width: 400px;font-weight: 400;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);" data-wx-hl-code="Exchange: dlx.exchange&lt;br/&gt;Queue: dlx.queue&lt;br/&gt;RoutingKey: # 代表接收所有路由 key" data-wx-hl-lang="Java" data-wx-hl-style="atom-one-dark">Exchange: dlx.exchange<br>Queue: dlx.queue<br>RoutingKey: # 代表接收所有路由 key</code></pre> </section> <section style="line-height: 1.5em;"> <br> </section> <ul class="list-paddingleft-2"> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">然后我们进行正常声明交换机、队列、绑定,只不过我们需要在普通队列加上一个参数即可: arguments.put("x-dead-letter-exchange",' dlx.exchange' )</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">这样消息在过期、requeue失败、 队列在达到最大长度时,消息就可以直接路由到死信队列!</span> </section></li> </ul> <section style="line-height: 1.5em;"> <br> </section> <pre style="overflow-x: auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;display: block;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;min-width: 400px;font-weight: 400;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);" data-wx-hl-code="import com.rabbitmq.client.AMQP;&lt;br/&gt;import com.rabbitmq.client.Channel;&lt;br/&gt;import com.rabbitmq.client.Connection;&lt;br/&gt;import com.rabbitmq.client.ConnectionFactory;&lt;br/&gt;public class DlxProducer {&lt;br/&gt; public static void main(String[] args) throws Exception {&lt;br/&gt; //设置连接以及创建 channel 湖绿&lt;br/&gt; String exchangeName = "test_dlx_exchange";&lt;br/&gt; String routingKey = "item.update";&lt;br/&gt; &lt;br/&gt; String msg = "this is dlx msg";&lt;br/&gt;&lt;br/&gt; //我们设置消息过期时间,10秒后再消费 让消息进入死信队列&lt;br/&gt; AMQP.BasicProperties properties = new AMQP.BasicProperties().builder()&lt;br/&gt; .deliveryMode(2)&lt;br/&gt; .expiration("10000")&lt;br/&gt; .build();&lt;br/&gt;&lt;br/&gt; channel.basicPublish(exchangeName, routingKey, true, properties, msg.getBytes());&lt;br/&gt; System.out.println("Send message : " + msg);&lt;br/&gt;&lt;br/&gt; channel.close();&lt;br/&gt; connection.close();&lt;br/&gt; }&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;Copy&lt;br/&gt;import com.rabbitmq.client.*;&lt;br/&gt;import java.io.IOException;&lt;br/&gt;import java.util.HashMap;&lt;br/&gt;import java.util.Map;&lt;br/&gt;&lt;br/&gt;public class DlxConsumer {&lt;br/&gt; public static void main(String[] args) throws Exception {&lt;br/&gt; //创建连接、创建channel忽略 内容可以在上面代码中获取&lt;br/&gt; String exchangeName = "test_dlx_exchange";&lt;br/&gt; String queueName = "test_dlx_queue";&lt;br/&gt; String routingKey = "item.#";&lt;br/&gt;&lt;br/&gt; //必须设置参数到 arguments 中&lt;br/&gt; Map<String, Object> arguments = new HashMap<String, Object>();&lt;br/&gt; arguments.put("x-dead-letter-exchange", "dlx.exchange");&lt;br/&gt;&lt;br/&gt; channel.exchangeDeclare(exchangeName, "topic", true, false, null);&lt;br/&gt; //将 arguments 放入队列的声明中&lt;br/&gt; channel.queueDeclare(queueName, true, false, false, arguments);&lt;br/&gt;&lt;br/&gt; //一般不用代码绑定,在管理界面手动绑定&lt;br/&gt; channel.queueBind(queueName, exchangeName, routingKey);&lt;br/&gt;&lt;br/&gt;&lt;br/&gt; //声明死信队列&lt;br/&gt; channel.exchangeDeclare("dlx.exchange", "topic", true, false, null);&lt;br/&gt; channel.queueDeclare("dlx.queue", true, false, false, null);&lt;br/&gt; //路由键为 # 代表可以路由到所有消息&lt;br/&gt; channel.queueBind("dlx.queue", "dlx.exchange", "#");&lt;br/&gt;&lt;br/&gt; Consumer consumer = new DefaultConsumer(channel) {&lt;br/&gt; @Override&lt;br/&gt; public void handleDelivery(String consumerTag, Envelope envelope,&lt;br/&gt; AMQP.BasicProperties properties, byte[] body)&lt;br/&gt; throws IOException {&lt;br/&gt;&lt;br/&gt; String message = new String(body, "UTF-8");&lt;br/&gt; System.out.println(" [x] Received '" + message + "'");&lt;br/&gt;&lt;br/&gt; }&lt;br/&gt; };&lt;br/&gt;&lt;br/&gt; //6. 设置 Channel 消费者绑定队列&lt;br/&gt; channel.basicConsume(queueName, true, consumer);&lt;br/&gt; }&lt;br/&gt;}&lt;br/&gt;" data-wx-hl-lang="Java" data-wx-hl-style="atom-one-dark"><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">import</span> com.rabbitmq.client.AMQP;<br><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">import</span> com.rabbitmq.client.Channel;<br><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">import</span> com.rabbitmq.client.Connection;<br><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">import</span> com.rabbitmq.client.ConnectionFactory;<br><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> <span style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">class</span> <span style="color: rgb(230, 192, 123);font-weight: 400;font-style: normal;">DlxProducer</span> </span>{<br> <span style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">static</span> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">void</span> <span style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">main</span><span style="font-weight: 400;font-style: normal;">(String[] args)</span> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">throws</span> Exception </span>{<br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//设置连接以及创建 channel 湖绿</span><br> String exchangeName = <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"test_dlx_exchange"</span>;<br> String routingKey = <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"item.update"</span>;<br> <br> String msg = <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"this is dlx msg"</span>;<br><br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//我们设置消息过期时间,10秒后再消费 让消息进入死信队列</span><br> AMQP.BasicProperties properties = <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">new</span> AMQP.BasicProperties().builder()<br> .deliveryMode(<span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">2</span>)<br> .expiration(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"10000"</span>)<br> .build();<br><br> channel.basicPublish(exchangeName, routingKey, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">true</span>, properties, msg.getBytes());<br> System.out.println(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"Send message : "</span> + msg);<br><br> channel.close();<br> connection.close();<br> }<br>}<br><br><br><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">import</span> com.rabbitmq.client.*;<br><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">import</span> java.io.IOException;<br><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">import</span> java.util.HashMap;<br><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">import</span> java.util.Map;<br><br><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> <span style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">class</span> <span style="color: rgb(230, 192, 123);font-weight: 400;font-style: normal;">DlxConsumer</span> </span>{<br> <span style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">static</span> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">void</span> <span style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">main</span><span style="font-weight: 400;font-style: normal;">(String[] args)</span> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">throws</span> Exception </span>{<br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//创建连接、创建channel忽略 内容可以在上面代码中获取</span><br> String exchangeName = <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"test_dlx_exchange"</span>;<br> String queueName = <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"test_dlx_queue"</span>;<br> String routingKey = <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"item.#"</span>;<br><br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//必须设置参数到 arguments 中</span><br> Map<String, Object> arguments = <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">new</span> HashMap<String, Object>();<br> arguments.put(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"x-dead-letter-exchange"</span>, <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"dlx.exchange"</span>);<br><br> channel.exchangeDeclare(exchangeName, <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"topic"</span>, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">true</span>, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">false</span>, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">null</span>);<br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//将 arguments 放入队列的声明中</span><br> channel.queueDeclare(queueName, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">true</span>, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">false</span>, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">false</span>, arguments);<br><br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//一般不用代码绑定,在管理界面手动绑定</span><br> channel.queueBind(queueName, exchangeName, routingKey);<br><br><br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//声明死信队列</span><br> channel.exchangeDeclare(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"dlx.exchange"</span>, <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"topic"</span>, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">true</span>, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">false</span>, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">null</span>);<br> channel.queueDeclare(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"dlx.queue"</span>, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">true</span>, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">false</span>, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">false</span>, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">null</span>);<br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//路由键为 # 代表可以路由到所有消息</span><br> channel.queueBind(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"dlx.queue"</span>, <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"dlx.exchange"</span>, <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"#"</span>);<br><br> Consumer consumer = <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">new</span> DefaultConsumer(channel) {<br> <span style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">@Override</span><br> <span style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;"><span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">public</span> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">void</span> <span style="color: rgb(97, 174, 238);font-weight: 400;font-style: normal;">handleDelivery</span><span style="color: rgb(171, 178, 191);font-weight: 400;font-style: normal;">(String consumerTag, Envelope envelope,<br> AMQP.BasicProperties properties, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">byte</span>[] body)</span><br> <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">throws</span> IOException </span>{<br><br> String message = <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">new</span> String(body, <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"UTF-8"</span>);<br> System.out.println(<span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">" [x] Received '"</span> + message + <span style="color: rgb(152, 195, 121);font-weight: 400;font-style: normal;">"'"</span>);<br><br> }<br> };<br><br> <span style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//6. 设置 Channel 消费者绑定队列</span><br> channel.basicConsume(queueName, <span style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">true</span>, consumer);<br> }<br>}</code></pre> <p><br></p> <h3 style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;letter-spacing: normal;">总结</span></strong></span></h3> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">DLX也是一个正常的 Exchange,和一般的 Exchange 没有区别,它能在任何的队列上被指定,实际上就是设置某个队列的属性。当这个队列中有死信时,RabbitMQ 就会自动的将这个消息重新发布到设置的 Exchange 上去,进而被路由到另一个队列。可以监听这个队列中消息做相应的处理。</span> </section> <section donone="shifuMouseDownCard('shifu_c_030')" label="Copyright Reserved by PLAYHUDONG." style="text-align: start;white-space: normal;margin-top: 1em;margin-bottom: 1em;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);border-width: 0px;border-style: initial;border-color: initial;"> <section style="margin-left: 1em;line-height: 1.4;"> <span style="padding: 3px 8px;border-top-left-radius: 4px;border-top-right-radius: 4px;border-bottom-right-radius: 4px;border-bottom-left-radius: 4px;color: rgb(255, 255, 255);background-color: rgb(255, 105, 31);font-family: inherit;text-align: inherit;text-decoration: inherit;font-size: 16px;">推荐阅读</span> <span style="margin-left: 4px;padding: 3px 8px;border-top-left-radius: 1.2em;border-top-right-radius: 1.2em;border-bottom-right-radius: 1.2em;border-bottom-left-radius: 1.2em;color: rgb(255, 255, 255);line-height: 1.2;background-color: rgb(204, 204, 204);font-family: inherit;text-align: inherit;text-decoration: inherit;border-color: rgb(249, 110, 87);font-size: 12px;">点击标题可跳转</span> </section> <section style="margin-top: -11px;padding: 22px 16px 16px;border-width: 1px;border-style: solid;border-color: rgb(255, 105, 31);color: rgb(51, 51, 51);font-size: 1em;font-family: inherit;text-align: inherit;text-decoration: inherit;"> <p><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&mid=2651483112&idx=1&sn=3173b656bbbeb6d28f65ac5035147a18&chksm=bd2507978a528e81e07dc03f9b4f68033084f7f00ddb3ed16b7247fb704d93f84f8d9a62e86d&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" style="font-size: 12px;text-decoration: underline;" data-linktype="2"><span style="font-size: 12px;">RabbitMQ 如何实现对同一个应用的多个节点进行广播</span></a><br></p> <p><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&mid=2651478806&idx=2&sn=925650bd8156ad13bd50452f685b1ecf&chksm=bd2537698a52be7fbe6340b8862c73228c165fa8b17b05b894274f692092afb9d5f4aa321a74&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" style="font-size: 12px;text-decoration: underline;" data-linktype="2"><span style="font-size: 12px;">消息队列探秘 – RabbitMQ 消息队列介绍</span></a><br></p> <p><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&mid=2651486667&idx=1&sn=21cbdfaeb1abc109c514a3ffef441eac&chksm=bd2515b48a529ca2c5f9721b46fcd4e8bb8c845f951156d988fcafdc6ab583c80b95fa3fb394&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" style="font-size: 12px;text-decoration: underline;" data-linktype="2"><span style="font-size: 12px;">SpringBoot+RabbitMQ (保证消息100%投递成功并被消费)</span></a></p> <p><span style="font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 12px;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);"></span></p> </section> </section> <p style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);text-align: start;white-space: normal;"><br></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;color: rgb(255, 169, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;">看完本文有收获?请转发分享给更多人</span></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><strong style="max-width: 100%;color: rgb(255, 169, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;">关注「ImportNew」,提升Java技能</strong></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img data-ratio="0.9166666666666666" data-s="300,640" data-type="jpeg" data-w="600" width="auto" src="/upload/899866149276fa5fddb73c61ae04be64.jpg" style="box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 600px !important;"></p> <p style="text-align: right;"><span style="font-size: 14px;text-align: right;">好文章,我</span><span style="font-size: 14px;text-align: right;color: rgb(255, 41, 65);">在看</span><span style="font-size: 14px;text-align: right;">❤️</span></p>
作者:微信小助手
<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, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-top: -10px;"> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;color: rgb(89, 89, 89);"><span style="display: none;"></span></h3> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com"> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;color: rgb(89, 89, 89);"><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" data-darkmode-color-15904575512225="rgb(137, 137, 137)" data-darkmode-original-color-15904575512225="rgb(89, 89, 89)" data-style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: rgb(89, 89, 89); visibility: visible;" class="js_darkmode__1" data-darkmode-color="rgb(137, 137, 137)" data-darkmode-original-color="rgb(89, 89, 89)"><br></p> <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, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-top: -10px;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">大家好,我是 Guide 哥,一个三观比主角还正的技术人。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">简单整理了一下自己日常经常使用的工具网站,分享给小伙伴们!</p> </section> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-top: -10px;"> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;color: rgb(89, 89, 89);"><span style="display: none;"></span>1.奶牛快传:用户体验更好的网盘工具。<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><span style="color: rgb(77, 168, 238);">https://cowtransfer.com/</span></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.5896674584323041" src="/upload/c079b7a33423735260585084f4df1667.png" data-type="png" data-w="3368" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">最近开始使用的一款网盘工具,和百度网盘类似,不过没有下载速度的限制,并且可以支持自定义分享文件的下载次数(需要开会员)。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">还有一点让我觉得比较舒服的是,你给别人分享文件,别人无需登录即可直接下载。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">就传输速度和体验感来说,奶牛快传真的没话说。缺点也比较明显,免费用户的容量容量只有 5 g并且单次传输上限是 2g,所以,可能需要付费才能更好的使用。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;color: rgb(89, 89, 89);"><span style="display: none;"></span>2.docsmall:压缩工具<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><span style="color: rgb(77, 168, 238);">https://docsmall.com/</span></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.5795724465558195" src="/upload/aeca904af063fc30ff9d751351bf571c.png" data-type="png" data-w="3368" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">我经常用来压缩图片、GIF、PDF,你们平时看到我放的 Gif 图片都是在这里压缩过一波的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">并且,还支持PDF合并和分割。不得不说这个网站做的简直不要太美观,体验感和好感 Max!!!</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;color: rgb(89, 89, 89);"><span style="display: none;"></span>3.创客贴:平面设计作图神器<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><span style="color: rgb(77, 168, 238);">https://www.chuangkit.com/</span></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.6133806986382475" src="/upload/3da2595cd20bb976419ea75949ab736b.png" data-type="png" data-w="3378" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">我的公众号首页封面图就是通过这个网站制作的。通过这个网站你可以制作好看的海报、简历、新媒体文章的首页图等等,这个网站甚至还有很多免费且好看的 PPT插件,简直是神器。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;color: rgb(89, 89, 89);"><span style="display: none;"></span>4.Dimmy.club:手机电脑等设备的展示模型<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><span style="color: rgb(77, 168, 238);">https://dimmy.club/</span></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.6060786650774732" src="/upload/8d8adbcafc82852cf3cf712c3ebafaa6.png" data-type="png" data-w="3356" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">可以让你的图片放在电脑、手机、ipad等模型中展示,大大提升了图片的档次。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;color: rgb(89, 89, 89);"><span style="display: none;"></span>5.BrowserFrame:浏览器展示模型<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><span style="color: rgb(77, 168, 238);">https://browserframe.com/</span></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.6017804154302671" src="/upload/64f18b7049585ba11016befbb1a126f6.png" data-type="png" data-w="3370" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">正如你们所看到的,本文所有的图片都是他通过这个网站在线转换的,相比直接展示要更加美观一些,节省了很多自己手动调整图片的时间。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;color: rgb(89, 89, 89);"><span style="display: none;"></span>6.Flourish:数据可视化<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><span style="color: rgb(77, 168, 238);">https://flourish.studio/</span></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <span style="color: rgb(89, 89, 89);letter-spacing: 0px;">通过这个网站,你可以快速地把表格数据转换为各种各样好看的图表,并且还支持动态可视化。</span> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.46797608881298036" src="/upload/a7b3b24f1e306b10d073073c4e4c30fa.gif" data-type="gif" data-w="2342" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;color: rgb(89, 89, 89);"><span style="display: none;"></span>7.PDF派:20个免费好用的PDF在线工具<span style="display: none;"></span></h3> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-top: -10px;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><span style="color: rgb(77, 168, 238);">https://www.pdfpai.com/</span></p> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-top: -10px;"> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.5784972510690287" src="/upload/24d07c0caecceaa9c232bfdaaaad85cb.png" data-type="png" data-w="3274" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">PDF在线工具挺多的,PDF派是我最喜欢的一个,功能强大稳定。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;color: rgb(89, 89, 89);"><span style="display: none;"></span>8.小码短连接:简单易用的渠道短链接统计工具<span style="display: none;"></span></h3> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-top: -10px;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><span style="color: rgb(77, 168, 238);">https://xiaomark.com/</span></p> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-top: -10px;"> <figure data-tool="mdnice编辑器" style="color: black;margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.5767634854771784" src="/upload/b22e33a5465e4f39af2a33ad8a806a3.png" data-type="png" data-w="3374" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);padding-top: 8px;padding-bottom: 8px;line-height: 26px;">非常好用的长链接转短链接工具,能够让链接看起来更简洁。并且,转换为短链接之后,还能在后台监测访问数据,如访问次数、访问人数。</p> <figure data-tool="mdnice编辑器" style="color: black;margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.13181148748159058" src="/upload/3c09d77a5b6d0c822eb324304bd0ee74.png" data-type="png" data-w="2716" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <h3 data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>9.Kapwing:一个用于创建图像,视频和GIF的协作平台<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="color: rgb(77, 168, 238);">https://www.kapwing.com/</span></p> <figure data-tool="mdnice编辑器" style="color: black;margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.5898673100120627" src="/upload/4372895d34e12f837111453c38faee15.png" data-type="png" data-w="3316" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong style="color: rgb(71, 193, 168);">神器网站!强烈推荐!</strong></p> <p data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kapwing 是一个在线视频编辑网站,集成了很多在线小工具,当有视频编辑的需求手头却没有什么趁手的小工具的话,不妨用来应应急。网站并没有复杂的操作界面,已经把常见的需求做成了单独的小功能,即使没有视频编辑经验的小白,也能三秒上手。</p> <h3 data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>10.removebg:抠图神器<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="color: rgb(77, 168, 238);">https://www.remove.bg/zh</span></p> <figure data-tool="mdnice编辑器" style="color: black;margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.5651658767772512" src="/upload/3b95996a72d37c736c48fb634bdbb488.png" data-type="png" data-w="3376" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);padding-top: 8px;padding-bottom: 8px;line-height: 26px;">抠图神器,绝对的神器!</p> <h3 data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>11.今日热榜:你关心的热点<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="color: rgb(77, 168, 238);">https://tophub.today/</span></p> <figure data-tool="mdnice编辑器" style="color: black;margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.5684647302904564" src="/upload/9b21578ab0df525cf86e610e165ed845.png" data-type="png" data-w="3374" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);padding-top: 8px;padding-bottom: 8px;line-height: 26px;">今日热榜提供各站热榜聚合:微信、今日头条、百度、知乎、V2EX、微博、贴吧、豆瓣、天涯、虎扑、Github、抖音...。追踪全网热点、简单高效阅读。</p> <h3 data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>12.Apkpure:安卓安装包下载<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="color: rgb(77, 168, 238);">https://apkpure.com/cn/</span></p> <p style="color: black;text-align: center;"><img class="rich_pages" data-ratio="0.63046875" data-s="300,640" src="/upload/dd5ffa2e4ece5fd8d787fddd29bfb882.png" data-type="png" data-w="1280" style=""></p> <figure data-tool="mdnice编辑器" style="color: black;margin-top: 10px;margin-bottom: 10px;"> <span style="color: rgb(89, 89, 89);letter-spacing: 0px;">因为我的手机无法正常访问 Google Store,所以,很多安装包我都是通过这个网站来下载的。</span> </figure> <h3 data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>13.crx4chrome:下载Chrome浏览器插件<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="color: rgb(77, 168, 238);">https://www.crx4chrome.com/</span></p> <figure data-tool="mdnice编辑器" style="color: black;margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.5092592592592593" src="/upload/5abec7e78092cca5be9d9cdbec4d6581.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如果你无法访问谷歌商店的话,可以通过这个网站来下载你想要的Chrome浏览器插件,毕竟没有插件的 Chrome浏览器 ,失去了很大一项乐趣。</p> <h3 data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>14.ProcessOn:在线绘图<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="color: rgb(77, 168, 238);">https://www.processon.com/</span></p> <figure data-tool="mdnice编辑器" style="color: black;margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.60381861575179" src="/upload/38cf4497cdafff78c3cd19aea9fc0daf.png" data-type="png" data-w="3352" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);padding-top: 8px;padding-bottom: 8px;line-height: 26px;">画图工具,支持流程图、思维导图、原型图、UML、网络拓扑图、组织结构图等。</p> <h3 data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>15.draw.io:又一个画图工具<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="color: rgb(77, 168, 238);">https://www.draw.io/</span></p> <figure data-tool="mdnice编辑器" style="color: black;margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.5820451843043996" src="/upload/b7851d72db2a5a0fbc5b3e4670f93a4a.png" data-type="png" data-w="3364" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);padding-top: 8px;padding-bottom: 8px;line-height: 26px;">相比于 ProcessOn 我更喜欢这块画图工具,不光有在线版还有电脑版,并且可以将文件保存到多个位置。</p> <figure data-tool="mdnice编辑器" style="color: black;margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.767511177347243" src="/upload/9d58aec85b690b1a2c3498bb7818a7e0.png" data-type="png" data-w="1342" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <h3 data-tool="mdnice编辑器" style="color: rgb(89, 89, 89);margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>16.其他</h3> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-top: -10px;"> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <strong style="color: rgb(71, 193, 168);">Carbon<sup style="line-height: 0;">[1]</sup></strong> :生成漂亮的代码图片。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <strong style="color: rgb(71, 193, 168);">图壳<sup style="line-height: 0;">[2]</sup></strong> :免费好用稳定的图床网站。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <strong style="color: rgb(71, 193, 168);">龙轩导航<sup style="line-height: 0;">[3]</sup></strong> :究极好用的导航网站(我平时用来追剧~~~)。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <strong style="color: rgb(71, 193, 168);">excalidraw<sup style="line-height: 0;">[4]</sup></strong> :简洁大方的在线画图工具。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <strong style="color: rgb(71, 193, 168);">mdnice<sup style="line-height: 0;">[5]</sup></strong> :支持自定义样式的 Markdown 编辑器。支持微信公众号、知乎和稀土掘金的排版。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> ...... </section></li> </ol> </section> <p style="color: rgb(62, 62, 62);white-space: normal;font-size: 16px;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;widows: 1;word-spacing: 2px;caret-color: rgb(255, 0, 0);background-color: rgb(255, 255, 255);text-align: center;"><img class="rich_pages" data-ratio="0.45818815331010454" data-s="300,640" src="/upload/3e080a01914f7849b65ecf41a0e33320.png" data-type="png" data-w="574"><br></p>
作者:微信小助手
<p style="color: rgb(62, 62, 62);font-size: 15px;white-space: normal;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-size-adjust: auto;word-spacing: 2px;text-align: center;" data-mpa-powered-by="yiban.io"><span style="letter-spacing: 0.544px;color: rgb(136, 136, 136);font-size: 14px;">点击蓝色“</span><span style="letter-spacing: 0.544px;font-size: 14px;color: rgb(0, 128, 255);">架构文摘</span><span style="letter-spacing: 0.544px;color: rgb(136, 136, 136);font-size: 14px;">”关注我哟</span></p> <p style="margin-bottom: 10px;color: rgb(62, 62, 62);font-size: 15px;white-space: normal;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-size-adjust: auto;word-spacing: 2px;text-align: center;"><span style="font-size: 14px;color: rgb(136, 136, 136);">加个“</span><span style="color: rgb(0, 128, 255);font-size: 14px;">星标</span><span style="font-size: 14px;color: rgb(136, 136, 136);">”,每天上午 09:25,干货推送!</span></p> <p style="color: rgb(62, 62, 62);white-space: normal;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;font-size: 14px;background-color: rgb(255, 255, 255);text-align: center;"><img data-backh="36" data-backw="578" data-ratio="0.0625" data-s="300,640" data-type="jpeg" data-w="640" width="100%" src="/upload/8c292e55ba5a23cb6ebc11f2a2c4fece.jpg" style="font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;widows: 1;word-spacing: 2px;color: rgb(136, 136, 136);box-sizing: border-box !important;visibility: visible !important;width: 677px !important;"></p> <section style="font-size: 14px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <p style="font-size: inherit;color: inherit;line-height: inherit;"><br></p> <p style="font-size: inherit;color: inherit;line-height: inherit;"><span style="color: rgb(136, 136, 136);font-size: 12px;">来源:https://www.cnblogs.com/xiejava/p/12452434.html</span><br></p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">由于nginx功能强大,性能突出,越来越多的web应用采用nginx作为http和反向代理的web服务器。而nginx的访问日志不管是做用户行为分析还是安全分析都是非常重要的数据源之一。如何有效便捷的采集nginx的日志进行有效的分析成为大家关注的问题。本文通过几个实例来介绍如何通过filebeat、logstash、rsyslog采集nginx的访问日志和错误日志。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">大家都知道ELK技术栈是采集、分析日志的利器。所以这里介绍的是从nginx采集日志到ES。当然至于日志采集以后存到看大家的需要。通过logstash可以方便的配置日志输出存储的方式。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">一般来说nginx默认安装后,日志文件在 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">/usr/local/nginx/logs</code> 目录下。分别有<code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">access.log</code>和<code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">error.log</code>访问日志和错误日志。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">这次示例Elasitcsearch是三个节点组成的集群172.28.65.22、172.28.65.23、172.28.65.24,172.28.65.30</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">是kibana的地址,172.28.65.32是数据采集服务器,上面装有logstash、nginx、<br>filebeat。一般来说采集服务器上有logstash,而nginx、 filebeat应该是装在采集目标上。</p> <h3 style="color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.3em;border-bottom: 2px solid rgb(0, 172, 193);"><span style="font-size: inherit;line-height: inherit;display: inline-block;font-weight: normal;background: rgb(0, 172, 193);color: rgb(255, 255, 255);padding-top: 3px;padding-right: 10px;padding-left: 10px;border-top-right-radius: 3px;border-top-left-radius: 4px;margin-right: 2px;">一、直接通过filebeat采集日志到ES</span></h3> <figure style="font-size: inherit;color: inherit;line-height: inherit;"> <img data-ratio="0.483695652173913" src="/upload/d3866e812a14173b681195c433208c2.png" data-type="png" data-w="368" style="font-size: inherit;color: inherit;line-height: inherit;display: block;margin-right: auto;margin-left: auto;" title="filebeat到ES"> <figcaption style="line-height: inherit;margin-top: 10px;text-align: center;color: rgb(153, 153, 153);font-size: 0.7em;"> filebeat到ES </figcaption> </figure> <br>在filebeat的安装目录找到filebeat.yml 配置获取日志文件的路径及输出到ES的配置。 <br>具体: <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><br></p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"> - <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">type</span>: <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">log</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># Change to true to enable this input configuration.</span><br> enabled: <span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">true</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;"># Paths that should be crawled and fetched. Glob based paths.</span><br> paths:<br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#- /var/log/*.log</span><br> - /usr/<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">local</span>/nginx/logs/*.<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">log</span><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#- c:\programdata\elasticsearch\logs\*</span><br></code></pre> <figure style="font-size: inherit;color: inherit;line-height: inherit;"> <img data-ratio="0.5814432989690722" src="/upload/9e9ce0e1bae655bf101abc343cb54e08.png" data-type="png" data-w="485" style="font-size: inherit;color: inherit;line-height: inherit;display: block;margin-right: auto;margin-left: auto;" title="filebeat.yml配置"> <figcaption style="line-height: inherit;margin-top: 10px;text-align: center;color: rgb(153, 153, 153);font-size: 0.7em;"> filebeat.yml配置 </figcaption> </figure> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><br></p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">如果需要在kibana中友好显示的化,可进行kibana配置</p> <figure style="font-size: inherit;color: inherit;line-height: inherit;"> <img data-ratio="0.4555160142348754" src="/upload/4519b07edaa57ea01bb948caa9b546b.png" data-type="png" data-w="562" style="font-size: inherit;color: inherit;line-height: inherit;display: block;margin-right: auto;margin-left: auto;" title="kibana配置"> <figcaption style="line-height: inherit;margin-top: 10px;text-align: center;color: rgb(153, 153, 153);font-size: 0.7em;"> kibana配置 </figcaption> </figure> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><br></p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">输出到es中,在hosts中配置好你的ES服务地址。如果单机只有一个节点,就可以只配一个ip和端口。</p> <figure style="font-size: inherit;color: inherit;line-height: inherit;"> <img data-ratio="0.334525939177102" src="/upload/ee659272c9ddbb753b66bd25ceae332f.png" data-type="png" data-w="559" style="font-size: inherit;color: inherit;line-height: inherit;display: block;margin-right: auto;margin-left: auto;" title="filebeat.yml配置中配置es"> <figcaption style="line-height: inherit;margin-top: 10px;text-align: center;color: rgb(153, 153, 153);font-size: 0.7em;"> filebeat.yml配置中配置es </figcaption> </figure> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><br></p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">启动filebeat 进行日志数据采集</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"> ./filebeat -e -c filebeat.yml -d <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"publish"</span><br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">通过elasticsearch-head插件查看es索引中的日志信息</p> <figure style="font-size: inherit;color: inherit;line-height: inherit;"> <img data-ratio="0.17155664221678893" src="/upload/39884781bb82fa8c56f89cacf8057010.png" data-type="png" data-w="2454" style="font-size: inherit;color: inherit;line-height: inherit;display: block;margin-right: auto;margin-left: auto;" title="elasticsearch-head插件查看es索引"> <figcaption style="line-height: inherit;margin-top: 10px;text-align: center;color: rgb(153, 153, 153);font-size: 0.7em;"> elasticsearch-head插件查看es索引 </figcaption> </figure> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><br></p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">可以看到nginx中的access.log和error.log的日志都已经上来了。 <br>在kibana中通过<code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">filebeat-*</code></p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">过滤看filebeat的索引,可以看到通过filebeat采过来的数据。 <br></p> <figure style="font-size: inherit;color: inherit;line-height: inherit;"> <img data-ratio="0.6837060702875399" src="/upload/31532d59ca28a5a30b865e849143efdd.png" data-type="png" data-w="1252" style="font-size: inherit;color: inherit;line-height: inherit;display: block;margin-right: auto;margin-left: auto;" title="kibana中通过filebeat-*过滤看filebeat的索引"> <figcaption style="line-height: inherit;margin-top: 10px;text-align: center;color: rgb(153, 153, 153);font-size: 0.7em;"> kibana中通过filebeat-*过滤看filebeat的索引 </figcaption> </figure> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><br></p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">这种直接通过filebeat直接对接ES采日志的方式简单直接,但是无法对采集的日志进行预处理和其他一些操作,也不够灵活。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">可以在filebeat 和ES之间加一层Logstash,可以将filebeat于ES解耦,通过Logstash可以做一些预处理,也可以通过Logstash采集到除ES以外的其他数据存储上。</p> <h3 style="color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.3em;border-bottom: 2px solid rgb(0, 172, 193);"><span style="font-size: inherit;line-height: inherit;display: inline-block;font-weight: normal;background: rgb(0, 172, 193);color: rgb(255, 255, 255);padding-top: 3px;padding-right: 10px;padding-left: 10px;border-top-right-radius: 3px;border-top-left-radius: 4px;margin-right: 2px;">二、通过filebeat采集日志到logstash再送到ES</span></h3> <figure style="font-size: inherit;color: inherit;line-height: inherit;"> <img data-ratio="0.24723247232472326" src="/upload/28508d89910afbdec810b4009a915924.png" data-type="png" data-w="813" style="font-size: inherit;color: inherit;line-height: inherit;display: block;margin-right: auto;margin-left: auto;" title="通过filebeat采集日志到logstash再送到ES"> <figcaption style="line-height: inherit;margin-top: 10px;text-align: center;color: rgb(153, 153, 153);font-size: 0.7em;"> 通过filebeat采集日志到logstash再送到ES </figcaption> </figure> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><br></p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">首先得安装 logstash ,安装完后在logstash的安装目录下新建vi filebeat-pipeline.conf filebeat-pipeline.conf的具体配置如下:</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"> input {<br> beats {<br> port => <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"5044"</span><br> }<br> }<br> output {<br> elasticsearch { hosts => [<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"172.28.65.24:9200"</span>] }<br> stdout { codec => rubydebug}<br> }<br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">input配置表示通过5044端口接收beats的数据,output配置表示输出到elasticsearch,并且同时输出到标准输出也就是控制台。 <br>然后通过命令</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"> bin/logstash -f filebeat-pipeline.conf <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">--config.reload.automatic</span><br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">应用filebeat-pipeline.conf启动logstash。</p> <figure style="font-size: inherit;color: inherit;line-height: inherit;"> <img data-ratio="0.14487632508833923" src="/upload/1f41c6a589209b22a1b34baed4b597d6.png" data-type="png" data-w="1132" style="font-size: inherit;color: inherit;line-height: inherit;display: block;margin-right: auto;margin-left: auto;" title="应用filebeat-pipeline.conf启动logstash"> <figcaption style="line-height: inherit;margin-top: 10px;text-align: center;color: rgb(153, 153, 153);font-size: 0.7em;"> 应用filebeat-pipeline.conf启动logstash </figcaption> </figure> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><br></p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">启动以后可以看到logstash的启动日志5044端口的服务已经起了,可以接受通过filebeat通过5044端口传过来的数据了。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">接下来配置filebeat在filebeat的安装目录找到filebeat.yml 配置获取日志文件的路径及输出到logstash的配置。不直接输出到ES了。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">具体配置如下:</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">将output.elasticsearch的配置屏蔽配置output.logstash,配置正确的logstash的服务主机和端口</p> <figure style="font-size: inherit;color: inherit;line-height: inherit;"> <img data-ratio="0.7354497354497355" src="/upload/90d11abdf32c0d9fcfb32f85329cf224.png" data-type="png" data-w="567" style="font-size: inherit;color: inherit;line-height: inherit;display: block;margin-right: auto;margin-left: auto;" title="配置output.logstash"> <figcaption style="line-height: inherit;margin-top: 10px;text-align: center;color: rgb(153, 153, 153);font-size: 0.7em;"> 配置output.logstash </figcaption> </figure> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><br></p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">启动filebeat 进行日志数据采集</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"> ./filebeat -e -c filebeat.yml -d <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"publish"</span><br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">我们访问nginx服务提供的web服务http://172.28.65.32/在logstash的控制台 可以看到相应的访问access.log 日志</p> <figure style="font-size: inherit;color: inherit;line-height: inherit;"> <img data-ratio="0.6408510638297872" src="/upload/7996a533a02435dd3dd88c6ad9e11217.png" data-type="png" data-w="1175" style="font-size: inherit;color: inherit;line-height: inherit;display: block;margin-right: auto;margin-left: auto;" title="logstash的控制台 可以看到相应的访问access.log 日志"> <figcaption style="line-height: inherit;margin-top: 10px;text-align: center;color: rgb(153, 153, 153);font-size: 0.7em;"> logstash的控制台 可以看到相应的访问access.log 日志 </figcaption> </figure> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><br></p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">同时在ES 中也可以看到有相应的日志数据</p> <figure style="font-size: inherit;color: inherit;line-height: inherit;"> <img data-ratio="0.6614535418583257" src="/upload/a4db58290da24d4a7cc651c9b3e8f86b.png" data-type="png" data-w="1087" style="font-size: inherit;color: inherit;line-height: inherit;display: block;margin-right: auto;margin-left: auto;" title="在ES 中也可以看到有相应的日志数据"> <figcaption style="line-height: inherit;margin-top: 10px;text-align: center;color: rgb(153, 153, 153);font-size: 0.7em;"> 在ES 中也可以看到有相应的日志数据 </figcaption> </figure> <h3 style="color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;font-weight: bold;font-size: 1.3em;border-bottom: 2px solid rgb(0, 172, 193);"><span style="font-size: inherit;line-height: inherit;display: inline-block;font-weight: normal;background: rgb(0, 172, 193);color: rgb(255, 255, 255);padding-top: 3px;padding-right: 10px;padding-left: 10px;border-top-right-radius: 3px;border-top-left-radius: 4px;margin-right: 2px;">三、直接通过rsyslog采集日志到logstash在送到ES</span></h3> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">在很多情况下你需要采集的web服务器并不是自己能够控制的,不是说你想装filebeat就可以让你装的,这时候就可以要求目标数据源通过 syslog的方式将日志发出来。我们可以再通过 logstash送到ES或其他的日志存储处理平台。</p> <figure style="font-size: inherit;color: inherit;line-height: inherit;"> <img data-ratio="0.24938875305623473" src="/upload/af4d58889ab110e830cb9158125b6e8a.png" data-type="png" data-w="818" style="font-size: inherit;color: inherit;line-height: inherit;display: block;margin-right: auto;margin-left: auto;" title="直接通过rsyslog采集日志到logstash在送到ES"> <figcaption style="line-height: inherit;margin-top: 10px;text-align: center;color: rgb(153, 153, 153);font-size: 0.7em;"> 直接通过rsyslog采集日志到logstash在送到ES </figcaption> </figure> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><br></p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">通过syslog往日志服务器上发nginx的日志有两种方式,一种就是利用nginx的配置往外发日志,一种就是通过配置linux的rsyslog的配置往外发日志。</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><strong style="font-size: inherit;color: inherit;line-height: inherit;">1、通过nginx配置发送syslog到logstash</strong> </p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">参考见nginx官方文档:http://nginx.org/en/docs/syslog.html <br>具体配置如下:</p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">在nginx的配置文件nginx.conf中在server下配置access_log和error_log的输出方式</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"> <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">access_log</span> syslog:server=<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">172.28.65.32:514</span>,facility=local7,tag=nginx_access_log,severity=<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">info</span>;<br> <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">error_log</span> syslog:server=<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">172.28.65.32:514</span>,facility=local7,tag=nginx_error_log,severity=<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">info</span>;<br></code></pre> <figure style="font-size: inherit;color: inherit;line-height: inherit;"> <img data-ratio="0.6670951156812339" src="/upload/204415d103635ffaf70af968f5962a25.png" data-type="png" data-w="778" style="font-size: inherit;color: inherit;line-height: inherit;display: block;margin-right: auto;margin-left: auto;" title="nginx.conf配置"> <figcaption style="line-height: inherit;margin-top: 10px;text-align: center;color: rgb(153, 153, 153);font-size: 0.7em;"> nginx.conf配置 </figcaption> </figure> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><br></p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">配置完成后执行 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">./nginx -s reload</code> 使配置生效。这样就通过linux的rsyslog服务将nginx的日志往外发了。接着来配置logstash的syslog的服务接收配置 。在logstash的安装目录下新建 vi syslog-pipeline.conf syslog-pipeline.conf的具体配置如下:</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"> input {<br> syslog{<br> type => <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"system-syslog"</span><br> port => <span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">514</span><br> }<br> }<br> output {<br> elasticsearch {<br> hosts => [<span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"172.28.65.24:9200"</span>]<br> index => <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">"system-syslog-%{+YYYY.MM}"</span><br> }<br> stdout { codec => rubydebug}<br> }<br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">input配置表示通过514端口接收syslog的数据 <br>output配置表示输出到elasticsearch,并且同时输出到标准输出也就是控制台。 <br>通过执行 <code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">bin/logstash -f syslog-pipeline.conf --config.reload.automatic</code></p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">启动logstash</p> <figure style="font-size: inherit;color: inherit;line-height: inherit;"> <img data-ratio="0.16231884057971013" src="/upload/23ef99d7b1bc46fb7943dc9fdaaabeb5.png" data-type="png" data-w="1035" style="font-size: inherit;color: inherit;line-height: inherit;display: block;margin-right: auto;margin-left: auto;" title="可以看到logstash启动以后开启了514端口的tcp和upd协议的侦听"> <figcaption style="line-height: inherit;margin-top: 10px;text-align: center;color: rgb(153, 153, 153);font-size: 0.7em;"> 可以看到logstash启动以后开启了514端口的tcp和upd协议的侦听 </figcaption> </figure> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><br></p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">可以看到logstash启动以后开启了514端口的tcp和upd协议的侦听。我们访问nginx服务提供的web服务http://172.28.65.32/在logstash的控制台 可以看到相应的nginx访问access和error的日志</p> <figure style="font-size: inherit;color: inherit;line-height: inherit;"> <img data-ratio="0.5172690763052209" src="/upload/c563d086de6b21d119c02de8093c5bf9.png" data-type="png" data-w="1245" style="font-size: inherit;color: inherit;line-height: inherit;display: block;margin-right: auto;margin-left: auto;" title="logstash的控制台可以看到相应的nginx访问access和error的日志"> <figcaption style="line-height: inherit;margin-top: 10px;text-align: center;color: rgb(153, 153, 153);font-size: 0.7em;"> logstash的控制台可以看到相应的nginx访问access和error的日志 </figcaption> </figure> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><br></p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">同样通过Elasticsearch-head在ES 中也可以看到有相应的日志数据</p> <figure style="font-size: inherit;color: inherit;line-height: inherit;"> <img data-ratio="0.7425213675213675" src="/upload/cb1478c3690f1b549b844dc750580cce.png" data-type="png" data-w="936" style="font-size: inherit;color: inherit;line-height: inherit;display: block;margin-right: auto;margin-left: auto;" title="通过Elasticsearch-head在ES 中也可以看到有相应的日志"> <figcaption style="line-height: inherit;margin-top: 10px;text-align: center;color: rgb(153, 153, 153);font-size: 0.7em;"> 通过Elasticsearch-head在ES 中也可以看到有相应的日志 </figcaption> </figure> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><br></p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><strong style="font-size: inherit;color: inherit;line-height: inherit;">2、通过配置rsyslog发送syslog日志到logstash</strong> </p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">有些老版本的nginx不支持配置syslog输出日志,或者说我想输出其他不是nginx的日志该怎么办呢?可以通过直接配置rsyslog的方式来往外发送日志。 <br>在/etc/rsyslog.conf 中配置</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"> <span style="font-size: inherit;line-height: inherit;color: rgb(98, 151, 85);overflow-wrap: inherit !important;word-break: inherit !important;">$IncludeConfig</span> /etc/rsyslog.d/*.conf<br></code></pre> <figure style="font-size: inherit;color: inherit;line-height: inherit;"> <img data-ratio="0.1352112676056338" src="/upload/c6ae6f8323e908051a72ccbcff0ec21d.png" data-type="png" data-w="355" style="font-size: inherit;color: inherit;line-height: inherit;display: block;margin-right: auto;margin-left: auto;" title="/etc/rsyslog.conf"> <figcaption style="line-height: inherit;margin-top: 10px;text-align: center;color: rgb(153, 153, 153);font-size: 0.7em;"> /etc/rsyslog.conf </figcaption> </figure> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;"><br></p> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">意思是可以引用外部的配置文件,引用外部的配置文件一方面可以不影响主配置文件,另一方面也比较好管理在/etc/rsyslog.d目录下新建nginx-log.conf <br>配置如下:</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"> $ModLoad imfile<br> $InputFilePollInterval <span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">1</span><br> $WorkDirectory /var/spool/rsyslog<br> $PrivDropToGroup adm<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">##Nginx访问日志文件路径,根据实际情况修改:</span><br> $InputFileName /usr/<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">local</span>/nginx/logs/access.log<br> $InputFileTag nginx-access:<br> $InputFileStateFile <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">stat</span>-nginx-access<br> $InputFileSeverity info<br> $InputFilePersistStateInterval <span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">25000</span><br> $InputRunFileMonitor<br><br> <span style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">##Nginx错误日志文件路径,根据实际情况修改:</span><br> $InputFileName /usr/<span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">local</span>/nginx/logs/error.log<br> $InputFileTag nginx-error:<br> $InputFileStateFile <span style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">stat</span>-nginx-error<br> $InputFileSeverity error<br> $InputFilePersistStateInterval <span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">25000</span><br> $InputRunFileMonitor<br><br> *.* @172.<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">28.65</span>:<span style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">514</span><br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">配置好了以后,重启rsyslog服务</p> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code style="margin-right: 2px;margin-left: 2px;line-height: 18px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"> <span style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);overflow-wrap: inherit !important;word-break: inherit !important;">systemctl</span> restart rsyslog<br></code></pre> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">我们访问nginx服务提供的web服务http://172.28.65.32/在logstash的控制台 可以看到同样的效果。</p> <figure style="font-size: inherit;color: inherit;line-height: inherit;"> <img data-ratio="0.5271132376395534" src="/upload/20f7bc344265168d9db94d2d4f47964e.png" data-type="png" data-w="1254" style="font-size: inherit;color: inherit;line-height: inherit;display: block;margin-right: auto;margin-left: auto;" title="logstash的控制台"> <figcaption style="line-height: inherit;margin-top: 10px;text-align: center;color: rgb(153, 153, 153);font-size: 0.7em;"> logstash的控制台 </figcaption> </figure> <p style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 1.5em;margin-bottom: 1.5em;">本文介绍了如何通过filebeat、logstash、rsyslog采集nginx的访问日志和错误日志的几种方式,具体需要根据实际情况灵活的运用。</p> </section> <p><br></p> <section mpa-from-tpl="t" style="padding-left: 5px;letter-spacing: 0.544px;white-space: normal;color: rgb(62, 62, 62);font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);font-size: medium;text-align: left;border-left: 5px solid rgb(51, 51, 51);line-height: 1.5em;"> <section powered-by="xiumi.us" mpa-from-tpl="t"> <section mpa-from-tpl="t"> <section mpa-from-tpl="t" style="font-size: 18px;"> <p><span style="font-size: 16px;">推荐阅读:</span></p> </section> </section> </section> </section> <ul class="list-paddingleft-2" mpa-from-tpl="t" style="letter-spacing: 0.544px;white-space: normal;color: rgb(62, 62, 62);font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);font-size: medium;text-align: left;"> <li style="font-size: 14px;"><p style="margin-top: 5px;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzA3ODIxNjYxNQ==&mid=2247489504&idx=1&sn=35c15727f3995dc1e52518b689663f7c&chksm=9f477b28a830f23eaaf02e7cd1ab749266aa5f35edbbddc25930e58667f96d6806461c7a1244&scene=21#wechat_redirect" textvalue="究竟什么是图数据库,它有哪些应用场景?" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="color: var(--weui-LINK);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;">究竟什么是图数据库,它有哪些应用场景?</a></p></li> <li style="font-size: 14px;"><p style="margin-top: 5px;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzA3ODIxNjYxNQ==&mid=2247489447&idx=1&sn=117aa2a683a64cef930243f1f46e8df8&chksm=9f477b6fa830f2796c40a28dc83fbcfd66babc060ac0444a6c1d032015ca24df8f2ac23b99fd&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="color: var(--weui-LINK);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;">Elasticsearch 在各大互联网公司大量真实的应用案例!</a></p></li> <li style="font-size: 14px;"><p style="margin-top: 5px;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzA3ODIxNjYxNQ==&mid=2247489202&idx=1&sn=57196270085c5898045a29e746e8bf5e&chksm=9f477a7aa830f36cbfd79bc0b7754ee4b5658a1fa312a32f6a027599bd6c503800ccc2296cf9&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="color: var(--weui-LINK);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;">应该选择RabbitMQ还是Kafka?</a><br></p></li> <li style="font-size: 14px;"><p style="margin-top: 5px;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzA3ODIxNjYxNQ==&mid=2247489178&idx=1&sn=7f7f730d290e8d09f1d83af674655f41&chksm=9f477a52a830f344a6e9cd916706c5bb9e88064baba787345feb076d70fd7a9fbfb16873fa9a&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="color: var(--weui-LINK);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;">前后端分离模式下的权限设计方案</a></p></li> <li style="font-size: 14px;"><p style="margin-top: 5px;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzA3ODIxNjYxNQ==&mid=2247489451&idx=1&sn=ce18154070fc3d868750bfca5dd6e5ba&chksm=9f477b63a830f275f4c3d1ddee3b352e67348069f142ecb3e1cf479c13054869f3a7e405cc5b&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="color: var(--weui-LINK);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;">究竟什么是图数据库,它有哪些应用场景?</a></p></li> </ul> <p style="margin-top: 5px;letter-spacing: 0.544px;white-space: normal;color: rgb(62, 62, 62);font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;background-color: rgb(255, 255, 255);"><br></p> <p style="margin-top: 5px;letter-spacing: 0.544px;white-space: normal;color: rgb(62, 62, 62);font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;background-color: rgb(255, 255, 255);text-align: right;"><img class="rich_pages" data-ratio="0.5555555555555556" data-s="300,640" data-type="jpeg" data-w="900" src="/upload/9d786e52e671b1ce4beb338d1e0d1815.jpg" style="letter-spacing: 0.544px;text-align: center;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;box-sizing: border-box !important;visibility: visible !important;width: 677px !important;"><span style="letter-spacing: 0.544px;font-size: 14px;">如有收获,点个在看,诚挚感谢</span><img data-ratio="1" data-type="png" data-w="19" width="19px" src="/upload/5f7454d24a334e968c32b8ce9ae53c5.png" style="font-size: 16px;letter-spacing: 0.544px;display: inline-block;vertical-align: text-bottom;box-sizing: border-box !important;visibility: visible !important;width: 19px !important;"></p>
作者:微信小助手
<p style="text-align: center;"><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;text-align: center;max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">(给</span><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;text-align: center;max-width: 100%;line-height: 22.4px;color: rgb(0, 128, 255);">ImportNew</span><span style="font-size: 14px;letter-spacing: 0.5440000295639038px;text-align: center;max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">加星标,提高Java技能)</span></p> <blockquote> <p><span style="font-size: 14px;">转自:CSDN 作者:不送花的程序员</span><br></p> <p style="text-align: start;">blog.csdn.net/Howinfun/article/details/105843500</p> </blockquote> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">你以为 Redis 这么快仅仅因为单线程和基于内存?</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">那么你想得太少了,我个人认为 Redis 的快是基于多方面的:不但是单线程和内存,还有底层的数据结构设计,网络通信的设计,主从、哨兵和集群等等方面的设计~</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">下面,我将 360° 为你揭开 Redis QPS达到10万/秒的神秘面纱。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <h3 cid="n4" mdtype="heading" style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;letter-spacing: normal;">一、底层数据结构设计</span></strong></span></h3> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;letter-spacing: normal;"><br></span></strong></span></p> <h4 cid="n5" mdtype="heading" style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;letter-spacing: normal;">1、底层架构:</span></strong></span></h4> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">首先值得称赞的第一点:Redis 底层使用的数据结构很多,但是却没有直接使用这些数据结构来实现键值对数据库,而是基于数据结构创建了一个对象(redisObject)系统。(是不是觉得有点面向对象编程的意思 😂 ~)</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">对象系统里面包括了字符串对象,列表对象,哈希对象、集合对象和有序集合对象。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">使用对象的好处:</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <ul class="list-paddingleft-2" cid="n9" mdtype="list" data-mark="-"> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">Redis 在执行命令之前,可以根据对象的类型判断这个对象是否可以执行给定的命令。</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">可以针对不用的使用场景,为对象设置多种不同的数据结构实现,从而优化对象在不同场景下的使用效率。</span> </section></li> </ul> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">一个对象怎么设置不同的数据结构实现?</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">在讲解前,我们必须要了解 Redis 对象的结构。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">它三个重要的部分:type 属性、encoding 属性,和 ptr 属性。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">我们用字符串对象为例:</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">我们都知道,Redis 的 SET 命令其实是针对字符串的,但是它也可以设置数值。那底层是怎么做的呢?</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">它会将 String 对象的 encoding 属性标识为 REDIS_ENCODING_INT,表示这个键对应的值是 Long 类型的整数。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;text-align: center;"> <img class="rich_pages" data-ratio="0.72544080604534" data-s="300,640" data-type="png" data-w="397" src="/upload/f15dd58d755d547d9350cdbf5db0f1db.png" style="box-sizing: border-box !important;overflow-wrap: break-word !important;width: 397px;visibility: visible !important;height: auto;"> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">而当我们利用 APPEND 命令往值后面添加字符串呢?<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">此时会将 String 对象的 encoding 属性的标识为 REDIS_ENCODING_RAW,表示这个值此时是简单动态字符串。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;text-align: center;"> <img class="rich_pages" data-ratio="0.42380261248185774" data-s="300,640" data-type="png" data-w="689" src="/upload/342fd2a512d25bc967d32000d39edfd2.png" style="box-sizing: border-box !important;overflow-wrap: break-word !important;width: 677px !important;visibility: visible !important;"> </section> <section style="line-height: 1.5em;"> <br> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">正是因为使用对象,通过 type、encoding和prt 属性,使得同一个对象可以适应在不同的场景下,使得不同的改变不需要创建新的键值对,这样使得 Redis 的对象使用效率非常的高。</span> <br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <h4 cid="n25" mdtype="heading" style="line-height: 1.5em;"><strong><span style="font-size: 15px;letter-spacing: normal;color: rgb(171, 25, 66);">2、灵活的字符串对象</span></strong></h4> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">Redis 的字符串对象采用三种编码:int、embstr 和 raw。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">int 编码就不用说了,就是为了兼容 SET 命令可以设置数值。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">而 embstr 和 raw 最大的区别就是内存分配操作次数:</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <ul class="list-paddingleft-2" cid="n29" mdtype="list" data-mark="-"> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">embstr 编码专门用于保存短字符串,所以它是通过调用一次内存分配函数来分配一块连续的空间,空间包含 redisObject 和 sdsshdr 两个结构,这样可以很好地利用缓存带来的优势。</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">raw 编码则是用于保存长字符串,它通过调用两次内存分配函数来分别创建 redisObject 结构和 sdshdr 结构</span> </section></li> </ul> <h4 cid="n34" mdtype="heading" style="line-height: 1.5em;"><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></h4> <h4 cid="n34" mdtype="heading" style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;letter-spacing: normal;">3、绝妙的字符串优化策略</span></strong></span></h4> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">Redis 中字符串对象的底层是使用 SDS (Simple Dynamic String)实现的。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">SDS 有三部分:</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <ul class="list-paddingleft-2" cid="n37" mdtype="list" data-mark="-"> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">len:记录 buf 数组中已使用字节的数量,等于 SDS 锁保存字符串的长度</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">free:记录 buf 数组中未使用字节的数量</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">buf[]:字节数组,用于保存字符串</span> </section></li> </ul> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">首先介绍一下使用 len 属性和 free 属性的好处:</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">得益于 SDS 有 len 属性,获取字符串长度的复杂度为 O(1);</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">得益于 SDS 有 free 属性,可以杜绝缓冲区溢出,字符串扩展前可以根据 free 属性来判断是否满足直接扩展,不满足则需要先执行内存重分配操作,然后再扩展字符串。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">我们都知道修改字符串长度很有可能导致触发内存重分配操作,但是 Redis 对于内存重分配有两个优化策略:</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">空间预分配:</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <ul class="list-paddingleft-2" cid="n49" mdtype="list" data-mark="-"> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">空间预分配用于优化 SDS 的字符串增长操作:当 SDS 的API对一个 SDS 进行修改,并且需要对 SDS 进行空间扩展的时候,程序不仅会为 SDS 分配修改所必须要的空间,还会为 SDS 分配额外的未使用空间,并使用 free 属性来记录这些额外分配的字节的数量。</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">通过空间预分配策略,下次字符串扩展时,可以充分利用上次预分配的未使用空间,而不用再触发内存重分配操作了。</span> </section></li> </ul> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">惰性空间释放:</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <ul class="list-paddingleft-2" cid="n55" mdtype="list" data-mark="-"> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">惰性空间释放用于优化 SDS 的字符串缩短操作:当 SDS 的API需要缩短 SDS 保存的字符串时,程序并不立即使用内存重分配来回收缩短后多出来的字节,而是使用上面提到的 free 属性将这些字节的数量记录起来,并等待将来使用。</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">通过惰性空间释放策略,SDS 避免了缩短字符串时所需的内存重分配操作,并为将来可能有的增长操作提供了优化。</span> </section></li> </ul> <h4 cid="n60" mdtype="heading" style="line-height: 1.5em;"><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></h4> <h4 cid="n60" mdtype="heading" style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;letter-spacing: normal;">4、字符串变量的共享和适配</span></strong></span></h4> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">对象中使用数字是非常常见的,例如设置用户的年龄、学生的分数、博客中文章的排名等等。所以 Redis 为了避免重复创建数字对应的字符串对象,它会将一个范围的整数对应的字符串对象用来共享。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">目前来说,Redis 会在初始化服务器时,创建一万个字符串对象,这些对象包含了从 0 到 9999 的所有整数值,当服务器需要用到值为 0 到 9999 的字符串对象时,服务器就会使用这些共享对象,而不是新创建对象。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">当然了,我们还可以通过修改 redis.h/REDIS_SHARED_INTEGERS 常量来修改创建共享字符串对象的数量。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">我们都知道 Redis 是使用 C 语言开发的,所以 SDS 一样遵循 C 字符串以空字符结尾的惯例,所以 SDS 可以重用很多 <string.h> 库定义的函数。</span> </section> <p style="line-height: 1.5em;"><br></p> <h4 cid="n65" mdtype="heading" style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;letter-spacing: normal;">5、强大的压缩列表 ziplist</span></strong></span></h4> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">简单介绍一下 ziplist 的结构:</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;text-align: center;"> <img class="rich_pages" data-ratio="0.1572700296735905" data-s="300,640" data-type="png" data-w="674" src="/upload/d5de663baadfb61ce5b12351efc2d574.png" style="box-sizing: border-box !important;overflow-wrap: break-word !important;width: 674px !important;visibility: visible !important;"> </section> <section style="line-height: 1.5em;"> <br> </section> <ul class="list-paddingleft-2" cid="n68" mdtype="list" data-mark="-"> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">zlbytes:记录整个压缩列表占用的内存字节数;在对压缩列表进行内存重分配时,或者计算 zlend 的位置时使用</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">zltail:记录压缩列表表尾节点距离压缩列表的起始地址有多少字节;通过这个偏移量,程序无须遍历整个压缩列表就可以确定表尾节点的地址</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">zlen:记录了压缩列表包含的节点数量;当这个属性的值大于 UINT16_MAX(65535)时,节点的真实数量需要遍历整个压缩列表才能计算出来。</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">entryX:压缩列表的包含的各个节点,节点的长度由节点保存的内容决定。</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">zlend:特殊值0XFF(十进制255),用于标记压缩列表的末端。</span> </section></li> </ul> <section style="line-height: 1.5em;"> <br> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">压缩列表是一种为节约内存而开发的顺序型数据结构,所以在 Redis 里面压缩列表被用做列表键和哈希键的底层实现之一。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <ul class="list-paddingleft-2" cid="n80" mdtype="list" data-mark="-"> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">当一个列表键只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做列表键的底层实现。</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">当一个哈希键只包含少量键值对,并且每个键值对的键和值要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做哈希键的底层实现。</span> </section></li> </ul> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">正是利用压缩列表,不但使得数据非常紧凑而节约内存,而且还可以利用它的结构来做到非常简单的顺序遍历、逆序遍历,O(1) 复杂度的获取长度和所占内存大小等等。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <h4 cid="n86" mdtype="heading" style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;letter-spacing: normal;">6、整数集合 intset 的升级策略</span></strong></span></h4> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">整数集合(intset)是 Redis 用于保存整数值的集合抽象数据结构,它可以保存类型为 int16_t、int32_t 或者 int64_t 的整数值,并且保证集合中不会出现重复元素。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">我们先看看整数集合的结构:</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><pre style="overflow-x:auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;display: block;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;min-width: 400px;font-weight: 400;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);" data-wx-hl-code="typeof struct intset{&lt;br/&gt; uint32_t encoding;&lt;br/&gt; uint32_t length;&lt;br/&gt; int8_t contents[];&lt;br/&gt;} intset;" data-wx-hl-lang="Java" data-wx-hl-style="atom-one-dark">typeof struct intset{<br> uint32_t encoding;<br> uint32_t length;<br> int8_t contents[];<br>} intset;</code></pre></span> </section> <p style="line-height: 1.5em;"><br></p> <p style="line-height: 1.5em;"><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">虽然 intset 结构将 contents 属性声明为 int8_t类型的数组,但实际上 contents 数组并不保存任何 int8_t 类型的值,contents 数组的真正类型取决于 encoding 属性的值。</span></p> <section style="line-height: 1.5em;"> <br> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">intset 一开始不会直接使用最大类型来定义数组,而是利用升级操作,当元素的值达到一定长度时,会重新为数组分配内存空间,并将数组里的旧元素的类型进行升级。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">这样做好处:</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <ul class="list-paddingleft-2" cid="n93" mdtype="list" data-mark="-"> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">避免错误类型,能自适应新添加的新元素的长度。只要是升级了,那么小于对应的长度的数值都可以存进来,而如果长度不足,大不了再升级一次即可。而且,intset 最多就升级两次,不用担心升级次数多而导致性能降低。</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">节约内存,只要当需要时才会进行升级操作,这样可以很好地节省内存。</span> </section></li> </ul> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">因为整数集合没有降级操作,所以从另外一个角度看,升级操作其实也会浪费内存:如果整数集合里只有一个数值是 int64_t ,而其他数值都是小于它的,但是整数集合的编码将还是保持 INTSET_ENC_INT64,就是说,小于 int64_t 的整数还是会用 int64_t 的空间来保存。</span> </section> <section style="line-height: 1.5em;"> <br> </section> <h3 cid="n100" mdtype="heading" style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;letter-spacing: normal;">二、单机数据库实现设计</span></strong></span></h3> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;letter-spacing: normal;"><br></span></strong></span></p> <h4 cid="n101" mdtype="heading" style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;letter-spacing: normal;">1、Reactor的I/O多路复用</span></strong></span></h4> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">每当别人问 Redis 为啥这么快?吐口而出的不是基于内存就是基于单线程。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">Redis 使用基于 Reactor 模式实现的网络通信,它使用 I/O 多路复用(multiplexing)程序来同时监听多个套接字,并根据套接字目前执行的任务来为套接字关联不同的事件处理器。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关闭(close)等操作时,与操作相对应的文件事件就会产生,这时文件事件分派器就会调用套接字之前关联好的事件处理器来处理这些事件。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">因为 Redis 是单线程的,所以I/O多路复用程序会利用队列来控制产生事件的套接字的并发;队列中的套接字以有序、同步、每次一个的方式分派给文件事件分派器。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;text-align: center;"> <img class="rich_pages" data-ratio="0.5299727520435967" data-s="300,640" data-type="png" data-w="734" src="/upload/62cdf9674b16069a1f514721cd18857a.png" style="box-sizing: border-box !important;overflow-wrap: break-word !important;width: 677px !important;visibility: visible !important;"> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">多种 I/O 复用机制:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">常见的 I/O 复用机制有很多种,例如 select、epoll、evport 和 kqueue 等等。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">Redis 对上面的多种 I/O 复用机制都进行了各自的封装,在程序编译时会自动选择系统中性能最高的 I/O 多路复用函数库来作为 Redis 的 I/O 多路复用程序的底层实现。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <h4 cid="n110" mdtype="heading" style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;letter-spacing: normal;">2、同步处理的文件事件和时间事件</span></strong></span></h4> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">我们都知道,文件事件的发生都是随机的,因为 Redis 服务器永远不可能知道客户端下次发送命令是什么时候,所以程序也不可能一直阻塞着直到发生文件事件。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">毕竟 Redis 是单线程的,文件事件的处理和时间事件的处理都在同一个线程里,如果线程被 aeApiPoll 函数一直阻塞着,那么即使时间事件的时间到了,也得不到资源来执行。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">所以 Redis 有这么一个策略,aeApiPoll 函数的最大阻塞时间由到达时间最接近当前时间的时间事件决定,这个方法既可以避免服务器对时间事件进行频繁的轮询(忙等待),也可以确保 aeApiPoll 函数不会阻塞过长时间。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">对文件事件和时间事件的处理都是同步、有序、原子地执行的,服务器不会中途中断事件处理,也不会对事件进行抢占,因此,不管是文件事件的处理器,还是时间事件的处理器,它们都会尽可地减少程序的阻塞时间,并在有需要时主动让出执行权,从而降低造成事件饥饿的可能性。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <h4 cid="n115" mdtype="heading" style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;letter-spacing: normal;">3、当前时间缓存</span></strong></span></h4> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">Redis 服务器中不少功能是要使用系统的当前时间的,而获取系统当前时间需要执行一次系统调用。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">为了减少系统调用,提升性能,服务器状态(redisServer)中的 unixtime 属性和 mstime 属性分别保存了秒级精度的系统当前 UNIX 时间戳和毫秒级精度的系统当前 UNIX 时间戳;然后 serverCron 函数会每隔 100 毫秒更新一次这两个属性。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="letter-spacing: normal;color: rgb(0, 0, 0);font-size: 15px;">这两个时间只会用在对时间精确度要求不高的功能上,例如打印日志、计算服务器上线时间等等。像设置键过期时间、添加慢查询日志这种需要时间精确度高的功能上,服务器还是会每次都调用系统来获取。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <h3 cid="n121" mdtype="heading" style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;letter-spacing: normal;">三、多机数据库实现设计</span></strong></span></h3> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;letter-spacing: normal;"><br></span></strong></span></p> <h4 cid="n122" mdtype="heading" style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;letter-spacing: normal;">1、主从模式 -> 复制算法优化</span></strong></span></h4> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">Redis 2.8 前的复制功能:</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <ul class="list-paddingleft-2" cid="n124" mdtype="list" data-mark="-"> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">从服务器向主服务器发送 SYNC 命令。</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">主服务器收到 SYNC 命令后,后台生成一个 RDB 文件(BGSAVE),并使用一个缓冲区记录从现在开始执行的所有写命令。</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">当主服务器的 BGSAVE 命令执行完毕,将生成的 RDB 文件发送给从服务器;从服务器接收并载入这个 RDB 文件。</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">主服务器将缓冲区里的所有写命令发送给从服务器;从服务器执行这些写命令。</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">至此,主从服务器两者的数据库将达到一致状态。</span> </section></li> </ul> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">缺点:</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">假设主从服务器断开连接,当从服务器重新连接上后,又要重新执行一遍同步(sync)操作;但是其实,从服务器重新连接时,数据库状态和主服务器大致是一样的,缺少的只是断开连接过程中,主服务器接收到的写命令;每次断线后都需要重新执行一遍完整的同步操作,这样会很浪费主服务器的性能,毕竟 BGSAVE 命令要读取此时主服务器完整的数据库状态。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">Redis 2.8 后对复制算法进行了很大的优化:</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">利用 PSYNC 命令代替 SYNC 命令,将复制操作分为完整重同步和部分重同步。只有当从服务器第一次复制或断开时间过长时,才会执行完整重同步,而从服务器短时间断开重连后,只需要将自己的 offset(复制偏移量)发送给主服务器,主服务器会根据从服务器的 offset 和自己的 offset,然后从复制积压缓冲区里将从服务器丢失的写命令发送给从服务器,从服务器只要接收并执行这些写命令,就可以将数据库更新至主服务器当前所处的状态。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <h4 cid="n139" mdtype="heading" style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;letter-spacing: normal;">2、主从模式 -> 心跳检测</span></strong></span></h4> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">在命令传播阶段,从服务器默认会以每秒一次的频率,向主服务器发送命令:REPLCONF ACK <replication_offset>,其中 replication_offet 是从服务器当前的复制偏移量。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">心跳检测的三大作用:</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <ul class="list-paddingleft-2" cid="n142" mdtype="list" data-mark="-"> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">心跳检测不但可以检测主从服务器之间的网络状态。</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">从服务器还会将它的复制偏移量发送给主服务器,让主服务器检查从服务器的命令是否丢失了。</span> </section><p><br></p></li> <li style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">心跳检测还能辅助实现 min-slaves 配置选项:</span> </section></li> </ul> <section style="line-height: 1.5em;"> <br> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><pre style="overflow-x:auto;"><code style="font-size: 0.85em;font-family: Consolas, Menlo, Courier, monospace;margin: 0px 0.15em;white-space: pre;overflow: auto;display: block;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;min-width: 400px;font-weight: 400;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);" data-wx-hl-code="min-slaves-to-write 3&lt;br/&gt;min-slaves-max-lag 10&lt;br/&gt;解释:那么在从服务器的数量少于3个,或者三个从服务器的延迟(lag)值都大于或等于10秒时;&lt;br/&gt;主服务器将拒绝执行写命令,这里的延迟值就是上面提到的INFO replication命令的lag值。" data-wx-hl-lang="Java" data-wx-hl-style="atom-one-dark">min-slaves-to-write <span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">3</span><br>min-slaves-max-lag <span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">10</span><br>解释:那么在从服务器的数量少于<span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">3</span>个,或者三个从服务器的延迟(lag)值都大于或等于<span style="color: rgb(209, 154, 102);font-weight: 400;font-style: normal;">10</span>秒时;<br>主服务器将拒绝执行写命令,这里的延迟值就是上面提到的INFO replication命令的lag值。</code></pre></span> </section> <section style="line-height: 1.5em;"> <br> </section> <section style="line-height: 1.5em;"> <span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;letter-spacing: normal;">3、哨兵模式的订阅连接设计</span></strong></span> </section> <section style="line-height: 1.5em;"> <br> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">Sentinel 不但会与主从服务器建立命令连接,还会建立订阅连接。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">在默认情况下,Sentinel会以每两秒一次的频率,通过命令连接向所有被监视的主服务器和从服务器发送 PUBLISH 命令,命令附带的是 Sentinel 本身的信息和所监听的主服务器的信息;接着接收到此命令的主从服务器会向 __sentinel__:hello 频道发送这些信息。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">而其他所有都是监听此主从服务器的 Sentinel 可以通过订阅连接获取到上面的信息。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">这也就是说,对于每个与 Sentinel 的服务器,Sentinel 既通过命令连接向服务器的 __sentinel__:hello 频道发送信息(PUBLISH),又通过订阅连接从服务器的 __sentinel__:hello 频道接收信息(SUBSCRIBE)。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">通过这种方式,监听同一个主服务器的 Sentinel 们可以互相知道彼此的存在,并且可以根据频道消息更新主服务器实例结构(sentinelRedisInstance)的 sentinels 字典,还可借此与其他 Sentinel 建立命令连接,方便之后关于主服务器下线检查、选举领头 Sentinel 等等的通信。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <h4 cid="n156" mdtype="heading" style="line-height: 1.5em;"><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;letter-spacing: normal;">4、集群模式中的 Gossip协议</span></strong></span></h4> <p><span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span></p> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">Redis 集群中的各个节点通过 Gossip 协议来交换各自关于不同节点的状态信息,其中 Gossip 协议由 MEET、PING、PONG 三种消息实现,这三种消息的正文都由两个 cluster.h/clusterMsgDataGossip 结构组成。</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">利用 Gossip 协议,可以使得集群中节点更新的信息像病毒一样扩散,这样不但扩散速度快,而且不需要每个节点之间都发送一次消息才能同步集群中最新的信息。</span> </section> <section style="line-height: 1.5em;"> <br> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">至此,我自己能想到的使得 Redis 性能优越的设计都在这里了。当然了,它的厉害之处远远不止这些~</span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;"><br></span> </section> <section style="line-height: 1.5em;"> <span style="font-size: 15px;color: rgb(0, 0, 0);letter-spacing: normal;">大家都知道,使用 Redis 是非常简单的,来来去去就几个命令,但是当你深入 Redis 底层的设计和实现,你会发现,这真的是一个非常值得大家深究的开源中间件!!!</span> </section> <p><span style="font-size: 15px;text-align: left;"><br></span></p> <section donone="shifuMouseDownCard('shifu_c_030')" label="Copyright Reserved by PLAYHUDONG." style="text-align: start;white-space: normal;margin-top: 1em;margin-bottom: 1em;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);border-width: 0px;border-style: initial;border-color: initial;"> <section style="margin-left: 1em;line-height: 1.4;"> <span style="padding: 3px 8px;border-top-left-radius: 4px;border-top-right-radius: 4px;border-bottom-right-radius: 4px;border-bottom-left-radius: 4px;color: rgb(255, 255, 255);background-color: rgb(255, 105, 31);font-family: inherit;text-align: inherit;text-decoration: inherit;font-size: 16px;">推荐阅读</span> <span style="margin-left: 4px;padding: 3px 8px;border-top-left-radius: 1.2em;border-top-right-radius: 1.2em;border-bottom-right-radius: 1.2em;border-bottom-left-radius: 1.2em;color: rgb(255, 255, 255);line-height: 1.2;background-color: rgb(204, 204, 204);font-family: inherit;text-align: inherit;text-decoration: inherit;border-color: rgb(249, 110, 87);font-size: 12px;">点击标题可跳转</span> </section> <section style="margin-top: -11px;padding: 22px 16px 16px;border-width: 1px;border-style: solid;border-color: rgb(255, 105, 31);color: rgb(51, 51, 51);font-size: 1em;font-family: inherit;text-align: inherit;text-decoration: inherit;"> <p><span style="font-size: 12px;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-decoration: underline;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&mid=2651487516&idx=1&sn=90a0a4c2dd0b736f00355948066e5c7c&chksm=bd2511638a529875d4c5455f4ead117e5a425c167e2c911e6994c3fcd2e5585fba3b29c69439&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2">Redis 的这些拓展方案</a><br></span></p> <p><span style="font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 12px;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-decoration: underline;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&mid=2651486720&idx=1&sn=878b78b200d07271940a4af638f094f8&chksm=bd25167f8a529f698ed47607203af5c4189e027d8add66c6f79d9e3b861b3d84a90dc909583b&scene=21#wechat_redirect" data-itemshowtype="11" tab="innerlink" data-linktype="2">当 Redis 发生高延迟时,到底发生了什么</a><br></span></p> <p><span style="font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 12px;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-decoration: underline;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&mid=2651486646&idx=2&sn=8927618dfb883a5fa133bb4fc815720c&chksm=bd2515c98a529cdf64f20be0b677e2027a0266ba6141cef2091e0aeecc4845b00d6fd506a648&scene=21#wechat_redirect" data-itemshowtype="11" tab="innerlink" data-linktype="2">Mysql百万量级数据高效导入Redis</a></span><span style="font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 12px;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);"><br></span></p> </section> </section> <p style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);text-align: start;white-space: normal;"><br></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;color: rgb(255, 169, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;">看完本文有收获?请转发分享给更多人</span></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><strong style="max-width: 100%;color: rgb(255, 169, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;">关注「ImportNew」,提升Java技能</strong></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img data-ratio="0.9166666666666666" data-s="300,640" data-type="jpeg" data-w="600" width="auto" src="/upload/899866149276fa5fddb73c61ae04be64.jpg" style="box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 600px !important;"></p> <p style="text-align: right;"><span style="font-size: 14px;text-align: right;">好文章,我</span><span style="font-size: 14px;text-align: right;color: rgb(255, 41, 65);">在看</span><span style="font-size: 14px;text-align: right;">❤️</span></p>
作者:微信小助手
<section style="display: none;" data-tools="新媒体管家" data-label="powered by xmt.cn"> <br> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-top: -10px;"> <section style="text-align: right;"> <img data-backh="314" data-backw="558" data-ratio="0.5629139072847682" src="/upload/77272772ca742bb70f2e997e659bb175.jpg" data-type="jpeg" data-w="604" style="width: 100%;height: auto;box-shadow: rgb(170, 170, 170) 0px 0px 14px 0px;border-radius: 12px;"> <span style="font-size: 14px;color: rgb(123, 127, 131);"> <span style="color: rgb(123, 127, 131);font-size: 14px;font-family: -apple-system, system-ui, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;text-align: start;background-color: rgb(248, 248, 248);">Srinath</span>|<span style="color: rgb(123, 127, 131);font-size: 14px;font-family: -apple-system, system-ui, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;text-align: start;background-color: rgb(248, 248, 248);">ImportSource</span></span> </section> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">今天把 RPC 框架乞丐版差不多写完了(断断续续写了差不多一个月),下周README写完之后就开源出来。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><img data-ratio="0.592707045735476" src="/upload/d7bf8493c761dd503b934f4491c084d.png" data-type="png" data-w="3236" style="color: rgb(89, 89, 89);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;text-align: left;white-space: normal;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><span style="color: rgb(89, 89, 89);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;text-align: left;">今天</span>本来预留有时间写一篇闲文的,关于时间管理的(小伙伴们问的比较多)。我寻思自己都没管理好时间,索性就先不写,后面等我收集点资料之后再写吧!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);text-align: center;"><img data-ratio="0.9473684210526315" src="/upload/fc996711a0083a47e608af6914abded0.gif" data-type="gif" data-w="247"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">今天还是安利一篇不错的文章给小伙伴们!(流下了明日又要上班的泪水~~~)</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">本文作者叫 Srinath,是一位科学家,软件架构师,也是一名在分布式系统上工作的程序员。他是 Apache Axis2 项目的联合创始人,也是 Apache Software 基金会的成员。他是 WSO2 流处理器(wso2.com/analytics)的联席架构师。Srinath 撰写了两本关于 MapReduce 和许多技术文章的书。他获得了博士学位。来自美国印第安纳大学。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">Srinath 通过不懈的努力最终总结出了 30 条架构原则,他主张架构师的角色应该由开发团队本身去扮演,而不是专门有个架构师团队或部门。Srinath 认为架构师应该扮演的角色是一个引导者,讨论发起者,花草修建者,而不是定义者和构建者。Srinath 为了解决团队内部的架构纷争和抉择,制定了以下 30 条原则,这些原则被成员们广泛认可,也成为了新手架构师的学习途径。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;font-weight: bold;font-size: 22px;border-bottom: 2px solid rgb(89, 89, 89);margin-bottom: 30px;color: rgb(89, 89, 89);"><span style="display: none;"></span><span style="display: inline-block;border-bottom: 2px solid rgb(89, 89, 89);">基本原则</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 1:</strong> KISS(Keep it simple,sutpid) :保持每件事情都尽可能的简单。用最简单的解决方案来解决问题。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><em style="color: rgb(76, 156, 215);">Guide:简单即是复杂!拿你的代码来说,你想要写的简单且容易理解的话,你就需要花更多的时间去思考。</em></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 2:</strong> YAGNI(You aren’t gonna need it):不要去搞一些不需要的东西,需要的时候再搞吧。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><em style="color: rgb(76, 156, 215);">Guide : 这一点我被 diss 过好几次,之前的时候,我总是臆想觉得某个功能以后可能会用到,然后就顺手把它实现了,实际到了后面并没用上,反而造成了代码冗余。</em></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 3:</strong> 爬,走,跑。换句话说就是先保证跑通,然后再优化变得更好,然后继续优化让其变得伟大。迭代着去做事情,敏捷开发的思路。对于每个功能点,创建里程碑(最大两周),然后去迭代。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 4:</strong> 创建稳定、高质量的产品的唯一方法就是自动化测试。所有的都可以自动化,当你设计时,不妨想想这一点。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><em style="color: rgb(76, 156, 215);">Guide:单侧还是很有必要的,但是没有一个恒定的标准说你应该怎么去做。</em></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 5:</strong> 时刻要想投入产出比(ROI)。就是划得来不。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 6:</strong> 了解你的用户,然后基于此来平衡你需要做哪些事情。不要花了几个月时间做了一个 devops 用户界面,最后你发现那些人只喜欢命令行。此原则是原则 5 的一个具体表现。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><em style="color: rgb(76, 156, 215);">Guide:是否有站在用户的角度思考问题呢?是否是为了用新技术而用新技术?</em></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 7:</strong> 设计和测试一个功能,尽可能的独立。当你做设计时,应该想想这一条。从长远来看这能给你解决很多问题,否则你的功能只能等待系统其他所有的功能都就绪了才能测试,这显然很不好。有了这个原则, 你的版本将会更加的顺畅。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 8:</strong> 不要搞花哨的。我们都喜欢高端炫酷的设计。最后我们搞了很多功能和解决方案到我们的架构中,然后这些东西根本不会被用到。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><em style="color: rgb(76, 156, 215);">Guide:简单点!说话的方式简单点!</em></p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;font-weight: bold;font-size: 22px;border-bottom: 2px solid rgb(89, 89, 89);margin-bottom: 30px;color: rgb(89, 89, 89);"><span style="display: none;"></span><span style="display: inline-block;border-bottom: 2px solid rgb(89, 89, 89);">功能选择</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 9:</strong> 不可能预测到用户将会如何使用我们的产品。所以要拥抱 MVP(Minimal Viable Product),最小可运行版本。这个观点主要思想就是你挑几个很少的使用场景,然后把它搞出来,然后发布上线让用户使用,然后基于体验和用户反馈再决定下一步要做什么。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 10:</strong> 尽可能的做较少的功能。当有疑问的时候,就不要去做,甚至干掉。很多功能从来不会被使用。最多留个扩展点就够了。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 11:</strong> 等到有人提出再说(除非是影响核心流程,否则就等到需要的时候再去做)。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 12:</strong> 有时候你要有勇气和客户说不。这时候你需要找到一个更好的解决方案来去解决。记住亨利福特曾经说过的 :”如果我问人们他们需要什么,他们会说我需要一匹速度更快的马”。记住:你是那个专家,你要去引导和领导。要去做正确的事情,而不是流行的事情。最终用户会感谢你为他们提供了汽车。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;font-weight: bold;font-size: 22px;border-bottom: 2px solid rgb(89, 89, 89);margin-bottom: 30px;color: rgb(89, 89, 89);"><span style="display: none;"></span><span style="display: inline-block;border-bottom: 2px solid rgb(89, 89, 89);">服务端设计和并发</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 13:</strong> 要知道一个 server 是如何运行的,从硬件到操作系统,直到编程语言。优化 IO 调用的数量是你通往最好架构的首选之路。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 14:</strong> 要了解 Amdhal 同步定律。在线程之间共享可变数据会让你的程序变慢。只在必要的时候才去使用并发的数据结构,只在必须使用同步(synchronization)的时候才去使用同步。如果要用锁,也要确保尽可能少的时间去 hold 住锁。如果要在加锁后做一些事情,要确保自己在锁内会做哪些事情。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 15:</strong> 如果你的设计是一个无阻塞且事件驱动的架构,那么千万不要阻塞线程或者在这些线程中做一些 IO 操作,如果你做了,你的系统会慢的像骡子一样。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;font-weight: bold;font-size: 22px;border-bottom: 2px solid rgb(89, 89, 89);margin-bottom: 30px;color: rgb(89, 89, 89);"><span style="display: none;"></span><span style="display: inline-block;border-bottom: 2px solid rgb(89, 89, 89);">分布式系统</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 16:</strong> 无状态的系统的是可扩展的和直接的。任何时候都要考虑这一点,不要搞个不可扩展的,有状态的东东出来,这是起码的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 17:</strong> 保证消息只被传递一次,不管失败,这很难,除非你要在客户端和服务端都做控制。试着让你的系统更轻便(使用原则 18)。你要知道大部分的承诺 exactly-once-delivery 的系统都是做了精简的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 18:</strong> 实现一个操作尽可能的幂等。这样的话就比较好恢复,而且你还处于至少一次传递(at least once delivery)的状态。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 19:</strong> 知道 CAP 理论。可扩展的事务(分布式事务)是很难的。如果可能的的话,尽可能的使用补偿机制。RDBMS 事务是无法扩展的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 20:</strong> 分布式一致性无法扩展,也无法进行组通信,也无法进行集群范围内的可靠通信。理想情况下最大的节点限制为 8 个节点。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 21:</strong> 在分布式系统中,你永远无法避免延迟和失败。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;font-weight: bold;font-size: 22px;border-bottom: 2px solid rgb(89, 89, 89);margin-bottom: 30px;color: rgb(89, 89, 89);"><span style="display: none;"></span><span style="display: inline-block;border-bottom: 2px solid rgb(89, 89, 89);">用户体验</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 22:</strong> 要了解你的用户和清楚他们的目标。他们是新手、专家还是偶然的用户?他们了解计算机科学的程度。极客喜欢扩展点,开发者喜欢示例和脚本,而普通人则喜欢 UI。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 23:</strong> 最好的产品是不需要产品手册的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><em style="color: rgb(76, 156, 215);">Guide:这个是说产品易用。很多人觉得敏捷开发下不需要文档,实际上,一个系统即是在敏捷开发的情况下,有些必要的文档比如重大更新记录、相关硬件设施等等还是需要的。</em></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 24:</strong> 当你无法在两个选择中做决定的时候,请不要直接把这个问题通过提供配置选项的方式传递给用户。这样只能让用户更加的发懵。如果连你这个专家都无法选择的情况下,交给一个比你了解的还少的人这样合适吗?最好的做法的是每次都找到一个可行的选项;次好的做法是自动的给出选项,第三好的做法是增加一个配置参数,然后设置一个合理的默认值。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 25:</strong> 总是要为配置设置一个合理的默认值。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 26:</strong> 设计不良的配置会造成一些困扰。应该总是为配置提供一些示例值。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 27:</strong> 配置值必须是用户能够理解和直接填写的。比如:不能让用户填写最大缓存条目的数量,而是应该让用户填写可被用于缓存的最大内存。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 28:</strong> 如果输入了未知的配置要抛出错误。永远不要悄悄的忽略。悄悄的忽略配置错误往往是找 bug 花了数小时的罪魁祸首。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;font-weight: bold;font-size: 22px;border-bottom: 2px solid rgb(89, 89, 89);margin-bottom: 30px;color: rgb(89, 89, 89);"><span style="display: none;"></span><span style="display: inline-block;border-bottom: 2px solid rgb(89, 89, 89);">艰难的问题</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 29:</strong> 梦想着新的编程语言就会变得简单和明了,但往往要想真正掌握会很难。不要轻易的去换编程语言。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">原则 30:</strong> 复杂的拖拉拽的界面是艰难的,不要去尝试这样的效果,除非你准备好了 10 人年的团队。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">最后,说一个我的感受。在一个理想的世界里,一个平台应该是有多个正交组件组成-每个组件都负责一个方面(比如,security,messaging,registry,mdidation,analytics)。好像一个系统构建成这样才是完美的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">但不幸的是,现实中我们很难达到这样的状态。因为在项目初始状态时,很多事情是不确定的,你无法做到这样的独立性,现在我更倾向于在开始的时候适当的重复是必要的,当你尝试铲除他们的时候,你会发现引入了新的复杂性,分布本身就意味着复杂。有时候治愈的过程要比疾病本身更加的糟糕。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;font-weight: bold;font-size: 22px;border-bottom: 2px solid rgb(89, 89, 89);margin-bottom: 30px;color: rgb(89, 89, 89);"><span style="display: none;"></span><span style="display: inline-block;border-bottom: 2px solid rgb(89, 89, 89);">总结</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">作为一个架构师,应该像园丁一般,更多的是修剪花草,除草而不是去定义和构建,你应该策划而不是指挥,你应该去修剪而不是去定义,应该是讨论而不是贴标签。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">虽然在短期内可能会觉得也没什么,但从长远看,指导团队找到自己的方式会带来好处。如果你稍不留神,就很容易让架构成为一个空洞的词汇。比如设计者会说他的架构是错误的,但不知道为什么是错误的。一个避免这种情况的好办法就是有一个原则列表,这个原则列表是被广泛接受的,这个列表是人们讨论问题的锚点,也是新手架构师学习的路径。</p> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="margin-top: -10px;padding-right: 10px;padding-left: 10px;white-space: normal;letter-spacing: 0px;color: rgb(62, 62, 62);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);line-height: 1.6;word-break: break-word;overflow-wrap: break-word;"> <section data-role="outer" label="Powered by 135editor.com"> <section data-tools="135编辑器" data-id="97823"> <section style="margin: 10px auto;width: 70px;"> <img data-ratio="0.39285714285714285" src="/upload/e6c763c1663007073ae0e41d9eb1024d.png" data-type="png" data-w="112" data-width="100%" style="width: 70px;display: block;"> </section> </section> </section> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="margin-top: -10px;padding-right: 10px;padding-left: 10px;white-space: normal;letter-spacing: 0px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);line-height: 1.6;word-break: break-word;overflow-wrap: break-word;"> <section data-tools="135编辑器" data-id="92648"> <section style="margin-top: 15px;margin-right: auto;margin-left: auto;"> <section style="margin-right: auto;margin-left: auto;display: -webkit-flex;justify-content: center;"> <section style="margin-top: -14px;padding-right: 5px;padding-left: 5px;width: 55px;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;"> <br> </section> </section> </section> </section> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><span style="word-spacing: 0.1em;color: rgb(74, 74, 74);font-size: 16px;letter-spacing: 0.544px;">我的 75k Star 开源项目 JavaGuide 总结而成的PDF版本的</span><strong><span style="word-spacing: 0.1em;font-size: 16px;letter-spacing: 0.544px;color: rgb(61, 170, 214);"><a target="_blank" href="https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247486324&idx=1&sn=e8b690ddaedabc486bd399310105aad3&chksm=cea244bff9d5cda9a627fa65235be09e7b089e92cf49c0eb0ceb35b39bbed86c1fab0125f5af&token=1745528586&lang=zh_CN&scene=21#wechat_redirect" textvalue="《JavaGuide面试突击版》" tab="innerlink" data-linktype="2">《JavaGuide面试突击版》</a></span></strong><span style="word-spacing: 0.1em;color: rgb(74, 74, 74);font-size: 16px;letter-spacing: 0.544px;">,公众号后台回复“</span><strong><span style="word-spacing: 0.1em;font-size: 16px;letter-spacing: 0.544px;color: rgb(61, 170, 214);">面试突击</span></strong><span style="word-spacing: 0.1em;color: rgb(74, 74, 74);font-size: 16px;letter-spacing: 0.544px;">”即可获取最新版本!安排!</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;line-height: 1.75;letter-spacing: 0.2em;font-size: 14px;word-spacing: 0.1em;"><img data-backh="326" data-backw="518" data-ratio="0.6287037037037037" src="/upload/8cceb9ea994a531f83dfdc26a7d53fdc.jpg" data-type="jpeg" data-w="1080" style="margin-right: auto;margin-bottom: 25px;margin-left: auto;word-spacing: 1.4px;color: rgb(63, 63, 63);font-size: 16px;letter-spacing: 0.544px;display: block;width: 518px;border-radius: 4px;"></p> <section data-role="outer" label="Powered by 135editor.com"> <section data-tools="135编辑器" data-id="95098"> <section style="margin: 1em auto;"> <section style="display: flex;justify-content: center;align-items: center;"> <section data-width="50%" style="width: 224.672px;height: 1px;background: rgb(203, 203, 239);overflow: hidden;"> <br> </section> <section style="margin-right: 4px;margin-left: 4px;width: 125px;"> <img data-ratio="0.37423312883435583" src="/upload/1478f46b3ef0456859a11fecc0a5479a.gif" data-type="gif" data-w="163" style="width: 125px;display: block;"> </section> <section data-width="50%" style="width: 224.672px;height: 1px;background: rgb(203, 203, 239);overflow: hidden;"> <br> </section> </section> </section> </section> </section> </section> <p style="color: rgb(74, 74, 74);font-size: 15px;letter-spacing: 2px;white-space: normal;"><br></p> <p style="color: rgb(74, 74, 74);font-size: 15px;letter-spacing: 2px;white-space: normal;"><br></p> <p style="color: rgb(74, 74, 74);font-size: 15px;letter-spacing: 2px;white-space: normal;"><br></p> <section data-from="xmt-recommend" data-tools="新媒体排版" style="color: rgb(74, 74, 74);font-size: 15px;letter-spacing: 2px;white-space: normal;"> <a href="http://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247487467&idx=1&sn=c2d2bd28918b58d8646f1441791aeaaf&chksm=cea24020f9d5c9364afc22013fac3266fabffcaa370f708434a6a304274c6b1d8bce235b7a24&scene=21#wechat_redirect" target="_blank" data-linktype="2" style="width: 100%;"> <section style="margin: 16px;white-space: nowrap;text-align: left;border-radius: 4px;box-sizing: border-box;overflow: hidden;box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 12px 0px;height: 154px;"> <section style="bottom: 0px;box-sizing: border-box;text-overflow: ellipsis;overflow: hidden;width: 516px;height: 154px;"> <section class="h5_image_link" style="font-size: inherit;display: inherit;width: 516px;height: 154px;background-size: cover;background-position: 50% center;background-image: url("https://mmbiz.qpic.cn/mmbiz_jpg/iaIdQfEric9TyPxLf7muN22ZqLbx05hvTyG6kh2Vp2WYQYT76h0NEXCfe08MPsQYbhhKibaicXUKkOFd4elaFDibcmQ/640?wx_fmt=jpeg");z-index: -1;"> </section> <section style="margin-top: -35px;padding: 5px 16px;bottom: 0px;width: 516px;box-sizing: border-box;background-color: rgba(0, 0, 0, 0.5);color: rgb(255, 255, 255);text-overflow: ellipsis;overflow: hidden;font-size: 16px;"> 解放双手,再来推荐5个Java项目开发快速开发脚手架!项目经验和私活都不愁了! </section> </section> </section></a> </section> <section data-from="xmt-recommend" data-tools="新媒体排版" style="color: rgb(74, 74, 74);font-size: 15px;letter-spacing: 2px;white-space: normal;"> <a href="http://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247487365&idx=1&sn=05940a5b088cbb4b0cb546785993feba&chksm=cea2404ef9d5c9580d8af22b9da03621d49c9cb4829d7fb146ddb19c0c25fb3d2cc6ba539be8&scene=21#wechat_redirect" target="_blank" data-linktype="2" style="width: 100%;"> <section style="margin: 16px;white-space: nowrap;text-align: left;border-radius: 4px;box-sizing: border-box;overflow: hidden;box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 12px 0px;height: 154px;"> <section style="bottom: 0px;box-sizing: border-box;text-overflow: ellipsis;overflow: hidden;width: 516px;height: 154px;"> <section class="h5_image_link" style="font-size: inherit;display: inherit;width: 516px;height: 154px;background-size: cover;background-position: 50% center;background-image: url("https://mmbiz.qpic.cn/mmbiz_jpg/iaIdQfEric9TwibUN7wYo2bkIvf8Ow9xMClxicibEwLQyCyPrUrWBpicKEdicbGiaXunM0pfbb0U5okCr57nJ1Jeic9BqaQ/640?wx_fmt=jpeg");z-index: -1;"> </section> <section style="margin-top: -35px;padding: 5px 16px;bottom: 0px;width: 516px;box-sizing: border-box;background-color: rgba(0, 0, 0, 0.5);color: rgb(255, 255, 255);text-overflow: ellipsis;overflow: hidden;font-size: 16px;"> 10分钟白嫖我常用的20个在线工具类网站清单。 </section> </section> </section></a> </section> <section data-from="xmt-recommend" data-tools="新媒体排版" style="color: rgb(74, 74, 74);font-size: 15px;letter-spacing: 2px;white-space: normal;"> <a href="http://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247487146&idx=1&sn=af093802965b99f7102cc19fe1695693&chksm=cea24161f9d5c877d717e9372de67fd2fe707f1448a3a27a15f0f0c43053b25497c08a8c91da&scene=21#wechat_redirect" target="_blank" data-linktype="2" style="width: 100%;"> <section style="margin: 16px;white-space: nowrap;text-align: left;border-radius: 4px;box-sizing: border-box;overflow: hidden;box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 12px 0px;height: 154px;"> <section style="bottom: 0px;box-sizing: border-box;text-overflow: ellipsis;overflow: hidden;width: 516px;height: 154px;"> <section class="h5_image_link" style="font-size: inherit;display: inherit;width: 516px;height: 154px;background-size: cover;background-position: 50% center;background-image: url("https://mmbiz.qpic.cn/mmbiz_jpg/iaIdQfEric9TyHib1icEXWmaic927ibhIEpw1GicVICickghX3LCksZ1Y6icXmkMvEqnxSBNQ9V2jwzjpd8GFHXS3E9kaxA/640?wx_fmt=jpeg");z-index: -1;"> </section> <section style="margin-top: -35px;padding: 5px 16px;bottom: 0px;width: 516px;box-sizing: border-box;background-color: rgba(0, 0, 0, 0.5);color: rgb(255, 255, 255);text-overflow: ellipsis;overflow: hidden;font-size: 16px;"> 【Java后端面试经历】我和阿里面试官的“又”一次“邂逅”(附问题详解) </section> </section> </section></a> </section> <section data-from="xmt-recommend" data-tools="新媒体排版" style="color: rgb(74, 74, 74);font-size: 15px;letter-spacing: 2px;white-space: normal;"> <a href="http://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247487002&idx=1&sn=e9db79f1bbd561b3ead1c9475f8fd7f5&chksm=cea241d1f9d5c8c72fa33764208a26a26de5d2d3e4ac2a6794e0792ba779bb11414b3fd506e3&scene=21#wechat_redirect" target="_blank" data-linktype="2" style="width: 100%;"> <section style="margin: 16px;white-space: nowrap;text-align: left;border-radius: 4px;box-sizing: border-box;overflow: hidden;box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 12px 0px;height: 154px;"> <section style="bottom: 0px;box-sizing: border-box;text-overflow: ellipsis;overflow: hidden;width: 516px;height: 154px;"> <section class="h5_image_link" style="font-size: inherit;display: inherit;width: 516px;height: 154px;background-size: cover;background-position: 50% center;background-image: url("https://mmbiz.qpic.cn/mmbiz_jpg/iaIdQfEric9TyibW29gc9tAJBaicjO0dDWsh8XttsDhq5KPUl0MbTIeIBn4ay8NfNokEhzZRwxkzdsokuzn73nfACg/640?wx_fmt=jpeg");z-index: -1;"> </section> <section style="margin-top: -35px;padding: 5px 16px;bottom: 0px;width: 516px;box-sizing: border-box;background-color: rgba(0, 0, 0, 0.5);color: rgb(255, 255, 255);text-overflow: ellipsis;overflow: hidden;font-size: 16px;"> 听说你要接私活?Guide连夜整理了5个开源免费的Java项目快速开发脚手架。 </section> </section> </section></a> </section> <p style="color: rgb(74, 74, 74);font-size: 15px;letter-spacing: 2px;white-space: normal;"><br></p> <p style="color: rgb(74, 74, 74);font-size: 15px;letter-spacing: 2px;white-space: normal;"><br></p> <p style="white-space: normal;font-size: 16px;color: rgb(62, 62, 62);font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;widows: 1;word-spacing: 2px;caret-color: rgb(255, 0, 0);background-color: rgb(255, 255, 255);text-align: center;"><img class="rich_pages" data-ratio="0.45818815331010454" data-s="300,640" src="/upload/3e080a01914f7849b65ecf41a0e33320.png" data-type="png" data-w="574"><br></p> <p style="letter-spacing: 2px;white-space: normal;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;text-align: left;color: rgb(0, 0, 0);"><br style="color: rgb(62, 62, 62);font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);"></p> <section data-role="outer" label="Powered by 135editor.com" style="white-space: normal;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;text-align: left;color: rgb(0, 0, 0);letter-spacing: 0.544px;widows: 1;word-spacing: 2px;caret-color: rgb(255, 0, 0);background-color: rgb(255, 255, 255);"> <section data-tools="135编辑器" data-id="95172"> <section style="display: flex;justify-content: flex-end;align-items: center;"> <section style="display: inline-block;text-align: right;"> <section data-brushtype="text" style="margin-bottom: -15px;color: rgb(63, 63, 63);letter-spacing: 2px;transform: rotate(0deg);"> 好文让朋友知道你“ <strong><span style="color: rgb(255, 120, 0);">在看</span></strong>” </section> </section> <section style="width: 22px;"> <section style="margin-top: 15px;width: 22px;"> <img data-ratio="1" src="/upload/204562348bb4bf4580aca5e70dce669f.png" data-type="png" data-w="18" style="width: 18px;display: inline-block;vertical-align: text-bottom;"> </section> </section> </section> </section> </section>
作者:微信小助手
<section data-mpa-template="t" mpa-paragraph-type="ignored" style="white-space: normal;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);" data-mpa-powered-by="yiban.io"> <section style="margin-top: 10px;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;text-align: left;background-color: rgb(255, 255, 255);color: rgb(62, 62, 62);font-size: 15px;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">往期热门文章:<br></span> </section> <section data-mpa-template="t" mpa-paragraph-type="ignored" style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;text-align: left;background-color: rgb(255, 255, 255);color: rgb(62, 62, 62);font-size: 15px;"> <h4 style="letter-spacing: 0.544px;word-spacing: 1px;"> <section style="margin-top: 10px;margin-bottom: 10px;font-size: 15px;font-variant-numeric: normal;font-variant-east-asian: normal;letter-spacing: 0.544px;widows: 1;caret-color: rgb(255, 0, 0);line-height: 1.75em;text-indent: 0em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">1、</span> <a href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&mid=2247489898&idx=1&sn=5c5e41a0f695649046de3db1b1810df5&chksm=e9c5e0dbdeb269cd52f798c675f58295ad0f632283c0fb07db4140949c72796deed7dfe7d434&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2" hasload="1" style="color: var(--weui-LINK);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">《</a> <a href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&mid=2247489402&idx=2&sn=af5c3bb38717e828d92ed48874f03fe8&chksm=e9c5eecbdeb267dd3d05c159bdb9c611f24c4ca7fb7dafa12daf459becb0ef4fab7bcc2d1a67&scene=21#wechat_redirect" target="_blank" data-linktype="2" style="color: var(--weui-LINK);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"><span style="font-size: 14px;vertical-align: inherit;">往期</span><span style="cursor: pointer;letter-spacing: 0.544px;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);font-size: 14px;vertical-align: inherit;">精选优秀博文都在这里了!</span><span style="cursor: pointer;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);letter-spacing: 0.544px;vertical-align: inherit;">》</span></a> </section> <section style="margin-top: 10px;margin-bottom: 10px;font-size: 15px;font-variant-numeric: normal;font-variant-east-asian: normal;letter-spacing: 0.544px;widows: 1;caret-color: rgb(255, 0, 0);line-height: 1.75em;text-indent: 0em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">2、</span> <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&mid=2247491961&idx=1&sn=3dfd7e005c30bb999ec309e87bc62c45&chksm=e9c618c8deb191de2aed9d74b1181cd08426efba7d37546b887185d7ab8c601e3cebb686cc54&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="color: var(--weui-LINK);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">RabbitMQ 如何实现一个可复用的分布式事务消息架构方案?</a> </section> <section style="margin-top: 10px;margin-bottom: 10px;font-size: 15px;font-variant-numeric: normal;font-variant-east-asian: normal;letter-spacing: 0.544px;widows: 1;caret-color: rgb(255, 0, 0);line-height: 1.75em;text-indent: 0em;"> <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&mid=2247491915&idx=1&sn=c8747cb83b8dcba1a2df531faea120fc&chksm=e9c618fadeb191ec5d9255e65deb7e72925b7fce5d160b802653acd10509bba4fbbfb4b1e625&scene=21#wechat_redirect" data-itemshowtype="11" tab="innerlink" data-linktype="2" hasload="1" style="color: var(--weui-LINK);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">3、我们已经不用AOP做操作日志了很久了!</a> </section> <section style="margin-top: 10px;margin-bottom: 10px;font-size: 15px;font-variant-numeric: normal;font-variant-east-asian: normal;letter-spacing: 0.544px;widows: 1;caret-color: rgb(255, 0, 0);line-height: 1.75em;text-indent: 0em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">4</span> <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&mid=2247491750&idx=1&sn=e863a71fbd87900aa4ca6a6ac35ce406&chksm=e9c61917deb19001bcc82db2bda4a55f33eef222418f08793598069252119f23e89cbf776109&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="color: var(--weui-LINK);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">、强烈推荐 16 款 IDEA 插件,让你的开发速度飞起来!</a> </section> <section style="margin-top: 10px;margin-bottom: 10px;font-size: 15px;font-variant-numeric: normal;font-variant-east-asian: normal;letter-spacing: 0.544px;widows: 1;caret-color: rgb(255, 0, 0);line-height: 1.75em;text-indent: 0em;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">5、</span> <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&mid=2247491728&idx=1&sn=d100e274cfb91d47e723b302ef27aad1&chksm=e9c61921deb1903758f75b8c2e2874423c4ee8344b75544caedf55f6e32ce1d17ffe8e0f1812&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="color: var(--weui-LINK);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">Java中当对象不再使用时,不赋值为null会导致什么后果 ?</a> </section></h4> </section> </section> <p mpa-paragraph-type="body" style="white-space: normal;letter-spacing: 0.544px;text-align: left;background-color: rgb(255, 255, 255);"><span style="font-size: 10px;color: rgb(136, 136, 136);">本文来源:</span><span style="letter-spacing: 0.544px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 10px;color: rgb(136, 136, 136);">cnblogs.com/jurendage/p/11255197.html</span><span style="letter-spacing: 0.034em;color: rgb(1, 1, 1);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;"></span></p> <p mpa-paragraph-type="body" style="white-space: normal;letter-spacing: 0.544px;text-align: left;background-color: rgb(255, 255, 255);"><span style="letter-spacing: 0.034em;color: rgb(1, 1, 1);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;"><br></span></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-top: -10px;line-height: 1.6;letter-spacing: 0.034em;color: rgb(63, 63, 63);font-size: 16px;"> <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(93, 186, 133, 0), rgba(93, 186, 133, 0.75), rgba(93, 186, 133, 0));"> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">背景</strong></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">软件开发过程中,不可避免的是需要处理各种异常,就我自己来说,至少有一半以上的时间都是在处理各种异常情况,所以代码中就会出现大量的<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">try {...} catch {...} finally {...}</code> 代码块,不仅有大量的冗余代码,而且还影响代码的可读性。比较下面两张图,看看您现在编写的代码属于哪一种风格?然后哪种编码风格您更喜欢?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">丑陋的 try catch 代码块</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><img data-ratio="0.8916666666666667" src="/upload/b6555021af48e184f5e8e0f0163909bd.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;">优雅的Controller</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><img data-ratio="0.5342592592592592" src="/upload/ce11e4b56e45b19993d8486380e98db7.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;">上面的示例,还只是在<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(40, 202, 113);">Controller</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(40, 202, 113);">Service</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(40, 202, 113);">try catch</code>代码块。这将会严重影响代码的可读性、“美观性”。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">所以如果是我的话,我肯定偏向于第二种,我可以把更多的精力放在业务代码的开发,同时代码也会变得更加简洁。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">既然业务代码不显式地对异常进行捕获、处理,而异常肯定还是处理的,不然系统岂不是动不动就崩溃了,所以必须得有其他地方捕获并处理这些异常。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">那么问题来了,如何优雅的处理各种异常?</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffbqE6ZiaBTOP2MLtPBbFS9hGZ7RZklibkrAiaCRUGicHwH24vabKlbV3UlLyCt1Q1aFoJSmRmmtPTwMg/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">什么是统一异常处理</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">Spring</code>在3.2版本增加了一个注解<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">@ControllerAdvice</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(40, 202, 113);">@ExceptionHandler</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(40, 202, 113);">@InitBinder</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(40, 202, 113);">@ModelAttribute</code> 等注解注解配套使用,对于这几个注解的作用,这里不做过多赘述,若有不了解的,可以参考Spring3.2新注解@ControllerAdvice,先大概有个了解。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">不过跟异常处理相关的只有注解<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">@ExceptionHandler</code>,从字面上看,就是 <strong style="line-height: 1.75em;">异常处理器</strong> 的意思,其实际作用也是:若在某个<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">Controller</code>类定义一个异常处理方法,并在方法上添加该注解,那么当出现指定的异常时,会执行该处理异常的方法,其可以使用springmvc提供的数据绑定,比如注入HttpServletRequest等,还可以接受一个当前抛出的Throwable对象。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">但是,这样一来,就必须在每一个<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">Controller</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(40, 202, 113);">Controller</code>类了,很不优雅。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">当然你可能会说,那就定义个类似<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">BaseController</code>的基类,这样总行了吧。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这种做法虽然没错,但仍不尽善尽美,因为这样的代码有一定的侵入性和耦合性。简简单单的<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">Controller</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(40, 202, 113);">Java</code>只能继承一个类。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">那有没有一种方案,既不需要跟<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">Controller</code>耦合,也可以将定义的 <strong style="line-height: 1.75em;">异常处理器</strong> 应用到所有控制器呢?所以注解<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">@ControllerAdvice</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(40, 202, 113);">@ControllerAdvice</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(40, 202, 113);">不同阶段的</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(40, 202, 113);">不同异常</code> 进行处理。这就是统一异常处理的原理。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">注意到上面对异常按阶段进行分类,大体可以分成:进入<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">Controller</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(40, 202, 113);">Service</code> 层异常,具体可以参考下图:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"> <img data-ratio="0.464839094159714" src="/upload/b8f919da401e6cccf2f9b7bdcd525e9c.png" data-type="png" data-w="839" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">不同阶段的异常</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffbqE6ZiaBTOP2MLtPBbFS9hGZ7RZklibkrAiaCRUGicHwH24vabKlbV3UlLyCt1Q1aFoJSmRmmtPTwMg/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">目标</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">消灭95%以上的 <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(40, 202, 113);">try catch</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(40, 202, 113);">Assert</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(40, 202, 113);">try catch</code> 代码块。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffbqE6ZiaBTOP2MLtPBbFS9hGZ7RZklibkrAiaCRUGicHwH24vabKlbV3UlLyCt1Q1aFoJSmRmmtPTwMg/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">统一异常处理实战</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">在定义统一异常处理类之前,先来介绍一下如何优雅的判定异常情况并抛异常。</p> <h3 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;margin-top: 1.2em;"><span style="background-image: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffbqE6ZiaBTOP2MLtPBbFS9hQafpsgmUwr7ibJ5B1qicuCeiamk0LzwJYwep5Wia1Sl22Wzlmic5iaYtRZzA/640?wx_fmt=png");background-size: 100% 100%;background-repeat: no-repeat;display: inline-block;width: 16px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 17px;display: inline-block;margin-left: 8px;color: rgb(72, 179, 120);">用 Assert(断言) 替换 throw exception</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">想必 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">Assert(断言)</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(40, 202, 113);">Spring</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(40, 202, 113);">org.springframework.util.Assert</code>,在我们写测试用例的时候经常会用到,使用断言能让我们编码的时候有一种非一般丝滑的感觉,比如:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffbqE6ZiaBTOP2MLtPBbFS9h2SdQibpQ5z3FUrZrrYeHLXfCLmWUxicE0ZBLshKA04yMmRZvrNLw4pvA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;"><span style="color: #75715e;line-height: 26px;">@Test</span><br> <span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">void</span> <span style="color: #a6e22e;font-weight: bold;line-height: 26px;">test1</span><span style="line-height: 26px;">()</span> </span>{<br> ...<br> User user = userDao.selectById(userId);<br> Assert.notNull(user, <span style="color: #a6e22e;line-height: 26px;">"用户不存在."</span>);<br> ...<br> }<br><br> <span style="color: #75715e;line-height: 26px;">@Test</span><br> <span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">void</span> <span style="color: #a6e22e;font-weight: bold;line-height: 26px;">test2</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #75715e;line-height: 26px;">// 另一种写法</span><br> User user = userDao.selectById(userId);<br> <span style="color: #f92672;font-weight: bold;line-height: 26px;">if</span> (user == <span style="color: #f92672;font-weight: bold;line-height: 26px;">null</span>) {<br> <span style="color: #f92672;font-weight: bold;line-height: 26px;">throw</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">new</span> IllegalArgumentException(<span style="color: #a6e22e;line-height: 26px;">"用户不存在."</span>);<br> }<br> }<br></code></pre> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">有没有感觉第一种判定非空的写法很优雅,第二种写法则是相对丑陋的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">if {...}</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(40, 202, 113);">Assert.notNull()</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(40, 202, 113);">Assert</code> 的部分源码:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffbqE6ZiaBTOP2MLtPBbFS9h2SdQibpQ5z3FUrZrrYeHLXfCLmWUxicE0ZBLshKA04yMmRZvrNLw4pvA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">abstract</span> <span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">class</span> <span style="font-weight: bold;color: white;line-height: 26px;">Assert</span> </span>{<br> <span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span> <span style="color: #a6e22e;font-weight: bold;line-height: 26px;">Assert</span><span style="line-height: 26px;">()</span> </span>{<br> }<br><br> <span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">static</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">void</span> <span style="color: #a6e22e;font-weight: bold;line-height: 26px;">notNull</span><span style="line-height: 26px;">(@Nullable Object object, String message)</span> </span>{<br> <span style="color: #f92672;font-weight: bold;line-height: 26px;">if</span> (object == <span style="color: #f92672;font-weight: bold;line-height: 26px;">null</span>) {<br> <span style="color: #f92672;font-weight: bold;line-height: 26px;">throw</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">new</span> IllegalArgumentException(message);<br> }<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">可以看到,<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">Assert</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(40, 202, 113);">if {...}</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(40, 202, 113);">org.springframework.util.Assert</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(40, 202, 113);">IllegalArgumentException</code> 这些内置异常,而是我们自己定义的异常。下面让我们来尝试一下。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/JdLkEI9sZffbqE6ZiaBTOP2MLtPBbFS9h2SdQibpQ5z3FUrZrrYeHLXfCLmWUxicE0ZBLshKA04yMmRZvrNLw4pvA/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">Assert<br><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">interface</span> <span style="font-weight: bold;color: white;line-height: 26px;">Assert</span> </span>{<br> <span style="color: #75715e;line-height: 26px;">/**<br> * 创建异常<br> * <span style="font-weight: bold;line-height: 26px;">@param</span> args<br> * <span style="font-weight: bold;line-height: 26px;">@return</span><br> */</span><br> <span style="line-height: 26px;">BaseException <span style="color: #a6e22e;font-weight: bold;line-height: 26px;">newException</span><span style="line-height: 26px;">(Object... args)</span></span>;<br><br> <span style="color: #75715e;line-height: 26px;">/**<br> * 创建异常<br> * <span style="font-weight: bold;line-height: 26px;">@param</span> t<br> * <span style="font-weight: bold;line-height: 26px;">@param<