文章列表

Java设计模式(转)——11.组合模式

作者:じ☆ve宝贝

##11.组合模式(Composite) 组合模式有时又叫部分-整体模式,在处理类似树形结构的问题时比较方便,看看关系图: ![组合设计模式](/upload/content20.png "组合设计模式") 直接来看代码: ``` public class TreeNode { private String name; private TreeNode parent; private Vector<TreeNode> children = new Vector<TreeNode>(); public TreeNode(String name){ this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public TreeNode getParent() { return parent; } public void setParent(TreeNode parent) { this.parent = parent; } //添加孩子节点 public void add(TreeNode node){ children.add(node); } //删除孩子节点 public void remove(TreeNode node){ children.remove(node); } //取得孩子节点 public Enumeration<TreeNode> getChildren(){ return children.elements(); } } ``` 测试类: ``` public class Tree { TreeNode root = null; public Tree(String name) { root = new TreeNode(name); } public static void main(String[] args) { Tree tree = new Tree("A"); TreeNode nodeB = new TreeNode("B"); TreeNode nodeC = new TreeNode("C"); nodeB.add(nodeC); tree.root.add(nodeB); System.out.println("build the tree finished!"); } } ``` 使用场景:将多个对象组合在一起进行操作,常用于表示树形结构中,例如二叉树等。

再有人问你分布式锁是什么,就把这篇文章发给他

作者:微信小助手

<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;">现在面试,一般都会聊聊分布式系统这块的东西。通常面试官都会从服务框架(Spring Cloud、Dubbo)聊起,一路聊到分布式事务、分布式锁、ZooKeeper 等知识。</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-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.5631399317406144" data-s="300,640" src="/upload/ce3534c84ec8dc81d7c6b2d429f6d49.png" data-type="png" data-w="586" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">所以咱们就来聊聊分布式锁这块的知识,先具体的来看看 Redis 分布式锁的实现原理。</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;">说实话,如果在公司里落地生产环境用分布式锁的时候,一定是会用开源类库的,比如 Redis 分布式锁,一般就是用 Redisson 框架就好了,非常的简便易用。</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;">大家如果有兴趣,可以去 Redisson 的官网,看看如何在项目中引入 Redisson 的依赖,然后基于 Redis 实现分布式锁的加锁与释放锁。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">下面给大家看一段简单的使用代码片段,先直观的感受一下:</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.21476510067114093" data-s="300,640" src="/upload/ddb2da3be2555d75aac1ff2ec248cc65.png" data-type="png" data-w="447" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">怎么样,上面那段代码,是不是感觉简单的不行!此外,人家还支持 Redis 单实例、Redis 哨兵、Redis Cluster、redis master-slave 等各种部署架构,都可以给你完美实现。</span></p> <p style="line-height: normal;"><br></p> <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.5em;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">Redisson 实现 Redis 分布式锁的底层原理</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">好的,接下来就通过一张手绘图,给大家说说 Redisson 这个开源框架对 Redis 分布式锁的实现原理。</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.5182815356489945" data-s="300,640" src="/upload/a930e2c4f0ddeeaf2a58b432c1a48c9d.png" data-type="png" data-w="1094" style=""></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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>加锁机制</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <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;">咱们来看上面那张图,现在某个客户端要加锁。如果该客户端面对的是一个 Redis Cluster 集群,他首先会根据 Hash 节点选择一台机器。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这里注意,仅仅只是选择一台机器!这点很关键!紧接着,就会发送一段 Lua 脚本到 Redis 上,那段 Lua 脚本如下所示:</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.5086071987480438" data-s="300,640" src="/upload/5a017e82b8d92fb8ddf09209e67bc501.png" data-type="png" data-w="639" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">为啥要用 Lua 脚本呢?因为一大坨复杂的业务逻辑,可以通过封装在 Lua 脚本中发送给 Redis,保证这段复杂业务逻辑执行的原子性。</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;">那么,这段 Lua 脚本是什么意思呢?这里 KEYS[1] 代表的是你加锁的那个 Key,比如说:RLock lock = redisson.getLock("myLock");这里你自己设置了加锁的那个锁 Key 就是“myLock”。</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;">ARGV[1] 代表的就是锁 Key 的默认生存时间,默认 30 秒。ARGV[2] 代表的是加锁的客户端的 ID,类似于下面这样:8743c9c0-0795-4907-87fd-6c719a6b4586:1。</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;">给大家解释一下,第一段 if 判断语句,就是用“exists myLock”命令判断一下,如果你要加锁的那个锁 Key 不存在的话,你就进行加锁。如何加锁呢?很简单,用下面的命令:hset myLock。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">8743c9c0-0795-4907-87fd-6c719a6b4586:1 1,通过这个命令设置一个 Hash 数据结构,这行命令执行后,会出现一个类似下面的数据结构:</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.23552123552123552" data-s="300,640" src="/upload/46785937774248196117514696c01fe.png" data-type="png" data-w="518" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">上述就代表“8743c9c0-0795-4907-87fd-6c719a6b4586:1”这个客户端对“myLock”这个锁 Key 完成了加锁。</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;">接着会执行“pexpire myLock 30000”命令,设置 myLock 这个锁 Key 的生存时间是 30 秒。好了,到此为止,加锁完成了。</span></p> <p style="line-height: normal;"><br></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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>锁互斥机制</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <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;">那么在这个时候,如果客户端 2 来尝试加锁,执行了同样的一段 Lua 脚本,会咋样呢?</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;">很简单,第一个 if 判断会执行“exists myLock”,发现 myLock 这个锁 Key 已经存在了。</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;">接着第二个 if 判断,判断一下,myLock 锁 Key 的 Hash 数据结构中,是否包含客户端 2 的 ID,但是明显不是的,因为那里包含的是客户端 1 的 ID。</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;">所以,客户端 2 会获取到 pttl myLock 返回的一个数字,这个数字代表了 myLock 这个锁 Key 的剩余生存时间。</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;">比如还剩 15000 毫秒的生存时间。此时客户端 2 会进入一个 while 循环,不停的尝试加锁。</span></p> <p style="line-height: normal;"><br></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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>watch dog 自动延期机制</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">客户端 1 加锁的锁 Key 默认生存时间才 30 秒,如果超过了 30 秒,客户端 1 还想一直持有这把锁,怎么办呢?</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;">简单!只要客户端 1 一旦加锁成功,就会启动一个 watch dog 看门狗,他是一个后台线程,会每隔 10 秒检查一下,如果客户端 1 还持有锁 Key,那么就会不断的延长锁 Key 的生存时间。</span></p> <p style="line-height: normal;"><br></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="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>可重入加锁机制</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">那如果客户端 1 都已经持有了这把锁了,结果可重入的加锁会怎么样呢?比如下面这种代码:</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.6801801801801802" data-s="300,640" src="/upload/c6a07949839edfed3658b361c71ef749.png" data-type="png" data-w="444" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这时我们来分析一下上面那段 Lua 脚本。第一个if判断肯定不成立,“exists myLock”会显示锁 Key 已经存在了。</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;">第二个 if 判断会成立,因为 myLock 的 Hash 数据结构中包含的那个 ID,就是客户端 1 的那个 ID,也就是“8743c9c0-0795-4907-87fd-6c719a6b4586:1”。</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;">此时就会执行可重入加锁的逻辑,他会用:</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">incrby myLock 8743c

Apache Maven 最全教程,7000 字总结!

作者:微信小助手

<section class="xmtedi

深入学习Redis高可用的基石:主从复制

作者:微信小助手

<section style="box-sizing: border-box;"> <section class="Powered-by-XIUMI V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section class="" style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section class="" 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;" class=""> <section style="box-sizing: border-box;"> “ </section></span> <section class="" 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;">在前面的两篇文章中,分别介绍了 <a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&amp;mid=2655816024&amp;idx=1&amp;sn=f4ee4afce1055d9f34c61a8a9d95918a&amp;chksm=bd74c48f8a034d99ffb4291e78a6b0c6654a8ab74d83bbf6739bc4ec20ce7ae1165d19e79d44&amp;scene=21#wechat_redirect" target="_blank">Redis 的内存模型</a>和 <a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&amp;mid=2655817009&amp;idx=1&amp;sn=1b28414b42179d506d4216112dfc36c7&amp;chksm=bd74c0e68a0349f0506ea75dccad96e4c302764b45e2b5c6869d5d1e5d1fd3bb5c4931caf2a6&amp;scene=21#wechat_redirect" target="_blank">Redis 的持久化</a>,今天我们来深入学习 Redis 的主从复制。</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-left: 8px;margin-right: 8px;margin-bottom: 5px;"><img class="" data-copyright="0" data-ratio="0.554006968641115" data-s="300,640" src="/upload/5ac61f5b294a85f5ea587740c0f925b3.png" data-type="png" data-w="574" style=""></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在 Redis 的持久化中曾提到,Redis 高可用的方案包括持久化、主从复制(及读写分离)、哨兵和集群。</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;">其中持久化侧重解决的是 Redis 数据的单机备份问题(从内存到硬盘的备份);而主从复制则侧重解决数据的多机热备。此外,主从复制还可以实现负载均衡和故障恢复。</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);">我将从以下几个部分详细介绍 Redis 主从复制的方方面面:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">主从复制概述</span></strong></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如何使用主从复制</span></strong></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">主从复制的实现原理</span></strong></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">应用中的问题</span></strong></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">总结</span></strong></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"></span></p> <section style="box-sizing: border-box;"> <section class="Powered-by-XIUMI V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section class="" style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section class="" style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">主从复制概述</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <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;">主从复制,是指将一台 Redis 服务器的数据,复制到其他的 Redis 服务器。前者称为主节点(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。</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;">默认情况下,每台 Redis 服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。</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="color: rgb(71, 193, 168);"><span style="font-size: 15px;letter-spacing: 1px;">主从复制的作用</span><span style="font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">主要包括:</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <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><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。</span></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><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。</span></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><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写 Redis 数据时应用连接主节点,读 Redis 数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高 Redis 服务器的并发量。</span></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><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是 Redis 高可用的基础。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"></span></p> <section style="box-sizing: border-box;"> <section class="Powered-by-XIUMI V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section class="" style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section class="" style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">如何使用主从复制</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">为了更直观的理解主从复制,在介绍其内部原理之前,先说明我们需要如何操作才能开启主从复制。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"></span></p> <section style="box-sizing: border-box;"> <section class="Powered-by-XIUMI V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section class="" style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section class="" style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section class="" style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>建立复制</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">需要注意,主从复制的开启,完全是在从节点发起的;不需要我们在主节点做任何事情。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">从节点开启主从复制,有 3 种方式:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">在从服务器的配置文件中加入:slaveof &lt;masterip&gt; &lt;masterport&gt;。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">redis-server 启动命令后加入 --slaveof &lt;masterip&gt; &lt;masterport&gt;。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">Redis 服务器启动后,直接通过客户端执行命令:slaveof &lt;masterip&gt; &lt;masterport&gt;,则该 Redis 实例成为从节点。</span></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">上述 3 种方式是等效的,下面以客户端命令的方式为例,看一下当执行了 slaveof 后,Redis 主节点和从节点的变化。</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;"></span></p> <section style="box-sizing: border-box;"> <section class="Powered-by-XIUMI V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section class="" style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section class="" style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section class="" style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>实例</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">准备工作:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">启动两个节点。为了</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">方便起见,实验所使用的主从节点是在一台机器上的不同 Redis 实例。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">其中主节点监听 6379 端口,从节点监听 6380 端口;从节点监听的端口号可以在配置文件中修改:</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="" data-ratio="0.10492505353319058" src="/upload/60f6f62bee2f89618fd36bfbcaf6c6f0.png" data-type="png" data-w="467" style="box-sizing: border-box !important;word-wrap: break-word !important;width: auto !important;visibility: visible !important;"></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">启动后可以看到:</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="" data-ratio="0.10830324909747292" src="/upload/8a691b375f0d337babe23e6367b94045.png" data-type="png" data-w="554" style="box-sizing: border-box !important;word-wrap: break-word !important;width: auto !important;visibility: visible !important;"></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;">两个 Redis 节点启动后(分别称为6379节点和6380节点),默认都是主节点。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">建立复制:</span></strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">此时在 6380 节点执行 slaveof 命令,使之变为从节点。</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><img class="" data-ratio="0.16666666666666666" src="/upload/ded51e25d51a53669b146ee9d609d4ac.png" data-type="png" data-w="312" style="box-sizing: border-box !important;word-wrap: break-word !important;width: auto !important;visibility: visible !important;"></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">观察效果:</span></strong><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">下面验证一下,在主从复制建立后,主节点的数据会复制到从节点中。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">首先在从节点查询一个不存在的 key:</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="" data-ratio="0.17117117117117117" src="/upload/54f94c4f7634408fc16c8869328a1f1b.png" data-type="png" data-w="222" style="box-sizing: border-box !important;word-wrap: break-word !important;width: auto !important;visibility: visible !important;"></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">然后在主节点中增加这个 key:</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="" data-ratio="0.1282051282051282" src="/upload/39403ea927bbcf3bfb92aab4f8114a44.png" data-type="png" data-w="273" style="box-sizing: border-box !important;word-wrap: break-word !important;width: auto !important;visibility: visible !important;"></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(89, 89, 89);">此时在从节点中再次查询这个 key,会发现主节点的操作已经同步至从节点:</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="" data-ratio="0.17289719626168223" src="/upload/44a517aa67992b9a03afb47d393672d7.png" data-type="png" data-w="214" style="box-sizing: border-box !important;word-wrap: break-word !important;width: auto !important;visibility: visible !important;"></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">然后在主节点删除这个 key:</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><img class="" data-ratio="0.18226600985221675" src="/upload/3e468e1e160051b3298c4e9cb22b885c.png" data-type="png" data-w="203" style="box-sizing: border-box !important;word-wrap: break-word !important;width: auto !important;visibility: visible !important;"></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(89, 89, 89);">此时在从节点中再次查询这个 key,会发现主节点的操作已经同步至从节点:</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="" data-ratio="0.17703349282296652" src="/upload/5fa7d4d028d55d90ece6c65ca8ab452e.png" data-type="png" data-w="209" style="box-sizing: border-box !important;word-wrap: break-word !important;width: auto !important;visibility: visible !important;"></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"></span></p> <section style="box-sizing: border-box;"> <section class="Powered-by-XIUMI V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section class="" style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section class="" style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section class="" style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>断开复制</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <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;">通过 slaveof &lt;masterip&gt; &lt;masterport&gt; 命令建立主从复制关系以后,可以通过 slaveof no one 断开。</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;">需要注意的是,从节点断开复制后,不会删除已有的数据,只是不再接受主节点新的数据变化。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">从节点执行 slaveof no one 后,打印日志如下图所示;可以看出断开复制后,从节点又变回为主节点。</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="" data-ratio="0.148014440433213" src="/upload/447af8486e631ecd7650d9d130228359.png" data-type="png" data-w="554" style="box-sizing: border-box !important;word-wrap: break-word !important;width: auto !important;visibility: visible !important;"></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">主节点打印日志如下:</span></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="" data-ratio="0.039711191335740074" src="/upload/645f0d603a209104413e81757b67f345.png" data-type="png" data-w="554" style="box-sizing: border-box !important;word-wrap: break-word !important;width: auto !important;visibility: visible !important;"></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"></span></p> <section style="box-sizing: border-box;"> <section class="Powered-by-XIUMI V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section class="" style="border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;"> <section class="" style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">主从复制的实现原理</p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">上面一节中,我们介绍了如何操作可以建立主从关系;本小节将介绍主从复制的实现原理。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">主从复制过程大体可以分为 3 个阶段:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">连接建立阶段(即准备阶段)</span></strong></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">数据同步阶段</span></strong></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">命令传播阶段</span></strong></p></li> </ul> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"></span></p> <section style="box-sizing: border-box;"> <section class="Powered-by-XIUMI V5" style="box-sizing: border-box;" powered-by="xiumi.us"> <section class="" style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section class="" style="width: 0.6em;display: inline-block;vertical-align: middle;box-sizing: border-box;"> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.2;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <strong><span style="width: 0.6em;height: 0.6em;display: block;opacity: 0.6;margin-top: 2px;margin-bottom: 2px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> <span style="width: 0.6em;height: 0.6em;display: block;opacity: 1;box-sizing: border-box;background-color: rgb(89, 89, 89);"></span> </strong> </section> <section class="" style="display: inline-block;vertical-align: middle;font-size: 18px;padding-left: 5px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong>连接建立阶段</strong></p> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">该阶段的主要作用是在主从节点之间建立连接,为数据同步做好准备。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 16px;"><strong><span style="color: rgb(89, 89, 89);letter-spacing: 1px;">步骤 1:保存主节点信息</span></strong></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;">从节点服务器内部维护了两个字段,即 masterhost 和 masterport 字段,用于存储主节点的 ip 和 port 信息。</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;">需要注意的是,slaveof 是异步命令,从节点完成主节点 ip 和 port 的保存后,向发送 slaveof 命令的客户端直接返回 OK,实际的复制操作在这之后才开始进行。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">这个过程中,可以看到从节点打印日志如下:</span></p> <p style="text-align: center;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;"><img class="" data-ratio="0.08303249097472924" src="/upload/5f993ac4edb00ec8052d36e33766f476.png" data-type="png" data-w="554" style="box-sizing: border-box !important;word-wrap: break-word !important;width: auto !important;visibility: visible !important;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 16px;"><strong><span style="color: rgb(89, 89, 89);letter-spacing: 1px;">步骤 2:建立 Socket 连接</span></strong></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;">从节点每秒 1 次调用复制定时函数 replicationCron(),如果发现了有主节点可以连接,便会根据主节点的 ip 和 port,创建 socket 连接。</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;"> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">从节点:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">为该 socket 建立一个专门处理复制工作的文件事件处理器,负责后续的复制工作,如接收 RDB 文件、接收命令传播等。</span></p></li> <li><p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">主节点:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">接收到从节点的 socket 连接后(即 accept 之后),为该 socket 创建相应的客户端状态,并将从节点看做是连接到主节点

PowerDesigner中导入MYSQL数据库结构的步骤及问题解决

作者:别丢下我不管

今天在使用PowerDesigner,要导入MySQL的表结构到PowerDesginer里,顺路记录一下步骤,给后续的同学学习。 1、安装MySQL的ODBC驱动 http://dev.mysql.com/downloads/connector/odbc/5.1.html 下载MSI Installer 2、打开Windows的**控制面板**——>**管理工具**——>**ODBC 数据源(32 位)**——>**文件DSN**——>添加——>**选择MySQL ODBC 5.1 Driver**,并输入ip端口等信息,保存为文件 3、在PowerDesigner里,菜单File——>Reverse Engineer——>Database——>DBMS选择MySQL5.0 确定 4、弹出的Database Reverse Engineering Options窗口的Selection里,选择Using a data source,点击右边的小数据库图标 在弹窗里,选择ODBC file data source,查找到第2步创建的DSN文件,然后在下面输入User ID和Password,点击Connect 5、点击确定就可以弹出数据库的表选择窗口,选择对应的需要导入的数据库表点击ok就行了 ![](/upload/a0dead0a39114151b2f82ae4cc18b036.png) ## 常见问题: 1、如果安装64为ODBC,可能会提示SQLSTATE=IM002[Microsoft][ODBC驱动程序 管理器未发现数据源名称并且未指定默认驱动程序。 解决:更换为32为的ODBC,因为PowerDesigner是32位的。

微信小程序设置tabBar后文字不显示bug解决

作者:じ☆ve宝贝

按照官方文档在json中定义好了Tabbar后,在模拟器上显示没问题,而在真机上不显示Tabar文字。 原因是: 在app.json定义Tabbar文字选中态和非选中态颜色时我用了英文例如black、orange。在模拟器没事,但是在真机一定要用色值,例如#050505 此类问题是由于模拟器环境与真机环境的差异造成的。

Java 面试题问与答:编译时与运行时

作者:微信小助手

<p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(255, 0, 0);font-size: 14px;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;box-sizing: border-box !important;word-wrap: break-word !important;">(点击</span><span style="max-width: 100%;line-height: 22.4px;color: rgb(0, 128, 255);box-sizing: border-box !important;word-wrap: break-word !important;">上方公众号</span><span style="max-width: 100%;color: rgb(255, 41, 65);line-height: 22.4px;box-sizing: border-box !important;word-wrap: break-word !important;">,可快速关注)</span></span></p> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;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> <blockquote style="max-width: 100%;color: rgb(51, 51, 51);font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;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;"> <p style="max-width: 100%;min-height: 1em;text-align: left;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;word-wrap: break-word !important;">来源:</span><span style="font-size: 14px;letter-spacing: 0.544px;">ImportNew - 朱伟杰</span></p> </blockquote> <p><br></p> <p>在开发和设计的时候,我们需要考虑编译时,运行时以及构建时这三个概念。理解这几个概念可以更好地帮助你去了解一些基本的原理。下面是初学者晋级中级水平需要知道的一些问题。</p> <p><br></p> <p>Q.下面的代码片段中,行A和行B所标识的代码有什么区别呢?</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">public class ConstantFolding {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; static final&nbsp; int number1 = 5;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; static final&nbsp; int number2 = 6;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; static int number3 = 5;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; static int number4= 6;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; public static void main(String[ ] args) {</span></p> <p><span style="color: rgb(136, 136, 136);font-size: 12px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int product1 = number1 * number2;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//line A</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int product2 = number3 * number4;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//line B</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; }</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">}</span></p> </blockquote> <p><br></p> <p>A.在行A的代码中,product的值是在编译期计算的,行B则是在运行时计算的。如果你使用Java反编译器(例如,jd-gui)来反编译ConstantFolding.class文件的话,那么你就会从下面的结果里得到答案。</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">public class ConstantFolding<br></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);">&nbsp; static final int number1 = 5;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; static final int number2 = 6;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; static int number3 = 5;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; static int number4 = 6;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; public static void main(String[ ] args)</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp; int product1 = 30;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp; int product2 = number3 * number4;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; }</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">}</span></p> </blockquote> <p><br></p> <p>常量折叠是一种Java编译器使用的优化技术。由于final变量的值不会改变,因此就可以对它们优化。Java反编译器和javap命令都是查看编译后的代码(例如,字节码)的利器。</p> <p><br></p> <p>Q.你能想出除了代码优化外,在什么情况下,查看编译过的代码是很有帮助的?</p> <p><br></p> <p>A.Java里的泛型是在编译时构造的,可以通过查看编译后的class文件来理解泛型,也可以通过查看它来解决泛型相关的问题。</p> <p><br></p> <p>Q.下面哪些是发生在编译时,运行时,或者两者都有?</p> <p><br></p> <p style="text-align: center;"><strong style="text-align: center;white-space: normal;border-width: 0px;border-style: initial;border-color: initial;font-size: 14px;font-family: &quot;Microsoft YaHei&quot;, 宋体, &quot;Myriad Pro&quot;, Lato, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif;background-color: rgb(255, 255, 255);"><img class="aligncenter size-full wp-image-1798" data-ratio="1.0947955390334572" src="/upload/9f51fd079a06ab9dd1d85f72a0ad12f0.jpg" data-type="jpeg" data-w="538" height="589" title="compile-vs-run-time" width="538" style="border-width: 0px;border-style: initial;border-color: initial;font-size: 0px;color: transparent;vertical-align: middle;top: 0px;left: 0px;right: 0px;bottom: 0px;"></strong></p> <p><br></p> <p>A.</p> <p><br></p> <p><strong>方法重载</strong>:这个是发生在编译时的。方法重载也被称为编译时多态,因为编译器可以根据参数的类型来选择使用哪个方法。</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">public class {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp;public static void evaluate(String param1);&nbsp; // method #1</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp;public static void evaluate(int param1);&nbsp; &nbsp;// method #2</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">}</span></p> </blockquote> <p><br></p> <p>如果编译器要编译下面的语句的话:</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">evaluate(“My Test Argument passed to param1”);</span></p> </blockquote> <p><br></p> <p>它会根据传入的参数是字符串常量,生成调用#1方法的字节码</p> <p><br></p> <p><strong>方法覆盖</strong>:这个是在运行时发生的。方法重载被称为运行时多态,因为在编译期编译器不知道并且没法知道该去调用哪个方法。JVM会在代码运行的时候做出决定。</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">public class A {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp;public int compute(int input) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //method #3</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp; &nbsp; return 3 * input;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp;}&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</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);">&nbsp;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">public class B extends A {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp;@Override</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp;public int compute(int input) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //method #4</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp; &nbsp; return 4 * input;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp;}&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">}</span></p> </blockquote> <p><br></p> <p>子类B中的compute(..)方法重写了父类的compute(..)方法。如果编译器遇到下面的代码:</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">public int evaluate(A reference, int arg2)&nbsp; {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp;int result = reference.compute(arg2);</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">}</span></p> </blockquote> <p><br></p> <p>编译器是没法知道传入的参数reference的类型是A还是B。因此,只能够在运行时,根据赋给输入变量“reference”的对象的类型(例如,A或者B的实例)来决定调用方法#3还是方法#4.</p> <p><br></p> <p><strong>泛型(又称类型检验)</strong>:这个是发生在编译期的。编译器负责检查程序中类型的正确性,然后把使用了泛型的代码翻译或者重写成可以执行在当前JVM上的非泛型代码。这个技术被称为“类型擦除“。换句话来说,编译器会擦除所有在尖括号里的类型信息,来保证和版本1.4.0或者更早版本的JRE的兼容性。</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">List&lt;String&gt; myList = new ArrayList&lt;String&gt;(10);</span></p> </blockquote> <p><br></p> <p>编译后成为了:</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">List myList = new ArrayList(10);</span></p> </blockquote> <p><br></p> <p>注解(Annotation):你可以使用运行时或者编译时的注解。</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">public class B extends A {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp;@Override</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; public int compute(int input){&nbsp; &nbsp; &nbsp; //method #4</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp; &nbsp; return 4 * input;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">}</span></p> </blockquote> <p><br></p> <p>@Override是一个简单的编译时注解,它可以用来捕获类似于在子类中把toString()写成tostring()这样的错误。在Java 5中,用户自定义的注解可以用注解处理工具(Anotation Process Tool ——APT)在编译时进行处理。到了Java 6,这个功能已经是编译器的一部分了。</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">public class MyTest{</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; @Test</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp;public void testEmptyness( ){</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;org.junit.Assert.assertTrue(getList( ).isEmpty( ));</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp;}</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp;private List getList( ){</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp; &nbsp; //implemenation goes here</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp;}</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">}</span></p> </blockquote> <p><br></p> <p>@Test是JUnit框架用来在运行时通过反射来决定调用测试类的哪个(些)方法的注解。</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">@Test (timeout=100)</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">public void testTimeout( ) {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; while(true);&nbsp; &nbsp;//infinite loop</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">}</span></p> </blockquote> <p><br></p> <p>如果运行时间超过100ms的话,上面的测试用例就会失败。</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">@Test (expected=IndexOutOfBoundsException.class)</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">public void testOutOfBounds( ) {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp; &nbsp;new ArrayList&lt;Object&gt;( ).get(1);</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">}</span></p> </blockquote> <p><br></p> <p>如果上面的代码在运行时没有抛出IndexOutOfBoundsException或者抛出的是其他的异常的话,那么这个用例就会失败。用户自定义的注解可以在运行时通过Java反射API里新增的AnnotatedElement和”Annotation”元素接口来处理。</p> <p><br></p> <p><strong>异常(Exception)</strong>:你可以使用运行时异常或者编译时异常。</p> <p><br></p> <p><strong>运行时异常(RuntimeException)</strong>也称作未检测的异常(unchecked exception),这表示这种异常不需要编译器来检测。RuntimeException是所有可以在运行时抛出的异常的父类。一个方法除要捕获异常外,如果它执行的时候可能会抛出RuntimeException的子类,那么它就不需要用throw语句来声明抛出的异常。</p> <p><br></p> <p>例如:NullPointerException,ArrayIndexOutOfBoundsException,等等</p> <p><br></p> <p><strong>受检查异常(checked exception)</strong>都是编译器在编译时进行校验的,通过throws语句或者try{}cathch{} 语句块来处理检测异常。编译器会分析哪些异常会在执行一个方法或者构造函数的时候抛出。</p> <p><br></p> <p><strong>面向切面的编程(Aspect Oriented Programming-AOP)</strong>:切面可以在编译时,运行时或,加载时或者运行时织入。</p> <p><br></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><strong>编译期</strong>:编译期织入是最简单的方式。如果你拥有应用的代码,你可以使用AOP编译器(例如,ajc – AspectJ编译器)对源码进行编译,然后输出织入完成的class文件。AOP编译的过程包含了waver的调用。切面的形式可以是源码的形式也可以是二进制的形式。如果切面需要针对受影响的类进行编译,那么你就需要在编译期织入了。</p><p><br></p></li> <li><p><strong>编译后</strong>:这种方式有时候也被称为二进制织入,它被用来织入已有的class文件和jar文件。和编译时织入方式相同,用来织入的切面可以是源码也可以是二进制的形式,并且它们自己也可以被织入切面。</p><p><br></p></li> <li><p><strong>装载期</strong>:这种织入是一种二进制织入,它被延迟到JVM加载class文件和定义类的时候。为了支持这种织入方式,需要显式地由运行时环境或者通过一种“织入代理(weaving agent)“来提供一个或者多个“织入类加载器(weaving class loader)”。</p><p><br></p></li> <li><p><strong>运行时</strong>:对已经加载到JVM里的类进行织入</p></li> </ul> <p>&nbsp;</p> <p><strong>继承</strong> – 发生在编译时,因为它是静态的</p> <p><br></p> <p><strong>代理或者组合 </strong>– 发生在运行时,因为它更加具有动态性和灵活性。</p> <p><br></p> <p>Q.你有没有听说过“组合优于继承”这样的说法呢?如果听说过的话,那么你是怎么理解的呢?</p> <p><br></p> <p>A.继承是一种多态工具,而不是一种代码复用工具。有些开发者喜欢用继承的方式来实现代码复用,即使是在没有多态关系的情况下。是否使用继承的规则是继承只能用在类之间有“父子”关系的情况下。</p> <p><br></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p>不要仅仅为了代码复用而继承。当你使用组合来实现代码复用的时候,是不会产生继承关系的。过度使用继承(通过“extends”关键字)的话,如果修改了父类,会损坏所有的子类。这是因为子类和父类的紧耦合关系是在编译期产生的。</p></li> <li><p>不要仅仅为了多态而继承。如果你的类之间没有继承关系,并且你想要实现多态,那么你可以通过接口和组合的方式来实现,这样不仅可以实现代码重用,同时也可以实现运行时的灵活性。</p></li> </ul> <p><br></p> <p>这就是为什么四人帮(Gang of Four)的设计模式里更倾向于使用组合而不是继承的原因。面试者会在你的答案里着重关注这几个词语——“耦合”,“静态还是动态”,以及“发生在编译期还是运行时”。运行时的灵活性可以通过组合来实现,因为类可以在运行时动态地根据一个结果有条件或者无条件地进行组合。但是继承却是静态的。</p> <p><br></p> <p>Q.你能够通过实例来区别编译期继承和运行时继承,以及指出Java支持哪种吗?</p> <p><br></p> <p>A.“继承”表示动作和属性从一个对象传递到另外一个对象的场景。Java语言本身只支持编译期继承,它是通过“extends”关键字来产生子类的方式实现的,如下所示:</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">public class Parent {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; public String saySomething( ) {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return “Parent is called”;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; }</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);">&nbsp;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">public class Child extends Parent {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp;@Override</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp;public String saySomething( ) {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return super.saySomething( ) +&nbsp; “, Child is called”;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; }</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">}</span></p> </blockquote> <p><br></p> <p>“Child”类的saySomething()方法的调用会返回“Parent is called,Child is Called”,因为,子类的调用继承了父类的“Parenet is called”。关键字“super”是用来调用“Parent”类的方法。运行时继承表示在运行时构建父/子类关系。Java语言本身不支持运行时继承,但是有一种替代的方案叫做“代理”或者“组合”,它表示在运行时组件一个层次对象的子类。这样可以模拟运行时继承的实现。在Java里,代理的典型实现方式如下:</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">public class Parent {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; public String saySomething( ) {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return “Parent is called”;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; }</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);">&nbsp;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">public class Child&nbsp; {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp;public String saySomething( ) {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return new Parent( ).saySomething( ) +&nbsp; “, Child is called”;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; }</span></p> </blockquote> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">}</span></p> </blockquote> <p><br></p> <p>子类代理了父类的调用。组合可以按照下面的方式来实现:</p> <p><br></p> <blockquote> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">public class Child&nbsp; {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp;private Parent parent = null;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp;public Child( ){</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; this.parent = new Parent( );</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp;}</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp;public String saySomething( ) {</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return this.parent.saySomething( ) +&nbsp; “, Child is called”;</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">&nbsp; &nbsp; }</span></p> <p><span style="font-size: 12px;color: rgb(136, 136, 136);">}</span></p> </blockquote> <p><br></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, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;text-align: justify;white-space: normal;background-color: rgb(255, 255, 255);word-wrap: break-word !important;"> <section class="" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;box-sizing: border-box;text-align: left;word-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;word-wrap: break-word !important;"> <section class="" powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box;word-wrap: break-word !important;"> <section class="" style="max-width: 100%;box-sizing: border-box;word-wrap: break-word !important;"> <section class="" style="max-width: 100%;box-sizing: border-box;color: rgb(93, 93, 93);word-wrap: break-word !important;"> <p class="" style="max-width: 100%;box-sizing: border-box;min-height: 1em;font-size: 13px;word-wrap: break-word !important;">【关于投稿】</p> <p class="" style="max-width: 100%;box-sizing: border-box;min-height: 1em;font-size: 13px;word-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"></p> <p class="" style="max-width: 100%;box-sizing: border-box;min-height: 1em;font-size: 13px;word-wrap: break-word !important;">如果大家有原创好文投稿,请直接给公号发送留言。</p> <p class="" style="max-width: 100%;box-sizing: border-box;min-height: 1em;font-size: 13px;word-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"></p> <p class="" style="max-width: 100%;box-sizing: border-box;min-height: 1em;font-size: 13px;word-wrap: break-word !important;"><span style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;word-wrap: break-word !important;">①&nbsp;留言格式:</span><br style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;word-wrap: break-word !important;">【投稿】+《&nbsp;文章标题》+&nbsp;文章链接</span><br style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;word-wrap: break-word !important;"><br style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;word-wrap: break-word !important;">②&nbsp;示例:</span><br style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;word-wrap: break-word !important;">【投稿】《不要自称是程序员,我十多年的&nbsp;IT&nbsp;职场总结》:http://blog.jobbole.com/94148/</span><br style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;word-wrap: break-word !important;"><br style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(53, 53, 53);box-sizing: border-box !important;word-wrap: break-word !important;">③&nbsp;最后请附上您的个人简介哈~</span></span></p> <p style="max-width: 100%;min-height: 1em;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> </section> </section> </section> </section> </section> </section> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;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, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;color: rgb(255, 169, 0);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;">看完本文有收获?请转发分享给更多人</span></p> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;word-wrap: break-word !important;"><strong style="max-width: 100%;color: rgb(255, 169, 0);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;">关注「ImportNew」,提升Java技能</strong></p> <p style="max-width: 100%;min-height: 1em;color: rgb(51, 51, 51);font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 17px;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);text-align: center;box-sizing: border-box !important;word-wrap: break-word !important;"><img class="" data-ratio="0.9166666666666666" data-s="300,640" data-type="png" data-w="600" width="auto" src="/upload/899866149276fa5fddb73c61ae04be64.png" style="box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;visibility: visible !important;width: 600px !important;"></p>

Send to Kindle | 有多少90后,每天都活在人生崩溃的边缘

作者:微信小助手

<p style="text-align: center;"><img class="" data-copyright="0" data-ratio="0.11875" src="/upload/7e46361941675db393b2b98242f4fa7d.gif" data-type="gif" data-w="640" style=""></p> <section data-role="outer" label="Powered by 135editor.com" style="font-size: 16px;font-family: 微软雅黑;"> <section data-role="outer" label="Powered by 135editor.com" style="font-size: 16px;"> <section style="white-space: normal;box-sizing: border-box;"> <section data-role="paragraph" class="_135editor" style="border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;box-sizing: border-box;"> <p style="text-align: justify;letter-spacing: 2px;line-height: 1.75em;"><span style="font-size: 13px;">有多少90后,每天都活在人生崩溃的边缘?</span></p> <p style="text-align: justify;letter-spacing: 2px;line-height: 1.75em;"><span style="font-size: 13px;"><br></span></p> <p style="text-align: justify;letter-spacing: 2px;line-height: 1.75em;"><span style="font-size: 13px;"><span style="font-size: 13px;">为了<span style="font-size: 13px;">捡拾日常生活里的美好细节</span>,那些被我们忽略的平凡,需要被重新装点。</span></span></p> <p style="text-align: justify;letter-spacing: 2px;line-height: 1.75em;"><br></p> <p style="text-align: justify;letter-spacing: 2px;line-height: 1.75em;"><span style="font-size: 13px;">在这本<strong>《我只是对人生过敏》里,</strong>有<strong><span style="color: rgb(192, 0, 0);font-size: 17px;">105</span></strong>条涂鸦作品,在观赏作者呆萌画风的同时,也是一本集合了无厘头的世界观、古怪的冷笑话以及诡异的名词解释的英文段子书。</span></p> <p style="text-align: justify;letter-spacing: 2px;line-height: 1.75em;"><span style="font-size: 13px;"><br></span></p> <p style="text-align: justify;letter-spacing: 2px;line-height: 1.75em;"><span style="font-size: 13px;"><span style="font-size: 13px;">它们也许不能帮你解决什么难题,但却能为你无聊烦躁的生活带来快乐。</span></span></p> <p style="text-align: justify;letter-spacing: 2px;line-height: 1.75em;"><br></p> <p style="text-align: justify;letter-spacing: 2px;line-height: 1.75em;"><span style="font-size: 15px;"><strong>或许,你的日常就在这本《我只是对人生过敏》里↓</strong></span><span style="font-size: 13px;"><br></span></p> <p><br></p> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 3px;margin-bottom: 3px;text-align: center;box-sizing: border-box;"> <section class="" style="margin-right: auto;margin-left: auto;display: inline-block;border-width: 1px;border-style: solid;border-color: rgb(190, 182, 145);width: 1.8em;height: 1.8em;line-height: 1.8em;border-radius: 100%;font-size: 18px;box-sizing: border-box;background-color: rgb(190, 182, 145);"> <p style="box-sizing: border-box;"><strong style="box-sizing: border-box;">1</strong></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 30px;margin-bottom: 5px;text-align: center;box-sizing: border-box;"> <section class="" style="padding: 10px 10px 5px;display: inline-block;width: 530.094px;vertical-align: top;box-shadow: 1.41421px 1.41421px 6px rgb(185, 185, 185);box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="margin-bottom: 15px;box-sizing: border-box;"> <section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;box-shadow: 0px 0px 0px rgb(0, 0, 0);box-sizing: border-box;overflow: hidden ! important;"> <img class="" data-ratio="1.0094876660341556" src="/upload/e7c67344f66e45f2cfb57e089e64ca59.jpg" data-type="jpeg" data-w="527" style="vertical-align: middle;box-sizing: border-box;"> </section> </section> </section> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 5px;margin-bottom: 10px;text-align: center;box-sizing: border-box;"> <section class="" style="padding: 10px 10px 15px;display: inline-block;width: 530.094px;vertical-align: top;box-shadow: 1.41421px 1.41421px 6px rgb(185, 185, 185);box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="margin-top: -30px;box-sizing: border-box;"> <section class="" style="display: inline-block;vertical-align: top;width: 255.047px;box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;box-shadow: 0px 0px 0px rgb(0, 0, 0);width: 242.281px;box-sizing: border-box;overflow: hidden ! important;"> <img class="" data-ratio="0.2196078431372549" src="/upload/7cf3089e8987dfc7edc1bc29fe8411bd.png" data-type="png" data-w="255" style="vertical-align: middle;width: 242.281px;box-sizing: border-box;"> </section> </section> </section> </section> <section class="" style="display: inline-block;vertical-align: top;width: 255.047px;box-shadow: 0px 0px 0px rgb(0, 0, 0);box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;width: 242.281px;box-sizing: border-box;overflow: hidden ! important;"> <img class="" data-ratio="0.2196078431372549" src="/upload/7cf3089e8987dfc7edc1bc29fe8411bd.png" data-type="png" data-w="255" style="vertical-align: middle;width: 242.281px;box-sizing: border-box;"> </section> </section> </section> </section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="text-align: left;box-sizing: border-box;"> <p style="box-sizing: border-box;"><br></p> </section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="padding-right: 5px;padding-left: 5px;text-align: justify;font-size: 15px;box-sizing: border-box;"> <p style="box-sizing: border-box;">I fail to understand the “good” part of<strong>&nbsp;“good morning”.</strong></p> </section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="margin-top: 8px;margin-bottom: 18px;box-sizing: border-box;"> <section class="" style="border-top: 1px dashed rgb(185, 185, 185);box-sizing: border-box;"></section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="text-align: left;box-sizing: border-box;"> <section class="" style="padding-right: 5px;padding-left: 5px;text-align: justify;font-size: 15px;letter-spacing: 2px;box-sizing: border-box;"> <p style="box-sizing: border-box;">我就不明白了,<strong>“早上好”</strong>到底好在哪里。</p> </section> </section> </section> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <p style="box-sizing: border-box;"><br></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <p style="box-sizing: border-box;"><br></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 3px;margin-bottom: 3px;text-align: center;box-sizing: border-box;"> <section class="" style="margin-right: auto;margin-left: auto;display: inline-block;border-width: 1px;border-style: solid;border-color: rgb(190, 182, 145);width: 1.8em;height: 1.8em;line-height: 1.8em;border-radius: 100%;font-size: 18px;box-sizing: border-box;background-color: rgb(190, 182, 145);"> <p style="box-sizing: border-box;"><strong style="box-sizing: border-box;">2</strong></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 30px;margin-bottom: 5px;text-align: center;box-sizing: border-box;"> <section class="" style="padding: 10px 10px 5px;display: inline-block;width: 530.094px;vertical-align: top;box-shadow: 1.41421px 1.41421px 6px rgb(185, 185, 185);box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="margin-bottom: 15px;box-sizing: border-box;"> <section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;box-shadow: 0px 0px 0px rgb(0, 0, 0);box-sizing: border-box;overflow: hidden ! important;"> <img class="" data-ratio="1.0094876660341556" src="/upload/b2e8e8f49d4343831b68ccf80672e0f9.jpg" data-type="jpeg" data-w="527" style="vertical-align: middle;box-sizing: border-box;"> </section> </section> </section> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 5px;margin-bottom: 10px;text-align: center;box-sizing: border-box;"> <section class="" style="padding: 10px 10px 15px;display: inline-block;width: 530.094px;vertical-align: top;box-shadow: 1.41421px 1.41421px 6px rgb(185, 185, 185);box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="margin-top: -30px;box-sizing: border-box;"> <section class="" style="display: inline-block;vertical-align: top;width: 255.047px;box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;box-shadow: 0px 0px 0px rgb(0, 0, 0);width: 242.281px;box-sizing: border-box;overflow: hidden ! important;"> <img class="" data-ratio="0.2196078431372549" src="/upload/7cf3089e8987dfc7edc1bc29fe8411bd.png" data-type="png" data-w="255" style="vertical-align: middle;width: 242.281px;box-sizing: border-box;"> </section> </section> </section> </section> <section class="" style="display: inline-block;vertical-align: top;width: 255.047px;box-shadow: 0px 0px 0px rgb(0, 0, 0);box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;width: 242.281px;box-sizing: border-box;overflow: hidden ! important;"> <img class="" data-ratio="0.2196078431372549" src="/upload/7cf3089e8987dfc7edc1bc29fe8411bd.png" data-type="png" data-w="255" style="vertical-align: middle;width: 242.281px;box-sizing: border-box;"> </section> </section> </section> </section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="text-align: left;box-sizing: border-box;"> <p style="box-sizing: border-box;"><br></p> </section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="padding-right: 5px;padding-left: 5px;text-align: justify;font-size: 15px;letter-spacing: 0px;box-sizing: border-box;"> <p style="box-sizing: border-box;">If robbers even broke into my house and searched for money, I’d just laugh and search with them.</p> </section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="margin-top: 8px;margin-bottom: 18px;box-sizing: border-box;"> <section class="" style="border-top: 1px dashed rgb(185, 185, 185);box-sizing: border-box;"></section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="text-align: left;box-sizing: border-box;"> <section class="" style="padding-right: 5px;padding-left: 5px;text-align: justify;font-size: 15px;letter-spacing: 2px;box-sizing: border-box;"> <p style="box-sizing: border-box;">要是有劫匪闯入我的屋子里找钱,我只会大笑着和他们一起找。</p> </section> </section> </section> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <p style="box-sizing: border-box;"><br></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <p style="box-sizing: border-box;"><br></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 3px;margin-bottom: 3px;text-align: center;box-sizing: border-box;"> <section class="" style="margin-right: auto;margin-left: auto;display: inline-block;border-width: 1px;border-style: solid;border-color: rgb(190, 182, 145);width: 1.8em;height: 1.8em;line-height: 1.8em;border-radius: 100%;font-size: 18px;box-sizing: border-box;background-color: rgb(190, 182, 145);"> <p style="box-sizing: border-box;"><strong style="box-sizing: border-box;">3</strong></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 30px;margin-bottom: 5px;text-align: center;box-sizing: border-box;"> <section class="" style="padding: 10px 10px 5px;display: inline-block;width: 530.094px;vertical-align: top;box-shadow: 1.41421px 1.41421px 6px rgb(185, 185, 185);box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="margin-bottom: 15px;box-sizing: border-box;"> <section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;box-shadow: 0px 0px 0px rgb(0, 0, 0);box-sizing: border-box;overflow: hidden ! important;"> <img class="" data-ratio="1.0094876660341556" src="/upload/2e41aff9e578b4f03a154b8fe804f08c.jpg" data-type="jpeg" data-w="527" style="vertical-align: middle;box-sizing: border-box;"> </section> </section> </section> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 5px;margin-bottom: 10px;text-align: center;box-sizing: border-box;"> <section class="" style="padding: 10px 10px 15px;display: inline-block;width: 530.094px;vertical-align: top;box-shadow: 1.41421px 1.41421px 6px rgb(185, 185, 185);box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="margin-top: -30px;box-sizing: border-box;"> <section class="" style="display: inline-block;vertical-align: top;width: 255.047px;box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;box-shadow: 0px 0px 0px rgb(0, 0, 0);width: 242.281px;box-sizing: border-box;overflow: hidden ! important;"> <img class="" data-ratio="0.2196078431372549" src="/upload/7cf3089e8987dfc7edc1bc29fe8411bd.png" data-type="png" data-w="255" style="vertical-align: middle;width: 242.281px;box-sizing: border-box;"> </section> </section> </section> </section> <section class="" style="display: inline-block;vertical-align: top;width: 255.047px;box-shadow: 0px 0px 0px rgb(0, 0, 0);box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;width: 242.281px;box-sizing: border-box;overflow: hidden ! important;"> <img class="" data-ratio="0.2196078431372549" src="/upload/7cf3089e8987dfc7edc1bc29fe8411bd.png" data-type="png" data-w="255" style="vertical-align: middle;width: 242.281px;box-sizing: border-box;"> </section> </section> </section> </section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="text-align: left;box-sizing: border-box;"> <p style="box-sizing: border-box;"><br></p> </section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="padding-right: 5px;padding-left: 5px;text-align: justify;font-size: 15px;box-sizing: border-box;"> <p style="box-sizing: border-box;">Every once in a while I check up on people I hate to&nbsp;<strong>make sure I still hate them.</strong></p> </section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="margin-top: 8px;margin-bottom: 18px;box-sizing: border-box;"> <section class="" style="border-top: 1px dashed rgb(185, 185, 185);box-sizing: border-box;"></section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="text-align: left;box-sizing: border-box;"> <section class="" style="padding-right: 5px;padding-left: 5px;text-align: justify;font-size: 15px;letter-spacing: 2px;box-sizing: border-box;"> <p style="box-sizing: border-box;">每隔一段时间,我就会核对一遍我讨厌的人,<strong>确保我仍然恨着他们</strong>。</p> </section> </section> </section> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <p style="box-sizing: border-box;"><br></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <p style="box-sizing: border-box;"><br></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 3px;margin-bottom: 3px;text-align: center;box-sizing: border-box;"> <section class="" style="margin-right: auto;margin-left: auto;display: inline-block;border-width: 1px;border-style: solid;border-color: rgb(190, 182, 145);width: 1.8em;height: 1.8em;line-height: 1.8em;border-radius: 100%;font-size: 18px;box-sizing: border-box;background-color: rgb(190, 182, 145);"> <p style="box-sizing: border-box;"><strong style="box-sizing: border-box;">4</strong></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 30px;margin-bottom: 5px;text-align: center;box-sizing: border-box;"> <section class="" style="padding: 10px 10px 5px;display: inline-block;width: 530.094px;vertical-align: top;box-shadow: 1.41421px 1.41421px 6px rgb(185, 185, 185);box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="margin-bottom: 15px;box-sizing: border-box;"> <section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;box-shadow: 0px 0px 0px rgb(0, 0, 0);box-sizing: border-box;overflow: hidden ! important;"> <img class="" data-ratio="1.0094876660341556" src="/upload/8489fa13bea08d614fec4b2a2795da1e.jpg" data-type="jpeg" data-w="527" style="vertical-align: middle;box-sizing: border-box;"> </section> </section> </section> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 5px;margin-bottom: 10px;text-align: center;box-sizing: border-box;"> <section class="" style="padding: 10px 10px 15px;display: inline-block;width: 530.094px;vertical-align: top;box-shadow: 1.41421px 1.41421px 6px rgb(185, 185, 185);box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="margin-top: -30px;box-sizing: border-box;"> <section class="" style="display: inline-block;vertical-align: top;width: 255.047px;box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;box-shadow: 0px 0px 0px rgb(0, 0, 0);width: 242.281px;box-sizing: border-box;overflow: hidden ! important;"> <img class="" data-ratio="0.2196078431372549" src="/upload/7cf3089e8987dfc7edc1bc29fe8411bd.png" data-type="png" data-w="255" style="vertical-align: middle;width: 242.281px;box-sizing: border-box;"> </section> </section> </section> </section> <section class="" style="display: inline-block;vertical-align: top;width: 255.047px;box-shadow: 0px 0px 0px rgb(0, 0, 0);box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;width: 242.281px;box-sizing: border-box;overflow: hidden ! important;"> <img class="" data-ratio="0.2196078431372549" src="/upload/7cf3089e8987dfc7edc1bc29fe8411bd.png" data-type="png" data-w="255" style="vertical-align: middle;width: 242.281px;box-sizing: border-box;"> </section> </section> </section> </section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="text-align: left;box-sizing: border-box;"> <p style="box-sizing: border-box;"><br></p> </section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="padding-right: 5px;padding-left: 5px;text-align: justify;font-size: 15px;box-sizing: border-box;"> <p style="box-sizing: border-box;">I’m waiting for the day that scientists discover a cure for&nbsp;<strong>Natural Causes</strong>.</p> </section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="margin-top: 8px;margin-bottom: 18px;box-sizing: border-box;"> <section class="" style="border-top: 1px dashed rgb(185, 185, 185);box-sizing: border-box;"></section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="text-align: left;box-sizing: border-box;"> <section class="" style="padding-right: 5px;padding-left: 5px;text-align: justify;font-size: 15px;letter-spacing: 2px;box-sizing: border-box;"> <p style="box-sizing: border-box;">我等待着科学家成功治愈<strong>“顺其自然综合征”</strong>的那一天。</p> </section> </section> </section> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <p style="box-sizing: border-box;"><br></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <p style="box-sizing: border-box;"><br></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 3px;margin-bottom: 3px;text-align: center;box-sizing: border-box;"> <section class="" style="margin-right: auto;margin-left: auto;display: inline-block;border-width: 1px;border-style: solid;border-color: rgb(190, 182, 145);width: 1.8em;height: 1.8em;line-height: 1.8em;border-radius: 100%;font-size: 18px;box-sizing: border-box;background-color: rgb(190, 182, 145);"> <p style="box-sizing: border-box;"><strong style="box-sizing: border-box;">5</strong></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 30px;margin-bottom: 5px;text-align: center;box-sizing: border-box;"> <section class="" style="padding: 10px 10px 5px;display: inline-block;width: 530.094px;vertical-align: top;box-shadow: 1.41421px 1.41421px 6px rgb(185, 185, 185);box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="margin-bottom: 15px;box-sizing: border-box;"> <section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;box-shadow: 0px 0px 0px rgb(0, 0, 0);box-sizing: border-box;overflow: hidden ! important;"> <img class="" data-ratio="1.0094876660341556" src="/upload/eb121f2593e91769cae446baba9c8cf3.jpg" data-type="jpeg" data-w="527" style="vertical-align: middle;box-sizing: border-box;"> </section> </section> </section> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 5px;margin-bottom: 10px;text-align: center;box-sizing: border-box;"> <section class="" style="padding: 10px 10px 15px;display: inline-block;width: 530.094px;vertical-align: top;box-shadow: 1.41421px 1.41421px 6px rgb(185, 185, 185);box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="margin-top: -30px;box-sizing: border-box;"> <section class="" style="display: inline-block;vertical-align: top;width: 255.047px;box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;box-shadow: 0px 0px 0px rgb(0, 0, 0);width: 242.281px;box-sizing: border-box;overflow: hidden ! important;"> <img class="" data-ratio="0.2196078431372549" src="/upload/7cf3089e8987dfc7edc1bc29fe8411bd.png" data-type="png" data-w="255" style="vertical-align: middle;width: 242.281px;box-sizing: border-box;"> </section> </section> </section> </section> <section class="" style="display: inline-block;vertical-align: top;width: 255.047px;box-shadow: 0px 0px 0px rgb(0, 0, 0);box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;width: 242.281px;box-sizing: border-box;overflow: hidden ! important;"> <img class="" data-ratio="0.2196078431372549" src="/upload/7cf3089e8987dfc7edc1bc29fe8411bd.png" data-type="png" data-w="255" style="vertical-align: middle;width: 242.281px;box-sizing: border-box;"> </section> </section> </section> </section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="text-align: left;box-sizing: border-box;"> <p style="box-sizing: border-box;"><br></p> </section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="padding-right: 5px;padding-left: 5px;font-size: 15px;box-sizing: border-box;"> <p style="text-align: justify;box-sizing: border-box;">Old people at weddings always poke me and say&nbsp;<strong style="box-sizing: border-box;">“you’re next.”</strong>&nbsp;So, I started doing the same thing to them at funerals.</p> </section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="margin-top: 8px;margin-bottom: 18px;box-sizing: border-box;"> <section class="" style="border-top: 1px dashed rgb(185, 185, 185);box-sizing: border-box;"></section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="text-align: left;box-sizing: border-box;"> <section class="" style="padding-right: 5px;padding-left: 5px;text-align: justify;font-size: 15px;letter-spacing: 2px;box-sizing: border-box;"> <p style="box-sizing: border-box;">老人们喜欢在婚礼上戳戳我,并对我说:“<strong>下一个就是你啦</strong>。”所以,我开始在葬礼上以牙还牙。</p> </section> </section> </section> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <p style="box-sizing: border-box;"><br></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <p style="box-sizing: border-box;"><br></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 3px;margin-bottom: 3px;text-align: center;box-sizing: border-box;"> <section class="" style="margin-right: auto;margin-left: auto;display: inline-block;border-width: 1px;border-style: solid;border-color: rgb(190, 182, 145);width: 1.8em;height: 1.8em;line-height: 1.8em;border-radius: 100%;font-size: 18px;box-sizing: border-box;background-color: rgb(190, 182, 145);"> <p style="box-sizing: border-box;"><strong style="box-sizing: border-box;">6</strong></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 30px;margin-bottom: 5px;text-align: center;box-sizing: border-box;"> <section class="" style="padding: 10px 10px 5px;display: inline-block;width: 530.094px;vertical-align: top;box-shadow: 1.41421px 1.41421px 6px rgb(185, 185, 185);box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="margin-bottom: 15px;box-sizing: border-box;"> <section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;box-shadow: 0px 0px 0px rgb(0, 0, 0);box-sizing: border-box;overflow: hidden ! important;"> <img class="" data-ratio="1.0094876660341556" src="/upload/58eda238170ec7ecc3c0b8045402cb0a.jpg" data-type="jpeg" data-w="527" style="vertical-align: middle;box-sizing: border-box;"> </section> </section> </section> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 5px;margin-bottom: 10px;text-align: center;box-sizing: border-box;"> <section class="" style="padding: 10px 10px 15px;display: inline-block;width: 530.094px;vertical-align: top;box-shadow: 1.41421px 1.41421px 6px rgb(185, 185, 185);box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="margin-top: -30px;box-sizing: border-box;"> <section class="" style="display: inline-block;vertical-align: top;width: 255.047px;box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;box-shadow: 0px 0px 0px rgb(0, 0, 0);width: 242.281px;box-sizing: border-box;overflow: hidden ! important;"> <img class="" data-ratio="0.2196078431372549" src="/upload/7cf3089e8987dfc7edc1bc29fe8411bd.png" data-type="png" data-w="255" style="vertical-align: middle;width: 242.281px;box-sizing: border-box;"> </section> </section> </section> </section> <section class="" style="display: inline-block;vertical-align: top;width: 255.047px;box-shadow: 0px 0px 0px rgb(0, 0, 0);box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;width: 242.281px;box-sizing: border-box;overflow: hidden ! important;"> <img class="" data-ratio="0.2196078431372549" src="/upload/7cf3089e8987dfc7edc1bc29fe8411bd.png" data-type="png" data-w="255" style="vertical-align: middle;width: 242.281px;box-sizing: border-box;"> </section> </section> </section> </section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="text-align: left;box-sizing: border-box;"> <p style="box-sizing: border-box;"><br></p> </section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="padding-right: 5px;padding-left: 5px;font-size: 15px;box-sizing: border-box;"> <p style="text-align: justify;box-sizing: border-box;">If I knew I wasn’t going to fall asleep in the last 2 hours, I could have been using the Internet!&nbsp;</p> </section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="margin-top: 8px;margin-bottom: 18px;box-sizing: border-box;"> <section class="" style="border-top: 1px dashed rgb(185, 185, 185);box-sizing: border-box;"></section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="text-align: left;box-sizing: border-box;"> <section class="" style="padding-right: 5px;padding-left: 5px;text-align: justify;font-size: 15px;letter-spacing: 2px;box-sizing: border-box;"> <p style="box-sizing: border-box;">早知道花了两个小时都没睡着,我本可以好好上上网的。</p> </section> </section> </section> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <p style="box-sizing: border-box;"><br></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <p style="box-sizing: border-box;"><br></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 3px;margin-bottom: 3px;text-align: center;box-sizing: border-box;"> <section class="" style="margin-right: auto;margin-left: auto;display: inline-block;border-width: 1px;border-style: solid;border-color: rgb(190, 182, 145);width: 1.8em;height: 1.8em;line-height: 1.8em;border-radius: 100%;font-size: 18px;box-sizing: border-box;background-color: rgb(190, 182, 145);"> <p style="box-sizing: border-box;"><strong style="box-sizing: border-box;">7</strong></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 30px;margin-bottom: 5px;text-align: center;box-sizing: border-box;"> <section class="" style="padding: 10px 10px 5px;display: inline-block;width: 530.094px;vertical-align: top;box-shadow: 1.41421px 1.41421px 6px rgb(185, 185, 185);box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="margin-bottom: 15px;box-sizing: border-box;"> <section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;box-shadow: 0px 0px 0px rgb(0, 0, 0);box-sizing: border-box;overflow: hidden ! important;"> <img class="" data-ratio="1.0114068441064639" src="/upload/c69ca7aefc8b4c7cf787f46e63c1b275.jpg" data-type="jpeg" data-w="526" style="vertical-align: middle;box-sizing: border-box;"> </section> </section> </section> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 5px;margin-bottom: 10px;text-align: center;box-sizing: border-box;"> <section class="" style="padding: 10px 10px 15px;display: inline-block;width: 530.094px;vertical-align: top;box-shadow: 1.41421px 1.41421px 6px rgb(185, 185, 185);box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="margin-top: -30px;box-sizing: border-box;"> <section class="" style="display: inline-block;vertical-align: top;width: 255.047px;box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;box-shadow: 0px 0px 0px rgb(0, 0, 0);width: 242.281px;box-sizing: border-box;overflow: hidden ! important;"> <img class="" data-ratio="0.2196078431372549" src="/upload/7cf3089e8987dfc7edc1bc29fe8411bd.png" data-type="png" data-w="255" style="vertical-align: middle;width: 242.281px;box-sizing: border-box;"> </section> </section> </section> </section> <section class="" style="display: inline-block;vertical-align: top;width: 255.047px;box-shadow: 0px 0px 0px rgb(0, 0, 0);box-sizing: border-box;"> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="max-width: 100%;vertical-align: middle;display: inline-block;width: 242.281px;box-sizing: border-box;overflow: hidden ! important;"> <img class="" data-ratio="0.2196078431372549" src="/upload/7cf3089e8987dfc7edc1bc29fe8411bd.png" data-type="png" data-w="255" style="vertical-align: middle;width: 242.281px;box-sizing: border-box;"> </section> </section> </section> </section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="text-align: left;box-sizing: border-box;"> <p style="box-sizing: border-box;"><br></p> </section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <section class="" style="padding-right: 5px;padding-left: 5px;text-align: justify;font-size: 15px;box-sizing: border-box;"> <p style="box-sizing: border-box;">Person is tying…</p> <p style="box-sizing: border-box;">Person is tying…</p> <p style="box-sizing: border-box;">Person is tying…</p> <p style="box-sizing: border-box;">Person is tying…</p> <p style="box-sizing: border-box;">Person is tying…</p> <p style="box-sizing: border-box;">Person says:<strong style="box-sizing: border-box;">hi</strong></p> </section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="margin-top: 8px;margin-bottom: 18px;box-sizing: border-box;"> <section class="" style="border-top: 1px dashed rgb(185, 185, 185);box-sizing: border-box;"></section> </section> </section> <section style="box-sizing: border-box;"> <section class="" style="text-align: left;box-sizing: border-box;"> <section class="" style="padding-right: 5px;padding-left: 5px;font-size: 15px;letter-spacing: 2px;box-sizing: border-box;"> <p style="box-sizing: border-box;">对方正在输入……</p> <p style="box-sizing: border-box;">对方正在输入……</p> <p style="box-sizing: border-box;">对方正在输入……</p> <p style="box-sizing: border-box;">对方正在输入……</p> <p style="box-sizing: border-box;">对方正在输入……</p> <p style="box-sizing: border-box;">对方说:<strong style="box-sizing: border-box;">嗨</strong>。</p> </section> </section> </section> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <p style="box-sizing: border-box;"><br></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="box-sizing: border-box;"> <section class="" style="box-sizing: border-box;"> <p style="box-sizing: border-box;"><br></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 3px;margin-bottom: 3px;text-align: center;box-sizing: border-box;"> <section class="" style="margin-right: auto;margin-left: auto;display: inline-block;border-width: 1px;border-style: solid;border-color: rgb(190, 182, 145);width: 1.8em;height: 1.8em;line-height: 1.8em;border-radius: 100%;font-size: 18px;box-sizing: border-box;background-color: rgb(190, 182, 145);"> <p style="box-sizing: border-box;"><strong style="box-sizing: border-box;">8</strong></p> </section> </section> </section> <section class="_135editor" style="box-sizing: border-box;border-width: 0px;border-style: none;border-color: currentcolor;-moz-border-top-colors: none;-moz-border-right-colors: none;-moz-border-bottom-colors: none;-moz-border-left-colors: none;"> <section class="" style="margin-top: 30px;margin-bottom: 5px;text-align: center;box-sizing: border-box;"> <section class="" style="padding: 10px 10px 5px;display: inline-block;width: 5

在Java中如何优雅地判空

作者:微信小助手

<p style="text-align: center;margin-bottom: 10px;" data-mpa-powered-by="yiban.io"><span style="color: rgb(51, 51, 51);letter-spacing: 0.544px;text-align: center;background-color: rgb(255, 255, 255);font-family: CourierNewPS-ItalicMT;font-size: 13px;">【公众号回复“</span><strong style="color: rgb(51, 51, 51);letter-spacing: 0.544px;text-align: center;white-space: normal;background-color: rgb(255, 255, 255);font-family: CourierNewPS-ItalicMT;font-size: 13px;"><span style="color: rgb(0, 82, 255);">1024</span></strong><span style="color: rgb(51, 51, 51);letter-spacing: 0.544px;text-align: center;background-color: rgb(255, 255, 255);font-family: CourierNewPS-ItalicMT;font-size: 13px;">”,送你一个特别推送】</span></p> <p style="text-align: center;"><img class="" data-backh="314" data-backw="498" data-before-oversubscription-url="https://mmbiz.qpic.cn/mmbiz_jpg/CvQa8Yf8vq3wwribUdUxuEEc9vIz7EHtibtjXDb19QxHax8ic8ruUY1vWdxKJRdPH8LUv9yqagS1EPYaNLRyGickWQ/0?wx_fmt=jpeg" data-copyright="0" data-oversubscription-url="http://mmbiz.qpic.cn/mmbiz_jpg/CvQa8Yf8vq3wwribUdUxuEEc9vIz7EHtibB7SpHrqhqnVQuswoJFS0rC3aYG1dJG4fLXVwAVE9ic34q2Rz98IlrMw/0?wx_fmt=jpeg" data-ratio="0.6292585170340681" data-s="300,640" src="/upload/9803b13e5d56b89580f5a7c9ffc6411a.jpg" data-type="jpeg" data-w="499" style="width: 100%;height: auto;"></p> <h3 style="margin-top: 20px;margin-bottom: 10px;font-size: 18px;letter-spacing: 1.4px;white-space: normal;color: rgb(0, 150, 136) !important;">判空灾难</h3> <p style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;margin: 1.5em 5px !important;"><img class="" data-ratio="0.215" src="/upload/d116859653b25b1597bfdeae48129713.png" data-type="png" data-w="1000"></p> <p style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;margin: 1.5em 5px !important;">作为搬砖党的一族们,我们对判空一定再熟悉不过了,不要跟我说你很少进行判空,除非你喜欢NullPointerException。</p> <p style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;margin: 1.5em 5px !important;">不过NullPointerException对于很多猿们来说,也是Exception家族中最亲近的一员了。</p> <p style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;margin: 1.5em 5px !important;text-align: center;"><img class="" data-ratio="0.6654545454545454" src="/upload/73df1eea2116386a9df9b55ed5078e15.png" data-type="png" data-w="275"></p> <p style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;margin: 1.5em 5px !important;">为了避免NullPointerException来找我们,我们经常会进行如下操作。</p> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" class="mpa-template" mpa-preserve="t"> <pre style="margin:0;padding:0;border-radius:none;background:none;"><code style="border-radius: 4px;font-size: 0.85em;margin: 0px 0.15em;background: rgb(40, 44, 52);color: rgb(171, 178, 191);display: block;padding: 5.95px;overflow-x: auto;white-space: nowrap;" class="hljs-default"><span class="hljs-default-keyword" style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 14px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">if</span> (data != <span class="hljs-default-keyword" style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">null</span>) {<br> &nbsp; &nbsp;<span class="hljs-default-keyword" style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 14px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">do</span> sth.<br>}</code></pre> </section> <p style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;margin: 1.5em 5px !important;">如果一个类中多次使用某个对象,那你可能要一顿操作,so:<br></p> <p style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;margin: 1.5em 5px !important;text-align: center;"><img class="" data-ratio="2.2931596091205213" src="/upload/c4b7a239fbac66a8b2699d1525a0afa4.png" data-type="png" data-w="307"></p> <p style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;margin: 1.5em 5px !important;">“世界第九大奇迹”就这样诞生了。Maybe你会想,项目中肯定不止你一个人会这样一顿操作,然后按下Command+Shift+F,真相就在眼前:</p> <p style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;margin: 1.5em 5px !important;text-align: center;"><img class="" data-ratio="0.21584699453551912" src="/upload/d35654d21845f0ec43b2a042132a0086.png" data-type="png" data-w="366"></p> <p style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;margin: 1.5em 5px !important;">What,我们有接近一万行的代码都是在判空?</p> <p style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;margin: 1.5em 5px !important;text-align: center;"><img class="" data-ratio="0.75" src="/upload/79df88a5fe8be0a8f595eb61f66366e7.png" data-type="png" data-w="480"></p> <p style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;margin: 1.5em 5px !important;">好了,接下来,要进入正题了。</p> <h3 style="margin-top: 20px;margin-bottom: 10px;font-size: 18px;letter-spacing: 1.4px;white-space: normal;color: rgb(0, 150, 136) !important;">NullObject模式</h3> <p style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;margin: 1.5em 5px !important;">对于项目中无数次的判空,对代码质量整洁度产生了十分之恶劣的影响,对于这种现象,我们称之为“判空灾难”。</p> <p style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;margin: 1.5em 5px !important;">那么,这种现象如何治理呢,你可能听说过NullObject模式,不过这不是我们今天的武器,但是还是需要介绍一下NullObject模式。</p> <p style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;margin: 1.5em 5px !important;">什么是NullObject模式呢?</p> <blockquote style="margin: 10px 5px 10px 1em;padding-right: 10px;border-left-width: 2px;border-left-color: rgb(0, 150, 136);font-size: 14px;letter-spacing: 1.4px;white-space: normal;color: rgb(119, 119, 119);quotes: none;"> <p style="margin: 1.5em 5px !important;">In object-oriented computer programming, a null object is an object with no referenced value or with defined neutral (“null”) behavior. The null object design pattern describes the uses of such objects and their behavior (or lack thereof).</p> </blockquote> <p style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;margin: 1.5em 5px !important;">以上解析来自Wikipedia。</p> <p style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;margin: 1.5em 5px !important;">NullObject模式首次发表在“ 程序设计模式语言 ”系列丛书中。一般的,在面向对象语言中,对对象的调用前需要使用判空检查,来判断这些对象是否为空,因为在空引用上无法调用所需方法。</p> <p style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;margin: 1.5em 5px !important;">空对象模式的一种典型实现方式如下图所示(图片来自网络):</p> <p style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;margin: 1.5em 5px !important;text-align: center;"><img class="" data-ratio="0.45481927710843373" src="/upload/9c4719b1232f813dd8edddcc0f80375b.png" data-type="png" data-w="332"></p> <p style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;margin: 1.5em 5px !important;">示例代码如下(命名来自网络,哈哈到底是有多懒):</p> <p style="font-size: 14px;letter-spacing: 1.4px;white-space: normal;margin: 1.5em 5px !important;">Nullable是空对象的相关操作接口,用于确定对象是否为空,因为在空对象模式中,对象为空会被包装成一个Object,成为Null Object,该对象会对原有对象的所有方法进行空实现。</p> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" class="mpa-template" mpa-preserve="t"> <pre style="margin:0;paddi

亿级Web系统搭建:单机到分布式集群

作者:じ☆ve宝贝

当一个Web系统从日访问量10万逐步增长到1000万,甚至超过1亿的过程中,Web系统承受的压力会越来越大,在这个过程中,我们会遇到很多的问题。为了解决这些性能压力带来问题,我们需要在Web系统架构层面搭建多个层次的缓存机制。在不同的压力阶段,我们会遇到不同的问题,通过搭建不同的服务和架构来解决。 ##Web负载均衡 Web负载均衡(Load Balancing),简单地说就是给我们的服务器集群分配“工作任务”,而采用恰当的分配方式,对于保护处于后端的Web服务器来说,非常重要。 ![web负载均衡](/upload/5e4d414cgw1ew9sqf5g60j20cd06yjri.jpg "web负载均衡") 载均衡的策略有很多,我们从简单的讲起哈。 ####1. HTTP重定向 当用户发来请求的时候,Web服务器通过修改HTTP响应头中的Location标记来返回一个新的url,然后浏览器再继续请求这个新url,实际上就是页面重定向。通过重定向,来达到“负载均衡”的目标。例如,我们在下载PHP源码包的时候,点击下载链接时,为了解决不同国家和地域下载速度的问题,它会返回一个离我们近的下载地址。重定向的HTTP返回码是302,如下图: ![http重定向](/upload/5e4d414cgw1ew9sqfdp7qj20dp060tac.jpg "http重定向") 如果使用PHP代码来实现这个功能,方式如下: ![http重定向](/upload/5e4d414cgw1ew9sqfohdrj20ci03g74m.jpg "http重定向") 这个重定向非常容易实现,并且可以自定义各种策略。但是,它在大规模访问量下,性能不佳。而且,给用户的体验也不好,实际请求发生重定向,增加了网络延时。 ####2. 反向代理负载均衡 反向代理服务的核心工作主要是转发HTTP请求,扮演了浏览器端和后台Web服务器中转的角色。因为它工作在HTTP层(应用层),也就是网络七层结构中的第七层,因此也被称为“七层负载均衡”。可以做反向代理的软件很多,比较常见的一种是Nginx。 ![nginx 反向负载均衡](/upload/5e4d414cgw1ew9sqg1unoj20be06oaa5.jpg "nginx 反向负载均衡") Nginx是一种非常灵活的反向代理软件,可以自由定制化转发策略,分配服务器流量的权重等。反向代理中,常见的一个问题,就是Web服务器存储的session数据,因为一般负载均衡的策略都是随机分配请求的。同一个登录用户的请求,无法保证一定分配到相同的Web机器上,会导致无法找到session的问题。 解决方案主要有两种: 1.配置反向代理的转发规则,让同一个用户的请求一定落到同一台机器上(通过分析cookie),复杂的转发规则将会消耗更多的CPU,也增加了代理服务器的负担。 2.将session这类的信息,专门用某个独立服务来存储,例如redis/memchache,这个方案是比较推荐的。 反向代理服务,也是可以开启缓存的,如果开启了,会增加反向代理的负担,需要谨慎使用。这种负载均衡策略实现和部署非常简单,而且性能表现也比较好。但是,它有“单点故障”的问题,如果挂了,会带来很多的麻烦。而且,到了后期Web服务器继续增加,它本身可能成为系统的瓶颈。 ####3. IP负载均衡 IP负载均衡服务是工作在网络层(修改IP)和传输层(修改端口,第四层),比起工作在应用层(第七层)性能要高出非常多。原理是,他是对IP层的数据包的IP地址和端口信息进行修改,达到负载均衡的目的。这种方式,也被称为“四层负载均衡”。常见的负载均衡方式,是LVS(Linux Virtual Server,Linux虚拟服务),通过IPVS(IP Virtual Server,IP虚拟服务)来实现。 ![IP负载均衡](/upload/5e4d414cgw1ew9sqgslaqj20ci0az0t0.jpg "IP负载均衡") 在负载均衡服务器收到客户端的IP包的时候,会修改IP包的目标IP地址或端口,然后原封不动地投递到内部网络中,数据包会流入到实际Web服务器。实际服务器处理完成后,又会将数据包投递回给负载均衡服务器,它再修改目标IP地址为用户IP地址,最终回到客户端。 ![ip负载均衡](/upload/5e4d414cgw1ew9sqgxp6nj209g0a0wes2.jpg "ip负载均衡") 上述的方式叫LVS-NAT,除此之外,还有LVS-RD(直接路由),LVS-TUN(IP隧道),三者之间都属于LVS的方式,但是有一定的区别,篇幅问题,不赘叙。 IP负载均衡的性能要高出Nginx的反向代理很多,它只处理到传输层为止的数据包,并不做进一步的组包,然后直接转发给实际服务器。不过,它的配置和搭建比较复杂。 ####4. DNS负载均衡 DNS(Domain Name System)负责域名解析的服务,域名url实际上是服务器的别名,实际映射是一个IP地址,解析过程,就是DNS完成域名到IP的映射。而一个域名是可以配置成对应多个IP的。因此,DNS也就可以作为负载均衡服务。 ![DNS负载均衡](/upload/5e4d414cgw1ew9sqh8ym4j20go02ejrg.jpg "DNS负载均衡") 这种负载均衡策略,配置简单,性能极佳。但是,不能自由定义规则,而且,变更被映射的IP或者机器故障时很麻烦,还存在DNS生效延迟的问题。 ####5. DNS/GSLB负载均衡 我们常用的CDN(Content Delivery Network,内容分发网络)实现方式,其实就是在同一个域名映射为多IP的基础上更进一步,通过GSLB(Global Server Load Balance,全局负载均衡)按照指定规则映射域名的IP。一般情况下都是按照地理位置,将离用户近的IP返回给用户,减少网络传输中的路由节点之间的跳跃消耗。 ![DNS/GSLB负载均衡](/upload/5e4d414cgw1ew9sqhufj6j20f505ajrm1.jpg "DNS/GSLB负载均衡") CDN在Web系统中,一般情况下是用来解决大小较大的静态资源(html/Js/Css/图片等)的加载问题,让这些比较依赖网络下载的内容,尽可能离用户更近,提升用户体验。 例如,我访问了一张imgcache.gtimg.cn上的图片(腾讯的自建CDN,不使用qq.com域名的原因是防止http请求的时候,带上了多余的cookie信息),我获得的IP是183.60.217.90。 ![DNS/GSLB负载均衡](/upload/5e4d414cgw1ew9sqi7dmlj20fq09gjrq1.jpg "DNS/GSLB负载均衡") 这种方式,和前面的DNS负载均衡一样,不仅性能极佳,而且支持配置多种策略。但是,搭建和维护成本非常高。互联网一线公司,会自建CDN服务,中小型公司一般使用第三方提供的CDN。 ##Web系统的缓存机制的建立和优化 刚刚我们讲完了Web系统的外部网络环境,现在我们开始关注我们Web系统自身的性能问题。我们的Web站点随着访问量的上升,会遇到很多的挑战,解决这些问题不仅仅是扩容机器这么简单,建立和使用合适的缓存机制才是根本。 最开始,我们的Web系统架构可能是这样的,每个环节,都可能只有1台机器。 ![web优化](/upload/5e4d414cgw1ew9sqiu61cj20cs028aa0.jpg "web优化") 我们从最根本的数据存储开始看哈。 ##一、 MySQL数据库内部缓存使用 MySQL的缓存机制,就从先从MySQL内部开始,下面的内容将以最常见的InnoDB存储引擎为主。 ####1. 建立恰当的索引 最简单的是建立索引,索引在表数据比较大的时候,起到快速检索数据的作用,但是成本也是有的。首先,占用了一定的磁盘空间,其中组合索引最突出,使用需要谨慎,它产生的索引甚至会比源数据更大。其次,建立索引之后的数据insert/update/delete等操作,因为需要更新原来的索引,耗时会增加。当然,实际上我们的系统从总体来说,是以select查询操作居多,因此,索引的使用仍然对系统性能有大幅提升的作用。 ####2. 数据库连接线程池缓存 如果,每一个数据库操作请求都需要创建和销毁连接的话,对数据库来说,无疑也是一种巨大的开销。为了减少这类型的开销,可以在MySQL中配置thread_cache_size来表示保留多少线程用于复用。线程不够的时候,再创建,空闲过多的时候,则销毁。 ![mysql 线程缓存](/upload/5e4d414cgw1ew9sqj4h6kj20be03m3yl1.jpg "mysql 线程缓存") 其实,还有更为激进一点的做法,使用pconnect(数据库长连接),线程一旦创建在很长时间内都保持着。但是,在访问量比较大,机器比较多的情况下,这种用法很可能会导致“数据库连接数耗尽”,因为建立连接并不回收,最终达到数据库的max_connections(最大连接数)。因此,长连接的用法通常需要在CGI和MySQL之间实现一个“连接池”服务,控制CGI机器“盲目”创建连接数。 ![mysql 线程缓存](/upload/5e4d414cgw1ew9sqj6rehj20cd03m74b.jpg "mysql 线程缓存") 建立数据库连接池服务,有很多实现的方式,PHP的话,我推荐使用swoole(PHP的一个网络通讯拓展)来实现。 ####3. Innodb缓存设置(innodb_buffer_pool_size) innodb_buffer_pool_size这是个用来保存索引和数据的内存缓存区,如果机器是MySQL独占的机器,一般推荐为机器物理内存的80%。在取表数据的场景中,它可以减少磁盘IO。一般来说,这个值设置越大,cache命中率会越高。 ####4. 分库/分表/分区。 MySQL数据库表一般承受数据量在百万级别,再往上增长,各项性能将会出现大幅度下降,因此,当我们预见数据量会超过这个量级的时候,建议进行分库/分表/分区等操作。最好的做法,是服务在搭建之初就设计为分库分表的存储模式,从根本上杜绝中后期的风险。不过,会牺牲一些便利性,例如列表式的查询,同时,也增加了维护的复杂度。不过,到了数据量千万级别或者以上的时候,我们会发现,它们都是值得的。 ##二、 MySQL数据库多台服务搭建 1台MySQL机器,实际上是高风险的单点,因为如果它挂了,我们Web服务就不可用了。而且,随着Web系统访问量继续增加,终于有一天,我们发现1台MySQL服务器无法支撑下去,我们开始需要使用更多的MySQL机器。当引入多台MySQL机器的时候,很多新的问题又将产生。 ####1. 建立MySQL主从,从库作为备份 这种做法纯粹为了解决“单点故障”的问题,在主库出故障的时候,切换到从库。不过,这种做法实际上有点浪费资源,因为从库实际上被闲着了。 ![mysql 主从同步](/upload/5e4d414cgw1ew9sqjhvgqj20d20693yn.jpg "mysql 主从同步") ####2. MySQL读写分离,主库写,从库读。 两台数据库做读写分离,主库负责写入类的操作,从库负责读的操作。并且,如果主库发生故障,仍然不影响读的操作,同时也可以将全部读写都临时切换到从库中(需要注意流量,可能会因为流量过大,把从库也拖垮)。 ![mysql 读写分离](/upload/5e4d414cgw1ew9sqjrlouj20d2069t8t.jpg "mysql 读写分离") ####3. 主主互备。 两台MySQL之间互为彼此的从库,同时又是主库。这种方案,既做到了访问量的压力分流,同时也解决了“单点故障”问题。任何一台故障,都还有另外一套可供使用的服务。 ![mysql 双主](/upload/5e4d414cgw1ew9sqk48qqj20a606ydfv.jpg "mysql 双主") 不过,这种方案,只能用在两台机器的场景。如果业务拓展还是很快的话,可以选择将业务分离,建立多个主主互备。 ##三、 MySQL数据库机器之间的数据同步 每当我们解决一个问题,新的问题必然诞生在旧的解决方案上。当我们有多台MySQL,在业务高峰期,很可能出现两个库之间的数据有延迟的场景。并且,网络和机器负载等,也会影响数据同步的延迟。我们曾经遇到过,在日访问量接近1亿的特殊场景下,出现,从库数据需要很多天才能同步追上主库的数据。这种场景下,从库基本失去效用了。 于是,解决同步问题,就是我们下一步需要关注的点。 ####1. MySQL自带多线程同步 MySQL5.6开始支持主库和从库数据同步,走多线程。但是,限制也是比较明显的,只能以库为单位。MySQL数据同步是通过binlog日志,主库写入到binlog日志的操作,是具有顺序的,尤其当SQL操作中含有对于表结构的修改等操作,对于后续的SQL语句操作是有影响的。因此,从库同步数据,必须走单进程。 ####2. 自己实现解析binlog,多线程写入。 以数据库的表为单位,解析binlog多张表同时做数据同步。这样做的话,的确能够加快数据同步的效率,但是,如果表和表之间存在结构关系或者数据依赖的话,则同样存在写入顺序的问题。这种方式,可用于一些比较稳定并且相对独立的数据表。 ![mysql 解析 binlog](/upload/5e4d414cgw1ew9sqkfj6gj20bj064glo.jpg "mysql 解析 binlog") 国内一线互联网公司,大部分都是通过这种方式,来加快数据同步效率。还有更为激进的做法,是直接解析binlog,忽略以表为单位,直接写入。但是这种做法,实现复杂,使用范围就更受到限制,只能用于一些场景特殊的数据库中(没有表结构变更,表和表之间没有数据依赖等特殊表)。 ##四、 在Web服务器和数据库之间建立缓存 实际上,解决大访问量的问题,不能仅仅着眼于数据库层面。根据“二八定律”,80%的请求只关注在20%的热点数据上。因此,我们应该建立Web服务器和数据库之间的缓存机制。这种机制,可以用磁盘作为缓存,也可以用内存缓存的方式。通过它们,将大部分的热点数据查询,阻挡在数据库之前。 ![web 缓存 redis memcached](/upload/5e4d414cgw1ew9sqksmdvj20cd01ya9z.jpg "web 缓存 redis memcached") ####1. 页面静态化 用户访问网站的某个页面,页面上的大部分内容在很长一段时间内,可能都是没有变化的。例如一篇新闻报道,一旦发布几乎是不会修改内容的。这样的话,通过CGI生成的静态html页面缓存到Web服务器的磁盘本地。除了第一次,是通过动态CGI查询数据库获取之外,之后都直接将本地磁盘文件返回给用户。 ![页面静态化](/upload/5e4d414cgw1ew9sql798cj20go06dt91.jpg "页面静态化") 在Web系统规模比较小的时候,这种做法看似完美。但是,一旦Web系统规模变大,例如当我有100台的Web服务器的时候。那样这些磁盘文件,将会有100份,这个是资源浪费,也不好维护。这个时候有人会想,可以集中一台服务器存起来,呵呵,不如看看下面一种缓存方式吧,它就是这样做的。 ####2. 单台内存缓存 通过页面静态化的例子中,我们可以知道将“缓存”搭建在Web机器本机是不好维护的,会带来更多问题(实际上,通过PHP的apc拓展,可通过Key/value操作Web服务器的本机内存)。因此,我们选择搭建的内存缓存服务,也必须是一个独立的服务。 内存缓存的选择,主要有redis/memcache。从性能上说,两者差别不大,从功能丰富程度上说,Redis更胜一筹。 ![缓存 redis memcached](/upload/5e4d414cgw1ew9sqlhhvvj20gj05uwen.jpg "缓存 redis memcached") ####3. 内存缓存集群 当我们搭建单台内存缓存完毕,我们又会面临单点故障的问题,因此,我们必须将它变成一个集群。简单的做法,是给他增加一个slave作为备份机器。但是,如果请求量真的很多,我们发现cache命中率不高,需要更多的机器内存呢?因此,我们更建议将它配置成一个集群。例如,类似redis cluster。 Redis cluster集群内的Redis互为多组主从,同时每个节点都可以接受请求,在拓展集群的时候比较方便。客户端可以向任意一个节点发送请求,如果是它的“负责”的内容,则直接返回内容。否则,查找实际负责Redis节点,然后将地址告知客户端,客户端重新请求。 ![缓存 redis](/upload/5e4d414cgw1ew9sqls9p6j20dh06jt91.jpg "缓存 redis") 对于使用缓存服务的客户端来说,这一切是透明的。 ![缓存 redis](/upload/5e4d414cgw1ew9sqmdwnbj20go028mx7.jpg "缓存 redis") 内存缓存服务在切换的时候,是有一定风险的。从A集群切换到B集群的过程中,必须保证B集群提前做好“预热”(B集群的内存中的热点数据,应该尽量与A集群相同,否则,切换的一瞬间大量请求内容,在B集群的内存缓存中查找不到,流量直接冲击后端的数据库服务,很可能导致数据库宕机)。 ####4. 减少数据库“写” 上面的机制,都实现减少数据库的“读”的操作,但是,写的操作也是一个大的压力。写的操作,虽然无法减少,但是可以通过合并请求,来起到减轻压力的效果。这个时候,我们就需要在内存缓存集群和数据库集群之间,建立一个修改同步机制。 先将修改请求生效在cache中,让外界查询显示正常,然后将这些sql修改放入到一个队列中存储起来,队列满或者每隔一段时间,合并为一个请求到数据库中更新数据库。 ![缓存 redis](/upload/5e4d414cgw1ew9sqmt49rj20go021aa4.jpg "缓存 redis") 除了上述通过改变系统架构的方式提升写的性能外,MySQL本身也可以通过配置参数innodb_flush_log_at_trx_commit来调整写入磁盘的策略。如果机器成本允许,从硬件层面解决问题,可以选择老一点的RAID(Redundant Arrays of independent Disks,磁盘列阵)或者比较新的SSD(Solid State Drives,固态硬盘)。 ####5. NoSQL存储 不管数据库的读还是写,当流量再进一步上涨,终会达到“人力有穷时”的场景。继续加机器的成本比较高,并且不一定可以真正解决问题的时候。这个时候,部分核心数据,就可以考虑使用NoSQL的数据库。NoSQL存储,大部分都是采用key-value的方式,这里比较推荐使用上面介绍过Redis,Redis本身是一个内存cache,同时也可以当做一个存储来使用,让它直接将数据落地到磁盘。 这样的话,我们就将数据库中某些被频繁读写的数据,分离出来,放在我们新搭建的Redis存储集群中,又进一步减轻原来MySQL数据库的压力,同时因为Redis本身是个内存级别的Cache,读写的性能都会大幅度提升。 ![redis](/upload/5e4d414cgw1ew9sqmpe1nj20go051glu.jpg "redis") 国内一线互联网公司,架构上采用的解决方案很多是类似于上述方案,不过,使用的cache服务却不一定是Redis,他们会有更丰富的其他选择,甚至根据自身业务特点开发出自己的NoSQL服务。 ####6. 空节点查询问题 当我们搭建完前面所说的全部服务,认为Web系统已经很强的时候。我们还是那句话,新的问题还是会来的。空节点查询,是指那些数据库中根本不存在的数据请求。例如,我请求查询一个不存在人员信息,系统会从各级缓存逐级查找,最后查到到数据库本身,然后才得出查找不到的结论,返回给前端。因为各级cache对它无效,这个请求是非常消耗系统资源的,而如果大量的空节点查询,是可以冲击到系统服务的。 ![redis](/upload/5e4d414cgw1ew9sqn4nsvj20cg03fdfu.jpg "redis") 在我曾经的工作经历中,曾深受其害。因此,为了维护Web系统的稳定性,设计适当的空节点过滤机制,非常有必要。 我们当时采用的方式,就是设计一张简单的记录映射表。将存在的记录存储起来,放入到一台内存cache中,这样的话,如果还有空节点查询,则在缓存这一层就被阻挡了。 ![redis](/upload/5e4d414cgw1ew9sqnefbtj20gh03fglp.jpg "redis") #异地部署(地理分布式) 完成了上述架构建设之后,我们的系统是否就已经足够强大了呢?答案当然是否定的哈,优化是无极限的。Web系统虽然表面上看,似乎比较强大了,但是给予用户的体验却不一定是最好的。因为东北的同学,访问深圳的一个网站服务,他还是会感到一些网络距离上的慢。这个时候,我们就需要做异地部署,让Web系统离用户更近。 ##一、 核心集中与节点分散 有玩过大型网游的同学都会知道,网游是有很多个区的,一般都是按照地域来分,例如广东专区,北京专区。如果一个在广东的玩家,去北京专区玩,那么他会感觉明显比在广东专区卡。实际上,这些大区的名称就已经说明了,它的服务器所在地,所以,广东的玩家去连接地处北京的服务器,网络当然会比较慢。 当一个系统和服务足够大的时候,就必须开始考虑异地部署的问题了。让你的服务,尽可能离用户更近。我们前面已经提到了Web的静态资源,可以存放在CDN上,然后通过DNS/GSLB的方式,让静态资源的分散“全国各地”。但是,CDN只解决的静态资源的问题,没有解决后端庞大的系统服务还只集中在某个固定城市的问题。 这个时候,异地部署就开始了。异地部署一般遵循:核心集中,节点分散。 核心集中:实际部署过程中,总有一部分的数据和服务存在不可部署多套,或者部署多套成本巨大。而对于这些服务和数据,就仍然维持一套,而部署地点选择一个地域比较中心的地方,通过网络内部专线来和各个节点通讯。 节点分散:将一些服务部署为多套,分布在各个城市节点,让用户请求尽可能选择近的节点访问服务。 例如,我们选择在上海部署为核心节点,北京,深圳,武汉,上海为分散节点(上海自己本身也是一个分散节点)。我们的服务架构如图: ![容灾部署](/upload/5e4d414cgw1ew9sqnqc8aj20fk0e63z4.jpg "容灾部署") 需要补充一下的是,上图中上海节点和核心节点是同处于一个机房的,其他分散节点各自独立机房。 国内有很多大型网游,都是大致遵循上述架构。它们会把数据量不大的用户核心账号等放在核心节点,而大部分的网游数据,例如装备、任务等数据和服务放在地区节点里。当然,核心节点和地域节点之间,也有缓存机制。 ##二、 节点容灾和过载保护 节点容灾是指,某个节点如果发生故障时,我们需要建立一个机制去保证服务仍然可用。毫无疑问,这里比较常见的容灾方式,是切换到附近城市节点。假如系统的天津节点发生故障,那么我们就将网络流量切换到附近的北京节点上。考虑到负载均衡,可能需要同时将流量切换到附近的几个地域节点。另一方面,核心节点自身也是需要自己做好容灾和备份的,核心节点一旦故障,就会影响全国服务。 过载保护,指的是一个节点已经达到最大容量,无法继续接接受更多请求了,系统必须有一个保护的机制。一个服务已经满负载,还继续接受新的请求,结果很可能就是宕机,影响整个节点的服务,为了至少保障大部分用户的正常使用,过载保护是必要的。 解决过载保护,一般2个方向: 拒绝服务,检测到满负载之后,就不再接受新的连接请求。例如网游登入中的排队。 分流到其他节点。这种的话,系统实现更为复杂,又涉及到负载均衡的问题。 ##小结 Web系统会随着访问规模的增长,渐渐地从1台服务器可以满足需求,一直成长为“庞然大物”的大集群。而这个Web系统变大的过程,实际上就是我们解决问题的过程。在不同的阶段,解决不同的问题,而新的问题又诞生在旧的解决方案之上。 系统的优化是没有极限的,软件和系统架构也一直在快速发展,新的方案解决了老的问题,同时也带来新的挑战。