作者:不要哭啦
#### 1.奇偶判断 不要使用 i % 2 == 1 来判断是否是奇数,因为i为负奇数时不成立,请使用 i % 2 != 0 来判断是否是奇数,或使用 高效式 (i & 1) != 0来判断。 #### 2.小数精度计算 ``` System.out.println(2.00 - 1.10 ); // 0.8999999999999999 ``` 解决方案1: ``` System.out.println(new BigDecimal("2.0" ).subtract(new BigDecimal("1.10" ))); // 0.9 ``` 一定要用BigDecimal(String)构造器,而千万不要用BigDecimal(double)来构造(也不能将float或double型转换成String再来使用BigDecimal(String) 来构造,因为在将float或double转换成String时精度已丢失)。 *另外,如果要比较两个浮点数的大小,要使用BigDecimal的compareTo方法*。 解决方案2: ``` System.out.println(200 - 110); // 90 ``` #### 3.int整数相乘溢出 ``` final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000; // 正确结果应为:86400000000 实际结果为500654080 ``` 24 * 60 * 60 * 1000 这个时候是int类型,当再次乘以1000的时候就会超过int的最大范围-2147483648--2147483647,因此结果错误 解决方案: ``` final long MICROS_PER_DAY = 24L * 60 * 60 * 1000 * 1000; // 把int类型转换成Long计算就行了 ``` #### 4.三元表达式(?:) ``` char x = 'X' ; int i = 0 ; System.out.println(true ? x : 0 );// X System.out.println(false ? i : x);// 88 ``` > 1.如果第二个和第三个操作数具有相同的类型,那么它就是条件表达式的类型。 果一个操作的类型是T,T表示byte、short或char,而另一个操作数是一个int类型的“字面常量”,并且 > 2.它的值可以用类型T表示,那条件表达式的类型就是T。 > 3.否则,将对操作数类型进行提升,而条件表达式的类型就是第二个和第三个操作被提升之后的类型
作者:微信小助手
<p style="white-space: normal;" data-mpa-powered-by="yiban.io"><img class="" data-ratio="0.6666666666666666" data-type="png" data-w="127" src="/upload/4c8b91599182ea5ba1ead110e9271956.png" style="vertical-align: bottom;width:auto !important;max-width:100% !important;height:auto !important;"><br></p> <p style="white-space: normal;color: rgb(153, 80, 51);line-height: normal;"><span style="font-size: 15px;letter-spacing: 1px;">还没关注?伸出中指点这里!</span></p> <p style="white-space: normal;"><br></p> <p style="line-height: 2em;"><span style="font-size: 14px;">本文作者为公众号<span style="font-size: 14px;color: rgb(64, 179, 230);">【</span><span style="font-size: 14px;color: rgb(64, 179, 230);">狸猫技术窝】</span>特约作者:<strong>爱钓鱼的桌子哥</strong>。目前就职于<strong>字节跳动</strong>,担任资深架构师。</span></p> <p style="line-height: normal;"><span style="font-size: 14px;"><br></span></p> <p style="line-height: 2em;text-align: right;"><span style="font-size: 12px;"><em>转载请联系</em><span style="color: rgb(64, 179, 230);">【狸猫技术窝】</span><em>获取授权</em></span></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><strong><span style="font-size: 20px;">众所周知</span></strong><span style="font-size: 15px;">,如果去百度、腾讯等一线大厂面试,一定会深入考候选人的基础技术功底,其中尤为关键和重视的就是IO相关的技术和知识。</span><br></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">而要搞明白IO相关的概念,首先就得弄清楚同步与异步,阻塞与非阻塞到底是什么意思。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-weight: bold;font-size: 15px;">同步与异步</span></p> <p style="line-height: normal;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">想要搞明白IO模型,就先得搞明白“同步”与“异步”的关系。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">所谓的“<strong>同步</strong>”,比如说调用者去调用一个接口,这个接口比如要执行一些磁盘文件读写操作,或者是网络通信操作。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">假设是“同步”的模式,调用者必须要等待这个接口的磁盘读写或者网络通信的操作执行完毕了,调用者才能返回,这就是“同步”,如下图所示:</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><img class="inline-img" data-ratio="0.6666666666666666" data-type="png" data-w="527" src="/upload/726bafe9a6decdd30f0cb11a766604c.png"></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">所谓的“<strong>异步</strong>”,就是说这个调用者调用接口之后,直接就返回了,他去干别的事儿了,也不管那个接口的磁盘读写或者是网络通信是否成功。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">然后这个接口后续如果干完了自己的任务,比如写完了文件或者是什么的,会反过来通知调用者,之前你的那个调用成功了。</span><span style="font-size: 15px;">可以通过一些内部通信机制来通知,也可以通过回调函数来通知,如下图。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><img class="inline-img" data-ratio="0.6666666666666666" data-type="png" data-w="441" src="/upload/718e386d42c28f0438af50846b537d14.png"></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-weight: bold;font-size: 15px;">用生活中的例子理解同步与异步</span></p> <p style="line-height: normal;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">如果给大家举个生活中的例子,那么就可以用买烟这个事儿来举个例子</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">比如说现在你要去一个柜台买很多条香烟,但是现在柜台没那么多货,他需要打电话给库房来查一下有没有足够的货。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">这个时候,库房的工作人员正好去吃饭了,那现在你有<strong>两种选择:</strong></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;color: rgb(57, 137, 31);">第一种选择</span><span style="font-size: 15px;">,你可以在柜台等着,一直等待库房工作人员回来,柜台专员打通电话给他查到了库存是否充足,你再走。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">这个就是“<span style="font-size: 15px;color: rgb(57, 137, 31);">同步</span>”,你找柜台工作人员买香烟,他要打电话给库房工作人员问库存,如果你选择“同步”模式,那么你就在柜台一直等着,直到成功查询到库存为止。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;color: rgb(57, 137, 31);">第二种选择</span><span style="font-size: 15px;">,你可以先回家干点儿别的,比如说洗衣服做饭之类的,然后过了一会儿,柜台工作人员打通电话给库房工作人员,查到香烟库存了,就会打个电话给你,告诉你这个事儿。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">这就是“<span style="font-size: 15px;color: rgb(57, 137, 31);">异步</span>”,你跟柜台工作人员说了这个事儿,就直接走了,干别的去了,柜台工作人员后面完成他的任务之后,就会反过来打电话回调通知你。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-weight: bold;font-size: 15px;">阻塞与非阻塞</span></p> <p style="line-height: normal;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">实际上阻塞与非阻塞的概念,通常是针对底层的IO操作来说的。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">比如现在我们的程序想要通过网络读取数据,如果是阻塞IO模式,一旦发起请求到操作系统内核去从网络中读取数据,就会<strong>阻塞</strong>在那里,必须要等待网络中的数据到达了之后,才能从网络读取数据到内核,再从内核返回给程序,如下图。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><img class="inline-img" data-ratio="0.6666666666666666" data-type="png" data-w="545" src="/upload/45ca81369cc46cb0f2d1d377ad02eb7d.png"></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">而<strong>非阻塞</strong>,指的就是程序发送请求给内核要从网络读取数据,但是此时网络中的数据还没到,此时不会阻塞住,内核会返回一个异常消息给程序。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">程序就可以干点儿别的,然后过一会儿再来发起一次请求给内核,让内核尝试从网络读取数据。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">因为如果网络中的数据还没到位,是不会阻塞住程序的,需要程序自己不断的轮询内核去尝试读取数据,所以这种IO就是非阻塞的。如下图:</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><img class="inline-img" data-ratio="0.6666666666666666" data-type="png" data-w="524" src="/upload/3507aed7212c9d1ef4b2dcb0708ef436.png"></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">大家不要把“同步/异步”概念和“阻塞/非阻塞”概念混淆起来,实际上他们是两组不同的概念。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">“同步/异步”更多的是针对比如接口调用,服务调用,API类库调用,类似这样的场景。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">而“阻塞/非阻塞”概念针对的是底层IO操作的场景,比如磁盘IO,网络IO。但是在Java IO模型里,两种概念之间是有一定的关联关系的 。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-weight: bold;font-size: 15px;">Unix支持的5种IO模型</span></p> <p style="line-height: normal;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">Unix操作系统支持的IO模型主要就是5种:</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p style="line-height: 2em;"><strong><span style="font-size: 15px;"><span style="font-size: 15px;color: rgb(57, 137, 31);">阻塞IO</span></span></strong><span style="font-size: 15px;"><span style="font-size: 15px;color: rgb(57, 137, 31);"></span>:就是上面图里的那种阻塞IO模式,程序发起请求之后会阻塞,一直到系统内核发现网络中有数据到达了,拷贝数据给程序进程了,才能返回</span></p><p style="line-height: 2em;"><span style="font-size: 15px;"></span></p></li> <li><p style="line-height: 2em;"><strong><span style="font-size: 15px;color: rgb(57, 137, 31);">非阻塞IO</span></strong><span style="font-size: 15px;">:就是上面图里的那种非阻塞IO模式,程序发起请求读取数据,系统内核发现网络数据还没到,就返回一个异常信息,程序不会阻塞在IO操作上,但是过一会儿还得再来发起请求给内核,直到内核发现网络数据到达了,此时就会拷贝数据给程序进程</span></p><p style="line-height: 2em;"><span style="font-size: 15px;"></span></p></li> <li><p style="line-height: 2em;"><span style="color: rgb(57, 137, 31);"><strong><span style="font-size: 15px;">IO多路复用</span></strong></span><span style="font-size: 15px;">:这个下面来讲</span></p><p style="line-height: 2em;"><span style="font-size: 15px;"></span></p></li> <li><p style="line-height: 2em;"><strong><span style="font-size: 15px;color: rgb(57, 137, 31);">信号驱动式IO</span></strong><span style="font-size: 15px;">:一般很少用到,这里不说明</span></p><p style="line-height: 2em;"><span style="font-size: 15px;"></span></p></li> <li><p style="line-height: 2em;"><span style="color: rgb(57, 137, 31);"><strong><span style="font-size: 15px;">异步IO</span></strong></span><span style="font-size: 15px;">:下面来讲</span></p></li> </ol> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-weight: bold;font-size: 15px;">JDK 1.4之前的同步阻塞IO</span></p> <p style="line-height: normal;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">在JDK 1.4之前,主要就是同步阻塞IO模型,在Java里叫做<span style="font-size: 15px;color: rgb(57, 137, 31);">BIO</span>。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">在Java代码里调用IO相关接口,发起IO操作之后,Java程序就会同步等待,这个同步指的是Java程序调用IO API接口的层面而言。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">而IO API在底层的IO操作是基于<strong>阻塞IO</strong>来的,向操作系统内核发起IO请求,系统内核会等待数据就位之后,才会执行IO操作,执行完毕了才会返回。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-weight: bold;font-size: 15px;">JDK 1.4之后的同步非阻塞NIO</span></p> <p style="line-height: normal;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">在JDK 1.4之后提供了<strong>NIO</strong>,他的概念是<strong>同步非阻塞</strong>,也就是说如果你调用NIO接口去执行IO操作,其实还是同步等待的,但是在底层的IO操作上 ,会对系统内核发起非阻塞IO请求,以非阻塞的形式来执行IO。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">也就是说,如果底层数据没到位,那么内核返回异常信息,不会阻塞住,但是NIO接口内部会采用非阻塞方式过一会儿再次调用内核发起IO请求,直到成功为止。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">但是之所以说是同步非阻塞,这里的“同步”指的就是因为在你的Java代码调用NIO接口层面是同步的,你还是要同步等待底层IO操作真正完成了才可以返回,只不过在执行底层IO的时候采用了非阻塞的方式来执行罢了。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-weight: bold;font-size: 15px;">NIO网络通信与IO多路复用模型</span></p> <p style="line-height: normal;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">实际上,如果基于NIO进行网络通信,采取的就是多路复用的IO模型,这个多路复用IO模型针对的是网络通信中的IO场景来说的。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">简单来说,就是在基于Socket进行网络通信的时候,如果有多个客户端跟你的服务端建立了Socket连接,那你就需要维护多个Socket连接。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">而所谓的多路复用IO模型,就是说你的Java代码直接通过一个select函数调用,直接会进入一个同步等待的状态。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">这也是为什么说NIO一定是“同步”的,因为你必须在这里同步等待某个Socket连接有请求到来。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">接着你就要同步等着select函数去对底层的多个 Socket 连接进行轮询,不断的查看各个 Socket 连接谁有请求到达,就可以让select函数返回,交给我们的Java程序来处理。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">select函数在底层会通过非阻塞的方式轮询各个Socket,任何一个Socket如果没有数据到达,那么非阻塞的特性会立即返回一个信息。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">然后select函数可以轮询下一个Socket,不会阻塞在某个Socket上,所以底层是基于这种非阻塞的模式来“监视”各个Socket谁有数据到达的。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">这就是所谓的“<strong>同步非阻塞</strong>”,但是因为操作系统把上述工作都封装在一个select函数调用里了,可以对多路Socket连接同时进行监视,所以就把这种模型称之为<strong>“IO多路复用”模型</strong>。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">通过这种IO多路复用的模型,就可以用一个线程,调用一个select函数,然后监视大量的客户端连接了,如下图。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><img class="inline-img" data-ratio="0.6666666666666666" data-type="png" data-w="585" src="/upload/5859d5e5ac1b65840249ffab8a8f254f.png"></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-weight: bold;font-size: 15px;">AIO以及异步IO模型</span></p> <p style="line-height: normal;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">最后就是JDK 1.7之后,又支持了<strong>AIO</strong>,也叫做NIO 2.0,他就支持<strong>异步IO模型</strong>了。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">我们先说一下异步IO模型是什么意思。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">简单来说,就是你的Java程序可以基于AIO API发起一个请求,比如说接收网络数据,AIO API底层会基于异步IO模型来调用操作系统内核。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">此时不需要去管这个IO是否成功了,AIO接口会直接返回,你的Java程序也会直接返回。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">然后,你的Java程序就可以去干别的事儿了。大家联想一下上面说的那个异步的例子,就可以理解这里为什么叫做异步了。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">因为BIO、NIO都是同步的,你发起IO请求,都必须同步等待IO操作完成。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">但是这里你发起一个IO请求,直接AIO接口就返回了,你就可以干别的事儿了,纯异步的方式。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">不过你需要提供一个回调函数给AIO接口,一旦底层系统内核完成了具体的IO请求,比如网络读写之类的,就会回调你提供的回调函数。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">比如说你要是通过网络读取数据,那么此时AIO接口就会把操作系统异步读取到的数据交给你的回调函数。</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><br></span></p> <p style="line-height: 2em;"><span style="font-size: 15px;">整个过程如下图:</span></p> <p style="line-height: 2em;"><span style="font-size: 15px;"><img class="inline-img" data-ratio="0.6666666666666666" data-type="png" data-w="628" src="/upload/7fec2fae6e4f4eb96b07fed588ea0206.png"></span></p> <p style="line-height: 2em;text-align: center;"><strong style="text-align: center;white-space: normal;color: rgb(64, 118, 0);font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 14px;"><span style="font-size: 24px;">End</span></strong></p> <p style="line-height: 2em;text-align: center;"><br></p> <section class="KolEditor"> <section style="margin: 10px;border-width: 1px;border-style: solid;border-color: rgb(246, 73, 13);padding: 17px 10px 15px 16px;display: flex;align-items: center;justify-content: center;flex-direction: column;"> <section style="display: flex;align-items:center;justify-content: flex-start;border-bottom: 4px solid #f6490d;width: 100%;padding-bottom: 5px;align-self: flex-start;"> <p style="font-size: 18px;color: rgb(246, 73, 13);font-weight: bold;"><span style="font-size: 18px;"><span style="color: rgb(255, 76, 0);"> 作者简介:</span><span style="color: rgb(40, 40, 40);"> </span></span></p> <p style="font-size: 15px;color: rgb(40, 40, 40);margin-left: 20px;"><strong style="font-size: 14px;white-space: normal;"></strong></p> </section> <section style="padding-right: 10px;padding-left: 10px;margin-top: 13px;"> <p style="white-space: normal;font-size: 18px;color: rgb(246, 73, 13);font-weight: bold;"><span style="font-size: 15px;"><em style="color: rgb(40, 40, 40);font-size: 14px;"><strong>爱钓鱼的桌子哥</strong></em></span><strong style="color: rgb(40, 40, 40);font-size: 14px;">,<em>字节跳动资深架构师</em></strong><br></p> <p style="white-space: normal;line-height: 2em;"><span style="font-size: 13px;">作者先后工作于滴滴、百度等国内一线互联网大厂,目前在字节跳动负责基础架构相关工作。</span><span style="font-size: 13px;">带领团队设计与构建了大规模的分布式存储系统、分布式消息中间件、分布式数据库,对分布式架构设计、系统高可用体系构建、基础中间件架构都有丰富的经验。</span><span style="font-size: 13px;"><br></span></p> <p style="white-space: normal;line-height: 2em;"><span style="font-size: 13px;"></span></p> </section> </section> </section> <p style="white-space: normal;text-align: center;"><br></p> <p style="white-space: normal;letter-spacing: 0.544px;line-height: 2em;text-align: center;"><span style="font-size: 14px;">扫描二维码,即刻关注【<span style="color: rgb(241, 136, 35);"><strong>狸猫技术窝</strong></span>】</span></p> <p style="white-space: normal;letter-spacing: 0.544px;line-height: 2em;text-align: center;"><span style="color: rgb(0, 0, 0);font-size: 14px;">阿里、京东、美团、字节跳动</span></p> <p style="white-space: normal;letter-spacing: 0.544px;line-height: 2em;text-align: center;"><span style="font-size: 14px;"><strong><span style="font-size: 13px;color: rgb(0, 0, 0);">顶尖技术专家</span></strong><span style="font-size: 13px;color: rgb(0, 0, 0);">坐镇</span></span></p> <p style="white-space: normal;letter-spacing: 0.544px;line-height: 2em;text-align: center;"><span style="color: rgb(0, 0, 0);font-size: 14px;">为IT人打造一个 “有温度” 的技术窝!</span></p> <section class="KolEditor" data-tools-id="69164" style="white-space: normal;"> <section style="margin-top: 20px;"> <section style="margin-right: auto;margin-left: auto;background-image: url(https://mmbiz.qpic.cn/mmbiz_gif/vnOqylzBGCSwjFsfNvrwxsRkgjr6jVfOHXUSyNIpYXY62BsG3zqZ8S3VEgqu9Ulib1Tdvqibcic8kag4XoVoygzew/640?wx_fmt=gif);background-repeat: no-repeat;width: 240px;background-size: 100%;text-align: center;"> <section class="" style="margin-right: auto;margin-left: auto;padding-top: 8px;padding-bottom: 20px;padding-left: 90px;width: 210px;"> <img class="" data-copyright="0" data-cropselx1="0" data-cropselx2="120" data-cropsely1="0" data-cropsely2="120" data-ratio="0.6666666666666666" data-type="jpeg" data-w="258" src="/upload/578288c965b89f74b583291d3fc98c86.jpg" style="height: 120px;width: 120px;"> </section> </section> </section> </section> <p><br></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> <p><br></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;">链接:blog.csdn.net/u012998254/article/details/79400549</span></p> </blockquote> <p><br></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">1、线程与进程</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。</span></p></li> <li><p><span style="font-size: 15px;">一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。</span></p></li> <li><p><span style="font-size: 15px;">区别不同 </span></p></li> </ol> <ol class=" list-paddingleft-2" style="list-style-type: lower-alpha;"> <li><p><span style="font-size: 15px;">地址空间:进程内的一个执行单元;进程至少有一个线程;它们共享进程的地址空间;而进程有自己独立的地址空间; </span></p></li> <li><p><span style="font-size: 15px;">资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资 </span></p></li> <li><p><span style="font-size: 15px;">线程是处理器调度的基本单位,但进程不是. </span></p></li> <li><p><span style="font-size: 15px;">二者均可并发执行.</span></p></li> </ol> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">2、 守护线程</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">在Java中有两类线程:用户线程 (User Thread)、守护线程 (Daemon Thread)。 </span></p> <p><span style="font-size: 15px;">守护线程和用户线程的区别在于:守护线程依赖于创建它的线程,而用户线程则不依赖。举个简单的例子:如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。</span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;"><br></span></strong></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;">3、java thread状态</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">NEW 状态是指线程刚创建, 尚未启动</span></p></li> <li><p><span style="font-size: 15px;">RUNNABLE 状态是线程正在正常运行中, 当然可能会有某种耗时计算/IO等待的操作/CPU时间片切换等, 这个状态下发生的等待一般是其他系统资源, 而不是锁, Sleep等</span></p></li> <li><p><span style="font-size: 15px;">BLOCKED 这个状态下, 是在多个线程有同步操作的场景, 比如正在等待另一个线程的synchronized 块的执行释放, 也就是这里是线程在等待进入临界区</span></p></li> <li><p><span style="font-size: 15px;">WAITING 这个状态下是指线程拥有了某个锁之后, 调用了他的wait方法, 等待其他线程/锁拥有者调用 notify / notifyAll 一遍该线程可以继续下一步操作, 这里要区分 BLOCKED 和 WATING 的区别, 一个是在临界点外面等待进入, 一个是在理解点里面wait等待别人notify, 线程调用了join方法 join了另外的线程的时候, 也会进入WAITING状态, 等待被他join的线程执行结束</span></p></li> <li><p><span style="font-size: 15px;">TIMED_WAITING 这个状态就是有限的(时间限制)的WAITING, 一般出现在调用wait(long), join(long)等情况下, 另外一个线程sleep后, 也会进入TIMED_WAITING状态</span></p></li> <li><p><span style="font-size: 15px;">TERMINATED 这个状态下表示 该线程的run方法已经执行完毕了, 基本上就等于死亡了(当时如果线程被持久持有, 可能不会被回收)</span></p></li> </ol> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">4、请说出与线程同步以及线程调度相关的方法。</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;</span></p></li> <li><p><span style="font-size: 15px;">sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常;</span></p></li> <li><p><span style="font-size: 15px;">notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;</span></p></li> <li><p><span style="font-size: 15px;">notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;</span></p></li> </ol> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;"><br></span></strong></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;">5、进程调度算法</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;color: rgb(171, 25, 66);">实时系统</span><span style="font-size: 15px;">:FIFO(First Input First Output,先进先出算法),SJF(Shortest Job First,最短作业优先算法),SRTF(Shortest Remaining Time First,最短剩余时间优先算法)。 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;color: rgb(171, 25, 66);">交互式系统</span><span style="font-size: 15px;">:RR(Round Robin,时间片轮转算法),HPF(Highest Priority First,最高优先级算法),多级队列,最短进程优先,保证调度,彩票调度,公平分享调度。</span></p> <p><br></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;">6、wait()和sleep()的区别</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">sleep来自Thread类,和wait来自Object类</span></p></li> <li><p><span style="font-size: 15px;">调用sleep()方法的过程中,线程不会释放对象锁。而 调用 wait 方法线程会释放对象锁</span></p></li> <li><p><span style="font-size: 15px;">sleep睡眠后不出让系统资源,wait让出系统资源其他线程可以占用CPU</span></p></li> <li><p><span style="font-size: 15px;">sleep(milliseconds)需要指定一个睡眠时间,时间一到会自动唤醒</span></p></li> </ol> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">7、ThreadLocal,以及死锁分析</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">hreadLocal为每个线程维护一个本地变量。 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">采用空间换时间,它用于线程间的数据隔离,为每一个使用该变量的线程提供一个副本,每个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">ThreadLocal类中维护一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值为对应线程的变量副本。 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">8、Synchronized 与Lock</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候 ,线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定, 如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断 ,如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情</span></p></li> <li><p><span style="font-size: 15px;">ReentrantLock获取锁定与三种方式: </span></p></li> </ol> <ol class=" list-paddingleft-2" style="list-style-type: lower-alpha;"> <li><p><span style="font-size: 15px;">lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁 </span></p></li> <li><p><span style="font-size: 15px;">tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false; </span></p></li> <li><p><span style="font-size: 15px;">tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false; </span></p></li> <li><p><span style="font-size: 15px;">lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断</span></p></li> </ol> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">总体的结论先摆出来:</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;color: rgb(171, 25, 66);">synchronized</span><span style="font-size: 15px;">: </span></p> <p><span style="font-size: 15px;">在资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronized,另外可读性非常好,不管用没用过5.0多线程包的程序员都能理解。 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;color: rgb(171, 25, 66);">ReentrantLock</span><span style="font-size: 15px;">: </span></p> <p><span style="font-size: 15px;">ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">9、Volatile和Synchronized</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">Volatile和Synchronized四个不同点:</span></p> <p><br></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">粒度不同,前者针对变量 ,后者锁对象和类</span></p></li> <li><p><span style="font-size: 15px;">syn阻塞,volatile线程不阻塞</span></p></li> <li><p><span style="font-size: 15px;">syn保证三大特性,volatile不保证原子性</span></p></li> <li><p><span style="font-size: 15px;">syn编译器优化,volatile不优化 </span></p></li> </ol> <p><span style="font-size: 15px;">要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件: </span></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">对变量的写操作不依赖于当前值。</span></p></li> <li><p><span style="font-size: 15px;">该变量没有包含在具有其他变量的不变式中。</span></p></li> </ol> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">10、CAS</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">CAS是乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">11、Java中Unsafe类详解</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">通过Unsafe类可以分配内存,可以释放内存;类中提供的3个本地方法allocateMemory、reallocateMemory、freeMemory分别用于分配内存,扩充内存和释放内存,与C语言中的3个方法对应。</span></p></li> <li><p><span style="font-size: 15px;">可以定位对象某字段的内存位置,也可以修改对象的字段值,即使它是私有的;</span></p></li> <li><p><span style="font-size: 15px;">挂起与恢复:将一个线程进行挂起是通过park方法实现的,调用 park后,线程将一直阻塞直到超时或者中断等条件出现。unpark可以终止一个挂起的线程,使其恢复正常。整个并发框架中对线程的挂起操作被封装在 LockSupport类中,LockSupport类中有各种版本pack方法,但最终都调用了Unsafe.park()方法。</span></p></li> <li><p><span style="font-size: 15px;">cas </span></p></li> </ol> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">12、线程池</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">线程池的作用: </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">第三:提高线程的可管理性。 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">常用线程池:ExecutorService 是主要的实现类,其中常用的有 </span></p> <p><span style="font-size: 15px;">Executors.newSingleT </span></p> <p><span style="font-size: 15px;">hreadPool(),newFixedThreadPool(),newcachedTheadPool(),newScheduledThreadPool()。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">13、ThreadPoolExecutor</span></strong></span><span style="font-size: 15px;"></span></p> <p><strong><span style="font-size: 15px;"><br></span></strong></p> <p><strong><span style="font-size: 15px;color: rgb(171, 25, 66);">构造方法参数说明</span></strong><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">corePoolSize:核心线程数,默认情况下核心线程会一直存活,即使处于闲置状态也不会受存keepAliveTime限制。除非将allowCoreThreadTimeOut设置为true。</span></p> <p><span style="font-size: 15px;"> </span></p> <p><span style="font-size: 15px;">maximumPoolSize:线程池所能容纳的最大线程数。超过这个数的线程将被阻塞。当任务队列为没有设置大小的LinkedBlockingDeque时,这个值无效。 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">keepAliveTime:非核心线程的闲置超时时间,超过这个时间就会被回收。 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">unit:指定keepAliveTime的单位,如TimeUnit.SECONDS。当将allowCoreThreadTimeOut设置为true时对corePoolSize生效。 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">workQueue:线程池中的任务队列. </span></p> <p><span style="font-size: 15px;">常用的有三种队列,SynchronousQueue,LinkedBlockingDeque,ArrayBlockingQueue。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">threadFactory:线程工厂,提供创建新线程的功能。ThreadFactory是一个接口,只有一个方法</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">原理</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">如果当前池大小 poolSize 小于 corePoolSize ,则创建新线程执行任务。</span></p></li> <li><p><span style="font-size: 15px;">如果当前池大小 poolSize 大于 corePoolSize ,且等待队列未满,则进入等待队列</span></p></li> <li><p><span style="font-size: 15px;">如果当前池大小 poolSize 大于 corePoolSize 且小于 maximumPoolSize ,且等待队列已满,则创建新线程执行任务。</span></p></li> <li><p><span style="font-size: 15px;">如果当前池大小 poolSize 大于 corePoolSize 且大于 maximumPoolSize ,且等待队列已满,则调用拒绝策略来处理该任务。</span></p></li> <li><p><span style="font-size: 15px;">线程池里的每个线程执行完任务后不会立刻退出,而是会去检查下等待队列里是否还有线程任务需要执行,如果在 keepAliveTime 里等不到新的任务了,那么线程就会退出。</span></p></li> </ol> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">13、Executor拒绝策略</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">AbortPolicy:为java线程池默认的阻塞策略,不执行此任务,而且直接抛出一个运行时异常,切记ThreadPoolExecutor.execute需要try </span></p><p><span style="font-size: 15px;">catch,否则程序会直接退出.</span></p></li> <li><p><span style="font-size: 15px;">DiscardPolicy:直接抛弃,任务不执行,空方法</span></p></li> <li><p><span style="font-size: 15px;">DiscardOldestPolicy:从队列里面抛弃head的一个任务,并再次execute 此task。</span></p></li> <li><p><span style="font-size: 15px;">CallerRunsPolicy:在调用execute的线程里面执行此command,会阻塞入</span></p></li> <li><p><span style="font-size: 15px;">用户自定义拒绝策略:实现RejectedExecutionHandler,并自己定义策略模式</span></p></li> </ol> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;"><br></span></strong></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;">14、CachedThreadPool 、 FixedThreadPool、SingleThreadPool</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">newSingleThreadExecutor :创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务, 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行 </span></p><p><span style="font-size: 15px;">适用场景:任务少 ,并且不需要并发执行</span></p></li> <li><p><span style="font-size: 15px;">newCachedThreadPool :创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程. </span></p><p><span style="font-size: 15px;">线程没有任务要执行时,便处于空闲状态,处于空闲状态的线程并不会被立即销毁(会被缓存住),只有当空闲时间超出一段时间(默认为60s)后,线程池才会销毁该线程(相当于清除过时的缓存)。新任务到达后,线程池首先会让被缓存住的线程(空闲状态)去执行任务,如果没有可用线程(无空闲线程),便会创建新的线程。 </span></p><p><span style="font-size: 15px;">适用场景:处理任务速度 > 提交任务速度,耗时少的任务(避免无限新增线程)</span></p></li> <li><p><span style="font-size: 15px;">newFixedThreadPool :创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。</span></p></li> <li><p><span style="font-size: 15px;">newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行</span></p></li> </ol> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">15、CopyOnWriteArrayList</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">CopyOnWriteArrayList : 写时加锁,当添加一个元素的时候,将原来的容器进行copy,复制出一个新的容器,然后在新的容器里面写,写完之后再将原容器的引用指向新的容器,而读的时候是读旧容器的数据,所以可以进行并发的读,但这是一种弱一致性的策略。 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">使用场景:CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">16、AQS</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">AQS使用一个int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。</span></p> <p><span style="font-size: 15px;"><br></span></p> <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;padding: 0.5em;color: rgb(171, 178, 191);text-size-adjust: none;display: block !important;min-width: 400px;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(40, 44, 52);font-weight: 400;" class="c hljs cpp"><span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">private</span> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">volatile</span> <span class="hljs-keyword" style="color: rgb(198, 120, 221);font-weight: 400;font-style: normal;">int</span> state;<span class="hljs-comment" style="color: rgb(92, 99, 112);font-weight: 400;font-style: italic;">//共享变量,使用volatile修饰保证线程可见性</span></code></pre> <p><br></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">2种同步方式:独占式,共享式。独占式如ReentrantLock,共享式如Semaphore,CountDownLatch,组合式的如ReentrantReadWriteLock</span></p></li> <li><p><span style="font-size: 15px;">节点的状态 </span></p><p><span style="font-size: 15px;">CANCELLED,值为1,表示当前的线程被取消; </span></p><p><span style="font-size: 15px;">SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark; </span></p><p><span style="font-size: 15px;">CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中; </span></p><p><span style="font-size: 15px;">PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行; </span></p><p><span style="font-size: 15px;">值为0,表示当前节点在sync队列中,等待着获取锁。</span></p></li> <li><p><span style="font-size: 15px;">模板方法模式 </span></p><p><span style="font-size: 15px;">protected boolean tryAcquire(int arg) : 独占式获取同步状态,试着获取,成功返回true,反之为false </span></p><p><span style="font-size: 15px;">protected boolean tryRelease(int arg) :独占式释放同步状态,等待中的其他线程此时将有机会获取到同步状态; </span></p><p><span style="font-size: 15px;">protected int tryAcquireShared(int arg) :共享式获取同步状态,返回值大于等于0,代表获取成功;反之获取失败; </span></p><p><span style="font-size: 15px;">protected boolean tryReleaseShared(int arg) :共享式释放同步状态,成功为true,失败为false </span></p><p><span style="font-size: 15px;">AQS维护一个共享资源state,通过内置的FIFO来完成获取资源线程的排队工作。该队列由一个一个的Node结点组成,每个Node结点维护一个prev引用和next引用,分别指向自己的前驱和后继结点。双端双向链表。 </span></p><p><span style="font-size: 15px;"></span></p><p><span style="font-size: 15px;"></span></p><p><span style="font-size: 15px;">1.独占式:乐观的并发策略 </span></p><p><span style="font-size: 15px;color: rgb(171, 25, 66);">acquire </span><span style="font-size: 15px;"></span></p><p><span style="font-size: 15px;">a.首先tryAcquire获取同步状态,成功则直接返回;否则,进入下一环节; </span></p><p><span style="font-size: 15px;">b.线程获取同步状态失败,就构造一个结点,加入同步队列中,这个过程要保证线程安全; </span></p><p><span style="font-size: 15px;">c.加入队列中的结点线程进入自旋状态,若是老二结点(即前驱结点为头结点),才有机会尝试去获取同步状态;否则,当其前驱结点的状态为SIGNAL,线程便可安心休息,进入阻塞状态,直到被中断或者被前驱结点唤醒。 </span></p><p><span style="font-size: 15px;color: rgb(171, 25, 66);">release </span><span style="font-size: 15px;"></span></p><p><span style="font-size: 15px;">release的同步状态相对简单,需要找到头结点的后继结点进行唤醒,若后继结点为空或处于CANCEL状态,从后向前遍历找寻一个正常的结点,唤醒其对应线程。</span></p></li> <li><p><span style="font-size: 15px;">共享式: </span></p><p><span style="font-size: 15px;">共享式地获取同步状态.同步状态的方法tryAcquireShared返回值为int。 </span></p><p><span style="font-size: 15px;">a.当返回值大于0时,表示获取同步状态成功,同时还有剩余同步状态可供其他线程获取; </span></p><p><span style="font-size: 15px;">b.当返回值等于0时,表示获取同步状态成功,但没有可用同步状态了; </span></p><p><span style="font-size: 15px;">c.当返回值小于0时,表示获取同步状态失败。</span></p></li> <li><p><span style="font-size: 15px;">AQS实现公平锁和非公平锁 </span></p><p><span style="font-size: 15px;">非公平锁中,那些尝试获取锁且尚未进入等待队列的线程会和等待队列head结点的线程发生竞争。公平锁中,在获取锁时,增加了isFirst(current)判断,当且仅当,等待队列为空或当前线程是等待队列的头结点时,才可尝试获取锁。 </span></p></li> </ol> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">16、Java里的阻塞队列</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;"><br></span></strong></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;">7个阻塞队列。分别是</span></strong></span><strong><span style="font-size: 15px;"></span></strong></p> <p><span style="font-size: 15px;"><br></span></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。 </span></p></li> <li><p><span style="font-size: 15px;">LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。 </span></p></li> <li><p><span style="font-size: 15px;">PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。 </span></p></li> <li><p><span style="font-size: 15px;">DelayQueue:一个使用优先级队列实现的无界阻塞队列。 </span></p></li> <li><p><span style="font-size: 15px;">SynchronousQueue:一个不存储元素的阻塞队列。 </span></p></li> <li><p><span style="font-size: 15px;">LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。 </span></p></li> <li><p><span style="font-size: 15px;">LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。</span></p></li> </ol> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">添加元素</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">Java中的阻塞队列接口BlockingQueue继承自Queue接口。BlockingQueue接口提供了3个添加元素方法。</span></p> <p><span style="font-size: 15px;"> </span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;">add:添加元素到队列里,添加成功返回true,由于容量满了添加失败会抛出IllegalStateException异常 </span></p></li> <li><p><span style="font-size: 15px;">offer:添加元素到队列里,添加成功返回true,添加失败返回false </span></p></li> <li><p><span style="font-size: 15px;">put:添加元素到队列里,如果容量满了会阻塞直到容量不满</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">删除方法</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">3个删除方法 </span></p> <p><span style="font-size: 15px;"><br></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;">poll:删除队列头部元素,如果队列为空,返回null。否则返回元素。 </span></p></li> <li><p><span style="font-size: 15px;">remove:基于对象找到对应的元素,并删除。删除成功返回true,否则返回false </span></p></li> <li><p><span style="font-size: 15px;">take:删除队列头部元素,如果队列为空,一直阻塞到队列有元素并删除</span></p></li> </ul> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">17、condition</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">对Condition的源码理解,主要就是理解等待队列,等待队列可以类比同步队列,而且等待队列比同步队列要简单,因为等待队列是单向队列,同步队列是双向队列。</span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;"><br></span></strong></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;">18、DelayQueue</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">队列中每个元素都有个过期时间,并且队列是个优先级队列,当从队列获取元素时候,只有过期元素才会出队列。</span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;"><br></span></strong></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;">19、Fork/Join框架</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">Fork/Join框架是Java 7提供的一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。Fork/Join框架要完成两件事情:</span></p> <p><span style="font-size: 15px;"><br></span></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">任务分割:首先Fork/Join框架需要把大的任务分割成足够小的子任务,如果子任务比较大的话还要对子任务进行继续分割</span></p></li> <li><p><span style="font-size: 15px;">执行任务并合并结果:分割的子任务分别放到双端队列里,然后几个启动线程分别从双端队列里获取任务执行。子任务执行完的结果都放在另外一个队列里,启动一个线程从队列里取数据,然后合并这些数据。</span></p></li> </ol> <p><span style="font-size: 15px;"> </span></p> <p><span style="font-size: 15px;">在Java的Fork/Join框架中,使用两个类完成上述操作</span></p> <p><br></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">ForkJoinTask:我们要使用Fork/Join框架,首先需要创建一个ForkJoin任务。该类提供了在任务中执行fork和join的机制。通常情况下我们不需要直接集成ForkJoinTask类,只需要继承它的子类,Fork/Join框架提供了两个子类:</span></p><p><span style="font-size: 15px;"> a.RecursiveAction:用于没有返回结果的任务</span></p><p><span style="font-size: 15px;"> b.RecursiveTask:用于有返回结果的任务</span></p></li> <li><p><span style="font-size: 15px;">ForkJoinPool:ForkJoinTask需要通过ForkJoinPool来执行</span></p></li> </ol> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">任务分割出的子任务会添加到当前工作线程所维护的双端队列中,进入队列的头部。当一个工作线程的队列里暂时没有任务时,它会随机从其他工作线程的队列的尾部获取一个任务(工作窃取算法)。 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;color: rgb(171, 25, 66);">Fork/Join框架的实现原理 </span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">ForkJoinPool由ForkJoinTask数组和ForkJoinWorkerThread数组组成,ForkJoinTask数组负责将存放程序提交给ForkJoinPool,而ForkJoinWorkerThread负责执行这</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">20、原子操作类</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">在java.util.concurrent.atomic包下,可以分为四种类型的原子更新类:原子更新基本类型、原子更新数组类型、原子更新引用和原子更新属性。</span></p> <p><span style="font-size: 15px;"><br></span></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">原子更新基本类型 </span></p><p><span style="font-size: 15px;">使用原子方式更新基本类型,共包括3个类: </span></p><p><span style="font-size: 15px;">AtomicBoolean:原子更新布尔变量 </span></p><p><span style="font-size: 15px;">AtomicInteger:原子更新整型变量 </span></p><p><span style="font-size: 15px;">AtomicLong:原子更新长整型变量</span></p></li> <li><p><span style="font-size: 15px;">原子更新数组 </span></p><p><span style="font-size: 15px;">通过原子更新数组里的某个元素,共有3个类: </span></p><p><span style="font-size: 15px;">AtomicIntegerArray:原子更新整型数组的某个元素 </span></p><p><span style="font-size: 15px;">AtomicLongArray:原子更新长整型数组的某个元素 </span></p><p><span style="font-size: 15px;">AtomicReferenceArray:原子更新引用类型数组的某个元素 </span></p><p><span style="font-size: 15px;">AtomicIntegerArray常用的方法有: </span></p><p><span style="font-size: 15px;">int addAndSet(int i, int delta):以原子方式将输入值与数组中索引为i的元素相加 </span></p><p><span style="font-size: 15px;">boolean compareAndSet(int i, int expect, int update):如果当前值等于预期值,则以原子方式更新数组中索引为i的值为update值</span></p></li> <li><p><span style="font-size: 15px;">原子更新引用类型 </span></p><p><span style="font-size: 15px;">AtomicReference:原子更新引用类型 </span></p><p><span style="font-size: 15px;">AtomicReferenceFieldUpdater:原子更新引用类型里的字段 </span></p><p><span style="font-size: 15px;">AtomicMarkableReference:原子更新带有标记位的引用类型。</span></p></li> <li><p><span style="font-size: 15px;">原子更新字段类 </span></p><p><span style="font-size: 15px;">如果需要原子更新某个类的某个字段,就需要用到原子更新字段类,可以使用以下几个类: </span></p><p><span style="font-size: 15px;">AtomicIntegerFieldUpdater:原子更新整型字段 </span></p><p><span style="font-size: 15px;">AtomicLongFieldUpdater:原子更新长整型字段 </span></p><p><span style="font-size: 15px;">AtomicStampedReference:原子更新带有版本号的引用类型。 </span></p><p><span style="font-size: 15px;">要想原子更新字段,需要两个步骤: </span></p><p><span style="font-size: 15px;">每次必须使用newUpdater创建一个更新器,并且需要设置想要更新的类的字段 </span></p><p><span style="font-size: 15px;">更新类的字段(属性)必须为public volatile</span></p></li> </ol> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">21、同步屏障CyclicBarrier</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。</span></p> <p><span style="font-size: 15px;"> </span></p> <p><span style="font-size: 15px;color: rgb(171, 25, 66);">CyclicBarrier和CountDownLatch的区别</span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken方法用来知道阻塞的线程是否被中断。比如以下代码执行完之后会返回true。</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">22、Semaphore</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">Semaphore可以用于做流量控制,特别公用资源有限的应用场景,比如数据库连接。假如有一个需求,要读取几万个文件的数据,因为都是IO密集型任务,我们可以启动几十个线程并发的读取,但是如果读到内存后,还需要存储到数据库中,而数据库的连接数只有10个,这时我们必须控制只有十个线程同时获取数据库连接保存数据,否则会报错无法获取数据库连接。这个时候,我们就可以使用Semaphore来做流控.</span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">23、死锁,以及解决死锁</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">死锁产生的四个必要条件</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;color: rgb(171, 25, 66);">互斥条件</span><span style="font-size: 15px;">:资源是独占的且排他使用,进程互斥使用资源,即任意时刻一个资源只能给一个进程使用,其他进程若申请一个资源,而该资源被另一进程占有时,则申请者等待直到资源被占有者释放。 </span></p> <p><span style="font-size: 15px;color: rgb(171, 25, 66);"><br></span></p> <p><span style="font-size: 15px;color: rgb(171, 25, 66);">不可剥夺条件</span><span style="font-size: 15px;">:进程所获得的资源在未使用完毕之前,不被其他进程强行剥夺,而只能由获得该资源的进程资源释放。 </span></p> <p><span style="font-size: 15px;color: rgb(171, 25, 66);"><br></span></p> <p><span style="font-size: 15px;color: rgb(171, 25, 66);">请求和保持条件</span><span style="font-size: 15px;">:进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源。 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;color: rgb(171, 25, 66);">循环等待条件</span><span style="font-size: 15px;">:在发生死锁时必然存在一个进程等待队列{P1,P2,…,Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路,环路中每一个进程所占有的资源同时被另一个申请,也就是前一个进程占有后一个进程所深情地资源。</span></p> <p><span style="font-size: 15px;color: rgb(171, 25, 66);"><br></span></p> <p><strong><span style="font-size: 15px;color: rgb(171, 25, 66);">解决死锁</span></strong><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">死锁预防,就是不让上面的四个条件同时成立。</span><span style="font-size: 15px;"> </span></p></li> <li><p><span style="font-size: 15px;">合理分配资源。 </span></p></li> <li><p><span style="font-size: 15px;">使用银行家算法,如果该进程请求的资源操作系统剩余量可以满足,那么就分配。</span></p></li> </ol> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">24、进程间的通信方式</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">管道( pipe):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。</span></p></li> <li><p><span style="font-size: 15px;">有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。</span></p></li> <li><p><span style="font-size: 15px;">信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。</span></p></li> <li><p><span style="font-size: 15px;">消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。</span></p></li> <li><p><span style="font-size: 15px;">信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。</span></p></li> <li><p><span style="font-size: 15px;">共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。</span></p></li> <li><p><span style="font-size: 15px;">套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。</span></p></li> </ol> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">中断</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">interrupt()的作用是中断本线程。 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">本线程中断自己是被允许的;其它线程调用本线程的interrupt()方法时,会通过checkAccess()检查权限。这有可能抛出SecurityException异常。 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">如果本线程是处于阻塞状态:调用线程的wait(), wait(long)或wait(long, int)会让它进入等待(阻塞)状态,或者调用线程的join(), join(long), join(long, int), sleep(long), sleep(long, int)也会让它进入阻塞状态。若线程在阻塞状态时,调用了它的interrupt()方法,那么它的“中断状态”会被清除并且会收到一个InterruptedException异常。例如,线程通过wait()进入阻塞状态,此时通过interrupt()中断该线程;调用interrupt()会立即将线程的中断标记设为“true”,但是由于线程处于阻塞状态,所以该“中断标记”会立即被清除为“false”,同时,会产生一个InterruptedException的异常。</span></p> <p><span style="font-size: 15px;"> </span></p> <p><span style="font-size: 15px;">如果线程被阻塞在一个Selector选择器中,那么通过interrupt()中断它时;线程的中断标记会被设置为true,并且它会立即从选择操作中返回。</span></p> <p><span style="font-size: 15px;"> </span></p> <p><span style="font-size: 15px;">如果不属于前面所说的情况,那么通过interrupt()中断线程时,它的中断标记会被设置为“true”。 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">中断一个“已终止的线程”不会产生任何操作。</span></p> <p><br></p> <ol class=" list-paddingleft-2" style="list-style-type: decimal;"> <li><p><span style="font-size: 15px;">终止处于“阻塞状态”的线程 </span></p><p><span style="font-size: 15px;">通常,我们通过“中断”方式终止处于“阻塞状态”的线程。 </span></p><p><span style="font-size: 15px;">当线程由于被调用了sleep(), wait(), join()等方法而进入阻塞状态;若此时调用线程的interrupt()将线程的中断标记设为true。由于处于阻塞状态,中断标记会被清除,同时产生一个InterruptedException异常。将InterruptedException放在适当的为止就能终止线程, </span></p></li> <li><p><span style="font-size: 15px;">终止处于“运行状态”的线程</span></p></li> </ol> <p><span style="font-size: 15px;"><br></span></p> <p><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;">interrupted() 和 isInterrupted()的区别</span></strong></span><span style="font-size: 15px;"></span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">最后谈谈 interrupted() 和 isInterrupted()。 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">interrupted() 和 isInterrupted()都能够用于检测对象的“中断标记”。 </span></p> <p><span style="font-size: 15px;"><br></span></p> <p><span style="font-size: 15px;">区别是,interrupted()除了返回中断标记之外,它还会清除中断标记(即将中断标记设为false);而isInterrupted()仅仅返回中断标记。 </span></p> <p><span style="font-size: 15px;"><br></span></p> <section style="white-space: normal;font-variant-ligatures: normal;orphans: 2;widows: 2;max-width: 100%;box-sizing: border-box;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <section class="" powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <section style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <section style="padding-top: 1.1em;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;display: inline-block;vertical-align: top;overflow-wrap: break-word !important;"> <section style="padding: 0.2em 0.4em;max-width: 100%;box-sizing: border-box;border-top-left-radius: 0px;border-top-right-radius: 0.5em;border-bottom-right-radius: 0.5em;border-bottom-left-radius: 0px;background-color: rgb(249, 110, 87);color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <p style="max-width: 100%;box-sizing: border-box;min-height: 1em;overflow-wrap: break-word !important;"><strong style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;">推荐阅读</strong></p> </section> <section style="max-width: 100%;box-sizing: border-box;width: 0px;border-right-width: 4px;border-right-style: solid;border-right-color: rgb(249, 110, 87);border-top-width: 4px;border-top-style: solid;border-top-color: rgb(249, 110, 87);overflow-wrap: break-word !important;border-left-width: 4px !important;border-left-style: solid !important;border-left-color: transparent !important;border-bottom-width: 4px !important;border-bottom-style: solid !important;border-bottom-color: transparent !important;"></section> </section> <section style="padding-left: 10px;max-width: 100%;box-sizing: border-box;display: inline-block;vertical-align: top;color: rgb(160, 160, 160);font-size: 14px;overflow-wrap: break-word !important;"> <p style="max-width: 100%;box-sizing: border-box;min-height: 1em;overflow-wrap: break-word !important;">(点击标题可跳转阅读)</p> </section> <section style="margin-top: -3.5em;margin-left: 8px;padding: 3.5em 10px 10px;max-width: 100%;box-sizing: border-box;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);overflow-wrap: break-word !important;"> <section class="" powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;font-size: 14px;line-height: 2.6;overflow-wrap: break-word !important;"> <p><a href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&mid=2651482464&idx=1&sn=486181f5354c5d0be5fe547c8b92ee24&chksm=bd25051f8a528c09542e45371d99d4e752c2aec9e48263ac1880890cf5a9df402b498d3dda5a&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2"><span style="font-size: 12px;">分布式、高并发、多线程,到底有什么区别?</span></a><br></p> <p><a href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&mid=2651477462&idx=2&sn=33785a99e8db0202e0db4fda02dc5003&chksm=bd2539a98a52b0bf264898f8baae7dae3dac717a7f7dde070da810e0dac9d1a1c784b8f6dae8&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2"><span style="font-size: 12px;">高并发Java(4):</span><span style="font-size: 12px;">无锁</span></a><br></p> <p><a href="http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&mid=2651479341&idx=2&sn=44e59ddb6b534503d8443eaa9a2fd213&chksm=bd2531528a52b8443641b53ab550327a898a7e11eceecb44a5ad385697b308ec10714322ac6a&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" style="font-size: 12px;text-decoration: underline;" data-linktype="2"><span style="font-size: 12px;">Java 高并发综合</span></a><br></p> <p><span style="font-size: 12px;"></span></p> </section> </section> </section> </section> </section> </section> </section> </section> <p style="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 class="" data-ratio="0.9166666666666666" data-s="300,640" data-type="png" data-w="600" width="auto" src="/upload/899866149276fa5fddb73c61ae04be64.png" 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;background-color: rgb(255, 254, 213);color: rgb(255, 41, 65);">喜欢就点一下「好看」呗~</span></p>
作者:微信小助手
<p><img class="currentImg" data-croporisrc="/upload/e0e53f457bd809ea971de4b0125e5d32.jpg" data-cropx1="0" data-cropx2="607.5733333333334" data-cropy1="259.41333333333336" data-cropy2="634.88" data-ratio="0.6194398682042833" src="https://mmbiz.qpic.cn/mmbiz_jpg/l89kosVutolpysKlnG95ic0VVSiaEBWMjE4yibP5GgNriachDicTmrMSINf4ZEo6OnicTAJnrOr4rjjD5HHz79farVicQ/640?wx_fmt=jpeg" data-type="jpeg" data-w="607" style="top: 0px;left: 52px;width: 534px;height: 330px;cursor: pointer;display: block;" title="点击查看源网页"><br></p> <section class="" style="margin: 0px;padding: 0px 10px 0px 9px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-style: normal;font-variant-caps: normal;orphans: auto;text-indent: 0px;text-transform: none;white-space: normal;widows: auto;word-spacing: 0px;-webkit-text-size-adjust: auto;-webkit-text-stroke-width: 0px;text-decoration: none;color: rgb(60, 60, 60);font-size: 16px;font-weight: bold;letter-spacing: 1px;text-align: center;line-height: 1.8;background-color: rgb(255, 255, 255);z-index: 10000;background-position: initial initial;background-repeat: initial initial;"> <span style="font-size: 17px;">导读<br><img class="" data-type="png" data-ratio="0.053125" data-w="640" style="margin-top: 5px !important;margin-right: 0px;margin-bottom: 0px;margin-left: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(60, 60, 60);font-weight: bold;letter-spacing: 1px;text-align: center;white-space: normal;background-color: rgb(255, 255, 255);font-size: 15px;display: inline-block;left: 0px;transform: rotateX(60deg);visibility: visible !important;width: 632px !important;height: auto !important;" src="/upload/16c3bc26d4547f0344ee22af769e1434.png"><br></span> </section> <section class="" style="margin: 0px;padding: 0px 10px 0px 9px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-style: normal;font-variant-caps: normal;orphans: auto;text-indent: 0px;text-transform: none;white-space: normal;widows: auto;word-spacing: 0px;-webkit-text-size-adjust: auto;-webkit-text-stroke-width: 0px;text-decoration: none;color: rgb(60, 60, 60);font-size: 16px;font-weight: bold;letter-spacing: 1px;text-align: center;line-height: 1.8;background-color: rgb(255, 255, 255);z-index: 10000;background-position: initial initial;background-repeat: initial initial;"> <span style="font-size: 17px;"><br></span> </section> <p style="line-height: 2em;"><span style="font-size: 16px;">今天和大家聊一聊在Spring Cloud微服务框架实践中,比较核心但是又很容易把人搞得稀里糊涂的一个问题,那就是在Spring Cloud中<strong>Hystrix、Ribbon以及Feign</strong>它们三者之间在处理微服务调用超时从而触发熔断降级的关系是什么?</span><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;"></span><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;">我们知道在Spring Cloud微服务体系下,微服务之间的互相调用可以通过Feign进行声明式调用,在这个服务调用过程中Feign会通过Ribbon从服务注册中心获取目标微服务的服务器地址列表,之后在网络请求的过程中Ribbon就会将请求以负载均衡的方式打到微服务的不同实例上,从而实现Spring Cloud微服务架构中最为关键的功能即<strong>服务发现及客户端负载均衡</strong>调用。</span></p> <p style="line-height: 2em;"><br></p> <p style="line-height: 2em;"><span style="font-size: 16px;">另一方面微服务在互相调用的过程中,为了防止某个微服务的故障消耗掉整个系统所有微服务的连接资源,所以在实施微服务调用的过程中我们会要求在调用方实施针对被调用<strong>微服务的熔断</strong>逻辑。而要实现这个逻辑场景在Spring Cloud微服务框架下我们是通过Hystrix这个框架来实现的。</span></p> <p style="line-height: 2em;"><br></p> <p style="text-align: left;line-height: 2em;"><span style="font-size: 16px;">调用方会针对被调用微服务<strong>设置调用超时时间</strong>,一旦超时就会进入熔断逻辑,而这个故障指标信息也会返回给Hystrix组件,Hystrix组件会根据熔断情况判断被调微服务的故障情况从而<strong>打开熔断器</strong>,之后所有针对该微服务的请求就会直接进入熔断逻辑,直到被调微服务故障恢复,Hystrix断路器关闭为止。</span></p> <p style="text-align: left;line-height: 2em;"><span style="font-size: 16px;"><br></span></p> <p style="text-align: center;"><span style="font-size: 16px;"><strong><span style="font-size: 17px;">Hystrix、Feign及Ribbon的配置说明<br><img class="" data-type="png" data-ratio="0.053125" data-w="640" style="margin-top: 5px !important;margin-right: 0px;margin-bottom: 0px;margin-left: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(60, 60, 60);font-weight: bold;letter-spacing: 1px;text-align: center;white-space: normal;background-color: rgb(255, 255, 255);font-size: 15px;display: inline-block;left: 0px;transform: rotateX(60deg);visibility: visible !important;width: 632px !important;height: auto !important;" src="/upload/16c3bc26d4547f0344ee22af769e1434.png"></span></strong></span></p> <p style="text-align: left;"><span style="font-size: 16px;"><br></span></p> <p style="text-align: left;line-height: 2em;"><span style="font-size: 16px;">接下来我们先来看看在Spring Cloud微服务系统中Hystrix、Feign及Ribbon的常用配置都有哪些以及它们的使用场景分别是什么?</span><br></p> <p style="text-align: left;line-height: 2em;"><br></p> <p style="text-align: left;line-height: 2em;"><span style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(61, 170, 214);font-size: 16px;"><strong style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;">Hystrix配置说明<br></strong></span></p> <p style="text-align: left;line-height: 2em;"><br><span style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(61, 170, 214);font-size: 16px;"><strong style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"></strong><strong style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"></strong><strong style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"></strong></span></p> <p style="text-align: left;line-height: 2em;"><span style="font-size: 16px;">在Spring Cloud微服务体系中Hystrix主要被用于实现实现微服务之间<strong>网络调用故障的熔断、过载保护及资源隔离</strong>等功能。而要正确使用Hystrix提供的这些功能就需要对Hystrix常用的配置有一定深入的了解,否则你会发现使用过程中认为一定会生效的配置常常不会起作用,接下来我们就按照参数配置的类型对Hystrix中的常见配置做一个梳理。</span></p> <p style="text-align: left;line-height: 2em;"><span style="font-size: 16px;"><br></span></p> <p style="text-align: left;line-height: 2em;"><strong><span style="font-size: 16px;color: rgb(0, 0, 0);">1)、线程隔离相关配置</span></strong></p> <p style="text-align: left;line-height: 2em;"><br></p> <p style="text-align: left;line-height: 2em;"><span style="font-size: 16px;">Hystrix具备的重要关键特性之一就是它能够实现对<strong>第三方服务依赖的资源隔离</strong>,而隔离最常见的方式是通过<strong>线程池资源的隔离</strong>来实现的,Hystrix会为每个第三方服务依赖配置单独的线程池资源,从而避免对第三方服务依赖的请求占用应用主线程资源以免造成系统雪崩。Hystrix中关于线程隔离相关的配置如下:</span></p> <p style="text-align: left;line-height: 2em;"><span style="font-size: 16px;"><br></span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code class="hljs bash" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: none 0% 0% repeat scroll rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">hystrix:<br> <span class="hljs-built_in" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">command</span>:<br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#全局默认配置</span><br> default:<br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#线程隔离相关</span><br> execution:<br> timeout:<br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#是否给方法执行设置超时时间,默认为true。一般我们不要改。</span><br> enabled: <span class="hljs-literal" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">true</span><br> isolation:<br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#配置请求隔离的方式,这里是默认的线程池方式。还有一种信号量的方式semaphore,使用比较少。</span><br> strategy: threadPool<br> thread:<br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#方式执行的超时时间,默认为1000毫秒,在实际场景中需要根据情况设置</span><br> timeoutInMilliseconds: 1000<br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#发生超时时是否中断方法的执行,默认值为true。不要改。</span><br> interruptOnTimeout: <span class="hljs-literal" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">true</span><br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#是否在方法执行被取消时中断方法,默认值为false。没有实际意义,默认就好!</span><br> interruptOnCancel: <span class="hljs-literal" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">false</span></code></pre> </section> <p style="white-space: normal;text-align: left;line-height: 2em;"><strong><span style="font-size: 16px;"></span></strong></p> <p style="white-space: normal;text-align: left;line-height: 2em;"><br></p> <p style="white-space: normal;text-align: left;line-height: 2em;"><strong><span style="font-size: 16px;">2)、熔断器相关配置</span></strong></p> <p style="white-space: normal;text-align: left;line-height: 2em;"><br></p> <p style="white-space: normal;text-align: left;line-height: 2em;"><span style="font-size: 16px;">熔断器是Hystrix最主要的功能,它开启和关闭的<strong>时机、灵敏度及准确性</strong>是Hystrix是否能够发挥重要的关键,而在Hystrix中与熔断器相关的几个配置如下:</span></p> <p style="white-space: normal;text-align: left;line-height: 2em;"><span style="font-size: 16px;"><br></span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code class="hljs bash" style="margin-right: 2px;margin-left: 2px;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: none 0% 0% repeat scroll rgb(40, 43, 46);padding: 0.5em;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;">hystrix:<br> <span class="hljs-built_in" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);overflow-wrap: inherit !important;word-break: inherit !important;">command</span>:<br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#全局默认配置</span><br> default:<br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#熔断器相关配置</span><br> circuitBreaker:<br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#说明:是否启动熔断器,默认为true。我们使用Hystrix的目的就是为了熔断器,不要改,否则就不要引入Hystrix。</span><br> enabled: <span class="hljs-literal" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);overflow-wrap: inherit !important;word-break: inherit !important;">true</span><br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#说明1:启用熔断器功能窗口时间内的最小请求数,假设我们设置的窗口时间为10秒,</span><br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#说明2:那么如果此时默认值为20的话,那么即便10秒内有19个请求都失败也不会打开熔断器。</span><br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#说明3:此配置项需要根据接口的QPS进行计算,值太小会有误打开熔断器的可能,而如果值太大超出了时间窗口内的总请求数,则熔断永远也不会被触发</span><br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);overflow-wrap: inherit !important;word-break: inherit !important;">#说明4:建议设置一般为:QPS*窗口描述*60%</sp
作者:微信小助手
<section style="box-sizing: border-box;font-size: 16px;"> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="padding-top: 10px;padding-right: 10px;padding-left: 10px;box-sizing: border-box;background-color: rgb(239, 239, 239);"> <span style="display: inline-block;width: 5%;line-height: 0.8;font-weight: bolder;font-size: 48px;box-sizing: border-box;"> <section style="box-sizing: border-box;"> “ </section></span> <section style="display: inline-block;vertical-align: top;float: right;width: 90%;line-height: 1.5;font-size: 15px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><span style="letter-spacing: 1px;">Kafka 的消息是保存或缓存在磁盘上的,一般认为在磁盘上读写数据是会降低性能的,因为寻址会比较消耗时间,但是实际上,Kafka 的特性之一就是高吞吐率。</span></p> </section> <section style="clear: both;box-sizing: border-box;"></section> </section> </section> </section> </section> <p style="line-height: 1.75em;"><br></p> <section data-role="outer" label="Powered by 135editor.com" style="line-height: 27.2px;white-space: normal;"> <p style="text-align: center;margin-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="rich_pages" data-copyright="0" data-ratio="0.5565217391304348" data-s="300,640" src="/upload/e29a279232229030192e42e8da96f3cb.png" data-type="png" data-w="690" style=""></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">即使是普通的服务器,Kafka 也可以轻松支持每秒百万级的写入请求,超过了大部分的消息中间件,这种特性也使得 Kafka 在日志处理等海量数据场景广泛应用。</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">针对 Kafka 的基准测试可以参考 Apache Kafka 基准测试(</span><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">可点击阅读原文查看</span><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">)《每秒写入 2 百万(在三台廉价机器上)<span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 29.75px;">》</span>:</span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code class="hljs coffeescript" style="margin-right: 2px;margin-left: 2px;padding: 0.5em;overflow-wrap: break-word;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: none 0% 0% repeat scroll rgb(40, 43, 46);">http:<span class="hljs-regexp" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">//i</span>feve.com<span class="hljs-regexp" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">/benchmarking-apache-kafka-2-million-writes-second-three-cheap-machines/</span><br></code></pre> </section> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">下面从数据写入和读取两方面分析,为什么 Kafka 速度这么快。</span></p> <section class="" data-tools="135编辑器" data-id="86122" data-color="#138bde"> <section> <section> <section> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <section style="margin-top: 0.5em;margin-bottom: 0.5em;border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;line-height: 1.2;box-sizing: border-box;"> <section style="margin-bottom: -1px;display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">数据写入</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> </section> </section> </section> </section> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Kafka 会把收到的消息都写入到硬盘中,它绝对不会丢失数据。为了优化写入速度 Kafka 采用了两个技术, 顺序写入和 MMFile(Memory Mapped <span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 29.75px;">File</span>)。</span></p> <p style="line-height: normal;"><br></p> <section class="" data-tools="135编辑器" data-id="39" data-color="#138bde"> <section class="" data-tools="135编辑器" data-id="39" data-color="#138bde" data-custom="#1e9be8"> <section> <section style="box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="margin-top: 2px;margin-bottom: 2px;width: 0.6em;height: 0.6em;display: block;opacity: 0.6;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span><span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span></strong> </section> <section style="padding-left: 5px;display: inline-block;vertical-align: middle;font-size: 18px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>顺序写入</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> </section> </section> </section> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">磁盘读写的快慢取决于你怎么使用它,也就是顺序读写或者随机读写。在顺序读写的情况下,磁盘的顺序读写速度和内存持平。</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">因为硬盘是机械结构,每次读写都会寻址->写入,其中寻址是一个“机械动作”,它是最耗时的。</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">所以硬盘最讨厌随机 I/O,最喜欢顺序 I/O。为了提高读写硬盘的速度,Kafka 就是使用顺序 I/O。</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">而且 Linux 对于磁盘的读写优化也比较多,包括 read-ahead 和 write-behind,磁盘缓存等。</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果在内存做这些操作的时候,一个是 Java 对象的内存开销很大,另一个是随着堆内存数据的增多,Java 的 GC 时间会变得很长。</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">使用磁盘操作有以下几个好处:</span></p> <ul class=" list-paddingleft-2" style=""> <li><p style="line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">磁盘顺序读写速度超过内存随机读写。</span></p></li> <li><p style="line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">JVM 的 GC 效率低,内存占用大。使用磁盘可以避免这一问题。</span></p></li> <li><p style="line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">系统冷启动后,磁盘缓存依然可用。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">下图就展示了 Kafka 是如何写入数据的, 每一个 Partition 其实都是一个文件 ,收到消息后 Kafka 会把数据插入到文件末尾(虚框部分):</span></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;text-align: center;"><img class="rich_pages" data-copyright="0" data-s="300,640" src="/upload/8af9869dd875de65cd511b771f2de17a.png" data-type="png" data-ratio="0.5075885328836425" data-w="593"></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这种方法有一个缺陷——没有办法删除数据 ,所以 Kafka 是不会删除数据的,它会把所有的数据都保留下来,每个消费者(Consumer)对每个 Topic 都有一个 Offset 用来表示读取到了第几条数据 。</span></p> <p style="margin-bottom: 5px;text-align: center;"><img class="rich_pages" data-copyright="0" data-s="300,640" src="/upload/5d12491701650d4b71d3c5d9d8d4dee9.png" data-type="png" data-ratio="0.5271867612293144" data-w="423"></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">两个消费者:</span></p> <ul class=" list-paddingleft-2" style=""> <li><p style="line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Consumer1 有两个 Offset 分别对应 Partition0、Partition1(假设每一个 Topic 一个 Partition)。</span></p></li> <li><p style="line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Consumer2 有一个 Offset 对应 Partition2。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这个 Offset 是由客户端 SDK 负责保存的,Kafka 的 Broker 完全无视这个东西的存在。</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">一般情况下 SDK 会把它保存到 Zookeeper 里面,所以需要给 Consumer 提供 Zookeeper 的地址。</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">如果不删除硬盘肯定会被撑满,所以 Kakfa 提供了两种策略来删除数据:</span></p> <ul class=" list-paddingleft-2" style=""> <li><p style="line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">基于时间</span></strong></p></li> <li><p style="line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">基于 Partition 文件大小</span></strong></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">具体配置可以参看它的配置文档。</span></p> <p style="line-height: normal;"><br></p> <section class="" data-tools="135编辑器" data-id="39" data-color="#138bde"> <section class="" data-tools="135编辑器" data-id="39" data-color="#138bde" data-custom="#1e9be8"> <section> <section style="box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="margin-top: 2px;margin-bottom: 2px;width: 0.6em;height: 0.6em;display: block;opacity: 0.6;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span></strong> </section> <section style="padding-left: 5px;display: inline-block;vertical-align: middle;font-size: 18px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>Memory Mapped Files</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> </section> </section> </section> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">即便是顺序写入硬盘,硬盘的访问速度还是不可能追上内存。所以 Kafka 的数据并不是实时的写入硬盘 ,它充分利用了现代操作系统分页存储来利用内存提高 I/O 效率。</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Memory Mapped Files(后面简称 mmap)也被翻译成内存映射文件 ,在 64 位操作系统中一般可以表示 20G 的数据文件,它的工作原理是直接利用操作系统的 Page 来实现文件到物理内存的直接映射。</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">完成映射之后你对物理内存的操作会被同步到硬盘上(操作系统在适当的时候)。</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">通过 mmap,进程像读写硬盘一样读写内存(当然是虚拟机内存),也不必关心内存的大小,有虚拟内存为我们兜底。</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">使用这种方式可以获取很大的 I/O 提升,省去了用户空间到内核空间复制的开销。(调用文件的 Read 会把数据先放到内核空间的内存中,然后再复制到用户空间的内存中)</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">但也有一个很明显的缺陷——不可靠,写到 mmap 中的数据并没有被真正的写到硬盘,操作系统会在程序主动调用 Flush 的时候才把数据真正的写到硬盘。</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Kafka 提供了一个参数 producer.type 来控制是不是主动 Flush:</span></p> <ul class=" list-paddingleft-2" style=""> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果 Kafka 写入到 mmap 之后就立即 Flush,然后再返回 Producer 叫同步 (Sync)。</span></p></li> <li><p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果 Kafka 写入 mmap 之后立即返回 Producer 不调用 Flush 叫异步 (Async)。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <section class="" data-tools="135编辑器" data-id="86122" data-color="#138bde"> <section> <section> <section> <section style="box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <section style="margin-top: 0.5em;margin-bottom: 0.5em;border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;line-height: 1.2;box-sizing: border-box;"> <section style="margin-bottom: -1px;display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">数据读取</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> </section> </section> </section> </section> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Kafka 在读取磁盘时做了哪些优化?</span></p> <p style="line-height: normal;"><br></p> <section class="" data-tools="135编辑器" data-id="39" data-color="#138bde"> <section class="" data-tools="135编辑器" data-id="39" data-color="#138bde" data-custom="#1e9be8"> <section> <section style="box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="margin-top: 2px;margin-bottom: 2px;width: 0.6em;height: 0.6em;display: block;opacity: 0.6;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </section> <section style="padding-left: 5px;display: inline-block;vertical-align: middle;font-size: 18px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>基于 Sendfile 实现Zero Copy</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> </section> </section> </section> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">传统模式下,当需要对一个文件进行传输的时候,其具体流程细节如下:</span></p> <ul class=" list-paddingleft-2" style=""> <li><p style="line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">调用 Read 函数,文件数据被 Copy 到内核缓冲区。</span></p></li> <li><p style="line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Read 函数返回,文件数据从内核缓冲区 Copy 到用户缓冲区</span></p></li> <li><p style="line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Write 函数调用,将文件数据从用户缓冲区 Copy 到内核与 Socket 相关的缓冲区。</span></p></li> <li><p style="line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">数据从 Socket 缓冲区 Copy 到相关协议引擎。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">以上细节是传统 Read/Write 方式进行网络文件传输的方式,我们可以看到,在这个过程当中,文件数据实际上是经过了四次 Copy 操作:</span></p> <section style="box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <section style="margin-top: 0.5em;margin-bottom: 0.5em;text-align: center;box-sizing: border-box;"> <section style="padding: 10px;border: 3px solid rgb(204, 204, 204);box-shadow: rgb(102, 102, 102) 0.2em 0.2em 0.5em;box-sizing: border-box;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section style="text-align: left;color: rgb(89, 89, 89);font-size: 15px;box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>硬盘—>内核 buf—>用户 buf—>Socket 相关缓冲区—>协议引擎</strong></p> </section> </section> </section> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">而 Sendfile 系统调用则提供了一种减少以上多次 Copy,提升文件传输性能的方法。</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在内核版本 2.1 中,引入了 Sendfile 系统调用,以简化网络上和两个本地文件之间的数据传输。</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Sendfile 的引入不仅减少了数据复制,还减少了上下文切换。</span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;"><code class="hljs perl" style="margin-right: 2px;margin-left: 2px;padding: 0.5em;overflow-wrap: break-word;line-height: 18px;font-size: 14px;letter-spacing: 0px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;background: none 0% 0% repeat scroll rgb(40, 43, 46);">sendfile(<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">socket</span>, file, len);<br></code></pre> </section> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">运行流程如下:</span></p> <ul class=" list-paddingleft-2" style=""> <li><p style="line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Sendfile 系统调用,文件数据被 Copy 至内核缓冲区。</span></p></li> <li><p style="line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">再从内核缓冲区 Copy 至内核中 Socket 相关的缓冲区。</span></p></li> <li><p style="line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">最后再 Socket 相关的缓冲区 Copy 到协议引擎。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">相较传统 Read/Write 方式,2.1 版本内核引进的 Sendfile 已经减少了内核缓冲区到 User 缓冲区,再由 User 缓冲区到 Socket 相关缓冲区的文件 Copy。</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">而在内核版本 2.4 之后,文件描述符结果被改变,Sendfile 实现了更简单的方式,再次减少了一次 Copy 操作。</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在 Apache、Nginx、Lighttpd 等 Web 服务器当中,都有一项 Sendfile 相关的配置,使用 Sendfile 可以大幅提升文件传输性能。</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Kafka 把所有的消息都存放在一个一个的文件中,当消费者需要数据的时候 Kafka 直接把文件发送给消费者,配合 mmap 作为文件读写方式,直接把它传给 Sendfile。</span></p> <p style="line-height: normal;"><br></p> <section class="" data-tools="135编辑器" data-id="39" data-color="#138bde"> <section class="" data-tools="135编辑器" data-id="39" data-color="#138bde" data-custom="#1e9be8"> <section> <section style="box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="margin-top: 2px;margin-bottom: 2px;width: 0.6em;height: 0.6em;display: block;opacity: 0.6;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span><span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span></strong> </section> <section style="padding-left: 5px;display: inline-block;vertical-align: middle;font-size: 18px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>批量压缩</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> </section> </section> </section> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在很多情况下,系统的瓶颈不是 CPU 或磁盘,而是网络 IO,对于需要在广域网上的数据中心之间发送消息的数据流水线尤其如此。</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">进行数据压缩会消耗少量的 CPU 资源,</span><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">不过对于 Kafka 而言,网络 IO 更应该考虑:</span></p> <ul class=" list-paddingleft-2" style=""> <li><p style="line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">因为每个消息都压缩,但是压缩率相对很低,所以 Kafka 使用了批量压缩,即将多个消息一起压缩而不是单个消息压缩。</span></p></li> <li><p style="line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Kafka 允许使用递归的消息集合,批量的消息可以通过压缩的形式传输并且在日志中也可以保持压缩格式,直到被消费者解压缩。</span></p></li> <li><p style="line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Kafka 支持多种压缩协议,包括 Gzip 和 Snappy 压缩协议。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <section class="" data-tools="135编辑器" data-id="86122" data-color="#138bde"> <section> <section> <section> <section style="box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <section style="margin-top: 0.5em;margin-bottom: 0.5em;border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;line-height: 1.2;box-sizing: border-box;"> <section style="margin-bottom: -1px;display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">总结</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> </section> </section> </section> </section> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">Kafka 速度的秘诀在于,它把所有的消息都变成一个批量的文件,并且进行合理的批量压缩,减少网络 IO 损耗,通过 mmap 提高 I/O 速度。</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">写入数据的时候由于单个 Partion 是末尾添加,所以速度最优;读取数据的时候配合 Sendfile 直接暴力输出。</span></p> </section> <p style="white-space: normal;line-height: normal;"><br></p> <p style="white-space: normal;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);letter-spacing: 1px;"><em><span style="font-size: 14px;">作者:邴越</span></em></span></p> <p style="white-space: normal;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);letter-spacing: 1px;"><em><span style="font-size: 14px;">简介:阿里资深工程师,关注分布式系统及高可用架构,方法论与认知升级,实践持续学习。</span></em></span></p> <p style="white-space: normal;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);letter-spacing: 1px;"><em><span style="font-size: 14px;">编辑:陶家龙、孙淑娟</span></em></span><br></p> <p style="margin-bottom: 5px;white-space: normal;line-height: 1.75em;"><span style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;"><em>出处:www.cnblogs.com/binyue/p/10308754.html</em></span></p> <p style="line-height: 27.2px;white-space: normal;text-align: center;"><img class="rich_pages" data-copyright="0" src="/upload/58a14061a632a0fe87d40beb73c1aa.gif" data-type="gif" data-ratio="0.39375" data-w="640"></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <section style="margin-top: 0.5em;margin-bottom: 0.5em;box-sizing: border-box;"> <section style="font-size: 15px;border-style: solid;border-width: 0px 0px 1px;color: rgb(89, 89, 89);border-bottom-color: rgba(215, 215, 215, 0.960784);box-sizing: border-box;"> <p style="box-sizing: border-box;"><span style="letter-spacing: 1px;"><strong>精彩文章推荐:</strong></span></p> </section> </section> </section> </section> <p style="white-space: normal;line-height: 2em;"><a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&mid=2655824028&idx=1&sn=d58e7f613171fb144cf2587390114901&chksm=bd74e54b8a036c5dba7530e203aa4912717773418ce7719c7918e0af3a2d1d4d1c4abce46cdf&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2" style="color: rgb(89, 89, 89);font-size: 14px;letter-spacing: 1px;">震惊了,原来这才是Kafka的“真面目”!</a><br></p> <p style="white-space: normal;line-height: 2em;"><a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&mid=2655823925&idx=1&sn=a976017125ca2e8e5a8b8404cccd6f73&chksm=bd74e5e28a036cf4c4451c570167c747ed53c53685cf14de2b01abc11f63dae99059ace216c4&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2" style="color: rgb(89, 89, 89);font-size: 14px;letter-spacing: 1px;">Kafka是靠什么机制保持高可靠,高可用的?</a><br></p> <p style="white-space: normal;line-height: 2em;"><a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&mid=2655822894&idx=1&sn=d9adcc33ec9331bc71f3cf937f56b1de&chksm=bd74e9f98a0360ef233d25ffa5755938cb5400a67271c5b06daa54982daf9794689cdd99c552&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2" style="color: rgb(89, 89, 89);font-size: 14px;letter-spacing: 1px;">Kafka如何实现每秒上百万的超高并发写入?</a></p>
作者:微信小助手
<section class="xmteditor" style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn"></section> <p style="text-align: center;"><strong style="max-width: 100%;letter-spacing: 0.544px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;font-size: 16px;white-space: pre-line;background-color: rgb(255, 255, 255);text-align: right;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;color: rgb(136, 136, 136);box-sizing: border-box !important;overflow-wrap: break-word !important;"></span></strong></p> <p style="white-space: normal;text-align: center;"><span style="font-size: 14px;color: rgb(136, 136, 136);">点击蓝色“</span><span style="color: rgb(0, 128, 255);font-size: 14px;">程序猿DD</span><span style="font-size: 14px;color: rgb(136, 136, 136);">”关注我哟</span></p> <p style="white-space: normal;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);">”,不忘签到哦</span></p> <p><br></p> <p style="text-align: left;"><img class="" data-ratio="0.688034188034188" src="/upload/bb784b2d6179bebeb8fdc47dde2c7ef6.png" data-type="png" data-w="702" style="color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;font-size: 16px;text-align: start;white-space: pre-line;box-sizing: border-box;border-width: 2px;border-style: solid;border-color: rgb(238, 238, 238);border-radius: 6px;"></p> <p style="text-align: right;margin-bottom: 10px;"><span style="color: rgb(136, 136, 136);font-family: -apple-system, system-ui, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", SimSun, sans-serif;font-size: 12px;letter-spacing: 0.16px;text-align: right;white-space: pre-line;background-color: rgb(255, 255, 255);">来源:SpringForAll社区</span></p> <hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"> <p style="text-align: right;"><span style="color: rgb(136, 136, 136);font-family: -apple-system, system-ui, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", SimSun, sans-serif;font-size: 12px;letter-spacing: 0.16px;text-align: right;white-space: pre-line;background-color: rgb(255, 255, 255);"></span><br></p> <p style="margin-top: 15px;margin-bottom: 15px;box-sizing: border-box;font-size: 16px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: left;">如果有人会问你有关Spring Cloud的问题,那么你想到的第一件事可能就是Netflix OSS的支持。对Eureka,Zuul或Ribbon等工具的支持不仅由Spring提供,还由用于构建Apache Camel,Vert.x或Micronaut等微服务架构的其他流行框架提供。目前,Spring Cloud Netflix是Spring Cloud中最受欢迎的项目。它在GitHub上有大约3.2k的星星,而第二个最好的大约有1.4k。因此,Pivotal宣布大部分Spring Cloud Netflix模块正在进入维护模式,这是非常令人惊讶的。您可以通过Spencer Gibb https://spring.io/blog/2018/12/12/spring-cloud-greenwich-rc1-available-now 在Spring博客上发布的帖子中了解更多信息。好的,让我们对这些变化进行简短的总结。从Spring Cloud Greenwich发布开始Netflix OSS Archaius,Hystrix,Ribbon和Zuul正在进入维护模式。这意味着这些模块不会有任何新功能,Spring Cloud团队只会执行一些错误修复并修复安全问题。维护模式不包括仍支持的Eureka模块。对这些变化的解释非常简单。特别是其中两个。目前,Netflix并未积极开发Ribbon和Hystrix,尽管它们仍在大规模部署。此外,Hystrix已经被称为Atlas的遥测新解决方案所取代。Zuul的情况并不那么明显。Netflix已宣布于2018年5月开放Zuul 2。新版Zuul网关建立在Netty服务器之上,包括一些改进和新功能。您可以在Netflix博客https://medium.com/netflix-techblog/open-sourcing-zuul-2-82ea476cb2b3 上阅读更多相关信息。。尽管Netflix云团队做出了这一决定,但Spring Cloud团队已经放弃了Zuul模块的开发。我只能猜测它是由于早先决定在Spring Cloud系列中启动新模块而特别是因为它是基于微服务的架构中的API网关 - Spring Cloud Gateway。最后一块拼图是Eureka--一个发现服务器。它仍在发展,但这里的情况也很有趣。我将在本文的下一部分中对此进行描述。所有这些新闻激励我看一下Spring Cloud的现状,并讨论未来的一些潜在变化。作为掌握Spring Cloud的一本书的作者,我试图跟随该项目的演变以保持最新状态。还值得一提的是,我们的组织内部有微服务 - 当然是在Spring Boot和Spring Cloud之上构建的,使用Eureka,Zuul和Ribbon等模块。在本文中,我想讨论一些潜在的......对于诸如服务发现,分布式配置,客户端负载平衡和API网关等流行的微服务模式。</p> <h2 style="margin-top: 1.5rem;margin-bottom: 1rem;font-size: 24px;white-space: normal;box-sizing: border-box;color: rgb(21, 153, 87);line-height: 1.35;text-align: start;font-family: Menlo, Monaco, "Source Code Pro", Consolas, Inconsolata, "Ubuntu Mono", "DejaVu Sans Mono", "Courier New", "Droid Sans Mono", "Hiragino Sans GB", 微软雅黑, monospace !important;"><strong style="box-sizing: border-box;color: rgb(0, 0, 0);">1.服务发现</strong></h2> <p style="margin-top: 15px;margin-bottom: 15px;box-sizing: border-box;font-size: 16px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: start;">Eureka是唯一一个尚未转移到维护模式的重要Spring Cloud Netflix模块。但是,我不会说它是积极开发的。Netflix维护的存储库中的最后一次提交是从1月11日开始的。前段时间他们已经开始研究Eureka 2,但看起来这些作品已被放弃,或者他们只是推迟了将未来的最新版本代码开源。在这里https://github.com/Netflix/eureka/tree/2.x 你可以找到一个有趣的评论:“2.x分支目前被冻结,因为我们已经对eureka2进行了一些内部更改,并且没有任何时间线来开源新的变更。” 所以,我们有两种可能性。也许,Netflix将决定将这些内部更改作为Eureka服务器的第2版开源。值得记住的是,Eureka是一个经过战争验证的解决方案,直接用于Scale by Netflix,可能还有许多其他组织通过Spring Cloud。第二个选项是选择另一个发现服务器。目前,Spring Cloud支持基于各种工具的发现:ZooKeeper,Consul,Alibaba Nacos,Kubernetes。事实上,Kubernetes基于etcd。Spring Cloud也正在开发对etcd的支持,但它还处于孵化阶段,目前还不知道它是否会被推广到官方发布中。在我看来,这些解决方案中有一位领导者--HashiCorp的领事。</p> <p style="margin-top: 15px;margin-bottom: 15px;box-sizing: border-box;font-size: 16px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: start;">Consul现在被描述为服务网格解决方案,提供具有服务发现,配置和分段功能的全功能控制平面。它可以用作基于微服务的体系结构中的发现服务器或键/值存储。与Consul的集成由Spring Cloud Consul项目实现。要为您的应用程序启用Consul客户端,您只需要在Maven中包含以下依赖项pom.xml:</p> <pre class="prettyprint linenums prettyprinted" data-initialized="true" data-gclp-id="0" style="padding-top: 8px;padding-bottom: 6px;box-sizing: border-box;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, "Microsoft Yahei"!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"> <ol class="linenums list-paddingleft-2" style="list-style-type: none;"> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pun" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"><dependency></span></code></span></p></li> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pln" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"> <groupId>org.springframework.cloud</groupId></span></code></span></p></li> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pln" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"> <artifactId>spring-cloud-starter-consul-discovery</artifactId></span></code></span></p></li> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pun" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"></dependency></span></code></span></p></li> </ol></pre> <p style="margin-top: 15px;margin-bottom: 15px;box-sizing: border-box;font-size: 16px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: start;">默认情况下,Spring尝试在地址localhost:8500上与Consul连接。如果您需要覆盖此地址,则应在其中设置适当的属性application.yml:</p> <pre class="prettyprint linenums prettyprinted" data-initialized="true" data-gclp-id="1" style="padding-top: 8px;padding-bottom: 6px;box-sizing: border-box;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, "Microsoft Yahei"!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"> <ol class="linenums list-paddingleft-2" style="list-style-type: none;"> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pln" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;">spring: </span></code></span></p></li> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pln" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"> cloud:</span></code></span></p></li> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pln" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"> consul:</span></code></span></p></li> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pln" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"> host: </span><span class="lit" style="box-sizing: border-box;color: rgb(223, 83, 32);line-height: 20px;font-size: 13px !important;white-space: inherit !important;">192.168</span><span class="pun" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;">.</span><span class="lit" style="box-sizing: border-box;color: rgb(223, 83, 32);line-height: 20px;font-size: 13px !important;white-space: inherit !important;">99.100</span></code></span></p></li> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pln" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"> port: </span><span class="lit" style="box-sizing: border-box;color: rgb(223, 83, 32);line-height: 20px;font-size: 13px !important;white-space: inherit !important;">8500</span></code></span></p></li> </ol></pre> <p style="margin-top: 15px;margin-bottom: 15px;box-sizing: border-box;font-size: 16px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: start;">您可以使用作为Docker容器启动的Consul的本地实例轻松测试此解决方案:</p> <pre class="prettyprint linenums prettyprinted" data-initialized="true" data-gclp-id="2" style="padding-top: 8px;padding-bottom: 6px;box-sizing: border-box;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, "Microsoft Yahei"!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"> <ol class="linenums list-paddingleft-2" style="list-style-type: none;"> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pln" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;">$ docker run -d --name consul -p </span><span class="lit" style="box-sizing: border-box;color: rgb(223, 83, 32);line-height: 20px;font-size: 13px !important;white-space: inherit !important;">8500</span><span class="pun" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;">:</span><span class="lit" style="box-sizing: border-box;color: rgb(223, 83, 32);line-height: 20px;font-size: 13px !important;white-space: inherit !important;">8500</span><span class="pln" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"> consul</span></code></span></p></li> </ol></pre> <p style="margin-top: 15px;margin-bottom: 15px;box-sizing: border-box;font-size: 16px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: start;">如您所见,使用Spring Cloud进行Consul发现实现非常简单 - 与Eureka相同。领事对Eureka有一个无可置疑的优势 - 它由HashiCorp持续维护和开发。它的受欢迎程度快速增长。它是HashiCorp最大的生态系统的一部分,包括Vault,Nomad和Terraform。与Eureka相比,Consul不仅可以用于服务发现,还可以用作基于微服务的体系结构中的配置服务器。</p> <h2 style="margin-top: 1.5rem;margin-bottom: 1rem;font-size: 24px;white-space: normal;box-sizing: border-box;color: rgb(21, 153, 87);line-height: 1.35;text-align: start;font-family: Menlo, Monaco, "Source Code Pro", Consolas, Inconsolata, "Ubuntu Mono", "DejaVu Sans Mono", "Courier New", "Droid Sans Mono", "Hiragino Sans GB", 微软雅黑, monospace !important;"><strong style="box-sizing: border-box;color: rgb(0, 0, 0);">2.分布式配置</strong></h2> <p style="margin-top: 15px;margin-bottom: 15px;box-sizing: border-box;font-size: 16px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: start;">Netflix Archaius是一个有趣的解决方案,用于管理微服务架构中的外部化配置。虽然它提供了一些有趣的功能,如动态和类型属性,或者支持动态数据源,如URL,JDBC或AWS DynamoDB,但Spring Cloud也决定将其转移到维护模式。然而,由于Pivotal团队和社区 - Spring Cloud Config完全创建的类似项目的存在,Spring Cloud Archaius的受欢迎程度有限。Spring Cloud Config支持多个源存储库,包括Git,JDBC,Vault或简单文件。您可以在我之前的帖子中找到许多使用此项目为您的微服务提供分布式配置的示例。今天,我不打算谈论它。我们将讨论另一种解决方案 - 也得到Spring Cloud的支持。正如我在上一节末尾提到的,Consul也可以用作配置服务器。如果您使用Eureka作为发现服务器,使用Spring Cloud Config作为配置服务器是很自然的选择,因为Eureka根本不提供此类功能。如果您决定使用Consul,情况就不是这样。现在选择两种解决方案是有意义的:Spring Cloud Consul Config和Spring Cloud Config。当然,它们都有其优点和缺点。例如,您可以使用Consul节点轻松构建集群,而使用Spring Cloud Config则必须依赖外部发现。现在,让我们看看如何使用Spring Cloud Consul来管理应用程序中的外部配置。要在应用程序端启用它,您只需要在Maven中包含以下依赖项pom.xml:</p> <pre class="prettyprint linenums prettyprinted" data-initialized="true" data-gclp-id="3" style="padding-top: 8px;padding-bottom: 6px;box-sizing: border-box;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, "Microsoft Yahei"!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"> <ol class="linenums list-paddingleft-2" style="list-style-type: none;"> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pun" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"><dependency></span></code></span></p></li> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pln" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"> <groupId>org.springframework.cloud</groupId></span></code></span></p></li> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pln" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"> <artifactId>spring-cloud-starter-consul-config</artifactId></span></code></span></p></li> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pun" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"></dependency></span></code></span></p></li> </ol></pre> <p style="margin-top: 15px;margin-bottom: 15px;box-sizing: border-box;font-size: 16px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: start;">与服务发现相同,如果要覆盖某些默认客户端设置,则需要设置属性spring.cloud.consul.*。但是,必须在内部提供这样的配置bootstrap.yml。</p> <pre class="prettyprint linenums prettyprinted" data-initialized="true" data-gclp-id="4" style="padding-top: 8px;padding-bottom: 6px;box-sizing: border-box;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, "Microsoft Yahei"!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"> <ol class="linenums list-paddingleft-2" style="list-style-type: none;"> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pln" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;">spring: </span></code></span></p></li> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pln" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"> application:</span></code></span></p></li> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pln" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"> name: callme-service</span></code></span></p></li> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pln" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"> cloud:</span></code></span></p></li> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pln" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"> consul:</span></code></span></p></li> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pln" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"> host: </span><span class="lit" style="box-sizing: border-box;color: rgb(223, 83, 32);line-height: 20px;font-size: 13px !important;white-space: inherit !important;">192.168</span><span class="pun" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;">.</span><span class="lit" style="box-sizing: border-box;color: rgb(223, 83, 32);line-height: 20px;font-size: 13px !important;white-space: inherit !important;">99.100</span></code></span></p></li> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pln" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"> port: </span><span class="lit" style="box-sizing: border-box;color: rgb(223, 83, 32);line-height: 20px;font-size: 13px !important;white-space: inherit !important;">8500</span></code></span></p></li> </ol></pre> <p style="margin-top: 15px;margin-bottom: 15px;box-sizing: border-box;font-size: 16px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: start;">在Consul上创建的属性源名称应与bootstrap.yml内部config文件夹中提供的应用程序名称相同。您应该server.port使用值创建密钥0,以强制Spring Boot随机生成侦听端口号。假设您需要设置应用程序默认侦听端口,您应该进行以下配置。<img class="" data-ratio="0.7019027484143763" src="/upload/bf12e623ecd41533fb9008b876f5f6d3.png" data-type="png" data-w="473" style="box-sizing: border-box;border-width: 2px;border-style: solid;border-color: rgb(238, 238, 238);border-radius: 6px;">启用动态端口号生成时,还需要覆盖应用程序实例ID,使其在单个计算机上保持唯一。如果在同一台计算机上运行单个服务的多个实例,则需要使用这些功能。我们将这样做callme-service,因此我们需要spring.cloud.consul.discovery.instance-id使用我们的值覆盖属性,如下所示。<img class="" data-ratio="0.6390041493775933" src="/upload/f579dc913f242e984334d885b455480c.png" data-type="png" data-w="482" style="box-sizing: border-box;border-width: 2px;border-style: solid;border-color: rgb(238, 238, 238);border-radius: 6px;">然后,您应该在应用程序启动时看到以下日志.<img class="" data-ratio="0.10466988727858294" src="/upload/10d49f09102b2afdb101294c3356b9d8.png" data-type="png" data-w="621" style="box-sizing: border-box;border-width: 2px;border-style: solid;border-color: rgb(238, 238, 238);border-radius: 6px;"></p> <h2 style="margin-top: 1.5rem;margin-bottom: 1rem;font-size: 24px;white-space: normal;box-sizing: border-box;color: rgb(21, 153, 87);line-height: 1.35;text-align: start;font-family: Menlo, Monaco, "Source Code Pro", Consolas, Inconsolata, "Ubuntu Mono", "DejaVu Sans Mono", "Courier New", "Droid Sans Mono", "Hiragino Sans GB", 微软雅黑, monospace !important;"><strong style="box-sizing: border-box;color: rgb(0, 0, 0);">3.API网关</strong></h2> <p style="margin-top: 15px;margin-bottom: 15px;box-sizing: border-box;font-size: 16px;white-space: pre-line;line-height: 30px;color: rgb(74, 74, 74);font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;text-align: start;">Spring Cloud Netflix Zuul的继任者是Spring Cloud Gateway。这个项目大约在两年前开始,现在是第二个最受欢迎的Spring Cloud项目,在GitHub上有1.4k星。它提供了一个建立在Spring Ecosystem之上的API网关,包括:Spring 5,Spring Boot 2和Project Reactor。它在Netty上运行,不能与Tomcat或Jetty等传统的servlet容器一起使用。它允许定义路由,谓词和过滤器。API网关与每个Spring Cloud微服务相同,可以轻松地与基于Consul的服务发现集成。我们只需要在里面包含适当的依赖项pom.xml。我们将使用Spring Cloud库的最新开发版本 - 2.2.0.BUILD-SNAPSHOT。这是所需依赖项的列表:</p> <pre class="prettyprint linenums prettyprinted" data-initialized="true" data-gclp-id="5" style="padding-top: 8px;padding-bottom: 6px;box-sizing: border-box;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, "Microsoft Yahei"!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"> <ol class="linenums list-paddingleft-2" style="list-style-type: none;"> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pun" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"><dependency></span></code></span></p></li> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pln" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"> <groupId>org.springframework.cloud</groupId></span></code></span></p></li> <li><p><span style="box-sizing: border-box;color: rgb(74, 74, 74);display: block;line-height: 22px;font-size: 14px !important;word-break: inherit !important;"><code class="java language-java" style="margin-left: -20px;box-sizing: border-box;display: flex;overflow: initial;line-height: 12px;overflow-wrap: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 10px;font-family: inherit !important;"><span class="pln" style="box-sizing: border-box;color: rgb(27, 25, 24);line-height: 20px;font-size: 13px !important;white-space: inherit !important;"> <artifactId>spring-cloud-starter-consul-discovery</artifactId></span></code></span>
作者:微信小助手
<p>最近在项目中遇到了类似“秒杀”的业务场景,在本篇博客中,我将用一个非常简单的demo,阐述实现所谓“秒杀”的基本思路。</p> <p><br></p> <p><strong><span style="font-size: 24px;">业务场景</span></strong></p> <p>所谓秒杀,从业务角度看,是短时间内多个用户“争抢”资源,这里的资源在大部分秒杀场景里是商品;将业务抽象,技术角度看,秒杀就是多个线程对资源进行操作,所以实现秒杀,就必须控制线程对资源的争抢,既要保证高效并发,也要保证操作的正确。</p> <p><br></p> <p><span style="font-size: 24px;"><strong>一些可能的实现</strong></span></p> <p>刚才提到过,实现秒杀的关键点是控制线程对资源的争抢,根据基本的线程知识,可以不加思索的想到下面的一些方法: </p> <p>1、秒杀在技术层面的抽象应该就是一个方法,在这个方法里可能的操作是将商品库存-1,将商品加入用户的购物车等等,在不考虑缓存的情况下应该是要操作数据库的。那么最简单直接的实现就是在这个方法上加上synchronized关键字,通俗的讲就是锁住整个方法; </p> <p>2、锁住整个方法这个策略简单方便,但是似乎有点粗暴。可以稍微优化一下,只锁住秒杀的代码块,比如写数据库的部分; </p> <p>3、既然有并发问题,那我就让他“不并发”,将所有的线程用一个队列管理起来,使之变成串行操作,自然不会有并发问题。</p> <p><br></p> <p>上面所述的方法都是有效的,但是都不好。为什么?第一和第二种方法本质上是“加锁”,但是锁粒度依然比较高。什么意思?试想一下,如果两个线程同时执行秒杀方法,这两个线程操作的是不同的商品,从业务上讲应该是可以同时进行的,但是如果采用第一二种方法,这两个线程也会去争抢同一个锁,这其实是不必要的。第三种方法也没有解决上面说的问题。</p> <p><br></p> <p>那么如何将锁控制在更细的粒度上呢?可以考虑为每个商品设置一个互斥锁,以和商品ID相关的字符串为唯一标识,这样就可以做到只有争抢同一件商品的线程互斥,不会导致所有的线程互斥。分布式锁恰好可以帮助我们解决这个问题。</p> <p><span style="font-size: 24px;"><strong>何为分布式锁</strong></span></p> <p>分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。</p> <p><br></p> <p>我们来假设一个最简单的秒杀场景:数据库里有一张表,column分别是商品ID,和商品ID对应的库存量,秒杀成功就将此商品库存量-1。现在假设有1000个线程来秒杀两件商品,500个线程秒杀第一个商品,500个线程秒杀第二个商品。我们来根据这个简单的业务场景来解释一下分布式锁。 </p> <p>通常具有秒杀场景的业务系统都比较复杂,承载的业务量非常巨大,并发量也很高。这样的系统往往采用分布式的架构来均衡负载。那么这1000个并发就会是从不同的地方过来,商品库存就是共享的资源,也是这1000个并发争抢的资源,这个时候我们需要将并发互斥管理起来。这就是分布式锁的应用。 </p> <p>而key-value存储系统,如redis,因为其一些特性,是实现分布式锁的重要工具。</p> <p><br></p> <p><strong><span style="font-size: 24px;">具体的实现</span></strong></p> <p>先来看看一些redis的基本命令: </p> <p><code style="box-sizing: border-box;outline: 0px;font-family: "Source Code Pro", "DejaVu Sans Mono", "Ubuntu Mono", "Anonymous Pro", "Droid Sans Mono", Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, "PingFang SC", "Microsoft YaHei", sans-serif;font-size: 14px;padding: 2px 4px;line-height: 22px;color: rgb(199, 37, 78);background-color: rgb(249, 242, 244);border-radius: 2px;word-wrap: break-word;font-variant-ligatures: common-ligatures;white-space: normal;">SETNX key value</code><span style="color: rgb(79, 79, 79);font-family: "Microsoft YaHei", "SF Pro Display", Roboto, Noto, Arial, "PingFang SC", sans-serif;font-size: 16px;font-variant-ligatures: common-ligatures;background-color: rgb(255, 255, 255);"> </span></p> <p>如果key不存在,就设置key对应字符串value。在这种情况下,该命令和SET一样。当key已经存在时,就不做任何操作。SETNX是”SET if Not eXists”。 </p> <p><code style="box-sizing: border-box;outline: 0px;font-family: "Source Code Pro", "DejaVu Sans Mono", "Ubuntu Mono", "Anonymous Pro", "Droid Sans Mono", Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, "PingFang SC", "Microsoft YaHei", sans-serif;font-size: 14px;padding: 2px 4px;line-height: 22px;color: rgb(199, 37, 78);background-color: rgb(249, 242, 244);border-radius: 2px;word-wrap: break-word;font-variant-ligatures: common-ligatures;white-space: normal;">expire KEY seconds</code><span style="color: rgb(79, 79, 79);font-family: "Microsoft YaHei", "SF Pro Display", Roboto, Noto, Arial, "PingFang SC", sans-serif;font-size: 16px;font-variant-ligatures: common-ligatures;background-color: rgb(255, 255, 255);"> </span></p> <p>设置key的过期时间。如果key已过期,将会被自动删除。 </p> <p><code style="box-sizing: border-box;outline: 0px;font-family: "Source Code Pro", "DejaVu Sans Mono", "Ubuntu Mono", "Anonymous Pro", "Droid Sans Mono", Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, "PingFang SC", "Microsoft YaHei", sans-serif;font-size: 14px;padding: 2px 4px;line-height: 22px;color: rgb(199, 37, 78);background-color: rgb(249, 242, 244);border-radius: 2px;word-wrap: break-word;font-variant-ligatures: common-ligatures;white-space: normal;">del KEY</code><span style="color: rgb(79, 79, 79);font-family: "Microsoft YaHei", "SF Pro Display", Roboto, Noto, Arial, "PingFang SC", sans-serif;font-size: 16px;font-variant-ligatures: common-ligatures;background-color: rgb(255, 255, 255);"> </span></p> <p>删除key </p> <p>由于笔者的实现只用到这三个命令,就只介绍这三个命令,更多的命令以及redis的特性和使用,可以参考redis官网。</p> <p><br></p> <p><strong><span style="font-size: 24px;">需要考虑的问题</span></strong></p> <p>1、用什么操作redis?幸亏redis已经提供了jedis客户端用于java应用程序,直接调用jedis API即可。 </p> <p>2、怎么实现加锁?“锁”其实是一个抽象的概念,将这个抽象概念变为具体的东西,就是一个存储在redis里的key-value对,key是于商品ID相关的字符串来唯一标识,value其实并不重要,因为只要这个唯一的key-value存在,就表示这个商品已经上锁。 </p> <p>3、如何释放锁?既然key-value对存在就表示上锁,那么释放锁就自然是在redis里删除key-value对。 </p> <p>4、阻塞还是非阻塞?笔者采用了阻塞式的实现,若线程发现已经上锁,会在特定时间内轮询锁。 </p> <p>5、如何处理异常情况?比如一个线程把一个商品上了锁,但是由于各种原因,没有完成操作(在上面的业务场景里就是没有将库存-1写入数据库),自然没有释放锁,这个情况笔者加入了锁超时机制,利用redis的expire命令为key设置超时时长,过了超时时间redis就会将这个key自动删除,即强制释放锁(可以认为超时释放锁是一个异步操作,由redis完成,应用程序只需要根据系统特点设置超时时间即可)。</p> <p></p> <h2 style="box-sizing: border-box;outline: 0px;margin-top: 8px;margin-bottom: 16px;font-size: 24px;font-family: "Microsoft YaHei", "SF Pro Display", Roboto, Noto, Arial, "PingFang SC", sans-serif;color: rgb(79, 79, 79);font-weight: 700;line-height: 32px;word-wrap: break-word;font-variant-ligatures: common-ligatures;white-space: normal;background-color: rgb(255, 255, 255);">talk is cheap,show me the code</h2> <p style="box-sizing: border-box;outline: 0px;margin-bottom: 16px;font-family: "Microsoft YaHei", "SF Pro Display", Roboto, Noto, Arial, "PingFang SC", sans-serif;font-size: 16px;color: rgb(79, 79, 79);line-height: 26px;word-wrap: break-word;font-variant-ligatures: common-ligatures;white-space: normal;background-color: rgb(255, 255, 255);">在代码实现层面,注解有并发的方法和参数,通过动态代理获取注解的方法和参数,在代理中加锁,执行完被代理的方法后释放锁。</p> <p style="box-sizing: border-box;outline: 0px;margin-bottom: 16px;font-family: "Microsoft YaHei", "SF Pro Display", Roboto, Noto, Arial, "PingFang SC", sans-serif;font-size: 16px;color: rgb(79, 79, 79);line-height: 26px;word-wrap: break-word;font-variant-ligatures: common-ligatures;white-space: normal;background-color: rgb(255, 255, 255);">几个注解定义: <br style="box-sizing: border-box;outline: 0px;word-wrap: break-word;">cachelock是方法级的注解,用于注解会产生并发问题的方法:</p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs java" style="margin-right: 2px;margin-left: 2px;line-height: 15px;font-size: 11px;word-spacing: -3px;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;display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;"><span class="hljs-meta" style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);word-wrap: inherit !important;word-break: inherit !important;">@Target</span>(ElementType.METHOD)<br><span class="hljs-meta" style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);word-wrap: inherit !important;word-break: inherit !important;">@Retention</span>(RetentionPolicy.RUNTIME)<br><span class="hljs-meta" style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);word-wrap: inherit !important;word-break: inherit !important;">@Documented</span><br><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">public</span> <span class="hljs-meta" style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);word-wrap: inherit !important;word-break: inherit !important;">@interface</span> CacheLock {<br> <span class="hljs-function" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">String <span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">lockedPrefix</span><span class="hljs-params" style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">()</span> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">default</span> ""</span>;<span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">//redis 锁key的前缀</span><br> <span class="hljs-function" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">long</span> <span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">timeOut</span><span class="hljs-params" style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">()</span> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">default</span> 2000</span>;<span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">//轮询锁的时间</span><br> <span class="hljs-function" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">int</span> <span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">expireTime</span><span class="hljs-params" style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">()</span> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">default</span> 1000</span>;<span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">//key在redis里存在的时间,1000S</span><br>}<br></code></pre> </section> <p><br></p> <blockquote> <p><code style="box-sizing: border-box;outline: 0px;font-family: "Source Code Pro", "DejaVu Sans Mono", "Ubuntu Mono", "Anonymous Pro", "Droid Sans Mono", Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, "PingFang SC", "Microsoft YaHei", sans-serif;font-size: 14px;padding: 2px 4px;line-height: 22px;color: rgb(199, 37, 78);background-color: rgb(249, 242, 244);border-radius: 2px;word-wrap: break-word;font-variant-ligatures: common-ligatures;white-space: normal;">lockedObject</code><span style="color: rgb(79, 79, 79);font-family: "Microsoft YaHei", "SF Pro Display", Roboto, Noto, Arial, "PingFang SC", sans-serif;font-size: 16px;font-variant-ligatures: common-ligatures;background-color: rgb(255, 255, 255);">是参数级的注解,用于注解商品ID等基本类型的参数:</span></p> </blockquote> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs kotlin" style="margin-right: 2px;margin-left: 2px;line-height: 15px;font-size: 11px;word-spacing: -3px;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;display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;"><span class="hljs-meta" style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);word-wrap: inherit !important;word-break: inherit !important;">@Target(ElementType.PARAMETER)</span><br><span class="hljs-meta" style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);word-wrap: inherit !important;word-break: inherit !important;">@Retention(RetentionPolicy.RUNTIME)</span><br><span class="hljs-meta" style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);word-wrap: inherit !important;word-break: inherit !important;">@Documented</span><br><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">public</span> <span class="hljs-meta" style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);word-wrap: inherit !important;word-break: inherit !important;">@interface</span> LockedObject {<br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">//不需要值</span><br>}<br></code></pre> </section> <p><br></p> <blockquote> <p><code style="box-sizing: border-box;outline: 0px;font-family: "Source Code Pro", "DejaVu Sans Mono", "Ubuntu Mono", "Anonymous Pro", "Droid Sans Mono", Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, "PingFang SC", "Microsoft YaHei", sans-serif;font-size: 14px;padding: 2px 4px;line-height: 22px;color: rgb(199, 37, 78);background-color: rgb(249, 242, 244);border-radius: 2px;word-wrap: break-word;font-variant-ligatures: common-ligatures;white-space: normal;">LockedComplexObject</code><span style="color: rgb(79, 79, 79);font-family: "Microsoft YaHei", "SF Pro Display", Roboto, Noto, Arial, "PingFang SC", sans-serif;font-size: 16px;font-variant-ligatures: common-ligatures;background-color: rgb(255, 255, 255);">也是参数级的注解,用于注解自定义类型的参数:</span></p> </blockquote> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs kotlin" style="margin-right: 2px;margin-left: 2px;line-height: 15px;font-size: 11px;word-spacing: -3px;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;display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;"><span class="hljs-meta" style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);word-wrap: inherit !important;word-break: inherit !important;">@Target(ElementType.PARAMETER)</span><br><span class="hljs-meta" style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);word-wrap: inherit !important;word-break: inherit !important;">@Retention(RetentionPolicy.RUNTIME)</span><br><span class="hljs-meta" style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);word-wrap: inherit !important;word-break: inherit !important;">@Documented</span><br><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">public</span> <span class="hljs-meta" style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);word-wrap: inherit !important;word-break: inherit !important;">@interface</span> LockedComplexObject {<br> String field() <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">default</span> <span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">""</span>;<span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">//含有成员变量的复杂对象中需要加锁的成员变量,如一个商品对象的商品ID</span><br><br>}<br></code></pre> </section> <p><br></p> <p><code style="box-sizing: border-box;outline: 0px;font-family: "Source Code Pro", "DejaVu Sans Mono", "Ubuntu Mono", "Anonymous Pro", "Droid Sans Mono", Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, "PingFang SC", "Microsoft YaHei", sans-serif;font-size: 14px;padding: 2px 4px;line-height: 22px;color: rgb(199, 37, 78);background-color: rgb(249, 242, 244);border-radius: 2px;word-wrap: break-word;font-variant-ligatures: common-ligatures;white-space: normal;">CacheLockInterceptor</code><span style="color: rgb(79, 79, 79);font-family: "Microsoft YaHei", "SF Pro Display", Roboto, Noto, Arial, "PingFang SC", sans-serif;font-size: 16px;font-variant-ligatures: common-ligatures;background-color: rgb(255, 255, 255);">实现</span><code style="box-sizing: border-box;outline: 0px;font-family: "Source Code Pro", "DejaVu Sans Mono", "Ubuntu Mono", "Anonymous Pro", "Droid Sans Mono", Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, "PingFang SC", "Microsoft YaHei", sans-serif;font-size: 14px;padding: 2px 4px;line-height: 22px;color: rgb(199, 37, 78);background-color: rgb(249, 242, 244);border-radius: 2px;word-wrap: break-word;font-variant-ligatures: common-ligatures;white-space: normal;">InvocationHandler</code><span style="color: rgb(79, 79, 79);font-family: "Microsoft YaHei", "SF Pro Display", Roboto, Noto, Arial, "PingFang SC", sans-serif;font-size: 16px;font-variant-ligatures: common-ligatures;background-color: rgb(255, 255, 255);">接口,在invoke方法中获取注解的方法和参数,在执行注解的方法前加锁,执行被注解的方法后释放锁:</span></p> <section class="output_wrapper" style="font-size: 16px;color: rgb(62, 62, 62);line-height: 1.6;letter-spacing: 0px;"> <pre style="font-size: inherit;color: inherit;line-height: inherit;margin-top: 0px;margin-bottom: 0px;padding: 0px;"><code class="hljs java" style="margin-right: 2px;margin-left: 2px;line-height: 15px;font-size: 11px;word-spacing: -3px;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;display: block !important;word-wrap: normal !important;word-break: normal !important;overflow: auto !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">public</span> <span class="hljs-class" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">class</span> <span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">CacheLockInterceptor</span> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">implements</span> <span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">InvocationHandler</span></span>{<br> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">public</span> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">static</span> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">int</span> ERROR_COUNT = <span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">0</span>;<br> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">private</span> Object proxied;<br><br> <span class="hljs-function" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">public</span> <span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">CacheLockInterceptor</span><span class="hljs-params" style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">(Object proxied)</span> </span>{<br> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">this</span>.proxied = proxied;<br> }<br><br> <span class="hljs-meta" style="font-size: inherit;line-height: inherit;color: rgb(91, 218, 237);word-wrap: inherit !important;word-break: inherit !important;">@Override</span><br> <span class="hljs-function" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">public</span> Object <span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">invoke</span><span class="hljs-params" style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">(Object proxy, Method method, Object[] args)</span> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">throws</span> Throwable </span>{<br><br> CacheLock cacheLock = method.getAnnotation(CacheLock.class);<br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">//没有cacheLock注解,pass</span><br> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">if</span>(<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">null</span> == cacheLock){<br> System.out.println(<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"no cacheLock annotation"</span>); <br> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">return</span> method.invoke(proxied, args);<br> }<br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">//获得方法中参数的注解</span><br> Annotation[][] annotations = method.getParameterAnnotations();<br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">//根据获取到的参数注解和参数列表获得加锁的参数</span><br> Object lockedObject = getLockedObject(annotations,args);<br> String objectValue = lockedObject.toString();<br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">//新建一个锁</span><br> RedisLock lock = <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">new</span> RedisLock(cacheLock.lockedPrefix(), objectValue);<br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">//加锁</span><br> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">boolean</span> result = lock.lock(cacheLock.timeOut(), cacheLock.expireTime());<br> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">if</span>(!result){<span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">//取锁失败</span><br> ERROR_COUNT += <span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">1</span>;<br> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">throw</span> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">new</span> CacheLockException(<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"get lock fail"</span>);<br><br> }<br> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">try</span>{<br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">//加锁成功,执行方法</span><br> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">return</span> method.invoke(proxied, args);<br> }<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">finally</span>{<br> lock.unlock();<span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">//释放锁</span><br> }<br><br> }<br> <span class="hljs-comment" style="font-size: inherit;line-height: inherit;color: rgb(128, 128, 128);word-wrap: inherit !important;word-break: inherit !important;">/**<br> * <br> * <span class="hljs-doctag" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">@param</span> annotations<br> * <span class="hljs-doctag" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">@param</span> args<br> * <span class="hljs-doctag" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">@return</span><br> * <span class="hljs-doctag" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">@throws</span> CacheLockException<br> */</span><br> <span class="hljs-function" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">private</span> Object <span class="hljs-title" style="font-size: inherit;line-height: inherit;color: rgb(165, 218, 45);word-wrap: inherit !important;word-break: inherit !important;">getLockedObject</span><span class="hljs-params" style="font-size: inherit;line-height: inherit;color: rgb(255, 152, 35);word-wrap: inherit !important;word-break: inherit !important;">(Annotation[][] annotations,Object[] args)</span> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">throws</span> CacheLockException</span>{<br> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">if</span>(<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">null</span> == args || args.length == <span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">0</span>){<br> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">throw</span> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">new</span> CacheLockException(<span class="hljs-string" style="font-size: inherit;line-height: inherit;color: rgb(238, 220, 112);word-wrap: inherit !important;word-break: inherit !important;">"方法参数为空,没有被锁定的对象"</span>);<br> }<br><br> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">if</span>(<span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">null</span> == annotations || annotations.length == <span class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">0</span>){<br> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">throw</span> <span class="hljs-keyword" style="font-size: inherit;line-height: inherit;color: rgb(248, 35, 117);word-wrap: inherit !important;word-break: inherit !important;">new</span> CacheLockException(<span class="hljs-string" style="font-size: inherit;line-height: inherit;colo
作者:じ☆ve宝贝
页面引用iframe页面,只有端口不同,导致访问该页面的时候本项目session丢失了的问题? 用的localhost地址,端口不同,原因还没找到,记录一下,以后想办法解决
作者:微信小助手
<section data-role="outer" label="Powered by 135editor.com" data-mpa-powered-by="yiban.io"> <section class="_135editor" data-tools="135编辑器" data-id="91525"> <section> <section> <p style="text-align: justify;"><span style="color: rgb(61, 70, 77);font-size: 14px;letter-spacing: 1px;"><span style="color: rgb(51, 51, 51);font-family: arial, sans-serif;font-size: 14px;text-indent: 28px;background-color: rgb(255, 255, 255);">IntelliJ 在业界被公认为最好的 java 开发工具之一,尤其在智能代码助手、代码自动提示、重构、CVS 整合、代码审查、 创新的 GUI 设计等方面的功能可以说是超常的。</span>以下是我用过不错的 IntelliJ 插件,分享出来希望可以帮到你。</span></p> </section> </section> </section> </section> <h1 style="box-sizing: inherit;font-size: 24px;margin: 1.2em 8px 0.8em;text-align: left;"><span style="color: rgb(255, 169, 0);"><em><span style="box-sizing: inherit;font-weight: bolder;font-size: 20px;">1. </span></em><span style="box-sizing: inherit;font-weight: bolder;font-size: 18px;">.ignore</span></span></h1> <blockquote> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(0, 0, 0);">地址:</span></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="color: rgb(0, 82, 255);font-size: 14px;text-decoration: underline;">https://plugins.jetbrains.com/plugin/7495--ignore</span></p> </blockquote> <p style="box-sizing: inherit;margin: 16px 8px 14px;line-height: 28px;color: rgb(61, 70, 77);text-align: justify;"><span style="font-size: 14px;letter-spacing: 1px;">生成各种 .ignore 文件,一键创建 git ignore 文件的模板,免得自己去写。</span></p> <p style="box-sizing: inherit;margin: 16px 8px 14px;line-height: 28px;color: rgb(61, 70, 77);text-align: justify;"><span style="color: rgb(217, 33, 66);"><strong><span style="font-size: 14px;letter-spacing: 1px;">截图: </span></strong></span></p> <p style="box-sizing: inherit;margin: 16px 8px 14px;line-height: 28px;color: rgb(61, 70, 77);text-align: justify;"><img class="" data-ratio="0.7728571428571429" data-type="gif" data-w="700" src="/upload/431569e1386a0d43ca9f8008d57c33cc.null" style="box-sizing: inherit;border-style: none;margin: auto;max-width: 80%;"></p> <h1 style="box-sizing: inherit;font-size: 24px;margin: 1.2em 8px 0.8em;"><span style="color: rgb(255, 169, 0);font-size: 20px;"><em><span style="box-sizing: inherit;font-weight: bolder;">2.</span></em></span><span style="color: rgb(255, 169, 0);font-size: 18px;"><em><span style="color: rgb(255, 169, 0);box-sizing: inherit;font-weight: bolder;"> </span></em><span style="color: rgb(255, 169, 0);box-sizing: inherit;font-weight: bolder;">lombok</span></span></h1> <blockquote> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(0, 0, 0);">地址:</span></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="color: rgb(0, 82, 255);font-size: 14px;text-decoration: underline;">https://plugins.jetbrains.com/plugin/6317-lombok-plugin</span></p> </blockquote> <p style="box-sizing: inherit;margin: 16px 8px 14px;line-height: 28px;color: rgb(61, 70, 77);text-align: justify;"><span style="font-size: 14px;letter-spacing: 1px;">支持 lombok 的各种注解,从此不用写 getter setter 这些,可以把注解还原为原本的 java 代码,非常方便。</span></p> <p style="box-sizing: inherit;margin: 16px 8px 14px;line-height: 28px;color: rgb(61, 70, 77);text-align: justify;"><span style="color: rgb(217, 33, 66);"><strong><span style="font-size: 14px;letter-spacing: 1px;">截图:</span></strong></span></p> <p style="box-sizing: inherit;margin: 16px 8px 14px;line-height: 28px;color: rgb(61, 70, 77);text-align: justify;"><img class="" data-ratio="0.77" data-type="gif" data-w="700" src="/upload/d35718497367c6d1883798d516b9e003.null" style="box-sizing: inherit;border-style: none;margin: auto;max-width: 80%;"></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="color: rgb(255, 169, 0);"><span style="font-size: 20px;"><em><span style="font-weight: bolder;">3. </span></em></span><span style="font-weight: bolder;font-size: 18px;">p3c</span></span><br></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 16px;font-weight: bolder;"><br></span></p> <blockquote> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;">地址:</span></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="color: rgb(0, 82, 255);font-size: 14px;text-decoration: underline;">https://plugins.jetbrains.com/plugin/10046-alibaba-java-coding-guidelines</span></p> </blockquote> <p style="box-sizing: inherit;margin: 16px 8px 14px;color: rgb(61, 70, 77);text-align: left;line-height: 1.5em;"><span style="font-size: 14px;color: rgb(86, 86, 86);letter-spacing: 1px;">阿里巴巴出品的 java 代码规范插件,</span><span style="color: rgb(86, 86, 86);font-size: 14px;letter-spacing: 1px;">可以扫描整个项目,找到不规范的地方,并且大部分可以自动修复。</span></p> <p style="text-align: left;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(86, 86, 86);">更多可看:</span></p> <blockquote> <p style="text-align: left;margin-left: 8px;margin-right: 8px;"><span style="color: rgb(0, 82, 255);font-size: 14px;text-decoration: underline;">https://github.com/alibaba/p3c/tree/master/idea-plugin</span></p> </blockquote> <p><br></p> <h1 style="margin-left: 8px;margin-right: 8px;"><span style="color: rgb(255, 169, 0);"><em><span style="font-size: 20px;"><strong>4.</strong></span></em><span style="font-size: 20px;"><strong> </strong></span><span style="font-size: 18px;"><strong>FindBugs-IDEA</strong></span></span></h1> <p style="margin-left: 8px;margin-right: 8px;"><strong><br></strong></p> <blockquote> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;">地址:</span></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(0, 82, 255);text-decoration: underline;">https://plugins.jetbrains.com/plugin/3847-findbugs-idea</span></p> </blockquote> <p style="box-sizing: inherit;margin: 16px 8px 14px;line-height: 28px;color: rgb(61, 70, 77);"><span style="font-size: 14px;letter-spacing: 1px;">检测代码中可能的 bug 及不规范的位置,检测的模式相比 p3c 更多,</span><span style="font-size: 14px;letter-spacing: 1px;">写完代码后检测下,避免低级 bug,强烈建议用一下,一不小心就发现很多老代码的 bug。</span></p> <p style="box-sizing: inherit;margin: 16px 8px 14px;line-height: 28px;color: rgb(61, 70, 77);"><span style="color: rgb(217, 33, 66);"><strong><span style="font-size: 14px;letter-spacing: 1px;">截图: </span></strong></span></p> <p style="box-sizing: inherit;margin: 16px 8px 14px;line-height: 28px;color: rgb(61, 70, 77);"><img class="" data-ratio="0.7285714285714285" data-type="gif" data-w="700" src="/upload/9730153340d0fd65715ae0d7a0f4b2b7.null" style="box-sizing: inherit;border-style: none;margin: auto;max-width: 80%;"></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="color: rgb(255, 169, 0);"><em><span style="font-weight: bolder;font-size: 20px;">5.</span></em></span><span style="font-weight: bolder;color: rgb(255, 169, 0);font-size: 18px;">GsonFormat</span><br></p> <p style="margin-left: 8px;margin-right: 8px;line-height: normal;"><span style="font-size: 16px;font-weight: bolder;"><br></span></p> <blockquote> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;">地址:</span></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(0, 82, 255);text-decoration: underline;">https://plugins.jetbrains.com/plugin/7654-gsonformat</span></p> </blockquote> <p style="box-sizing: inherit;margin: 16px 8px 14px;line-height: 28px;color: rgb(61, 70, 77);text-align: justify;"><span style="font-size: 14px;letter-spacing: 1px;">一键根据 json 文本生成 java 类,非常方便。</span></p> <p style="box-sizing: inherit;margin: 16px 8px 14px;line-height: 28px;color: rgb(61, 70, 77);"><span style="color: rgb(217, 33, 66);"><strong><span style="font-size: 14px;letter-spacing: 1px;">截图: </span></strong></span></p> <p style="box-sizing: inherit;margin: 16px 8px 14px;line-height: 28px;color: rgb(61, 70, 77);"><img class="" data-ratio="0.7957142857142857" data-type="gif" data-w="700" src="/upload/f3434d4d813bfcbd4c7aa538e84989ae.null" style="box-sizing: inherit;border-style: none;margin: auto;max-width: 80%;"></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 20px;color: rgb(255, 169, 0);"><em><span style="font-size: 20px;font-weight: bolder;">6. </span></em></span><span style="font-weight: bolder;font-size: 18px;color: rgb(255, 169, 0);">Maven Helper</span><br></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 16px;font-weight: bolder;"><br></span></p> <blockquote> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;">地址:</span></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="color: rgb(0, 82, 255);font-size: 14px;text-decoration: underline;">https://plugins.jetbrains.com/plugin/7179-maven-helper</span></p> </blockquote> <p style="box-sizing: inherit;margin: 16px 8px 14px;color: rgb(61, 70, 77);line-height: normal;"><span style="font-size: 14px;letter-spacing: 1px;">一键查看 maven 依赖,查看冲突的依赖,一键进行 exclude 依赖,</span><span style="font-size: 14px;letter-spacing: 1px;">对于大型项目非常方便。</span></p> <p style="box-sizing: inherit;margin: 16px 8px 14px;color: rgb(61, 70, 77);line-height: normal;"><span style="color: rgb(217, 33, 66);"><strong><span style="font-size: 14px;letter-spacing: 1px;">截图:</span></strong></span></p> <p style="box-sizing: inherit;margin: 16px 8px 14px;line-height: 28px;color: rgb(61, 70, 77);"><img class="" data-ratio="0.8514285714285714" data-type="png" data-w="700" src="/upload/8463e75c392a14ad368e7396abafc27f.null" style="box-sizing: inherit;border-style: none;margin: auto;max-width: 80%;"></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="color: rgb(255, 169, 0);"><em><span style="font-weight: bolder;font-size: 20px;">7.</span></em></span><span style="font-weight: bolder;font-size: 18px;color: rgb(255, 169, 0);"> VisualVM Launcher</span><br></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 16px;font-weight: bolder;"><br></span></p> <blockquote> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;">地址:</span></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(0, 82, 255);text-decoration: underline;">https://plugins.jetbrains.com/plugin/7115-visualvm-launcher</span></p> </blockquote> <p style="box-sizing: inherit;margin: 16px 8px 14px;color: rgb(61, 70, 77);line-height: 1.5em;"><span style="letter-spacing: 1px;font-size: 14px;">运行 java 程序的时候启动 visualvm,方便查看 jvm 的情况 比如堆内存大小的分配,</span><span style="font-size: 14px;letter-spacing: 1px;">某个对象占用了多大的内存,jvm 调优必备工具。</span></p> <p style="box-sizing: inherit;margin: 16px 8px 14px;color: rgb(61, 70, 77);line-height: normal;"><strong><span style="letter-spacing: 1px;font-size: 14px;color: rgb(217, 33, 66);">截图:</span></strong></p> <p style="box-sizing: inherit;margin: 16px 8px 14px;line-height: 28px;color: rgb(61, 70, 77);"><img class="" data-ratio="0.7285714285714285" data-type="gif" data-w="700" src="/upload/e7fe0a3903a33280a3c820753b53efca.null" style="box-sizing: inherit;border-style: none;margin: auto;max-width: 80%;"></p> <p style="margin-left: 8px;margin-right: 8px;"><em><span style="font-weight: bolder;color: rgb(255, 169, 0);font-size: 20px;">8. </span></em><span style="font-weight: bolder;color: rgb(255, 169, 0);font-size: 18px;">GenerateAllSetter</span><br></p> <p style="margin-left: 8px;margin-right: 8px;line-height: normal;"><span style="font-size: 16px;font-weight: bolder;"><br></span></p> <blockquote> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;">地址:</span></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(0, 82, 255);text-decoration: underline;">https://plugins.jetbrains.com/plugin/9360-generateallsetter</span></p> </blockquote> <p style="box-sizing: inherit;margin: 16px 8px 14px;color: rgb(61, 70, 77);text-align: justify;line-height: 1.5em;"><span style="font-size: 14px;letter-spacing: 1px;">一键调用一个对象的所有 set 方法并且赋予默认值,在对象字段多的时候非常方便。</span></p> <p style="box-sizing: inherit;margin: 16px 8px 14px;color: rgb(61, 70, 77);line-height: normal;"><span style="color: rgb(217, 33, 66);"><strong><span style="font-size: 14px;letter-spacing: 1px;">截图:</span></strong></span></p> <p style="box-sizing: inherit;margin: 16px 8px 14px;line-height: 28px;color: rgb(61, 70, 77);"><img class="" data-ratio="0.7285714285714285" data-type="gif" data-w="700" src="/upload/f17d76db7aac09136584135c333e4cc2.null" style="box-sizing: inherit;border-style: none;margin: auto;max-width: 80%;"></p> <p style="margin-left: 8px;margin-right: 8px;"><em><span style="font-weight: bolder;font-size: 20px;color: rgb(255, 169, 0);">9. </span></em><span style="font-weight: bolder;color: rgb(255, 169, 0);font-size: 18px;">MyBatisCodeHelperPro</span><br></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 16px;font-weight: bolder;"><br></span></p> <blockquote> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;">地址:</span></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(0, 82, 255);text-decoration: underline;">https://plugins.jetbrains.com/plugin/9837-mybatiscodehelperpro</span></p> </blockquote> <p style="box-sizing: inherit;margin: 16px 8px 14px;color: rgb(61, 70, 77);text-align: justify;line-height: 1.5em;"><span style="font-size: 14px;letter-spacing: 1px;">mybatis 代码自动生成插件,大部分单表操作的代码可自动生成,减少重复劳动 大幅提升效率。</span></p> <p style="box-sizing: inherit;margin: 16px 8px 14px;color: rgb(61, 70, 77);text-align: justify;line-height: normal;"><span style="color: rgb(217, 33, 66);"><strong><span style="letter-spacing: 1px;font-size: 14px;">截图: </span></strong></span></p> <p style="margin-left: 8px;margin-right: 8px;"><img class="" data-copyright="0" data-ratio="0.8158295281582952" data-s="300,640" data-type="png" data-w="657" src="/upload/239a2ed12117159f22d07d6b35a1bec5.null" style=""></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 16px;"><span style="font-size: 16px;box-sizing: inherit;color: rgb(61, 70, 77);"></span><span style="font-size: 16px;color: rgb(61, 70, 77);"></span></span></p> <h2 style="box-sizing: inherit;margin-left: 8px;margin-right: 8px;"><span style="color: rgb(255, 169, 0);"><em><span style="box-sizing: inherit;font-weight: bolder;font-size: 20px;">10. </span></em></span><span style="box-sizing: inherit;font-weight: bolder;color: rgb(255, 169, 0);font-size: 18px;letter-spacing: 1px;">Rainbow Brackets</span></h2> <p style="margin-left: 8px;margin-right: 8px;"><span style="box-sizing: inherit;font-weight: bolder;font-size: 16px;"><br></span></p> <blockquote> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;">地址:</span></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="color: rgb(0, 82, 255);font-size: 14px;text-decoration: underline;">https://plugins.jetbrains.com/plugin/10080-rainbow-brackets</span></p> </blockquote> <p style="box-sizing: inherit;margin: 16px 8px 14px;color: rgb(61, 70, 77);line-height: normal;"><span style="font-size: 14px;letter-spacing: 1px;">彩虹颜色的括号,看着很舒服,敲代码效率变高。</span></p> <p style="box-sizing: inherit;margin: 16px 8px 14px;color: rgb(61, 70, 77);line-height: normal;"><span style="color: rgb(217, 33, 66);"><strong><span style="font-size: 14px;letter-spacing: 1px;">截图:</span></strong></span></p> <p style="box-sizing: inherit;margin: 16px 8px 14px;line-height: 28px;color: rgb(61, 70, 77);"><img class="" data-ratio="0.3357142857142857" data-type="png" data-w="700" src="/upload/b2e574ddf13394924c62b34d58a9ee03.null" style="box-sizing: inherit;border-style: none;margin: auto;max-width: 80%;"></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 16px;"><span style="font-size: 16px;box-sizing: inherit;color: rgb(61, 70, 77);"></span><span style="font-size: 16px;color: rgb(61, 70, 77);"></span></span></p> <h2 style="box-sizing: inherit;margin-left: 8px;margin-right: 8px;"><span style="color: rgb(255, 169, 0);"><em><span style="box-sizing: inherit;font-weight: bolder;font-size: 20px;">11. </span></em></span><span style="box-sizing: inherit;font-weight: bolder;font-size: 18px;color: rgb(255, 169, 0);">Translation</span></h2> <p style="margin-left: 8px;margin-right: 8px;line-height: normal;"><span style="box-sizing: inherit;font-weight: bolder;font-size: 16px;"><br></span></p> <blockquote> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;">地址:</span></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(0, 82, 255);text-decoration: underline;">https://plugins.jetbrains.com/plugin/8579-translation</span></p> </blockquote> <p style="box-sizing: inherit;margin: 16px 8px 14px;line-height: 28px;color: rgb(61, 70, 77);"><span style="font-size: 14px;letter-spacing: 1px;">最好用的翻译插件,功能很强大,界面很漂亮。</span></p> <p style="box-sizing: inherit;margin: 16px 8px 14px;line-height: 28px;color: rgb(61, 70, 77);"><img class="" data-ratio="1" data-type="gif" data-w="525" src="/upload/f0cb1614d014dce051dee46553619773.null" style="box-sizing: inherit;border-style: none;margin: auto;max-width: 80%;"></p> <p style="box-sizing: inherit;margin: 16px 8px 14px;color: rgb(61, 70, 77);line-height: normal;"><span style="font-size: 14px;letter-spacing: 1px;">以上插件均可在 Intelli 插件市场中搜索到或者打开插件名字下方的链接从硬盘安装插件。</span></p> <p style="box-sizing: inherit;margin: 16px 8px 14px;color: rgb(61, 70, 77);line-height: normal;"><span style="font-size: 14px;letter-spacing: 1px;">详细的可以看 p3c 插件的安装文档:</span></p> <blockquote> <p style="box-sizing: inherit;margin: 16px 8px 14px;line-height: 28px;color: rgb(61, 70, 77);"><span style="color: rgb(0, 82, 255);font-size: 14px;text-decoration: underline;">https://github.com/alibaba/p3c/tree/master/idea-plugin</span></p> </blockquote> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;letter-spacing: 1px;color: rgb(86, 86, 86);">有其他插件推荐的话,欢迎留言;</span></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;letter-spacing: 1px;color: rgb(86, 86, 86);">点击 </span><span style="font-size: 14px;letter-spacing: 1px;color: rgb(217, 33, 66);"><strong>阅读原文</strong></span><span style="font-size: 14px;letter-spacing: 1px;color: rgb(86, 86, 86);"> ,深入了解 Java 技术<img class="mpa-image" data-ratio="1" src="/upload/29316807de8eec165a101cfe6173a39c.null" data-w="64" style="height: 20px !important;max-height: 20px !important;width: 20px !important;"><img class="mpa-image" data-ratio="1" src="/upload/29316807de8eec165a101cfe6173a39c.null" data-w="64" style="height: 20px !important;max-height: 20px !important;width: 20px !important;"></span></p>
作者:微信小助手
<section class="mpa-template" data-mpa-category="背景" data-mpa-powered-by="yiban.io"> <section class="" mpa-from-tpl="t"> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"></section> </section> <section style="margin-right: 16px;margin-bottom: 20px;margin-left: 16px;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;white-space: normal;background-color: rgb(255, 255, 255);text-align: left;line-height: 1.75em;"> <img class="" data-copyright="0" data-ratio="0.4" data-s="300,640" data-type="png" data-w="750" src="/upload/f65931186c03d7e3c9feec291b6e213f.null" style="color: rgb(0, 0, 0);font-size: medium;letter-spacing: 0.544px;text-align: justify;visibility: visible !important;width: auto !important;"> <br> </section> <p style="margin-right: 16px;margin-bottom: 20px;margin-left: 16px;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;white-space: normal;background-color: rgb(255, 255, 255);line-height: 1.75em;text-align: center;"><span style="color: rgb(0, 122, 170);font-size: 13px;">源 /</span><span style="color: rgb(64, 62, 62);font-size: 13px;"> </span><span style="color: rgb(136, 136, 136);font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 13px;">良许Linux</span></p> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">本文我们来谈谈项目中常用的MySQL优化方法,共19条,具体如下:</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="color: rgb(255, 120, 0);"><strong><span style="font-size: 15px;">1、EXPLAIN</span></strong></span> <span style="font-size: 15px;color: rgb(64, 62, 62);"></span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">做MySQL优化,我们要善用EXPLAIN查看SQL执行计划。</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">下面来个简单的示例,标注(1、2、3、4、5)我们要重点关注的数据:</span> </section> <section style="text-align: center;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <img class="" data-copyright="0" data-ratio="0.12916666666666668" data-s="300,640" data-type="jpeg" data-w="720" src="/upload/c3b62dbf692f51cd191cfab62a1095e0.jpg" style="box-sizing: border-box !important;overflow-wrap: break-word !important;width: 677px !important;visibility: visible !important;"> </section> <ul class=" list-paddingleft-2" style="margin-left: 16px;margin-right: 16px;"> <li> <section style="text-align: left;line-height: normal;margin-bottom: 5px;"> <strong><span style="font-size: 15px;color: rgb(64, 62, 62);">type列,</span></strong> <span style="font-size: 15px;color: rgb(64, 62, 62);">连接类型。一个好的SQL语句至少要达到range级别。杜绝出现all级别。</span> </section></li> <li> <section style="text-align: left;line-height: normal;margin-bottom: 5px;"> <strong><span style="font-size: 15px;color: rgb(64, 62, 62);">key列,</span></strong> <span style="font-size: 15px;color: rgb(64, 62, 62);">使用到的索引名。如果没有选择索引,值是NULL。可以采取强制索引方式。</span> </section></li> <li> <section style="text-align: left;line-height: normal;margin-bottom: 5px;"> <strong><span style="font-size: 15px;color: rgb(64, 62, 62);">key_len列,</span></strong> <span style="font-size: 15px;color: rgb(64, 62, 62);">索引长度。</span> </section></li> <li> <section style="text-align: left;line-height: normal;margin-bottom: 5px;"> <strong><span style="font-size: 15px;color: rgb(64, 62, 62);">rows列,</span></strong> <span style="font-size: 15px;color: rgb(64, 62, 62);">扫描行数。该值是个预估值。</span> </section></li> <li> <section style="text-align: left;margin-bottom: 20px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(64, 62, 62);">extra列,</span></strong> <span style="font-size: 15px;color: rgb(64, 62, 62);">详细说明。注意,常见的不太友好的值,如下:Using filesort,Using temporary。</span> </section></li> </ul> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="color: rgb(255, 120, 0);"><strong><span style="font-size: 15px;">2、SQL语句中IN包含的值不应过多</span></strong></span> <span style="font-size: 15px;color: rgb(64, 62, 62);"></span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">MySQL对于IN做了相应的优化,即将IN中的常量全部存储在一个数组里面,而且这个数组是排好序的。但是如果数值较多,产生的消耗也是比较大的。再例如:select id from t where num in(1,2,3) 对于连续的数值,能用between就不要用in了;再或者使用连接来替换。</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="color: rgb(255, 120, 0);"><strong><span style="font-size: 15px;">3、SELECT语句务必指明字段名称</span></strong></span> <span style="font-size: 15px;color: rgb(64, 62, 62);"></span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">SELECT*增加很多不必要的消耗(CPU、IO、内存、网络带宽);增加了使用覆盖索引的可能性;当表结构发生改变时,前断也需要更新。所以要求直接在select后面接上字段名。</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="color: rgb(255, 120, 0);"><strong><span style="font-size: 15px;">4、当只需要一条数据的时候,使用limit 1</span></strong></span> <span style="font-size: 15px;color: rgb(64, 62, 62);"></span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">这是为了使EXPLAIN中type列达到const类型</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="color: rgb(255, 120, 0);"><strong><span style="font-size: 15px;">5、如果排序字段没有用到索引,就尽量少排序</span></strong></span> <span style="font-size: 15px;color: rgb(64, 62, 62);"></span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="color: rgb(255, 120, 0);"><strong><span style="font-size: 15px;">6、如果限制条件中其他字段没有索引,尽量少用or</span></strong></span> <span style="font-size: 15px;color: rgb(64, 62, 62);"></span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">or两边的字段中,如果有一个不是索引字段,而其他条件也不是索引字段,会造成该查询不走索引的情况。很多时候使用union all或者是union(必要的时候)的方式来代替“or”会得到更好的效果。</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="color: rgb(255, 120, 0);"><strong><span style="font-size: 15px;">7、尽量用union all代替union</span></strong></span> <span style="font-size: 15px;color: rgb(64, 62, 62);"></span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">union和union all的差异主要是前者需要将结果集合并后再进行唯一性过滤操作,这就会涉及到排序,增加大量的CPU运算,加大资源消耗及延迟。当然,union all的前提条件是两个结果集没有重复数据。</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="color: rgb(255, 120, 0);"><strong><span style="font-size: 15px;">8、不使用ORDER BY RAND()</span></strong></span> <span style="font-size: 15px;color: rgb(64, 62, 62);"></span> </section> <section class="" data-tools="135编辑器" data-id="88286"> <section> <section> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> </ul> <pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">select</span> <span class="code-snippet__keyword">id</span> <span class="code-snippet__keyword">from</span> <span class="code-snippet__string">`dynamic`</span> <span class="code-snippet__keyword">order</span> <span class="code-snippet__keyword">by</span> <span class="code-snippet__keyword">rand</span>() <span class="code-snippet__keyword">limit</span> <span class="code-snippet__number">1000</span>;</span></code></pre> </section> </section> </section> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">上面的SQL语句,可优化为:</span> </section> <section class="" data-tools="135编辑器" data-id="88286"> <section> <section> <section class=""> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> </ul> <pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">select</span> <span class="code-snippet__keyword">id</span> <span class="code-snippet__keyword">from</span> <span class="code-snippet__string">`dynamic`</span> t1 <span class="code-snippet__keyword">join</span> (<span class="code-snippet__keyword">select</span> <span class="code-snippet__keyword">rand</span>() * (<span class="code-snippet__keyword">select</span> <span class="code-snippet__keyword">max</span>(<span class="code-snippet__keyword">id</span>) <span class="code-snippet__keyword">from</span> <span class="code-snippet__string">`dynamic`</span>) <span class="code-snippet__keyword">as</span> nid) t2 <span class="code-snippet__keyword">on</span> t1.id > t2.nidlimit <span class="code-snippet__number">1000</span>;</span></code></pre> </section> </section> </section> </section> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="color: rgb(255, 120, 0);"><strong><span style="font-size: 15px;">9、区分in和exists、not in和not exists</span></strong></span> <span style="font-size: 15px;color: rgb(64, 62, 62);"></span> </section> <section class="" data-tools="135编辑器" data-id="88286"> <section> <section> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> </ul> <pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">select</span> * <span class="code-snippet__keyword">from</span> 表A <span class="code-snippet__keyword">where</span> <span class="code-snippet__keyword">id</span> <span class="code-snippet__keyword">in</span> (<span class="code-snippet__keyword">select</span> <span class="code-snippet__keyword">id</span> <span class="code-snippet__keyword">from</span> 表B)</span></code></pre> </section> </section> </section> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">上面SQL语句相当于</span> </section> <section class="" data-tools="135编辑器" data-id="88286"> <section> <section> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> </ul> <pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">select</span> * <span class="code-snippet__keyword">from</span> 表A <span class="code-snippet__keyword">where</span> <span class="code-snippet__keyword">exists</span>(<span class="code-snippet__keyword">select</span> * <span class="code-snippet__keyword">from</span> 表B <span class="code-snippet__keyword">where</span> 表B.id=表A.id)</span></code></pre> </section> </section> </section> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">区分in和exists主要是造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为驱动表,先被访问,如果是IN,那么先执行子查询。所以IN适合于外表大而内表小的情况;EXISTS适合于外表小而内表大的情况。</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">关于not in和not exists,推荐使用not exists,不仅仅是效率问题,not in可能存在逻辑问题。如何高效的写出一个替代not exists的SQL语句?</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">原SQL语句:</span> </section> <section class="" data-tools="135编辑器" data-id="88286"> <section> <section> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> </ul> <pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">select</span> colname … <span class="code-snippet__keyword">from</span> A表 <span class="code-snippet__keyword">where</span> a.id <span class="code-snippet__keyword">not</span> <span class="code-snippet__keyword">in</span> (<span class="code-snippet__keyword">select</span> b.id <span class="code-snippet__keyword">from</span> B表)</span></code></pre> </section> </section> </section> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">高效的SQL语句:</span> </section> <section class="" data-tools="135编辑器" data-id="88286"> <section> <section> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> </ul> <pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">select</span> colname … <span class="code-snippet__keyword">from</span> A表 <span class="code-snippet__keyword">Left</span> <span class="code-snippet__keyword">join</span> B表 <span class="code-snippet__keyword">on</span> <span class="code-snippet__keyword">where</span> a.id = b.id <span class="code-snippet__keyword">where</span> b.id <span class="code-snippet__keyword">is</span> <span class="code-snippet__literal">null</span></span></code></pre> </section> </section> </section> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">取出的结果集如下图表示,A表不在B表中的数据:</span> </section> <section style="text-align: center;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <img class="" data-croporisrc="/upload/14a924d1d04c09cc5d1b975666388d78.jpg" data-cropx1="0" data-cropx2="331" data-cropy1="9" data-cropy2="198" data-ratio="0.5709969788519638" data-s="300,640" data-type="jpeg" data-w="331" src="https://mmbiz.qpic.cn/mmbiz_jpg/tibrg3AoIJTsfu3nd3L2RGjl6VO7yxISPT9CIGTQ687SojVpDT1ahRgOKMJMJ3Ax33fFibn5RluXicXNEoYYdUZVQ/640?wx_fmt=jpeg" style="background-color: rgb(238, 237, 235);border-width: 1px;border-style: solid;border-color: rgb(238, 237, 235);background-size: 22px;background-position: center center;background-repeat: no-repeat;height: 189px !important;box-sizing: border-box !important;overflow-wrap: break-word !important;width: 331px !important;"> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="color: rgb(255, 120, 0);"><strong><span style="font-size: 15px;">10、使用合理的分页方式以提高分页的效率</span></strong></span> <span style="font-size: 15px;color: rgb(64, 62, 62);"></span> </section> <section class="" data-tools="135编辑器" data-id="88286"> <section> <section> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> </ul> <pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">select</span> <span class="code-snippet__keyword">id</span>,<span class="code-snippet__keyword">name</span> <span class="code-snippet__keyword">from</span> product <span class="code-snippet__keyword">limit</span> <span class="code-snippet__number">866613</span>, <span class="code-snippet__number">20</span></span></code></pre> </section> </section> </section> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">使用上述SQL语句做分页的时候,可能有人会发现,随着表数据量的增加,直接使用limit分页查询会越来越慢。</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">优化的方法如下:可以取前一页的最大行数的id,然后根据这个最大的id来限制下一页的起点。比如此列中,上一页最大的id是866612。SQL可以采用如下的写法:</span> </section> <section class="" data-tools="135编辑器" data-id="88286"> <section> <section> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> </ul> <pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">select</span> <span class="code-snippet__keyword">id</span>,<span class="code-snippet__keyword">name</span> <span class="code-snippet__keyword">from</span> product <span class="code-snippet__keyword">where</span> <span class="code-snippet__keyword">id</span>> <span class="code-snippet__number">866612</span> <span class="code-snippet__keyword">limit</span> <span class="code-snippet__number">20</span></span></code></pre> </section> </section> </section> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="color: rgb(255, 120, 0);"><strong><span style="font-size: 15px;">11、分段查询</span></strong></span> <span style="font-size: 15px;color: rgb(64, 62, 62);"></span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">在一些用户选择页面中,可能一些用户选择的时间范围过大,造成查询缓慢。主要的原因是扫描行数过多。这个时候可以通过程序,分段进行查询,循环遍历,将结果合并处理进行展示。</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">如下图这个SQL语句,扫描的行数成百万级以上的时候就可以使用分段查询:</span> </section> <section style="text-align: center;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <img class="" data-copyright="0" data-ratio="0.125" data-s="300,640" data-type="jpeg" data-w="720" src="/upload/df81eac8eae3e1284a959c143b645eb9.jpg" style="box-sizing: border-box !important;overflow-wrap: break-word !important;width: 677px !important;visibility: visible !important;"> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="color: rgb(255, 120, 0);"><strong><span style="font-size: 15px;">12、避免在where子句中对字段进行null值判断</span></strong></span> <span style="font-size: 15px;color: rgb(64, 62, 62);"></span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">对于null的判断会导致引擎放弃使用索引而进行全表扫描。</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="color: rgb(255, 120, 0);"><strong><span style="font-size: 15px;">13、不建议使用%前缀模糊查询</span></strong></span> <span style="font-size: 15px;color: rgb(64, 62, 62);"></span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">例如LIKE“%name”或者LIKE“%name%”,这种查询会导致索引失效而进行全表扫描。但是可以使用LIKE “name%”。</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">那如何查询%name%?</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">如下图所示,虽然给secret字段添加了索引,但在explain结果并没有使用:</span> </section> <section style="text-align: center;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <img class="" data-copyright="0" data-ratio="0.4625" data-s="300,640" data-type="jpeg" data-w="640" src="/upload/77820afedf2179d7cf191d46ca77e71d.jpg" style="box-sizing: border-box !important;overflow-wrap: break-word !important;width: 640px !important;visibility: visible !important;"> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">那么如何解决这个问题呢,答案:使用全文索引。</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">在我们查询中经常会用到select id,fnum,fdst from dynamic_201606 where user_name like '%zhangsan%'; 。这样的语句,普通索引是无法满足查询需求的。庆幸的是在MySQL中,有全文索引来帮助我们。</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">创建全文索引的SQL语法是:</span> </section> <section class="" data-tools="135编辑器" data-id="88286"> <section> <section> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> </ul> <pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">ALTER</span> <span class="code-snippet__keyword">TABLE</span> <span class="code-snippet__string">`dynamic_201606`</span> <span class="code-snippet__keyword">ADD</span> FULLTEXT <span class="code-snippet__keyword">INDEX</span> <span class="code-snippet__string">`idx_user_name`</span> (<span class="code-snippet__string">`user_name`</span>);</span></code></pre> </section> </section> </section> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">使用全文索引的SQL语句是:</span> </section> <section class="" data-tools="135编辑器" data-id="88286"> <section> <section> <section class=""> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> </ul> <pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">select</span> <span class="code-snippet__keyword">id</span>,fnum,fdst <span class="code-snippet__keyword">from</span> dynamic_201606 <span class="code-snippet__keyword">where</span> <span class="code-snippet__keyword">match</span>(user_name) against(<span class="code-snippet__string">'zhangsan'</span> <span class="code-snippet__keyword">in</span> <span class="code-snippet__built_in">boolean</span> <span class="code-snippet__keyword">mode</span>);</span></code></pre> </section> </section> </section> </section> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(64, 62, 62);">注意:</span></strong> <span style="font-size: 15px;color: rgb(64, 62, 62);">在需要创建全文索引之前,请联系DBA确定能否创建。同时需要注意的是查询语句的写法与普通索引的区别。</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="color: rgb(255, 120, 0);"><strong><span style="font-size: 15px;">14、避免在where子句中对字段进行表达式操作</span></strong></span> <span style="font-size: 15px;color: rgb(64, 62, 62);"></span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">比如:</span> </section> <section class="" data-tools="135编辑器" data-id="88286"> <section> <section> <section class=""> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> </ul> <pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">select</span> user_id,user_project <span class="code-snippet__keyword">from</span> user_base <span class="code-snippet__keyword">where</span> age*<span class="code-snippet__number">2</span>=<span class="code-snippet__number">36</span>;</span></code></pre> </section> </section> </section> </section> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">中对字段就行了算术运算,这会造成引擎放弃使用索引,建议改成:</span> </section> <section class="" data-tools="135编辑器" data-id="88286"> <section> <section> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> </ul> <pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">select</span> user_id,user_project <span class="code-snippet__keyword">from</span> user_base <span class="code-snippet__keyword">where</span> age=<span class="code-snippet__number">36</span>/<span class="code-snippet__number">2</span>;</span></code></pre> </section> </section> </section> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="color: rgb(255, 120, 0);"><strong><span style="font-size: 15px;">15、避免隐式类型转换</span></strong></span> <span style="font-size: 15px;color: rgb(64, 62, 62);"></span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">where子句中出现column字段的类型和传入的参数类型不一致的时候发生的类型转换,建议先确定where中的参数类型。</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="color: rgb(255, 120, 0);"><strong><span style="font-size: 15px;">16、对于联合索引来说,要遵守最左前缀法则</span></strong></span> <span style="font-size: 15px;color: rgb(64, 62, 62);"></span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">举列来说索引含有字段id、name、school,可以直接用id字段,也可以id、name这样的顺序,但是name;school都无法使用这个索引。所以在创建联合索引的时候一定要注意索引字段顺序,常用的查询字段放在最前面。</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="color: rgb(255, 120, 0);"><strong><span style="font-size: 15px;">17、必要时可以使用force index来强制查询走某个索引</span></strong></span> <span style="font-size: 15px;color: rgb(64, 62, 62);"></span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">有的时候MySQL优化器采取它认为合适的索引来检索SQL语句,但是可能它所采用的索引并不是我们想要的。这时就可以采用forceindex来强制优化器使用我们制定的索引。</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="color: rgb(255, 120, 0);"><strong><span style="font-size: 15px;">18、注意范围查询语句</span></strong></span> <span style="font-size: 15px;color: rgb(64, 62, 62);"></span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">对于联合索引来说,如果存在范围查询,比如between、>、<等条件时,会造成后面的索引字段失效。</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="color: rgb(255, 120, 0);"><strong><span style="font-size: 15px;">19、关于JOIN优化</span></strong></span> <span style="font-size: 15px;color: rgb(64, 62, 62);"></span> </section> <section style="text-align: center;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <img class="" data-croporisrc="/upload/70e0b8fd6956402d37884bfc4b7ceb31.jpg" data-cropx1="0" data-cropx2="640" data-cropy1="33.26164874551972" data-cropy2="198.4229390681004" data-ratio="0.2578125" data-s="300,640" data-type="jpeg" data-w="640" src="https://mmbiz.qpic.cn/mmbiz_jpg/tibrg3AoIJTsfu3nd3L2RGjl6VO7yxISPcKk5H7XnmAaZzuhtOJ4ODpsuBhmBNCic1aF8t9nITOxDRYsRa3A10ew/640?wx_fmt=jpeg" style="box-sizing: border-box !important;overflow-wrap: break-word !important;width: 558px !important;visibility: visible !important;"> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">LEFT JOIN A表为驱动表,INNER JOIN MySQL会自动找出那个数据少的表作用驱动表,RIGHT JOIN B表为驱动表。</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(64, 62, 62);">注意:</span></strong> <span style="font-size: 15px;color: rgb(64, 62, 62);"></span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">1)MySQL中没有full join,可以用以下方式来解决:</span> </section> <section class="" data-tools="135编辑器" data-id="88286"> <section> <section> <section class=""> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> <li></li> </ul> <pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">select</span> * <span class="code-snippet__keyword">from</span> A <span class="code-snippet__keyword">left</span> <span class="code-snippet__keyword">join</span> B <span class="code-snippet__keyword">on</span> B.name = A.namewhere B.name <span class="code-snippet__keyword">is</span> nullunion allselect * <span class="code-snippet__keyword">from</span> B;</span></code></pre> </section> </section> </section> </section> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">2)尽量使用inner join,避免left join:</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">参与联合查询的表至少为2张表,一般都存在大小之分。如果连接方式是inner join,在没有其他过滤条件的情况下MySQL会自动选择小表作为驱动表,但是left join在驱动表的选择上遵循的是左边驱动右边的原则,即left join左边的表名为驱动表。</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">3)合理利用索引:</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">被驱动表的索引字段作为on的限制字段。</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">4)利用小表去驱动大表:</span> </section> <section style="text-align: center;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <img class="" data-croporisrc="/upload/df22c1560306615ba9a933fdefd5fb56.jpg" data-cropx1="0" data-cropx2="391" data-cropy1="23" data-cropy2="312" data-ratio="0.7391304347826086" data-s="300,640" data-type="jpeg" data-w="391" src="https://mmbiz.qpic.cn/mmbiz_jpg/tibrg3AoIJTsfu3nd3L2RGjl6VO7yxISPbiag8y4LggbC1B0VSIAFyoUWeOPzb4dsjdhlz7NAV7zSyFhmtF77xyw/640?wx_fmt=jpeg" style="background-color: rgb(238, 237, 235);border-width: 1px;border-style: solid;border-color: rgb(238, 237, 235);background-size: 22px;background-position: center center;background-repeat: no-repeat;height: 289px !important;box-sizing: border-box !important;overflow-wrap: break-word !important;width: 391px !important;"> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">从原理图能够直观的看出如果能够减少驱动表的话,减少嵌套循环中的循环次数,以减少 IO总量及CPU运算的次数。</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">5)巧用STRAIGHT_JOIN:</span> </section> <section style="text-align: left;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(64, 62, 62);">inner join是由MySQL选择驱动表,但是有些特殊情况需要选择另个表作为驱动表,比如有group by、order by等「Using filesort」、「Using temporary」时。STRAIGHT_JOIN来强制连接顺序,在STRAIGHT_JOIN左边的表名就是驱动表,右边则是被驱动表。在使用STRAIGHT_JOIN有个前提条件是该查询是内连接,也就是inner join。其他链接不推荐使用STRAIGHT_JOIN,否则可能造成查询结果不准确。</span> </section> <section style="text-align: center;margin-left: 16px;margin-right: 16px;margin-bottom: 20px;line-height: 1.75em;"> <img class="" data-croporisrc="/upload/2fd7dbf575790dbae66655ae50ba304.jpg" data-cropx1="0" data-cropx2="623" data-cropy1="0" data-cropy2="430.9641577060932" data-ratio="0.6918138041733547" data-s="300,640" data-type="jpeg" data-w="623" src="https://mmbiz.qpic.cn/mmbiz_jpg/tibrg3AoIJTsfu3nd3L2RGjl6VO7yxISPY46CzOXhvOrlSuFKghXic9EnRot4jD3ibzIFS3DQNcuZOojibXoJcXqYw/640?wx_fmt=jpeg" style="box-sizing: border-box !important;overflow-wrap: break-word !important;width: 558px !important;visibility: visible !important;"> </section> <p style="margin-right: 16px;margin-bottom: 20px;margin-left: 16px;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;white-space: normal;background-color: rgb(255, 255, 255);font-size: medium;color: rgb(0, 0, 0);text-align: center;line-height: 1.75em;"><span style="color: rgb(136, 136, 136);font-size: 15px;"><strong mpa-from-tpl="t" style="color: rgb(0, 122, 170);">-END-</strong></span></p> <p style="margin-right: 16px;margin-bottom: 10px;margin-left: 16px;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;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;line-height: normal;"><strong><span style="color: rgb(255, 120, 0);font-size: 15px;">【读者福利】9.9元Python编程课</span></strong></p> <p style="margin-right: 16px;margin-bottom: 10px;margin-left: 16px;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;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;line-height: normal;"><strong><span style="font-size: 15px;color: rgb(255, 120, 0);">互动对话教学+在线编程</span></strong></p> <p style="margin-right: 16px;margin-bottom: 10px;margin-left: 16px;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;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;line-height: normal;"><strong><span style="font-size: 15px;color: rgb(255, 120, 0);">专为零基础小白打造<br></span><span style="font-size: 15px;color: rgb(255, 120, 0);"></span></strong></p> <p style="margin-right: 16px;margin-bottom: 10px;margin-left: 16px;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;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;line-height: normal;"><strong><span style="font-size: 15px;color: rgb(255, 120, 0);">百元奖学金红包免费领</span></strong></p> <p style="margin-right: 16px;margin-bottom: 10px;margin-left: 16px;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;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;line-height: normal;"><strong><span style="font-size: 15px;color: rgb(255, 120, 0);"><strong>⏰</strong>限时24h,速速抢购👇</span></strong></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="1.7777777777777777" data-s="300,640" src="/upload/e1dcc5c903df0a7a284e3ebe0c35669b.jpg" data-type="jpeg" data-w="720" style="box-shadow: rgb(170, 170, 170) 0em 0em 1em 0px;width: 90%;height: auto !important;"></p> </section>