作者:じ☆ve宝贝
# ZeroClipboard ###### 使用场景: 当我做邀请好友模块的时候无法避免点击copy邀请链接,虽然js可以实现,但是会有一个特别恶心的让用可以允许的提示。ZeroClipboard可以完美解决这个问题,接下来就是我给大家带来的例子! ##### 代码部分 <!-- 这里是HTML代码部分 --> ``` <input type="text" id="content" value="这里是要复制的内容" /> <input id="copy" type="button" data-clipboard-target="content" value="复制"> ``` <!-- 这里是JS代码部分 --> ``` <script type="text/javascript" src="js/ZeroClipboard.min.js" ></script> <script type="text/javascript"> // 将【复制】按钮充当复制数据的元素载体 var clip = new ZeroClipboard( document.getElementById("copy") ); </script> ``` 以上就是引入并使用 ZeroClipboard 的最简代码。我们为【复制】按钮指定了data-clipboard-target属性,其值为将被复制数据的元素id。此时,我们点击【复制】按钮就可以复制id为content的textarea中的文本数据。 ##### 关于文件引入和本地化使用 上面我们引入的JS文件是 ZeroClipboard 官方提供的 CDN,你可以直接使用。如果你想将其下载到本地服务器上使用,你可以进入官方网站下载最新版本。然后将dist目录下的ZeroClipboard.js(或者压缩版的ZeroClipboard.min.js)和ZeroClipboard.swf这两个文件上传到自己的服务器即可。 请确保它们被放在同一目录下,以便于 ZeroClipboard.js 自动加载 ZeroClipboard.swf 文件。否则你需要在使用前额外配置swf文件的路径。 // 在 new ZeroClipboard()之前,需要先配置 ZeroClipboard.swf 文件的路径 ZeroClipboard.config( { swfPath: 'http://YOURSERVER/path/ZeroClipboard.swf' } ); 事件处理 ZeroClipboard 还为我们提供了事件支持,以便于处理ZeroClipboard触发的各种事件。ZeroClipboard支持的事件有"ready"、 "beforecopy"、 "copy"、 "aftercopy"、 "destroy"、 "error"。 我们可以通过on()方法来注册事件处理函数。 ``` // 当Flash SWF文件加载完成并准备就绪时触发ready事件 clip.on("ready", function(){ alert("加载完成!"); }); // 当触发copy事件时,设置用于复制的文本数据 clip.on("copy", function(e){ e.clipboardData.setData("text/plain", "这里是用于复制的纯文本数据") }); ``` 此外,off()方法用来取消注册的事件处理函数,emit()方法用来手动触发事件。其用法与jQuery的on()、 off()、 trigger()方法非常相似。 此外,如果你有多个ZeroClipboard对象,你想为它们都注册事件处理函数。你可以使用全局对象ZeroClipboard的静态方法ZeroClipboard.on()、 ZeroClipboard.off()、 ZeroClipboard.emit()来全局性地设置事件。全局事件将对每个对象都生效。
作者:微信小助手
<p><span style="font-size: 15px;">在我们的代码中,经常会编写是否为真的代码,比如用户名是否存在,客户是否存在等。类似如下代码:</span></p> <p><span style="font-size: 15px;">public boolean exist(Long userId){</span></p> <p><span style="font-size: 15px;"> ...</span></p> <p><span style="font-size: 15px;"> return false;</span></p> <p><span style="font-size: 15px;">}</span></p> <p><br></p> <p><span style="font-size: 15px;">这样做已经很棒了。但你需要了解一个java8的Predicate。通过Predicate可以让你的代码更加的简洁。学习下Predicate吧。</span></p> <p><br></p> <p><span style="font-size: 15px;">Predicate是一个函数接口。它包含了一个接口方法和三个默认方法以及一个静态方法。</span></p> <p style="text-align: center;"><img class="" data-ratio="0.7011494252873564" data-s="300,640" src="/upload/6f89131b71f9be7ce71ca0c5a2e5e5f0.png" data-type="png" data-w="870" style="width: 315px;height: 220px;"></p> <p style="text-align: left;"><span style="font-size: 15px;">Predicate表示断定和假设的意思。</span></p> <p style="text-align: left;"><br></p> <p style="text-align: left;"><span style="font-size: 17px;">test</span></p> <p style="text-align: left;"><br></p> <p style="text-align: left;"><span style="font-size: 15px;">test接口就是为了让你实现判断的效果。最原始的就是去实现这个接口,然后写我们的判断逻辑,如下:</span></p> <p style="text-align: center;"><img class="" data-ratio="0.3834080717488789" data-s="300,640" src="/upload/9d06a4e14046c6439cdf446994f23bf4.png" data-type="png" data-w="892" style="width: 365px;height: 140px;"></p> <p style="text-align: left;"><span style="font-size: 15px;">你也看到有一部分灰色的代码,告诉我们这个可以被优化为lambda表达式,如下:</span></p> <p style="text-align: center;"><img class="" data-ratio="0.2375366568914956" data-s="300,640" src="/upload/85cfb0891a1e1da2764782c4c9a43be.png" data-type="png" data-w="682" style="width: 336px;height: 80px;"></p> <p style="text-align: left;"><span style="font-size: 15px;">你也可以把Predicate的实现单独抽离成一个实现类,方便重用。</span></p> <p style="text-align: center;"><img class="" data-ratio="0.23459715639810427" data-s="300,640" src="/upload/a536b8e6aaaba02c00a9b267dfecf26b.png" data-type="png" data-w="844" style="width: 364px;height: 85px;"></p> <p style="text-align: left;"><span style="font-size: 15px;">Predicate不仅可以单独在代码中使用,也可以在测试代码中用来做判断,同时还可以被用在Stream的filter中,用来做过滤。</span></p> <p style="text-align: center;"><img class="" data-ratio="0.1497659906396256" data-s="300,640" src="/upload/fce52ddea029a9f2e577d2448c27b5ab.png" data-type="png" data-w="1282" style="width: 355px;height: 54px;"></p> <p><span style="font-size: 15px;">使用Predicate可以让你的判断逻辑代码更加的简洁和解耦,增加了可读性、可测试性,同时符合<strong>DRY原则</strong>。</span></p> <p><br></p> <blockquote> <p><span style="font-size: 15px;">DRY原则:(don’t repeat yourself): writing code more than once is not a good fit for a lazy developer ;)It also makes your software more difficult to maintain because it becomes harder to make your business logic consistent。一句话:别写重复代码</span></p> </blockquote> <p><br></p> <p><span style="font-size: 15px;">现在你的代码看起来已经很炫酷了。但别急,接下来的几个操作会让你的代码看起来更加的清晰而明了。当你写了一个“大于20”的条件时,此时需求变了,需要一个新的条件20<x<30。</span></p> <p><br></p> <p><span style="font-size: 15px;">此时你有两条路,一条路是直接修改Predicate的test方法中的逻辑。还有一条路是新建一个新的Predicate,然后和现有的组装成为一个新的Predicate。第一条路相信你经常走,接下来就来介绍下第二条路:组装。</span></p> <p><br></p> <p><span style="font-size: 17px;">and</span></p> <p><br></p> <p><span style="font-size: 15px;">首先新建一个LessThan30Predicate:</span></p> <p style="text-align: center;"><img class="" data-ratio="0.22" data-s="300,640" src="/upload/4d2153bcbbd704d3a284e4d4fee17a63.png" data-type="png" data-w="900" style="width: 355px;height: 78px;"></p> <p style="text-align: left;"><span style="font-size: 15px;">然后和上面的CustomPredicate用and方法组装成一个新的Predicate。如下:</span></p> <p style="text-align: center;"><img class="" data-ratio="0.11435239206534423" data-s="300,640" src="/upload/d4a7dec6c1ffcac70ff33efd7be34f34.png" data-type="png" data-w="1714" style=""></p> <p style="text-align: left;"><span style="font-size: 15px;">输出结果:<br></span></p> <p style="text-align: left;"><img class="" data-ratio="0.6" data-s="300,640" src="/upload/64237a7fe42d9fc7cf9ab7e975e43457.png" data-type="png" data-w="60" style="width: 25px;height: 20px;"></p> <p style="text-align: left;"><span style="font-size: 17px;">or</span></p> <p style="text-align: left;"><span style="font-size: 15px;">同样也可以使用or来组装。</span></p> <p style="text-align: center;"><img class="" data-ratio="0.11438679245283019" data-s="300,640" src="/upload/1e43891540c81cec0d13df843d585edf.png" data-type="png" data-w="1696" style=""></p> <p style="text-align: left;"><span style="font-size: 15px;">输出结果:<br></span></p> <p style="text-align: left;"><img class="" data-ratio="6.148148148148148" data-s="300,640" src="/upload/8606a07181382317d6fcd8993a83e672.png" data-type="png" data-w="54" style="width: 20px;height: 124px;"></p> <p style="text-align: left;"><span style="font-size: 17px;"><br></span></p> <p style="text-align: left;"><span style="font-size: 17px;">negate</span></p> <p style="text-align: left;"><span style="font-size: 17px;"><br></span></p> <p style="text-align: left;"><span style="font-size: 15px;">你也可以使用negate方法把现有的Predicate变为否定的Predicate。</span></p> <p style="text-align: center;"><img class="" data-ratio="0.14122681883024252" data-s="300,640" src="/upload/c3cad7e3a6a5b60a74b2749ae8b29d1c.png" data-type="png" data-w="1402" style="width: 465px;height: 65px;"></p> <p style="text-align: left;"><span style="font-size: 15px;">输出结果:<br></span></p> <p style="text-align: left;"><img class="" data-ratio="4" data-s="300,640" src="/upload/1d6c3a2257561bacff5bc33f4699d5dd.png" data-type="png" data-w="52" style="width: 20px;height: 80px;"></p>
作者:微信小助手
<section style="max-width: 100%;box-sizing: border-box;color: rgb(51, 51, 51);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-variant-numeric: normal;font-variant-east-asian: normal;letter-spacing: 0.544px;line-height: 27.2px;text-align: justify;white-space: normal;widows: 1;overflow-wrap: break-word;background-color: rgb(255, 255, 255);word-wrap: break-word !important;"> <section style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;word-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;display: inline-block;width: 628px;border-width: 0px 0px 0px 10px;border-style: solid;border-left-color: rgb(12, 137, 24);border-right-color: rgb(12, 137, 24);box-shadow: rgb(0, 0, 0) 0px 0px 0px 0px;border-bottom-left-radius: 0px;word-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;word-wrap: break-word !important;"> <section style="padding-right: 3px;padding-left: 3px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;word-wrap: break-word !important;"> <section style="margin-top: 0.95em;margin-bottom: -0.65em;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;display: inline-block;width: 612px;vertical-align: top;word-wrap: break-word !important;"> <section style="margin-top: -1em;padding: 3px 10px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;display: inline-block;vertical-align: top;color: rgb(255, 255, 255);background-color: rgb(12, 137, 24);word-wrap: break-word !important;"> <p style="max-width: 100%;box-sizing: border-box;min-height: 1em;overflow-wrap: break-word;font-size: 14px;word-wrap: break-word !important;"><strong style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;">专题简介</strong></p> </section> </section> </section> </section> </section> </section> </section> <section class="" style="max-width: 100%;color: rgb(51, 51, 51);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-variant-numeric: normal;font-variant-east-asian: normal;letter-spacing: 0.544px;line-height: 27.2px;text-align: justify;white-space: normal;widows: 1;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;"> <section style="margin: 10px 5px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;border-width: 1px;border-style: solid;border-color: rgb(74, 168, 130);box-shadow: rgb(61, 198, 150) 0px 0px 5px;word-wrap: break-word !important;overflow-wrap: break-word !important;"> <section style="margin: 15px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;"> <p style="max-width: 100%;min-height: 1em;letter-spacing: 2px;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;color: rgb(136, 136, 136);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;">作为一个合格的Java程序员,必须要对并发编程有一个深层次的了解,在很多互联网企业都会重点考察这一块。可能很多工作3年以上的Java程序员对于这一领域几乎没有太多研究。所以在接下来内容中,我会将并发编程整个领域由浅到深做非常全面的分析。</span></p> </section> </section> </section> </section> <section class="" style="max-width: 100%;color: rgb(51, 51, 51);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-variant-numeric: normal;font-variant-east-asian: normal;letter-spacing: 0.544px;line-height: 27.2px;text-align: justify;white-space: normal;widows: 1;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;"> <p style="max-width: 100%;min-height: 1em;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;"></p> </section> <section class="" data-color="rgb(12, 137, 24)" style="max-width: 100%;color: rgb(51, 51, 51);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-variant-numeric: normal;font-variant-east-asian: normal;letter-spacing: 0.544px;line-height: 27.2px;text-align: justify;white-space: normal;widows: 1;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;word-wrap: break-word !important;"> <section style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;word-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;display: inline-block;width: 628px;border-width: 0px 0px 0px 10px;border-style: solid;border-left-color: rgb(12, 137, 24);border-right-color: rgb(12, 137, 24);box-shadow: rgb(0, 0, 0) 0px 0px 0px 0px;border-bottom-left-radius: 0px;word-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;word-wrap: break-word !important;"> <section style="padding-right: 3px;padding-left: 3px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;word-wrap: break-word !important;"> <section style="margin-top: 0.95em;margin-bottom: -0.65em;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;display: inline-block;width: 612px;vertical-align: top;word-wrap: break-word !important;"> <section style="margin-top: -1em;padding: 3px 10px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;display: inline-block;vertical-align: top;color: rgb(255, 255, 255);background-color: rgb(12, 137, 24);word-wrap: break-word !important;"> <p style="max-width: 100%;box-sizing: border-box;min-height: 1em;overflow-wrap: break-word;font-size: 14px;word-wrap: break-word !important;"><strong style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;">内容导航</strong></p> </section> </section> </section> </section> </section> </section> </section> <section style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;word-wrap: break-word !important;"> <section style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;word-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;word-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;word-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;word-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;word-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;word-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;word-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word;word-wrap: break-word !important;"> <p style="max-width: 100%;min-height: 1em;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"></p> </section> </section> </section> <ul class=" list-paddingleft-2" style="list-style-type: square;"> <li><p style="margin-top: 5px;margin-bottom: 10px;max-width: 100%;min-height: 1em;line-height: normal;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;">从操作系统的发展了解进程、线程模型</span></p></li> <li><p style="margin-top: 5px;margin-bottom: 10px;max-width: 100%;min-height: 1em;line-height: normal;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;">线程的优势</span></p></li> <li><p style="margin-top: 5px;margin-bottom: 10px;max-width: 100%;min-height: 1em;line-height: normal;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;">线程的生命周期</span></p></li> <li><p style="margin-top: 5px;margin-bottom: 10px;max-width: 100%;min-height: 1em;line-height: normal;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;">线程的应用场景</span><img class="" data-ratio="1" data-type="png" data-w="1" src="/upload/d8f124848562876cac4acfa6276d4b45.png" style="vertical-align: bottom;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;width: 1px !important;visibility: visible !important;"></p></li>
作者:微信小助手
<p><span style="font-size: 15px;letter-spacing: 1px;">《<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651962738&idx=1&sn=d2d91a380bad06af9f7b9f7a80db26b3&chksm=bd2d08ae8a5a81b8a7f044af52c5e6e77ec3df2bb4a9c91cd450c3fd932e8dade56afb09f784&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2">并发扣款,如何保证数据的一致性?</a>》,分享了同一个用户并发扣款时,有一定概率出现数据不一致,可以使用CAS乐观锁的方式,在不降低吞吐量,并且只有少量修改的情况下,保证数据的一致性。</span></p> <p><span style="font-size: 15px;letter-spacing: 1px;"><br></span></p> <p><span style="font-size: 15px;letter-spacing: 1px;">文章发布不到24小时,就有近200的评论。</span></p> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">其中,问的比较多的是<strong>ABA问题</strong>,这个问题已经在《<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651962761&idx=1&sn=11513a9e34bf77b4545ecdc289f4c8fd&chksm=bd2d08558a5a8143ce15fbd9722fbcc42593b55480ca5eba7b19b2911763bb15bde65e9a2c4b&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2">并发扣款一致性优化,CAS下ABA问题,这个话题还没聊完!!!</a>》中扩展。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">其次,问的比较多的是作业题,为什么一定<strong>要用</strong></span> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(255, 76, 0);"><strong>select&set</strong></span> <span style="font-size: 15px;letter-spacing: 1px;">的方式进行<strong>余额写回</strong>:</span> </section> <p style="line-height: normal;"><em><span style="letter-spacing: 1px;font-size: 12px;">UPDATE t_yue SET money=$new_money WHERE uid=$uid AND money=$old_money;</span></em></p> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">为什么<strong>不能采用</strong></span> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(255, 76, 0);">直接扣减</span> <span style="font-size: 15px;letter-spacing: 1px;">的方法:</span> </section> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="letter-spacing: 1px;">UPDATE t_yue SET money=money-$diff WHERE uid=$uid;</span></em></span><span style="font-size: 15px;letter-spacing: 1px;"></span></p> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">很人说,在并发情况下,</span> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(255, 76, 0);">会将money扣成负数</span> <span style="font-size: 15px;letter-spacing: 1px;">。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">为了保证余额<strong>不被扣成负数</strong>,再加一个where条件:</span> </section> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="letter-spacing: 1px;">UPDATE t_yue SET money=money-$diff WHERE uid=$uid </span></em></span><span style="font-size: 12px;color: rgb(255, 76, 0);"><em><span style="font-size: 12px;letter-spacing: 1px;">AND money-$diff>0</span></em></span><span style="font-size: 12px;"><em><span style="letter-spacing: 1px;">;</span></em><em><span style="letter-spacing: 1px;"></span></em></span></p> <section style="line-height: 1.75em;"> <strong><span style="font-size: 15px;letter-spacing: 1px;">这样是否可行?</span></strong> <span style="font-size: 15px;letter-spacing: 1px;"></span> </section> <section style="line-height: 1.75em;"> <span style="color: rgb(0, 82, 255);"><em><span style="font-size: 15px;letter-spacing: 1px;">画外音:</span></em><em><span style="font-size: 15px;letter-spacing: 1px;">额,撇开业务不谈,这个SQL用列做运算,其实是不好的,建议使用:</span></em></span> <span style="font-size: 15px;letter-spacing: 1px;"></span> </section> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="letter-spacing: 1px;">UPDATE t_yue SET money=money-$diff WHERE uid=$uid AND </span></em></span><span style="font-size: 12px;color: rgb(255, 76, 0);"><em><span style="font-size: 12px;letter-spacing: 1px;">money>$diff</span></em></span><span style="font-size: 12px;"><em><span style="letter-spacing: 1px;">;</span></em></span><span style="font-size: 15px;letter-spacing: 1px;"></span></p> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">很遗憾,仍然不行。原因在《<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651962738&idx=1&sn=d2d91a380bad06af9f7b9f7a80db26b3&chksm=bd2d08ae8a5a81b8a7f044af52c5e6e77ec3df2bb4a9c91cd450c3fd932e8dade56afb09f784&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2">并发扣款,如何保证数据的一致性?</a>》一文里点赞最多的评论,<strong>不幂等</strong>。</span> </section> <section style="line-height: 1.75em;"> <span style="color: rgb(0, 82, 255);"><em><span style="font-size: 15px;letter-spacing: 1px;">画外音:</span></em><em><span style="font-size: 15px;letter-spacing: 1px;">说明绝大部分同学,能够回答正确作业。</span></em></span> <span style="font-size: 15px;letter-spacing: 1px;"></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">聊幂等性之前,先看另一个测试用例的case。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">假设有一个服务接口,<strong>注册新用户</strong>:</span> </section> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="letter-spacing: 1px;">bool RegisterUser($uid, $name){</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> //<strong>查看uid是否已经存在</strong></span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> select uid from t_user where uid=$uid;</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> //<strong>不是新用户,返回失败</strong></span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> if(rows>0)return false;</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> else{</span></em></span></p> <section style="line-height: normal;"> <em><span style="font-size: 12px;letter-spacing: 1px;"> //<strong>把新用户插入用户表</strong></span><span style="font-size: 12px;letter-spacing: 1px;"></span></em> </section> <section style="line-height: normal;"> <em><span style="letter-spacing: 1px;font-size: 12px;"> insert into t_user values($uid, $name);</span><span style="letter-spacing: 1px;font-size: 12px;"></span></em> </section> <section style="line-height: normal;"> <em><span style="letter-spacing: 1px;font-size: 12px;"> //<strong>返回成功</strong></span><span style="letter-spacing: 1px;font-size: 12px;"></span></em> </section> <section style="line-height: normal;"> <em><span style="letter-spacing: 1px;font-size: 12px;"> return true;</span><span style="letter-spacing: 1px;font-size: 12px;"></span></em> </section> <section style="line-height: normal;"> <em><span style="letter-spacing: 1px;font-size: 12px;"> }</span><span style="letter-spacing: 1px;font-size: 12px;"></span></em> </section> <section style="line-height: normal;"> <em><span style="letter-spacing: 1px;font-size: 12px;">}</span></em> <span style="letter-spacing: 1px;font-size: 12px;"></span> <span style="font-size: 15px;letter-spacing: 1px;"></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">有一个测试工程师,对该接口写了一个<strong>测试用例</strong>:</span> </section> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="letter-spacing: 1px;">bool TestCase_RegisterUser(){</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> //<strong>造一些假数据</strong></span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> long uid=123;</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> String name='shenjian';</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> //<strong>调用被测试的接口</strong></span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> bool result= RegisterUser(uid,name);</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> //预期注册成功,<strong>对结果进行断言判断</strong></span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> Assert(result,true);</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> //<strong>返回测试结果</strong></span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> return result;</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;">}</span></em></span><span style="font-size: 15px;letter-spacing: 1px;"></span></p> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <strong><span style="font-size: 15px;letter-spacing: 1px;">这是不是一个好的测试用例?<br>这个用例存在什么问题?</span></strong> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">你会发现,</span> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(255, 76, 0);">相同条件下</span> <span style="font-size: 15px;letter-spacing: 1px;">,这个</span> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(255, 76, 0);">测试用例执行两次,得到的结果不一样</span> <span style="font-size: 15px;letter-spacing: 1px;">:</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(1)第一次执行,第一次造数据,调用接口,注册成功;</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(2)第二次执行,又造了一次相同的数据,调用接口,注册会失败;</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">这</span> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(255, 76, 0);">不是一个好的测试用例,多次执行结果不同</span> <span style="font-size: 15px;letter-spacing: 1px;">。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <strong><span style="font-size: 15px;letter-spacing: 1px;">什么是幂等性?</span></strong> <span style="font-size: 15px;letter-spacing: 1px;"></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">相同条件下,执行同一请求,得到的结果相同,才符合幂等性。</span> </section> <section style="line-height: 1.75em;"> <span style="color: rgb(0, 82, 255);"><em><span style="font-size: 15px;letter-spacing: 1px;">画外音:Google一下,比我解释得更好,但意思应该说清楚了。</span></em></span> <em><span style="font-size: 15px;letter-spacing: 1px;"></span></em> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <strong><span style="font-size: 15px;letter-spacing: 1px;">如何将上面的测试用例改为符合“幂等性”的测试用例呢?</span></strong> <span style="font-size: 15px;letter-spacing: 1px;"></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">只需要加一行代码:</span> </section> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="letter-spacing: 1px;">bool TestCase_RegisterUser(){</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> //造一些假数据</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> long uid=123;</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> String name=’shenjian’;</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> //<strong>先删除这个伪造的用户</strong></span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> </span></em></span><span style="font-size: 12px;color: rgb(255, 76, 0);"><em><span style="font-size: 12px;letter-spacing: 1px;">DeleteUser(uid);</span></em></span><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"></span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> //调用被测试的接口</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> bool result= RegisterUser(uid,name);</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> //预期注册成功,对结果进行断言判断</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> Assert(result,true);</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> //返回测试结果</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> return result;</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;">}</span></em></span><span style="font-size: 15px;letter-spacing: 1px;"></span></p> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">这样,<strong>在相同条件下</strong>,</span> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(255, 76, 0);">不管这个用例执行多少次,得到的测试结果都是相同的</span> <span style="font-size: 15px;letter-spacing: 1px;">。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">是不是对幂等性有点感觉了。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">读请求,一般是幂等的。<br><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">写请求,视情况而定:</span> </section> <ul style="list-style-type: disc;" class=" list-paddingleft-2"> <li><p><span style="font-size: 15px;letter-spacing: 1px;">insert x,一般来说不是幂等的,重复插入得到的结果不一定一样</span></p></li> <li><p><span style="font-size: 15px;letter-spacing: 1px;">delete x,一般来说是幂等的,删除多次得到的结果仍相同</span></p></li> <li><p><span style="font-size: 15px;letter-spacing: 1px;color: rgb(255, 76, 0);">set a=x是幂等的</span></p></li> <li><p><span style="font-size: 15px;letter-spacing: 1px;color: rgb(255, 76, 0);">set a=a-x不是幂等的</span></p></li> <li><p><span style="font-size: 15px;letter-spacing: 1px;">…</span></p></li> </ul> <section style="line-height: 1.75em;"></section> <section style="line-height: 1.75em;"></section> <section style="line-height: 1.75em;"></section> <section style="line-height: 1.75em;"></section> <section style="line-height: 1.75em;"></section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">因此,这么扣减余额:</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 12px;"><em><span style="letter-spacing: 1px;">UPDATE t_yue SET money=$new_money WHERE uid=$uid AND money=$old_money;</span></em><em><span style="letter-spacing: 1px;"></span></em></span> </section> <section style="line-height: 1.75em;"> <strong><span style="font-size: 15px;letter-spacing: 1px;">是幂等操作</span></strong> <span style="font-size: 15px;letter-spacing: 1px;">。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">要是这么扣减余额:</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 12px;"><em><span style="letter-spacing: 1px;">UPDATE t_yue SET money=money-$diff WHERE uid=$uid AND money-$diff>0;</span></em></span> <span style="font-size: 15px;letter-spacing: 1px;"></span> </section> <section style="line-height: 1.75em;"> <strong><span style="font-size: 15px;letter-spacing: 1px;">不是幂等操作</span></strong> <span style="font-size: 15px;letter-spacing: 1px;">。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">聊到这里,或许有朋友要抬杠了,<strong>测试用例会重复执行,扣款怎么会重复执行呢?</strong></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(255, 76, 0);">重试。</span> <span style="font-size: 15px;letter-spacing: 1px;"></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">重试,是异常处理里很常见的手段。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">你在写业务的时候有没有写过这样的代码:</span> </section> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="letter-spacing: 1px;">result = DoSomething();</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;">if(false==result || TIMEOUT){</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> //<strong>错误,或者超时,重试一次</strong></span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;"> result= DoSomething();</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;">}</span></em></span></p> <p style="line-height: normal;"><span style="font-size: 12px;"><em><span style="font-size: 12px;letter-spacing: 1px;">return result;</span></em></span><span style="font-size: 15px;letter-spacing: 1px;"></span></p> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">当然,又会有朋友抬杠了,我从来不重试!!!</span> </section> <section style="line-height: 1.75em;"> <span style="color: rgb(0, 82, 255);"><em><span style="font-size: 15px;letter-spacing: 1px;">画外音:额,这是合格,还是不合格呢?</span></em></span> <em><span style="font-size: 15px;letter-spacing: 1px;"></span></em> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">你可以决定业务代码怎么写,你<strong>不能决定底层框架代码怎么写</strong>:</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(1)</span> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(255, 76, 0);">站点框架</span> <span style="font-size: 15px;letter-spacing: 1px;">有没有自动重试?</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(2)</span> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(255, 76, 0);">服务框架</span> <span style="font-size: 15px;letter-spacing: 1px;">有没有自动重试?</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(3)</span> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(255, 76, 0);">服务连接池</span> <span style="font-size: 15px;letter-spacing: 1px;">,</span> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(255, 76, 0);">数据库连接池</span> <span style="font-size: 15px;letter-spacing: 1px;">有没有自动重试?</span> </section> <section style="line-height: 1.75em;"> <span style="color: rgb(0, 82, 255);"><em><span style="font-size: 15px;letter-spacing: 1px;">画外音:</span></em></span> </section> <section style="line-height: 1.75em;"> <span style="color: rgb(0, 82, 255);"><em><span style="color: rgb(0, 82, 255);font-size: 15px;letter-spacing: 1px;">(1)服务化分层的架构中,建议只入口层重试,服务层不要重试,防止雪崩;</span></em></span> </section> <section style="line-height: 1.75em;"> <span style="color: rgb(0, 82, 255);"><em><span style="color: rgb(0, 82, 255);font-size: 15px;letter-spacing: 1px;">(2)dubbo底层,调用超时是默认重试的,这个设计不好;</span></em></span> <span style="font-size: 15px;letter-spacing: 1px;"></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">因此,<strong>在有重试的架构体系里,幂等性是需要考虑的一个问题</strong>。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">现在该懂了,为啥扣款和充值业务,一般使用:</span> </section> <ul style="list-style-type: disc;" class=" list-paddingleft-2"> <li><p><span style="font-size: 15px;letter-spacing: 1px;">select&set,配合CAS方案</span></p></li> </ul> <section style="line-height: 1.75em;"></section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">而不使用:</span> </section> <ul style="list-style-type: disc;" class=" list-paddingleft-2"> <li><p><span style="font-size: 15px;letter-spacing: 1px;">set money-=X方案<br></span></p></li> </ul> <section style="line-height: 1.75em;"></section> <section style="line-height: 1.75em;"> <span style="color: rgb(0, 82, 255);"><em><span style="font-size: 15px;letter-spacing: 1px;">画外音:充了100电话费,怎么多了200块?</span></em></span> <em><span style="font-size: 15px;letter-spacing: 1px;"></span></em> <span style="font-size: 15px;letter-spacing: 1px;"><br><br> </span> </section> <section style="line-height: 1.75em;"> <strong><span style="font-size: 15px;letter-spacing: 1px;">知其然,知其所以然</span></strong> <span style="font-size: 15px;letter-spacing: 1px;">,希望大家有收获。</span> </section> <section style="text-align: center;color: rgb(51, 51, 51);line-height: normal;clear: both;box-sizing: border-box;background-color: rgb(255, 255, 255);"> <span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-size: 15px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><img style="box-sizing: border-box;margin: 0px;max-width: 677px;padding: 0px;visibility: visible !important;overflow-wrap: break-word;width: 130px !important;height: auto !important;" data-ratio="1" data-type="jpeg" data-w="250" data-s="300,640" class="" src="/upload/7ddc9700032e2c5cee163f1f1a37b46c.jpg" width="auto"></strong></span> </section> <section style="text-align: center;color: rgb(51, 51, 51);line-height: normal;clear: both;box-sizing: border-box;background-color: rgb(255, 255, 255);"> <span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-size: 12px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;">架构师之路</strong>-分享技术思路</strong><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"></strong><br style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"></span> </section> <section style="margin: 0px;padding: 0px;text-align: left;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.54px;clear: both;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;word-wrap: break-word;min-height: 17px;max-width: 677px;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);"> <span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-size: 15px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">相关文章:</span> </section> <section style="margin: 0px;padding: 0px;text-align: left;color: rgb(51, 51, 51);text-transform: none;line-height: 1.75em;text-indent: 0px;letter-spacing: 0.54px;clear: both;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;word-wrap: break-word;min-height: 17px;max-width: 677px;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);"> <span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-size: 15px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">《<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651962738&idx=1&sn=d2d91a380bad06af9f7b9f7a80db26b3&chksm=bd2d08ae8a5a81b8a7f044af52c5e6e77ec3df2bb4a9c91cd450c3fd932e8dade56afb09f784&scene=21#wechat_redirect" target="_blank" data-linktype="2" data-itemshowtype="0">并发扣款,如何保证数据的一致性?</a>》<br>《<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651962761&idx=1&sn=11513a9e34bf77b4545ecdc289f4c8fd&chksm=bd2d08558a5a8143ce15fbd9722fbcc42593b55480ca5eba7b19b2911763bb15bde65e9a2c4b&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2">并发扣款一致性优化,CAS下ABA问题</a>》</span> <br> </section> <p><span style="text-align: justify;color: rgb(51, 51, 51);text-transform: none;text-indent: 0px;letter-spacing: 1px;font-family: "mp-quote",-apple-system-font,BlinkMacSystemFont,"Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei UI","Microsoft YaHei",Arial,sans-serif;font-size: 15px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;display: inline !important;orphans: 2;float: none;-webkit-text-stroke-width: 0px;background-color: transparent;"><br></span></p> <p><span style="text-align: justify;color: rgb(51, 51, 51);text-transform: none;text-indent: 0px;letter-spacing: 1px;font-family: "mp-quote",-apple-system-font,BlinkMacSystemFont,"Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei UI","Microsoft YaHei",Arial,sans-serif;font-size: 15px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;display: inline !important;orphans: 2;float: none;-webkit-text-stroke-width: 0px;background-color: transparent;">还有很多朋友评论:版本号,CAS只能在低并发中使用,</span><span style="text-align: justify;text-transform: none;text-indent: 0px;letter-spacing: 1px;font-family: "mp-quote", -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;display: inline !important;float: none;-webkit-text-stroke-width: 0px;background-color: transparent;color: rgb(255, 76, 0);">高并发会有大量的修改失败</span><span style="text-align: justify;color: rgb(51, 51, 51);text-transform: none;text-indent: 0px;letter-spacing: 1px;font-family: "mp-quote",-apple-system-font,BlinkMacSystemFont,"Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei UI","Microsoft YaHei",Arial,sans-serif;font-size: 15px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;display: inline !important;orphans: 2;float: none;-webkit-text-stroke-width: 0px;background-color: transparent;">。或许大家对“高并发”有所误解,下一篇聊一聊相关问题。</span></p>
作者:微信小助手
<p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, system-ui, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;" data-mpa-powered-by="yiban.io"><span style="max-width: 100%;color: rgb(255, 0, 0);font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">(点击</span><span style="max-width: 100%;line-height: 22.4px;color: rgb(0, 128, 255);">上方公众号</span><span style="max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;">,可快速关注)</span></span></p> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, system-ui, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;text-align: justify;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <blockquote style="max-width: 100%;color: rgb(51, 51, 51);font-family: -apple-system-font, system-ui, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;text-align: justify;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <p style="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="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;">www.cnblogs.com/wenanry/archive/2010/02/25/1673368.html</span></p> </blockquote> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">当你输入一个网址的时候,实际会发生什么?</span></strong></p> <p><br></p> <p>原文:http://igoro.com/archive/what-really-happens-when-you-navigate-to-a-url/ </p> <p><br></p> <p>作为一个软件开发者,你一定会对网络应用如何工作有一个完整的层次化的认知,同样这里也包括这些应用所用到的技术:像浏览器,HTTP,HTML,网络服务器,需求处理等等。</p> <p><br></p> <p>本文将更深入的研究当你输入一个网址的时候,后台到底发生了一件件什么样的事~</p> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">1. 首先嘛,你得在浏览器里输入要网址:</span></strong></p> <p><br></p> <p style="text-align: center;"><img class="" data-ratio="0.20034246575342465" data-s="300,640" src="/upload/34721596ff0727bfb7811793d5153d25.null" data-type="png" data-w="1168" style=""></p> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">2. 浏览器查找域名的IP地址</span></strong></p> <p><br></p> <p style="text-align: center;"><img class="" data-ratio="0.340625" data-s="300,640" src="/upload/4b87ed259f52e007a3d0121bc346bb69.null" data-type="png" data-w="640" style="width: 249px;height: 85px;"></p> <p><br></p> <p>导航的第一步是通过访问的域名找出其IP地址。DNS查找过程如下:</p> <p><br></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><strong>浏览器缓存</strong> – 浏览器会缓存DNS记录一段时间。 有趣的是,操作系统没有告诉浏览器储存DNS记录的时间,这样不同浏览器会储存个自固定的一个时间(2分钟到30分钟不等)。</p></li> <li><p><strong>系统缓存</strong> – 如果在浏览器缓存里没有找到需要的记录,浏览器会做一个系统调用(windows里是gethostbyname)。这样便可获得系统缓存中的记录。</p></li> <li><p>路由器缓存 – 接着,前面的查询请求发向路由器,它一般会有自己的DNS缓存。</p></li> <li><p><strong>ISP DNS 缓存</strong> – 接下来要check的就是ISP缓存DNS的服务器。在这一般都能找到相应的缓存记录。</p></li> <li><p><strong>递归搜索</strong> – 你的ISP的DNS服务器从跟域名服务器开始进行递归搜索,从.com顶级域名服务器到Facebook的域名服务器。一般DNS服务器的缓存中会有.com域名服务器中的域名,所以到顶级服务器的匹配过程不是那么必要了。</p></li> </ul> <p><br></p> <p>DNS递归查找如下图所示:</p> <p><br></p> <p style="text-align: center;"><img class="" data-ratio="0.25609756097560976" data-s="300,640" src="/upload/cce84fc85827bb387bdce0277eaef195.null" data-type="png" data-w="1148" style=""></p> <p><br></p> <p>DNS有一点令人担忧,这就是像wikipedia.org 或者 facebook.com这样的整个域名看上去只是对应一个单独的IP地址。还好,有几种方法可以消除这个瓶颈:</p> <p><br></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p>循环 DNS 是DNS查找时返回多个IP时的解决方案。举例来说,Facebook.com实际上就对应了四个IP地址。</p></li> <li><p>负载平衡器 是以一个特定IP地址进行侦听并将网络请求转发到集群服务器上的硬件设备。 一些大型的站点一般都会使用这种昂贵的高性能负载平衡器。</p></li> <li><p>地理 DNS 根据用户所处的地理位置,通过把域名映射到多个不同的IP地址提高可扩展性。这样不同的服务器不能够更新同步状态,但映射静态内容的话非常好。</p></li> <li><p>Anycast 是一个IP地址映射多个物理主机的路由技术。 美中不足,Anycast与TCP协议适应的不是很好,所以很少应用在那些方案中。</p></li> </ul> <p><br></p> <p>大多数DNS服务器使用Anycast来获得高效低延迟的DNS查找。</p> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">3. 浏览器给web服务器发送一个HTTP请求</span></strong></p> <p><br></p> <p style="text-align: center;"><img class="" data-ratio="0.3283582089552239" data-s="300,640" src="/upload/b0efde88c1348885fd8cf5d820324702.null" data-type="png" data-w="670" style="width: 245px;height: 80px;"></p> <p><br></p> <p>因为像Facebook主页这样的动态页面,打开后在浏览器缓存中很快甚至马上就会过期,毫无疑问他们不能从中读取。</p> <p><br></p> <p>所以,浏览器将把一下请求发送到Facebook所在的服务器:</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">GET http://facebook.com/ HTTP/1.1</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Accept: application/x-ms-application, image/jpeg, application/xaml+xml, [...]</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; [...]</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Accept-Encoding: gzip, deflate</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Connection: Keep-Alive</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Host: facebook.com</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Cookie: datr=1265876274-[...]; locale=en_US; lsd=WW[...]; c_user=2101[...]</span></p> </blockquote> <p><br></p> <p>GET 这个请求定义了要读取的URL: “http://facebook.com/”。 浏览器自身定义 (User-Agent 头), 和它希望接受什么类型的相应 (Accept and Accept-Encoding 头). Connection头要求服务器为了后边的请求不要关闭TCP连接。</p> <p><br></p> <p>请求中也包含浏览器存储的该域名的cookies。可能你已经知道,在不同页面请求当中,cookies是与跟踪一个网站状态相匹配的键值。这样cookies会存储登录用户名,服务器分配的密码和一些用户设置等。Cookies会以文本文档形式存储在客户机里,每次请求时发送给服务器。</p> <p><br></p> <p>用来看原始HTTP请求及其相应的工具很多。作者比较喜欢使用fiddler,当然也有像FireBug这样其他的工具。这些软件在网站优化时会帮上很大忙。</p> <p><br></p> <p>除了获取请求,还有一种是发送请求,它常在提交表单用到。发送请求通过URL传递其参数(e.g.: http://robozzle.com/puzzle.aspx?id=85)。发送请求在请求正文头之后发送其参数。</p> <p><br></p> <p>像“http://facebook.com/”中的斜杠是至关重要的。这种情况下,浏览器能安全的添加斜杠。而像“http: //example.com/folderOrFile”这样的地址,因为浏览器不清楚folderOrFile到底是文件夹还是文件,所以不能自动添加 斜杠。这时,浏览器就不加斜杠直接访问地址,服务器会响应一个重定向,结果造成一次不必要的握手。 </p> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">4. facebook服务的永久重定向响应</span></strong></p> <p><br></p> <p style="text-align: center;"><img class="" data-ratio="0.29780564263322884" data-s="300,640" src="/upload/1585ac8638d8c1bd1efee60f9838b3a5.null" data-type="png" data-w="638" style="width: 248px;height: 74px;"></p> <p><br></p> <p>图中所示为Facebook服务器发回给浏览器的响应:</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">HTTP/1.1 301 Moved Permanently</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Cache-Control: private, no-store, no-cache, must-revalidate, post-check=0,</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> pre-check=0</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Expires: Sat, 01 Jan 2000 00:00:00 GMT</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Location: http://www.facebook.com/</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> P3P: CP="DSP LAW"</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Pragma: no-cache</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Set-Cookie: made_write_conn=deleted; expires=Thu, 12-Feb-2009 05:09:50 GMT;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> path=/; domain=.facebook.com; httponly</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Content-Type: text/html; charset=utf-8</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> X-Cnection: close</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Date: Fri, 12 Feb 2010 05:09:51 GMT</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Content-Length: 0</span></p> </blockquote> <p><br></p> <p>服务器给浏览器响应一个301永久重定向响应,这样浏览器就会访问“http://www.facebook.com/” 而非“http://facebook.com/”。</p> <p><br></p> <p>为什么服务器一定要重定向而不是直接发会用户想看的网页内容呢?这个问题有好多有意思的答案。</p> <p><br></p> <p>其中一个原因跟搜索引擎排名有 关。你看,如果一个页面有两个地址,就像http://www.igoro.com/ 和http://igoro.com/,搜索引擎会认为它们是两个网站,结果造成每一个的搜索链接都减少从而降低排名。而搜索引擎知道301永久重定向是 什么意思,这样就会把访问带www的和不带www的地址归到同一个网站排名下。</p> <p><br></p> <p>还有一个是用不同的地址会造成缓存友好性变差。当一个页面有好几个名字时,它可能会在缓存里出现好几次。</p> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">5. 浏览器跟踪重定向地址</span></strong></p> <p><br></p> <p style="text-align: center;"><img class="" data-ratio="0.33653846153846156" data-s="300,640" src="/upload/a0fded96d6772746d3de79b7e5b6be55.null" data-type="png" data-w="624" style="width: 278px;height: 94px;"></p> <p><br></p> <p>现在,浏览器知道了“http://www.facebook.com/”才是要访问的正确地址,所以它会发送另一个获取请求:</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">GET http://www.facebook.com/ HTTP/1.1</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Accept: application/x-ms-application, image/jpeg, application/xaml+xml, [...]</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Accept-Language: en-US</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; [...]</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Accept-Encoding: gzip, deflate</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Connection: Keep-Alive</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Cookie: lsd=XW[...]; c_user=21[...]; x-referer=[...]</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Host: www.facebook.com</span></p> </blockquote> <p><br></p> <p>头信息以之前请求中的意义相同。</p> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">6. 服务器“处理”请求</span></strong></p> <p><br></p> <p style="text-align: center;"><img class="" data-ratio="0.46111111111111114" data-s="300,640" src="/upload/19db4dbc6d6976a9a515b2c79a8e6e65.null" data-type="png" data-w="360" style="width: 179px;height: 83px;"></p> <p><br></p> <p>服务器接收到获取请求,然后处理并返回一个响应。</p> <p><br></p> <p>这表面上看起来是一个顺向的任务,但其实这中间发生了很多有意思的东西- 就像作者博客这样简单的网站,何况像facebook那样访问量大的网站呢!</p> <p><br></p> <p><strong>Web 服务器软件</strong></p> <p><br></p> <p>web服务器软件(像IIS和阿帕奇)接收到HTTP请求,然后确定执行什么请求处理来处理它。请求处理就是一个能够读懂请求并且能生成HTML来进行响应的程序(像ASP.NET,PHP,RUBY...)。</p> <p><br></p> <p>举 个最简单的例子,需求处理可以以映射网站地址结构的文件层次存储。像http://example.com/folder1/page1.aspx这个地 址会映射/httpdocs/folder1/page1.aspx这个文件。web服务器软件可以设置成为地址人工的对应请求处理,这样 page1.aspx的发布地址就可以是http://example.com/folder1/page1。</p> <p><br></p> <p><strong>请求处理</strong></p> <p><br></p> <p>请求处理阅读请求及它的参数和cookies。它会读取也可能更新一些数据,并讲数据存储在服务器上。然后,需求处理会生成一个HTML响应。</p> <p><br></p> <p>所有动态网站都面临一个有意思的难点 -如何存储数据。小网站一半都会有一个SQL数据库来存储数据,存储大量数据和/或访问量大的网站不得不找一些办法把数据库分配到多台机器上。解决方案 有:sharding (基于主键值讲数据表分散到多个数据库中),复制,利用弱语义一致性的简化数据库。</p> <p><br></p> <p>委托工作给批处理是一个廉价保持数据更新的技术。举例来讲,Fackbook得及时更新新闻feed,但数据支持下的“你可能认识的人”功能只需要每晚更新 (作者猜测是这样的,改功能如何完善不得而知)。批处理作业更新会导致一些不太重要的数据陈旧,但能使数据更新耕作更快更简洁。</p> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">7. 服务器发回一个HTML响应</span></strong></p> <p><br></p> <p style="text-align: center;"><img class="" data-ratio="0.3037974683544304" data-s="300,640" src="/upload/2337dd0b3bf50cf306234bbbb3cbf86c.null" data-type="png" data-w="632" style="width: 274px;height: 83px;"></p> <p><br></p> <p>图中为服务器生成并返回的响应:</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">HTTP/1.1 200 OK</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Cache-Control: private, no-store, no-cache, must-revalidate, post-check=0,</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> pre-check=0</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Expires: Sat, 01 Jan 2000 00:00:00 GMT</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> P3P: CP="DSP LAW"</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Pragma: no-cache</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Content-Encoding: gzip</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Content-Type: text/html; charset=utf-8</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> X-Cnection: close</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Transfer-Encoding: chunked</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> Date: Fri, 12 Feb 2010 09:05:55 GMT</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> </span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> 2b3Tn@[...]</span></p> </blockquote> <p><br></p> <p>整个响应大小为35kB,其中大部分在整理后以blob类型传输。</p> <p><br></p> <p>内容编码头告诉浏览器整个响应体用gzip算法进行压缩。解压blob块后,你可以看到如下期望的HTML:</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" </span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"></span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> lang="en" id="facebook" class=" no_js"></span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> <head></span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> <meta http-equiv="Content-type" content="text/html; charset=utf-8" /></span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> <meta http-equiv="Content-language" content="en" /></span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);"> ...</span></p> </blockquote> <p><br></p> <p>关于压缩,头信息说明了是否缓存这个页面,如果缓存的话如何去做,有什么cookies要去设置(前面这个响应里没有这点)和隐私信息等等。</p> <p><br></p> <p>请注意报头中把Content-type设置为“text/html”。报头让浏览器将该响应内容以HTML形式呈现,而不是以文件形式下载它。浏览器会根据报头信息决定如何解释该响应,不过同时也会考虑像URL扩展内容等其他因素。</p> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">8. 浏览器开始显示HTML</span></strong></p> <p><br></p> <p>在浏览器没有完整接受全部HTML文档时,它就已经开始显示这个页面了:</p> <p><br></p> <p style="text-align: center;"><img class="" data-ratio="0.42296918767507" data-s="300,640" src="/upload/d2443ea26f169b9a262c6fe456387a92.null" data-type="png" data-w="714" style="width: 427px;height: 181px;"></p> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">9. 浏览器发送获取嵌入在HTML中的对象</span></strong></p> <p><br></p> <p style="text-align: center;"><img class="" data-ratio="0.45364238410596025" data-s="300,640" src="/upload/918f62a2b5fad46e138841d350a426f4.null" data-type="png" data-w="604" style="width: 276px;height: 125px;"></p> <p><br></p> <p>在浏览器显示HTML时,它会注意到需要获取其他地址内容的标签。这时,浏览器会发送一个获取请求来重新获得这些文件。</p> <p><br></p> <p>下面是几个我们访问facebook.com时需要重获取的几个URL:</p> <p><br></p> <p><strong>图片</strong></p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">http://static.ak.fbcdn.net/rsrc.php/z12E0/hash/8q2anwu7.gif</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">http://static.ak.fbcdn.net/rsrc.php/zBS5C/hash/7hwy7at6.gif</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">…</span></p> </blockquote> <p><br></p> <p><strong>CSS 式样表</strong></p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">http://static.ak.fbcdn.net/rsrc.php/z448Z/hash/2plh8s4n.css</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">http://static.ak.fbcdn.net/rsrc.php/zANE1/hash/cvtutcee.css</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">…</span></p> </blockquote> <p><br></p> <p><strong>JavaScript 文件</strong></p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">http://static.ak.fbcdn.net/rsrc.php/zEMOA/hash/c8yzb6ub.js</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">http://static.ak.fbcdn.net/rsrc.php/z6R9L/hash/cq2lgbs8.js</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">…</span></p> </blockquote> <p><br></p> <p>这些地址都要经历一个和HTML读取类似的过程。所以浏览器会在DNS中查找这些域名,发送请求,重定向等等...</p> <p><br></p> <p>但 不像动态页面那样,静态文件会允许浏览器对其进行缓存。有的文件可能会不需要与服务器通讯,而从缓存中直接读取。服务器的响应中包含了静态文件保存的期限 信息,所以浏览器知道要把它们缓存多长时间。还有,每个响应都可能包含像版本号一样工作的ETag头(被请求变量的实体值),如果浏览器观察到文件的版本 ETag信息已经存在,就马上停止这个文件的传输。</p> <p><br></p> <p>试着猜猜看“fbcdn.net”在地址中代表什么?聪明的答案是"Facebook内容分发网络"。Facebook利用内容分发网络(CDN)分发像图片,CSS表和JavaScript文件这些静态文件。所以,这些文件会在全球很多CDN的数据中心中留下备份。</p> <p><br></p> <p>静态内容往往代表站点的带宽大小,也能通过CDN轻松的复制。通常网站会使用第三方的CDN。例如,Facebook的静态文件由最大的CDN提供商Akamai来托管。</p> <p><br></p> <p>举例来讲,当你试着ping static.ak.fbcdn.net的时候,可能会从某个akamai.net服务器上获得响应。有意思的是,当你同样再ping一次的时候,响应的服务器可能就不一样,这说明幕后的负载平衡开始起作用了。</p> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">10. 浏览器发送异步(AJAX)请求</span></strong></p> <p><br></p> <p style="text-align: center;"><img class="" data-ratio="0.42295081967213116" data-s="300,640" src="/upload/4d83b8d733059a274e4780d0aaab5604.null" data-type="png" data-w="610" style="width: 257px;height: 109px;"></p> <p><br></p> <p>在Web 2.0伟大精神的指引下,页面显示完成后客户端仍与服务器端保持着联系。</p> <p><br></p> <p>以 Facebook聊天功能为例,它会持续与服务器保持联系来及时更新你那些亮亮灰灰的好友状态。为了更新这些头像亮着的好友状态,在浏览器中执行的 JavaScript代码会给服务器发送异步请求。这个异步请求发送给特定的地址,它是一个按照程式构造的获取或发送请求。还是在Facebook这个例 子中,客户端发送给http://www.facebook.com/ajax/chat/buddy_list.php一个发布请求来获取你好友里哪个 在线的状态信息。</p> <p><br></p> <p>提起这个模式,就必须要讲讲"AJAX"-- “异步JavaScript 和 XML”,虽然服务器为什么用XML格式来进行响应也没有个一清二白的原因。再举个例子吧,对于异步请求,Facebook会返回一些JavaScript的代码片段。</p> <p><br></p> <p>除了其他,fiddler这个工具能够让你看到浏览器发送的异步请求。事实上,你不仅可以被动的做为这些请求的看客,还能主动出击修改和重新发送它们。AJAX请求这么容易被蒙,可着实让那些计分的在线游戏开发者们郁闷的了。(当然,可别那样骗人家~)</p> <p><br></p> <p>Facebook聊天功能提供了关于AJAX一个有意思的问题案例:把数据从服务器端推送到客户端。因为HTTP是一个请求-响应协议,所以聊天服务器不能把新消息发给客户。取而代之的是客户端不得不隔几秒就轮询下服务器端看自己有没有新消息。</p> <p><br></p> <p>这些情况发生时长轮询是个减轻服务器负载挺有趣的技术。如果当被轮询时服务器没有新消息,它就不理这个客户端。而当尚未超时的情况下收到了该客户的新消息,服务器就会找到未完成的请求,把新消息做为响应返回给客户端。</p> <p><br></p> <p><strong><span style="color: rgb(255, 76, 65);">总结一下</span></strong></p> <p><br></p> <p>希望看了本文,你能明白不同的网络模块是如何协同工作的</p> <section class="" powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box;color: rgb(51, 51, 51);font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;text-align: justify;white-space: normal;background-color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <section class="" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;box-sizing: border-box;text-align: left;overflow-wrap: break-word !important;"> <section class="" style="padding: 10px;max-width: 100%;box-sizing: border-box;display: inline-block;width: 668px;border-width: 1px;border-style: solid;border-color: rgb(226, 226, 226);box-shadow: rgb(226, 226, 226) 0px 16px 1px -13px;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 class="" style="max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;"> <section class="" style="max-width: 100%;box-sizing: border-box;color: rgb(93, 93, 93);overflow-wrap: break-word !important;"> <p class="" style="max-width: 100%;box-sizing: border-box;min-height: 1em;font-size: 13px;overflow-wrap: break-word !important;"><br></p> </section> </section> </section> </section> </section> </section>
作者:微信小助手
<h1 style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="color: rgb(64, 118, 0);"><strong><span style="font-size: 14px;">1. 概述</span></strong></span></h1> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">本文以淘宝作为例子,介绍从一百个并发到千万级并发情况下服务端的架构的演进过程,同时列举出每个演进阶段会遇到的相关技术,让大家对架构的演进有一个整体的认知,文章最后汇总了一些架构设计的原则。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <h1 style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="color: rgb(64, 118, 0);"><strong><span style="font-size: 14px;">2. 基本概念</span></strong></span></h1> <p><span style="color: rgb(64, 118, 0);"><strong><span style="font-size: 14px;"><br></span></strong></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">在介绍架构之前,为了避免部分读者对架构设计中的一些概念不了解,下面对几个最基础的概念进行介绍:</span></p> <ul class=" list-paddingleft-2" style="margin-left: 8px;margin-right: 8px;"> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;"><strong>分布式</strong><br>系统中的多个模块在不同服务器上部署,即可称为分布式系统,如Tomcat和数据库分别部署在不同的服务器上,或两个相同功能的Tomcat分别部署在不同服务器上</span></p></li> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;"><strong>高可用</strong><br>系统中部分节点失效时,其他节点能够接替它继续提供服务,则可认为系统具有高可用性</span></p></li> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;"><strong>集群</strong><br>一个特定领域的软件部署在多台服务器上并作为一个整体提供一类服务,这个整体称为集群。如Zookeeper中的Master和Slave分别部署在多台服务器上,共同组成一个整体提供集中配置服务。在常见的集群中,客户端往往能够连接任意一个节点获得服务,并且当集群中一个节点掉线时,其他节点往往能够自动的接替它继续提供服务,这时候说明集群具有高可用性</span></p></li> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;"><strong>负载均衡</strong><br>请求发送到系统时,通过某些方式把请求均匀分发到多个节点上,使系统中每个节点能够均匀的处理请求负载,则可认为系统是负载均衡的</span></p></li> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;"><strong>正向代理和反向代理</strong><br>系统内部要访问外部网络时,统一通过一个代理服务器把请求转发出去,在外部网络看来就是代理服务器发起的访问,此时代理服务器实现的是正向代理;当外部请求进入系统时,代理服务器把该请求转发到系统中的某台服务器上,对外部请求来说,与之交互的只有代理服务器,此时代理服务器实现的是反向代理。简单来说,正向代理是代理服务器代替系统内部来访问外部网络的过程,反向代理是外部请求访问系统时通过代理服务器转发到内部服务器的过程。</span></p></li> </ul> <h1 style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="color: rgb(64, 118, 0);"><strong><span style="font-size: 14px;"><br></span></strong></span></h1> <h1 style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="color: rgb(64, 118, 0);"><strong><span style="font-size: 14px;">3. 架构演进</span></strong></span></h1> <p><span style="color: rgb(64, 118, 0);"><strong><span style="font-size: 14px;"><br></span></strong></span></p> <h2 style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><strong><span style="font-size: 14px;">3.1 单机架构</span></strong></h2> <p><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;text-align: center;"><span style="font-size: 14px;"><img class="" src="/upload/b8d823769b9cdb661b8922208ddf7243.png" data-type="png" style="cursor: pointer;display: inline;" title="clipboard.png" data-ratio="0.3626943005181347" data-w="579"></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">以淘宝作为例子。在网站最初时,应用数量与用户数都较少,可以把Tomcat和数据库部署在同一台服务器上。浏览器往www.taobao.com发起请求时,首先经过DNS服务器(域名系统)把域名转换为实际IP地址10.102.4.1,浏览器转而访问该IP对应的Tomcat。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong><br></strong></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong>随着用户数的增长,Tomcat和数据库之间竞争资源,单机性能不足以支撑业务</strong></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong><br></strong></span></p> <h2 style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><strong><span style="font-size: 14px;">3.2 第一次演进:Tomcat与数据库分开部署</span></strong></h2> <p><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;text-align: center;"><span style="font-size: 14px;"><img class="" src="/upload/29f4c7009a3062d9feeeab2abc55f50d.png" data-type="png" style="cursor: pointer;display: inline;" title="clipboard.png" data-ratio="0.35628227194492257" data-w="581"></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">Tomcat和数据库分别独占服务器资源,显著提高两者各自性能。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong>随着用户数的增长,并发读写数据库成为瓶颈</strong></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong><br></strong></span></p> <h2 style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><strong><span style="font-size: 14px;">3.3 第二次演进:引入本地缓存和分布式缓存</span></strong></h2> <p><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;text-align: center;"><span style="font-size: 14px;"><img class="" src="/upload/133926563349a45899a93b995fae6d06.png" data-type="png" style="cursor: pointer;display: inline;" title="clipboard.png" data-ratio="0.5524956970740104" data-w="581"></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">在Tomcat同服务器上或同JVM中增加本地缓存,并在外部增加分布式缓存,缓存热门商品信息或热门商品的html页面等。通过缓存能把绝大多数请求在读写数据库前拦截掉,大大降低数据库压力。其中涉及的技术包括:使用memcached作为本地缓存,使用Redis作为分布式缓存,还会涉及缓存一致性、缓存穿透/击穿、缓存雪崩、热点数据集中失效等问题。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong>缓存抗住了大部分的访问请求,随着用户数的增长,并发压力主要落在单机的Tomcat上,响应逐渐变慢</strong></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong><br></strong></span></p> <h2 style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><strong><span style="font-size: 14px;">3.4 第三次演进:引入反向代理实现负载均衡</span></strong></h2> <p><strong><span style="font-size: 14px;"><br></span></strong></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;text-align: center;"><span style="font-size: 14px;"><img class="" src="/upload/ecda9be97c64bf2f483eeb51324e1004.png" data-type="png" style="cursor: pointer;display: inline;" title="clipboard.png" data-ratio="0.8229426433915212" data-w="401"></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">在多台服务器上分别部署Tomcat,使用反向代理软件(Nginx)把请求均匀分发到每个Tomcat中。此处假设Tomcat最多支持100个并发,Nginx最多支持50000个并发,那么理论上Nginx把请求分发到500个Tomcat上,就能抗住50000个并发。其中涉及的技术包括:Nginx、HAProxy,两者都是工作在网络第七层的反向代理软件,主要支持http协议,还会涉及session共享、文件上传下载的问题。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong>反向代理使应用服务器可支持的并发量大大增加,但并发量的增长也意味着更多请求穿透到数据库,单机的数据库最终成为瓶颈</strong></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong><br></strong></span></p> <h2 style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><strong><span style="font-size: 14px;">3.5 第四次演进:数据库读写分离</span></strong></h2> <p><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;text-align: center;"><span style="font-size: 14px;"><img class="" src="/upload/8924548f309eb01e1adfdc6ada752dca.png" data-type="png" style="cursor: pointer;display: inline;" title="clipboard.png" data-ratio="0.6861826697892272" data-w="427"></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">把数据库划分为读库和写库,读库可以有多个,通过同步机制把写库的数据同步到读库,对于需要查询最新写入数据场景,可通过在缓存中多写一份,通过缓存获得最新数据。其中涉及的技术包括:Mycat,它是数据库中间件,可通过它来组织数据库的分离读写和分库分表,客户端通过它来访问下层数据库,还会涉及数据同步,数据一致性的问题。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong>业务逐渐变多,不同业务之间的访问量差距较大,不同业务直接竞争数据库,相互影响性能</strong></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong><br></strong></span></p> <h2 style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><strong><span style="font-size: 14px;">3.6 第五次演进:数据库按业务分库</span></strong></h2> <p><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><img class="" src="/upload/1abebdecb82aac5f22915c27c85feeef.png" data-type="png" style="cursor: pointer;display: inline;" title="clipboard.png" data-ratio="0.8230912476722533" data-w="537"></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">把不同业务的数据保存到不同的数据库中,使业务之间的资源竞争降低,对于访问量大的业务,可以部署更多的服务器来支撑。这样同时导致跨业务的表无法直接做关联分析,需要通过其他途径来解决,但这不是本文讨论的重点,有兴趣的可以自行搜索解决方案。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong>随着用户数的增长,单机的写库会逐渐会达到性能瓶颈</strong></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong><br></strong></span></p> <h2 style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><strong><span style="font-size: 14px;">3.7 第六次演进:把大表拆分为小表</span></strong></h2> <p><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><img class="" src="/upload/5e4ecd693a65b39632ad22d0f34d191b.png" data-type="png" style="cursor: pointer;display: inline;" title="clipboard.png" data-ratio="0.7568493150684932" data-w="584"></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">比如针对评论数据,可按照商品ID进行hash,路由到对应的表中存储;针对支付记录,可按照小时创建表,每个小时表继续拆分为小表,使用用户ID或记录编号来路由数据。只要实时操作的表数据量足够小,请求能够足够均匀的分发到多台服务器上的小表,那数据库就能通过水平扩展的方式来提高性能。其中前面提到的Mycat也支持在大表拆分为小表情况下的访问控制。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">这种做法显著的增加了数据库运维的难度,对DBA的要求较高。数据库设计到这种结构时,已经可以称为分布式数据库,但是这只是一个逻辑的数据库整体,数据库里不同的组成部分是由不同的组件单独来实现的,如分库分表的管理和请求分发,由Mycat实现,SQL的解析由单机的数据库实现,读写分离可能由网关和消息队列来实现,查询结果的汇总可能由数据库接口层来实现等等,这种架构其实是MPP(大规模并行处理)架构的一类实现。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">目前开源和商用都已经有不少MPP数据库,开源中比较流行的有Greenplum、TiDB、Postgresql XC、HAWQ等,商用的如南大通用的GBase、睿帆科技的雪球DB、华为的LibrA等等,不同的MPP数据库的侧重点也不一样,如TiDB更侧重于分布式OLTP场景,Greenplum更侧重于分布式OLAP场景,这些MPP数据库基本都提供了类似Postgresql、Oracle、MySQL那样的SQL标准支持能力,能把一个查询解析为分布式的执行计划分发到每台机器上并行执行,最终由数据库本身汇总数据进行返回,也提供了诸如权限管理、分库分表、事务、数据副本等能力,并且大多能够支持100个节点以上的集群,大大降低了数据库运维的成本,并且使数据库也能够实现水平扩展。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong>数据库和Tomcat都能够水平扩展,可支撑的并发大幅提高,随着用户数的增长,最终单机的Nginx会成为瓶颈</strong></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong><br></strong></span></p> <h2 style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><strong><span style="font-size: 14px;">3.8 第七次演进:使用LVS或F5来使多个Nginx负载均衡</span></strong></h2> <p><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><img class="" src="/upload/e1a649f549f0c630ff48ffc7e52d8435.png" data-type="png" style="cursor: pointer;display: inline;" title="clipboard.png" data-ratio="0.8743633276740238" data-w="589"></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">由于瓶颈在Nginx,因此无法通过两层的Nginx来实现多个Nginx的负载均衡。图中的LVS和F5是工作在网络第四层的负载均衡解决方案,其中LVS是软件,运行在操作系统内核态,可对TCP请求或更高层级的网络协议进行转发,因此支持的协议更丰富,并且性能也远高于Nginx,可假设单机的LVS可支持几十万个并发的请求转发;F5是一种负载均衡硬件,与LVS提供的能力类似,性能比LVS更高,但价格昂贵。由于LVS是单机版的软件,若LVS所在服务器宕机则会导致整个后端系统都无法访问,因此需要有备用节点。可使用keepalived软件模拟出虚拟IP,然后把虚拟IP绑定到多台LVS服务器上,浏览器访问虚拟IP时,会被路由器重定向到真实的LVS服务器,当主LVS服务器宕机时,keepalived软件会自动更新路由器中的路由表,把虚拟IP重定向到另外一台正常的LVS服务器,从而达到LVS服务器高可用的效果。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">此处需要注意的是,上图中从Nginx层到Tomcat层这样画并不代表全部Nginx都转发请求到全部的Tomcat,在实际使用时,可能会是几个Nginx下面接一部分的Tomcat,这些Nginx之间通过keepalived实现高可用,其他的Nginx接另外的Tomcat,这样可接入的Tomcat数量就能成倍的增加。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong>由于LVS也是单机的,随着并发数增长到几十万时,LVS服务器最终会达到瓶颈,此时用户数达到千万甚至上亿级别,用户分布在不同的地区,与服务器机房距离不同,导致了访问的延迟会明显不同。</strong></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong><br></strong></span></p> <h2 style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><strong><span style="font-size: 14px;">3.9 第八次演进:通过DNS轮询实现机房间的负载均衡</span></strong></h2> <p><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;text-align: center;"><span style="font-size: 14px;"><img class="" src="/upload/a6149ca7099ee7f976b4530991215c34.png" data-type="png" style="cursor: pointer;display: inline;" title="clipboard.png" data-ratio="0.711436170212766" data-w="752"></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">在DNS服务器中可配置一个域名对应多个IP地址,每个IP地址对应到不同的机房里的虚拟IP。当用户访问www.taobao.com时,DNS服务器会使用轮询策略或其他策略,来选择某个IP供用户访问。此方式能实现机房间的负载均衡,至此,系统可做到机房级别的水平扩展,千万级到亿级的并发量都可通过增加机房来解决,系统入口处的请求并发量不再是问题。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong>随着数据的丰富程度和业务的发展,检索、分析等需求越来越丰富,单单依靠数据库无法解决如此丰富的需求</strong></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong><br></strong></span></p> <h2 style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><strong><span style="font-size: 14px;">3.10 第九次演进:引入NoSQL数据库和搜索引擎等技术</span></strong></h2> <p><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;text-align: center;"><span style="font-size: 14px;"><img class="" src="/upload/559b610c515377ba06d466e5a74acfba.png" data-type="png" style="cursor: pointer;display: inline;" title="clipboard.png" data-ratio="0.6467153284671533" data-w="685"></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">当数据库中的数据多到一定规模时,数据库就不适用于复杂的查询了,往往只能满足普通查询的场景。对于统计报表场景,在数据量大时不一定能跑出结果,而且在跑复杂查询时会导致其他查询变慢,对于全文检索、可变数据结构等场景,数据库天生不适用。因此需要针对特定的场景,引入合适的解决方案。如对于海量文件存储,可通过分布式文件系统HDFS解决,对于key value类型的数据,可通过HBase和Redis等方案解决,对于全文检索场景,可通过搜索引擎如ElasticSearch解决,对于多维分析场景,可通过Kylin或Druid等方案解决。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">当然,引入更多组件同时会提高系统的复杂度,不同的组件保存的数据需要同步,需要考虑一致性的问题,需要有更多的运维手段来管理这些组件等。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong>引入更多组件解决了丰富的需求,业务维度能够极大扩充,随之而来的是一个应用中包含了太多的业务代码,业务的升级迭代变得困难</strong></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong><br></strong></span></p> <h2 style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><strong><span style="font-size: 14px;">3.11 第十次演进:大应用拆分为小应用</span></strong></h2> <p><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;text-align: center;"><span style="font-size: 14px;"><img class="" src="/upload/c2f7a4e9233501a4848f6f6054567a56.png" data-type="png" style="cursor: pointer;display: inline;" title="clipboard.png" data-ratio="0.6622998544395924" data-w="687"></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">按照业务板块来划分应用代码,使单个应用的职责更清晰,相互之间可以做到独立升级迭代。这时候应用之间可能会涉及到一些公共配置,可以通过分布式配置中心Zookeeper来解决。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong>不同应用之间存在共用的模块,由应用单独管理会导致相同代码存在多份,导致公共功能升级时全部应用代码都要跟着升级</strong></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong><br></strong></span></p> <h2 style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><strong><span style="font-size: 14px;">3.12 第十一次演进:复用的功能抽离成微服务</span></strong></h2> <p><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;text-align: center;"><span style="font-size: 14px;"><img class="" src="/upload/98f9dad8c5b98aba22865095ea2ea032.png" data-type="png" style="cursor: pointer;display: inline;" title="clipboard.png" data-ratio="0.7859237536656891" data-w="682"></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">如用户管理、订单、支付、鉴权等功能在多个应用中都存在,那么可以把这些功能的代码单独抽取出来形成一个单独的服务来管理,这样的服务就是所谓的微服务,应用和服务之间通过HTTP、TCP或RPC请求等多种方式来访问公共服务,每个单独的服务都可以由单独的团队来管理。此外,可以通过Dubbo、SpringCloud等框架实现服务治理、限流、熔断、降级等功能,提高服务的稳定性和可用性。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong>不同服务的接口访问方式不同,应用代码需要适配多种访问方式才能使用服务,此外,应用访问服务,服务之间也可能相互访问,调用链将会变得非常复杂,逻辑变得混乱</strong></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong><br></strong></span></p> <h2 style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><strong><span style="font-size: 14px;">3.13 第十二次演进:引入企业服务总线ESB屏蔽服务接口的访问差异</span></strong></h2> <p><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;text-align: center;"><span style="font-size: 14px;"><img class="" src="/upload/f4375f65e0efd4e4add3582316d5ca6b.png" data-type="png" style="cursor: pointer;display: inline;" title="clipboard.png" data-ratio="0.8556998556998557" data-w="693"></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">通过ESB统一进行访问协议转换,应用统一通过ESB来访问后端服务,服务与服务之间也通过ESB来相互调用,以此降低系统的耦合程度。这种单个应用拆分为多个应用,公共服务单独抽取出来来管理,并使用企业消息总线来解除服务之间耦合问题的架构,就是所谓的SOA(面向服务)架构,这种架构与微服务架构容易混淆,因为表现形式十分相似。个人理解,微服务架构更多是指把系统里的公共服务抽取出来单独运维管理的思想,而SOA架构则是指一种拆分服务并使服务接口访问变得统一的架构思想,SOA架构中包含了微服务的思想。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong>业务不断发展,应用和服务都会不断变多,应用和服务的部署变得复杂,同一台服务器上部署多个服务还要解决运行环境冲突的问题,此外,对于如大促这类需要动态扩缩容的场景,需要水平扩展服务的性能,就需要在新增的服务上准备运行环境,部署服务等,运维将变得十分困难</strong></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong><br></strong></span></p> <h2 style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><strong><span style="font-size: 14px;">3.14 第十三次演进:引入容器化技术实现运行环境隔离与动态服务管理</span></strong></h2> <p><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;text-align: center;"><span style="font-size: 14px;"><img class="" src="/upload/58e7caf953fad9e924478fde2b52865.png" data-type="png" style="cursor: pointer;display: inline;" title="clipboard.png" data-ratio="0.951937984496124" data-w="645"></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">目前最流行的容器化技术是Docker,最流行的容器管理服务是Kubernetes(K8S),应用/服务可以打包为Docker镜像,通过K8S来动态分发和部署镜像。Docker镜像可理解为一个能运行你的应用/服务的最小的操作系统,里面放着应用/服务的运行代码,运行环境根据实际的需要设置好。把整个“操作系统”打包为一个镜像后,就可以分发到需要部署相关服务的机器上,直接启动Docker镜像就可以把服务起起来,使服务的部署和运维变得简单。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">在大促的之前,可以在现有的机器集群上划分出服务器来启动Docker镜像,增强服务的性能,大促过后就可以关闭镜像,对机器上的其他服务不造成影响(在3.14节之前,服务运行在新增机器上需要修改系统配置来适配服务,这会导致机器上其他服务需要的运行环境被破坏)。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong>使用容器化技术后服务动态扩缩容问题得以解决,但是机器还是需要公司自身来管理,在非大促的时候,还是需要闲置着大量的机器资源来应对大促,机器自身成本和运维成本都极高,资源利用率低</strong></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong><br></strong></span></p> <h2 style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><strong><span style="font-size: 14px;">3.15 第十四次演进:以云平台承载系统</span></strong></h2> <p><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;text-align: center;"><span style="font-size: 14px;"><img class="" src="/upload/2575102bcc964096fe42806ff02afadb.png" data-type="png" style="cursor: pointer;display: inline;" title="clipboard.png" data-ratio="0.59625" data-w="800"></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">系统可部署到公有云上,利用公有云的海量机器资源,解决动态硬件资源的问题,在大促的时间段里,在云平台中临时申请更多的资源,结合Docker和K8S来快速部署服务,在大促结束后释放资源,真正做到按需付费,资源利用率大大提高,同时大大降低了运维成本。</span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;">所谓的云平台,就是把海量机器资源,通过统一的资源管理,抽象为一个资源整体,在之上可按需动态申请硬件资源(如CPU、内存、网络等),并且之上提供通用的操作系统,提供常用的技术组件(如Hadoop技术栈,MPP数据库等)供用户使用,甚至提供开发好的应用,用户不需要关系应用内部使用了什么技术,就能够解决需求(如音视频转码服务、邮件服务、个人博客等)。在云平台中会涉及如下几个概念:</span></p> <ul class=" list-paddingleft-2" style="margin-left: 8px;margin-right: 8px;"> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;"><strong>IaaS:</strong>基础设施即服务。对应于上面所说的机器资源统一为资源整体,可动态申请硬件资源的层面;</span></p></li> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;"><strong>PaaS:</strong>平台即服务。对应于上面所说的提供常用的技术组件方便系统的开发和维护;</span></p></li> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;"><strong>SaaS:</strong>软件即服务。对应于上面所说的提供开发好的应用或服务,按功能或性能要求付费。</span></p></li> </ul> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong>至此,以上所提到的从高并发访问问题,到服务的架构和系统实施的层面都有了各自的解决方案,但同时也应该意识到,在上面的介绍中,其实是有意忽略了诸如跨机房数据同步、分布式事务实现等等的实际问题,这些问题以后有机会再拿出来单独讨论</strong></span></p> <p style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="font-size: 14px;"><strong><br></strong></span></p> <h1 style="margin-left: 8px;margin-right: 8px;line-height: 1.5em;"><span style="color: rgb(64, 118, 0);"><strong><span style="font-size: 14px;">4. 架构设计总结</span></strong></span></h1> <p><span style="font-size: 14px;"><br></span></p> <ul class=" list-paddingleft-2" style="margin-left: 8px;margin-right: 8px;"> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;"><strong>架构的调整是否必须按照上述演变路径进行?</strong><br>不是的,以上所说的架构演变顺序只是针对某个侧面进行单独的改进,在实际场景中,可能同一时间会有几个问题需要解决,或者可能先达到瓶颈的是另外的方面,这时候就应该按照实际问题实际解决。如在政府类的并发量可能不大,但业务可能很丰富的场景,高并发就不是重点解决的问题,此时优先需要的可能会是丰富需求的解决方案。</span></p></li> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;"><strong>对于将要实施的系统,架构应该设计到什么程度?</strong><br>对于单次实施并且性能指标明确的系统,架构设计到能够支持系统的性能指标要求就足够了,但要留有扩展架构的接口以便不备之需。对于不断发展的系统,如电商平台,应设计到能满足下一阶段用户量和性能指标要求的程度,并根据业务的增长不断的迭代升级架构,以支持更高的并发和更丰富的业务。</span></p></li> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;"><strong>服务端架构和大数据架构有什么区别?</strong><br>所谓的“大数据”其实是海量数据采集清洗转换、数据存储、数据分析、数据服务等场景解决方案的一个统称,在每一个场景都包含了多种可选的技术,如数据采集有Flume、Sqoop、Kettle等,数据存储有分布式文件系统HDFS、FastDFS,NoSQL数据库HBase、MongoDB等,数据分析有Spark技术栈、机器学习算法等。总的来说大数据架构就是根据业务的需求,整合各种大数据组件组合而成的架构,一般会提供分布式存储、分布式计算、多维分析、数据仓库、机器学习算法等能力。而服务端架构更多指的是应用组织层面的架构,底层能力往往是由大数据架构来提供。</span></p></li> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;"><strong>有没有一些架构设计的原则?</strong></span></p></li> <ul class=" list-paddingleft-2" style="list-style-type: square;"> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;">N+1设计。系统中的每个组件都应做到没有单点故障;</span></p></li> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;">回滚设计。确保系统可以向前兼容,在系统升级时应能有办法回滚版本;</span></p></li> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;">禁用设计。应该提供控制具体功能是否可用的配置,在系统出现故障时能够快速下线功能;</span></p></li> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;">监控设计。在设计阶段就要考虑监控的手段;</span></p></li> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;">多活数据中心设计。若系统需要极高的高可用,应考虑在多地实施数据中心进行多活,至少在一个机房断电的情况下系统依然可用;</span></p></li> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;">采用成熟的技术。刚开发的或开源的技术往往存在很多隐藏的bug,出了问题没有商业支持可能会是一个灾难;</span></p></li> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;">资源隔离设计。应避免单一业务占用全部资源;</span></p></li> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;">架构应能水平扩展。系统只有做到能水平扩展,才能有效避免瓶颈问题;</span></p></li> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;">非核心则购买。非核心功能若需要占用大量的研发资源才能解决,则考虑购买成熟的产品;</span></p></li> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;">使用商用硬件。商用硬件能有效降低硬件故障的机率;</span></p></li> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;">快速迭代。系统应该快速开发小功能模块,尽快上线进行验证,早日发现问题大大降低系统交付的风险;</span></p></li> <li><p style="line-height: 1.5em;"><span style="font-size: 14px;">无状态设计。服务接口应该做成无状态的,当前接口的访问不依赖于接口上次访问的状态。</span></p></li> </ul> </ul> <p style="border-color: currentcolor;border-style: none;border-width: medium;margin: 5px 8px 0px;padding: 10px 0px;background: none 0% 0% repeat scroll rgba(0, 0, 0, 0);line-height: normal;text-align: left;"><span style="color: rgb(136, 136, 136);font-size: 14px;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);">出处:https://segmentfault.com/a/1190000018626163</span></p> <p style="border-color: currentcolor;border-style: none;border-width: medium;margin: 5px 8px 0px;padding: 10px 0px;background: none 0% 0% repeat scroll rgba(0, 0, 0, 0);line-height: normal;"><span style="color: rgb(136, 136, 136);font-size: 14px;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);">版权申明:内容来源网络,版权归原创者所有。除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除并表示歉意。谢谢。</span></p> <section label="Copyright © 2014 playhudong All Rights Reserved." style="border-color: currentcolor;border-style: none;border-width: medium;-moz-border-top-colors: none;-moz-border-left-colors: none;-moz-border-bottom-colors: none;-moz-border-right-colors: none;margin: 5px 0px 0px;padding: 10px 0px;background: rgba(0, 0, 0, 0) none repeat scroll 0% 0%;"> <img class="" data-ratio="0.05278592375366569" src="/upload/895b409cfe0a5638b397d0f31154356a.png" data-type="png" data-w="341" style="width: 160px;margin: 0px auto;display: block;"> <br> </section> <p style="text-align: center;line-height: 25.6px;font-size: 14px;white-space: normal;min-height: 1em;max-width: 100%;background-color: rgb(255, 255, 255);overflow-wrap: break-word;margin-left: 8px;margin-right: 8px;box-sizing: border-box !important;"><span style="font-size: 12px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word;"><strong style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word;">架构文摘</strong></span></p> <p style="text-align: center;line-height: 25.6px;font-size: 14px;white-space: normal;min-height: 1em;max-width: 100%;background-color: rgb(255, 255, 255);overflow-wrap: break-word;margin-left: 8px;margin-right: 8px;box-sizing: border-box !important;"><span style="font-size: 12px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word;">ID:ArchDigest</span></p> <p style="text-align: center;line-height: 25.6px;font-size: 14px;white-space: normal;min-height: 1em;max-width: 100%;background-color: rgb(255, 255, 255);overflow-wrap: break-word;margin-left: 8px;margin-right: 8px;box-sizing: border-box !important;"><span style="font-size: 12px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word;">互联网应用架构丨架构技术丨大型网站丨大数据</span></p> <p style="text-align: center;"><img class="rich_pages" data-copyright="0" data-ratio="1.0505836575875487" data-s="300,640" src="/upload/34c06f9af7df907d751d410136379287.png" data-type="png" data-w="257" style="width: 163px;height: 171px;"></p> <p style="text-align: center;white-space: normal;min-height: 1em;max-width: 100%;background-color: rgb(255, 255, 255);overflow-wrap: break-word;margin-left: 8px;margin-right: 8px;box-sizing: border-box !important;"><span style="font-size: 12px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word;"><span style="color: rgb(255, 255, 255);line-height: 25.6px;font-family: 宋体;font-size: 12px;max-width: 100%;background-color: rgb(79, 97, 40);">更多精彩文章,请点击下方:</span><span style="color: rgb(255, 255, 255);line-height: 25.6px;font-family: 宋体;font-size: 12px;max-width: 100%;background-color: rgb(192, 0, 0);">阅读原文</span></span></p>
作者:微信小助手
<section style="box-sizing: border-box;"> <section class="V5" 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;">随着新浪微博业务的不断推进,对数据处理的实时性要求越来越高。例如,大家所熟悉的微博热词,需要在很短的时间内完成数据处理以供在线系统使用。</span></p> </section> <section style="clear: both;box-sizing: border-box;"></section> </section> </section> </section> </section> <p style="line-height: 1.75em;"><br></p> <p style="text-align: center;margin-bottom: 5px;margin-left: 8px;margin-right: 8px;"><img class="" data-copyright="0" data-ratio="0.5632183908045977" data-s="300,640" src="/upload/dd0368bdfa4664a1ef70aeb1506611cc.png" data-type="png" data-w="957" style=""></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;"><em><span style="font-size: 14px;color: rgb(89, 89, 89);">图片来自包图网</span></em></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">WAIC 实时流计算平台为新浪微博提供可靠的毫秒级和秒级实时数据处理服务,通过提供统一的数据源和配置化接入方式,帮助提高新浪微博实时作业的开发效率,降低部门开发与运营的成本。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">2018 年 5 月 18-19 日,由 51CTO 主办的全球软件与运维技术峰会在北京召开。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在“高并发与实时处理”分会场,新浪微博实时流技术平台负责人廖博带来了《WAIC 实时流计算平台的成长和繁衍》的主题演讲。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">本文将按照如下四个阶段分享微博实时流计算平台的搭建历程,以及在创建过程中的一些问题和解决方案:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;margin-left: 8px;margin-right: 8px;"> <li><p style="text-align: justify;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="text-align: justify;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="text-align: justify;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="text-align: justify;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">总结 DQRA 设计模式</span></strong></p></li> </ul> <h1 style="line-height: normal;"><br></h1> <section style="box-sizing: border-box;"> <section class="V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5e
作者:じ☆ve宝贝
1.查找文件 ------------ find / -name filename.txt根据名称查找/目录下的filename.txt文件。 find . -name “*.xml”递归查找所有的xml文件 find . -name “*.xml” |xargs grep “Hello World”递归查找所有文件内容中包含hello world的xml文件 grep -H ‘spring’ *.xml查找所以有的包含spring的xml文件 find ./ -size 0 | xargs rm -f &删除文件大小为零的文件 ls -l | grep ‘jar’查找当前目录中的所有jar文件 grep ‘test’ d*显示所有以d开头的文件中包含test的行。 grep ‘test’ aa bb cc显示在aa,bb,cc文件中匹配test的行。 grep ‘[a-z]/{5/}’ aa显示所有包含每个字符串至少有5个连续小写字符的字符串的行。 2.查看一个程序是否运行 ------------ ps –ef|grep tomcat查看所有有关tomcat的进程 3.终止线程 ------------ kill -9 19979终止线程号位19979的线程 4.查看文件,包含隐藏文件 ------------ ls -al 5.当前工作目录 ------------ pwd 6.复制文件 ------------ cp sourceFolder targetFolder scp sourecFile romoteUserName@remoteIp:remoteAddr远程拷贝 7.创建目录 ------------ mkdir newfolder 8.删除目录 ------------ rmdir deleteEmptyFolder删除空目录rm -rf deleteFile递归删除目录中所有内容 9.移动文件 ------------ mv /temp/movefile /targetFolder 10.重命令 ------------ mv oldNameFile newNameFile 11.切换用户 ------------ su -username 12.修改文件权限 ------------ chmod 777 file.java//file.java的权限-rwxrwxrwx,r表示读、w表示写、x表示可执行 13.压缩文件 ------------ tar -czf test.tar.gz /test1 /test2 14.列出压缩文件列表 ------------ tar -tzf test.tar.gz 15.解压文件 ------------ tar -xvzf test.tar.gz 16.查看文件头10行 ------------ head -n 10 example.txt 17.查看文件尾10行 ------------ tail -n 10 example.txt 18.查看日志类型文件 ------------ tail -f exmaple.log//这个命令会自动显示新增内容,屏幕只显示10行内容的(可设置)。 19.使用超级管理员身份执行命令 ------------ sudo rm a.txt使用管理员身份删除文件 20.查看端口占用情况 ------------ netstat -tln | grep 8080查看端口8080的使用情况 21.查看端口属于哪个程序 ------------ lsof -i :8080 22.查看进程 ------------ ps aux|grep java查看java进程 ps aux查看所有进程 23.以树状图列出目录的内容 ------------ tree a ps:Mac下使用tree命令 24. 文件下载 ------------ wget http://file.tgz mac下安装wget命令 curl http://file.tgz 25. 网络检测 ------------ ping www.taobao.com 26.远程登录 ------------ ssh userName@ip 27.打印信息 ------------ echo $JAVA_HOME打印java home环境变量的值 28.java 常用命令 ------------ java javacjps , jstat , jmap , jstack 29.其他命令 ------------ svn git maven 30.查看系统内存使用排序 ------------ ps -e -o "%C : %p : %z : %a"|sort -k5 -nr 或者 top
作者:微信小助手
<p><span style="font-size: 14px;letter-spacing: 1px;">有朋友问:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 14px;letter-spacing: 1px;">微信如何实现手机端、PC端</span><span style="font-size: 14px;letter-spacing: 1px;color: rgb(255, 76, 0);">同时登录,同时收消息</span><span style="font-size: 14px;letter-spacing: 1px;">?</span></p></li> <li><p><span style="font-size: 14px;letter-spacing: 1px;">微信能不能实现,</span><span style="font-size: 14px;letter-spacing: 1px;color: rgb(255, 76, 0);">换一个手机,仍能拉取到历史消息</span><span style="font-size: 14px;letter-spacing: 1px;">?</span></p></li> </ul> <p><span style="font-size: 14px;letter-spacing: 1px;">这是<strong>多点登录</strong>和<strong>消息漫游</strong>的问题。</span></p> <p><br></p> <p><strong><span style="font-size: 14px;letter-spacing: 1px;">什么是多点登录?</span></strong></p> <p><span style="font-size: 14px;letter-spacing: 1px;">以微信为例,可以PC端,phone端同时登录,同时收发消息。</span></p> <p><span style="color: rgb(0, 82, 255);"><em><span style="font-size: 14px;letter-spacing: 1px;">画外音:需要注意的是,一个端只能登录一个实例,例如同一个QQ号,在pc1上登录,再到pc2上登录,后者会把前者踢出,pc1会收到通知“你已在别处登录xxoo”。</span></em></span></p> <p><span style="font-size: 14px;letter-spacing: 1px;"> </span></p> <p><strong><span style="font-size: 14px;letter-spacing: 1px;">什么是消息漫游?</span></strong></p> <p><span style="font-size: 14px;letter-spacing: 1px;">在任何一个终端的任何一个实例登录,都能够拉取到所有历史聊天消息,这个就是消息漫游。</span></p> <p><span style="color: rgb(0, 82, 255);"><em><span style="font-size: 14px;letter-spacing: 1px;">画外音:微信目前只支持“多点登录”同时收发在线消息,以及最近几条消息的“消息漫游”,没有实现全部消息的“漫游”。</span></em></span></p> <p><span style="font-size: 14px;letter-spacing: 1px;"> </span></p> <p><strong><span style="font-size: 14px;letter-spacing: 1px;">典型即时通讯架构如何?</span></strong></p> <p><span style="font-size: 14px;letter-spacing: 1px;">典型即时通讯架构可以抽象成这么几层:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 14px;letter-spacing: 1px;">客户端:例如pc微信,手机qq</span></p></li> <li><p><span style="font-size: 14px;letter-spacing: 1px;">服务端:</span></p></li> </ul> <p><strong><span style="font-size: 14px;letter-spacing: 1px;">入口层gate</span></strong><span style="font-size: 14px;letter-spacing: 1px;">:保持与客户端的连接;</span></p> <p><strong><span style="font-size: 14px;letter-spacing: 1px;">逻辑层logic、路由层router</span></strong><span style="font-size: 14px;letter-spacing: 1px;">:实现业务逻辑,进行消息的路由;</span></p> <p><strong><span style="font-size: 14px;letter-spacing: 1px;">cache</span></strong><span style="font-size: 14px;letter-spacing: 1px;">:用来存储用户的在线状态,与接入节点(用户具体连接在哪个gate节点);</span></p> <p><strong><span style="font-size: 14px;letter-spacing: 1px;">db</span></strong><span style="font-size: 14px;letter-spacing: 1px;">:固化存储消息,群信息,好友关系链等信息;</span></p> <p><span style="color: rgb(0, 82, 255);"><em><span style="font-size: 14px;letter-spacing: 1px;">画外音:都需要考虑高可用,扩展性。</span></em></span></p> <p><span style="font-size: 14px;letter-spacing: 1px;"> <img data-ratio="0.7506561679790026" data-s="300,640" src="/upload/75cdf2547fd04f442e6b6658cb3b0887.png" data-type="png" data-w="381" style="white-space: normal;"></span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">一个典型的消息投递流程如上图步骤1-5:</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">(1) 用户A登录在gate1上,发出消息;</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">(2) gate1将消息给logic/router;</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">(3) logic/router查询接收方的在线状态(B在线,C不在线);</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">(4.1) 假设接收方C不在线,存储离线;</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">(4.2) 假设接收方B在线,且登录在gate2上,消息投递给gate2;</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">(5) gate2将消息投递给B;</span></p> <p><span style="color: rgb(0, 82, 255);"><em><span style="font-size: 14px;letter-spacing: 1px;">画外音:单对单消息有一系列应用层超时、重传、确认、去重的机制,以保证消息的可靠投递,这不是本文的重点,不进行展开。</span></em></span></p> <p><span style="font-size: 14px;letter-spacing: 1px;"> </span></p> <p><strong><span style="font-size: 14px;letter-spacing: 1px;">实现“接收方”多点登陆,架构要做什么调整?</span></strong></p> <p><img data-ratio="0.5290806754221389" data-s="300,640" src="/upload/52e8866a7b85deea33e347d111f8f018.png" data-type="png" data-w="533"><br><span style="font-size: 14px;letter-spacing: 1px;">接收方多点登录,pc也登录,phone也登录,后一端登录不会将前一端踢出,cache中存储状态与登录点时,不再以user_id为key,</span><span style="font-size: 14px;letter-spacing: 1px;color: rgb(255, 76, 0);">改为以user_id+终端类型为key即可</span><span style="font-size: 14px;letter-spacing: 1px;">。</span></p> <p><strong><span style="font-size: 14px;letter-spacing: 1px;"><br></span></strong></p> <p><strong><span style="font-size: 14px;letter-spacing: 1px;">单点登录</span></strong></p> <p><span style="font-size: 14px;letter-spacing: 1px;">B:online(状态),gate2(登录点)</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;"><br></span></p> <p><strong><span style="font-size: 14px;letter-spacing: 1px;">多点登陆</span></strong></p> <p><span style="font-size: 14px;letter-spacing: 1px;">B+pc:online(状态),gate2(登录点)</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">B+phone:online(状态),gate3(登录点)</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;"> </span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">当用户A给用户B发送消息时,取出所有B的登录点,进行消息群发即可(如上图中步骤4与步骤5)。</span></p> <p><span style="color: rgb(0, 82, 255);"><em><span style="font-size: 14px;letter-spacing: 1px;">画外音:接收方多点登录,比较容易。</span></em></span></p> <p><span style="font-size: 14px;letter-spacing: 1px;"> </span></p> <p><span style="font-size: 14px;letter-spacing: 1px;"><strong style="white-space: normal;"><span style="font-size: 14px;letter-spacing: 1px;">实现“发送方”多点登陆,架构要做什么调整?</span></strong></span></p> <p><span style="font-size: 14px;letter-spacing: 1px;"><br></span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">有朋友可能要问,<strong>发送方和多点登录有什么关系?</strong></span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">假设:</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">用户A登录了两个点,A1和A2;</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">用户B登录了两个点,B1和B2;</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">这样:</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">A(A1发出的)发送消息给B(B1和B2);</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">B(B1发出的)发送消息给A(A1和A2);</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">不就可以了么?</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;"><br></span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">其实不然:</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">A(A1发出的)发送消息给B(B1和B2);</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">B(B1发出的)发送消息给A(A1和A2);</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">但是:</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">A2端虽然收到了所有B回复的消息,但消息其实是在A1端发出的,故A2端只知道聊天消息的一半(所有B的回复),缺失了聊天的上下文(所有A1端的发出)。</span></p> <p><span style="color: rgb(0, 82, 255);"><em><span style="font-size: 14px;letter-spacing: 1px;">画外音:这个逻辑有点绕,多看几遍。</span></em></span></p> <p><span style="font-size: 14px;letter-spacing: 1px;"><br></span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">故,</span><span style="font-size: 14px;letter-spacing: 1px;color: rgb(255, 76, 0);">如果发送方也进行了多点登录</span><span style="font-size: 14px;letter-spacing: 1px;">,发送出去的任何消息,除了要投递给多点登录的接收方,</span><span style="font-size: 14px;letter-spacing: 1px;color: rgb(255, 76, 0);">还需要投递给多点登录的发送方</span><span style="font-size: 14px;letter-spacing: 1px;">。</span></p> <p><span style="color: rgb(0, 82, 255);"><em><span style="font-size: 14px;letter-spacing: 1px;">画外音:发送方发出的消息,也需要发给“发送方”。</span></em></span></p> <p><img data-ratio="0.4609250398724083" data-s="300,640" src="/upload/269b0c562ee7394abae79c75d07e1667.png" data-type="png" data-w="627"><br><span style="font-size: 14px;letter-spacing: 1px;">如上图,发送方A和接收方B都进行了多点登陆,cache中存储的信息为:</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">A+pc:online(状态),gate0(登录点)</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">A+phone:online(状态),gate1(登录点)</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">B+pc:online(状态),gate2(登录点)</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">B+phone:online(状态),gate3(登录点)</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;"> </span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">当用户A(phone端)给用户B发送消息时,除了要投递给B的所有多点登录端,还需要投递给A多点登陆的其他端(pc端),如上图中步骤4与步骤5。</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;"><br></span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">只有这样,才能在所有用户的所有端,恢复与还原双方聊天的上下文。</span></p> <p><span style="color: rgb(0, 82, 255);"><em><span style="font-size: 14px;letter-spacing: 1px;">画外音:搞清楚了原理,修改并不难。</span></em></span></p> <p><span style="font-size: 14px;letter-spacing: 1px;"> </span></p> <p><span style="font-size: 14px;letter-spacing: 1px;"><strong style="white-space: normal;"><span style="font-size: 14px;letter-spacing: 1px;">实现“消息漫游”,架构要做什么调整?</span></strong></span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">如果不需要支持“消息漫游”,对于在线消息,如果用户接收到,是不需要存储到数据库的。但如果要支持“换一台机器也能看到历史的聊天消息”,</span><span style="font-size: 14px;letter-spacing: 1px;color: rgb(255, 76, 0);">就需要对所有消息进行存储</span><span style="font-size: 14px;letter-spacing: 1px;">。</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;"> </span></p> <p><img data-ratio="0.5373134328358209" data-s="300,640" src="/upload/9679c0400f6ee445b2e70c29bb84dff.png" data-type="png" data-w="536"><br><span style="font-size: 14px;letter-spacing: 1px;">消息投递如上图,用户A发送消息给用户B,虽然B在线,仍然要增加一个步骤2.5,在</span><span style="font-size: 14px;letter-spacing: 1px;color: rgb(255, 76, 0);">投递之前进行存储</span><span style="font-size: 14px;letter-spacing: 1px;">,</span><span style="font-size: 14px;letter-spacing: 1px;color: rgb(255, 76, 0);">以备B的其他端登陆时,可以拉取到历史消息</span><span style="font-size: 14px;letter-spacing: 1px;">。</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;"> </span></p> <p><img data-ratio="0.4957983193277311" data-s="300,640" src="/upload/b2868ae603af2a54828482eb3a6d1b88.png" data-type="png" data-w="595"><br><span style="font-size: 14px;letter-spacing: 1px;">消息拉取如上图,<strong>原本不在线的B(phone端)重新登录了,怎么拉取历史消息呢?</strong></span></p> <p><span style="font-size: 14px;letter-spacing: 1px;"><br></span></p> <p><span style="font-size: 14px;letter-spacing: 1px;color: rgb(255, 76, 0);">只需要在客户端本地存储一个上一次拉取到的msg_id(time)</span><span style="font-size: 14px;letter-spacing: 1px;">,到服务端重新拉取即可。</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;"><br></span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">这里还有个问题,由于服务端存储所有消息成本是非常高的,所以一般“消息漫游”是有时间(或者消息数)限制,</span><span style="font-size: 14px;letter-spacing: 1px;color: rgb(255, 76, 0);">不能拉取所有所有几年前的历史消息</span><span style="font-size: 14px;letter-spacing: 1px;">,只能拉取最近的云端消息。</span></p> <p><span style="color: rgb(0, 82, 255);"><em><span style="font-size: 14px;letter-spacing: 1px;">画外音:</span></em></span></p> <p><span style="color: rgb(0, 82, 255);"><em><span style="color: rgb(0, 82, 255);font-size: 14px;letter-spacing: 1px;">你猜,微信有没有存储,几年前的消息呢?</span></em></span></p> <p><span style="color: rgb(0, 82, 255);"><em><span style="color: rgb(0, 82, 255);font-size: 14px;letter-spacing: 1px;">你猜,交一个超级会员费,是不是可以查询呢?</span></em></span></p> <p><span style="font-size: 14px;letter-spacing: 1px;"> </span></p> <p><strong><span style="font-size: 14px;letter-spacing: 1px;">总结</span></strong></p> <p><strong><span style="font-size: 14px;letter-spacing: 1px;color: rgb(0, 0, 0);">“多点登录”</span></strong><span style="font-size: 14px;letter-spacing: 1px;">是指多个端同时登录一个帐号,同时收发消息,关键点是:</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">(1) 需要在</span><span style="font-size: 14px;letter-spacing: 1px;color: rgb(255, 76, 0);">服务端存储同一个用户多个端的状态与登陆点</span><span style="font-size: 14px;letter-spacing: 1px;">;</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">(2) 发出消息时,要对</span><span style="font-size: 14px;letter-spacing: 1px;color: rgb(255, 76, 0);">发送方的多端与接收端的多端,都进行消息投递</span><span style="font-size: 14px;letter-spacing: 1px;">;</span></p> <p><br></p> <p><strong><span style="font-size: 14px;letter-spacing: 1px;color: rgb(0, 0, 0);">“消息漫游”</span></strong><span style="font-size: 14px;letter-spacing: 1px;">是指一个用户在任何端,都可以拉取到历史消息,关键点是:</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">(1)</span><span style="font-size: 14px;letter-spacing: 1px;color: rgb(255, 76, 0);"> 所有消息存储在云端</span><span style="font-size: 14px;letter-spacing: 1px;">;</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">(2) 每个</span><span style="font-size: 14px;letter-spacing: 1px;color: rgb(255, 76, 0);">端本地存储last_msg_id</span><span style="font-size: 14px;letter-spacing: 1px;">,在登录时可以到云端同步历史消息;</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;">(3) 云端存储所有消息成本较高,一般会对历史消息时间(或者条数)进行限制;</span></p> <p><span style="font-size: 14px;letter-spacing: 1px;"><br></span></p> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;text-align: justify;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;"><strong style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;letter-spacing: 1px;box-sizing: border-box !important;word-wrap: break-word !important;">假如你来实现,你会怎么做?</span></strong></p> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;text-align: justify;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;letter-spacing: 1px;box-sizing: border-box !important;word-wrap: break-word !important;">希望大家有收获。</span></p> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;text-align: justify;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;"><br></p> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;line-height: normal;box-sizing: border-box !important;word-wrap: break-word !important;"><img width="auto" data-ratio="1" data-w="250" data-type="jpeg" data-s="300,640" class="" src="/upload/7ddc9700032e2c5cee163f1f1a37b46c.jpg" style="max-width: 677px;box-sizing: border-box;word-wrap: break-word;visibility: visible !important;width: 130px !important;"></p> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;line-height: normal;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;letter-spacing: 1px;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;font-size: 12px;">架构师之路-分享</span><span style="max-width: 100%;font-size: 12px;color: rgb(255, 76, 0);">可落地</span><span style="max-width: 100%;font-size: 12px;">的技术文章</span></span></p> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;text-align: justify;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"></p> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;text-align: justify;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;letter-spacing: 1px;box-sizing: border-box !important;word-wrap: break-word !important;">相关推荐:</span></p> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;text-align: justify;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;letter-spacing: 1px;box-sizing: border-box !important;word-wrap: break-word !important;">《<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651961812&idx=1&sn=592e3cc722bdfc4201d07cb1b087ed06&chksm=bd2d0c088a5a851eec1e01cea1d68a365edab46bc6985c469adb4b0df17f0e6c2f07bda8da47&scene=21#wechat_redirect" target="_blank" style="color: rgb(87, 107, 149);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;">GFS架构启示</a>》</span></p> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;text-align: justify;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;letter-spacing: 1px;box-sizing: border-box !important;word-wrap: break-word !important;">《<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651961826&idx=1&sn=98f6ec5271c7a4153c3e50c7b8be4f33&chksm=bd2d0c3e8a5a8528177b34db26876e510486c78f331bd5e51082a22eda9d65b9bfc35abe1c53&scene=21#wechat_redirect" target="_blank" style="color: rgb(87, 107, 149);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;">Google MapReduce解决什么问题?</a>》</span></p> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;text-align: justify;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;letter-spacing: 1px;box-sizing: border-box !important;word-wrap: break-word !important;">《<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651961851&idx=1&sn=56e85dc9bd82d48577e487f42876748d&chksm=bd2d0c278a5a85313431f6ad7ba47f1721f6d5823e4c072d77cdc9fe18bac53dc1a1e8a6fc3a&scene=21#wechat_redirect" target="_blank" style="color: rgb(87, 107, 149);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;">Google MapReduce巧妙优化思路?</a>》</span></p> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;text-align: justify;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;letter-spacing: 1px;box-sizing: border-box !important;word-wrap: break-word !important;">《<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651961871&idx=1&sn=c825c72910791413c54f48dbe5452519&chksm=bd2d0fd38a5a86c5cbca7e0b3693f6d2d0c2fff82026561c81259c6e96c22bb8048735a66f9f&scene=21#wechat_redirect" target="_blank" style="color: rgb(87, 107, 149);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;">Google MapReduce架构设计实践</a>》</span></p> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;text-align: justify;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;letter-spacing: 1px;box-sizing: border-box !important;word-wrap: break-word !important;">《<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651961881&idx=1&sn=a417acab437b7dea6a7ce6b5b9b31bfa&chksm=bd2d0fc58a5a86d30462ab461443683e164425136ba8169efb4cd9ea95e79bff74d3f25fdf0e&scene=21#wechat_redirect" target="_blank">MapReduce,颠覆了分层架构的本质?</a>》</span></p>
作者:若是有缘何须誓言