在上一篇文章中,通过阅读 《In Search of an Understandable Consensus Algorithm》 前三节的内容,对论文的大致内容做了简介,简单说明了一下 Replicated state machines 的用途以及 Paxos 本身存在的问题。
4. Designing for understandability several goals in designing Raft:
it must providea complete and practical foundation for system building;
it must be safe under all conditions and available under typical operating conditions;
it must be efficient for common operations.
前一段时间,在一次开组会的时候,给小组成员简单介绍了一下 Raft 协议。大概四年前读过 Raft 的论文,这次分享的时候,好多好多细节都忘了。所以,再次把 《In Search of an Understandable Consensus Algorithm》 这篇论文找出来,重读一遍,做个笔记和摘要,方便后续学习和复习。
Abstract Raft is a consensus algorithm for managing a replicated log. 开篇摘要就点出了 Raft 的特点: Raft 是一种管理复制日志的共识算法。
In order to enhance understandability, Raft separates the key elements of consensus, such as leader election, log replication, and safety, and it enforcesa stronger degree of coherency to reduce the number of states that must be considered.
D瓜哥经常关注 Spring 的 PR 与 Issue。在众多 Contributor 中,除了 Spring 团队成员之外,我对 stsypanov (Сергей Цыпанов) 印象很深刻。这哥们给 Spring 提了非常多的 PR,请看列表 Pull requests · spring-projects/spring-framework,而且这个哥们的 PR 都非常有特点,绝大部分是性能提升方面的 PR,而且还会给出 JMH 的测试结果。不愧是毛熊人,做事细致严谨。
这周心血来潮,把这哥们的 PR 翻一翻,希望可以学习一些编码技巧。简单记录一下,以备以后回顾学习。
提高 Map 的遍历性能 请看: SPR-17074 Replace iteration over Map::keySet with Map::entrySet by stsypanov · Pull Request #1891
摘取一个示例如下:
今天七夕情人节,选摘几首情诗做个纪念。
邶风 · 静女 静女其姝,俟我于城隅。爱而不见,搔首踟蹰。 静女其娈,贻我彤管。彤管有炜,说怿女美。 自牧归荑,洵美且异。匪女之为美,美人之贻。
郑风 · 子衿 青青子衿,悠悠我心。纵我不往,子宁不嗣音? 青青子佩,悠悠我思。纵我不往,子宁不来? 挑兮达兮,在城阙兮。一日不见,如三月兮!
舒婷 · 致橡树 我如果爱你—— 绝不像攀援的凌霄花, 借你的高枝炫耀自己; 我如果爱你—— 绝不学痴情的鸟儿, 为绿荫重复单调的歌曲; 也不止像泉源, 常年送来清凉的慰藉; 也不止像险峰, 增加你的高度,衬托你的威仪。 甚至日光。 甚至春雨。 不,这些都还不够! 我必须是你近旁的一株木棉, 作为树的形像和你站在一起。 根,紧握在地下, 叶,相触在云里。 每一阵风过, 我们都互相致意, 但没有人, 听懂我们的言语。 你有你的铜枝铁干, 像刀,像剑, 也像戟; 我有我红硕的花朵, 像沉重的叹息, 又像英勇的火炬。 我们分担寒潮、风雷、霹雳; 我们共享雾霭、流岚、虹霓。 仿佛永远分离, 却又终身相依。 这才是伟大的爱情, 坚贞就在这里: 爱—— 不仅爱你伟岸的身躯, 也爱你坚持的位置,足下的土地。
最近向一个朋友了解一家公司情况时,聊到职业发展的问题。就随手推荐了《远见:如何规划职业生涯3大阶段》这本书。正好利用这个机会,把以前的读书摘要发布出来。
这本书,D瓜哥在 告别 2019,迎接 2020:《远见》 中也提到过。这里把当时写的读书笔记直接拷贝过来:
本书讲人生的职业生涯分为三个阶段:
第一阶段是强势开局的时候。你在职业上的努力必须着重于为前方的漫长道路挖掘和装备自己。你的学习曲线要比职位、职称更加重要。在这一阶段,要为职业生涯打好基础并建立起良好的早期习惯。
第二阶段是聚焦长板的时候。该阶段的首要目标是寻找自己的甜蜜区,即你所擅长的、所热爱的和这个世界所需要的这三者之间的交集。这个时候你要展现自我,让自己鹤立鸡群,想方设法平稳地走在那条收获最大的职场路径上。你要专注于自己的长板,且大可忽略自己的短板。
第三阶段致力于实现持续的影响力,以及寻找一条可以稳定延续到60多岁甚至70多岁的新的可持续职业道路。你要在第三阶段完成三个关键任务:完成继任计划、保持关联性,以及为自己点燃一团新的职业之火。
用一句话来总结:第一阶段:加添燃料,强势开局; 第二阶段:聚焦长板,达到高点; 第三阶段:优化长尾,持续发挥影响力。
三大职场燃料来源:可迁移技能、有意义的经验和持久的关系。
5个数字,树立正确的职场思维
职业生涯的长度:用62减去你目前的年龄。
精通一项技能所需的时间:要花多少小时才能在某一方面达到“精通”?
40岁之后能赚到的个人财富百分比:在40岁之后,你赚到的钱会占你一生个人财富的百分之多少?大部分人的估计是60%。
社交货币:你有多少社交网络好友?
职场支持者的人数:你认为能在“职业生涯的天堂”里遇到多少人,也就是说有多少人能对你的职业生涯和人生带来真正的变化?
这本书强烈推荐给对未来职业有追求的小伙伴!
好戏开始,下面👇是读书摘要:
无论是在计算机课上,还是在网上,对数据结构有一定了解的童鞋,应该都对树的遍历不陌生。常见的遍历方式有如下两种:
基于栈的遍历:需要额外的空间复杂度。实现略复杂。
基于递归的遍历:实现简单。空间复杂度上,与栈类似,只是这里的栈维护是由系统自动完成的。
在看左程云的《程序员代码面试指南》时,里面介绍了一种只需 O(1) 的额外空间复杂度的遍历方法:Morris 树遍历。感觉非常神奇。这里给大家介绍一下。
Morris 树遍历的神奇之处在于,它充分利用了树里面大量的空闲节点(比如叶子节点的左右子树节点就为空,可以利用起来)来建立起必要的连接,推动遍历的进行。核心思想非常简单:找出当前节点左子树的最右节点,此时最右节点的右子树为空,将最右节点的右子树指向当前节点。然后左移,递归完成所有相关连接。没有左子树时,则向右移动,依次完成上述操作。我们来结合图来说明。如图:
图 1. Morris 树遍历 如上图所示,当访问根节点 4 时,它的左子树的最右节点就是 3,将 3 的右子树指向当前节点 4,如线条 ⑥ 所示。向左启动,建立起 1 到 2 的连接 ④。再向左移动到 1,1 没有左子树,则向右移动,此时就利用上了刚刚建立起的连接 ④。依次类推,即可完成遍历。在遍历过程中,也需要把建立的临时连接给取消掉。
/** * Morris 树遍历 * * @author D瓜哥 · https://www.diguage.com */ public void morris(TreeNode head) { TreeNode curr = head; TreeNode mostRight = null; while (curr != null) { // 当前节点左子树的最右节点,当然是从左子树开始了 mostRight = curr.left; if (mostRight != null) { // 左子树不为空,则找出左子树的最右节点 while (mostRight.right != null // 由于需要建立最右节点到当前节点的连接,所以,需要判断是否已建立连接来打破死循环 && mostRight.right != curr) { mostRight = mostRight.right; } if (mostRight.right == null) { // 最右节点的右子树为空,则第一次访问,那么建立起连接 mostRight.right = curr; curr = curr.left; continue; } else { // 最右节点的右子树不为空,则第二次访问,打破连接,恢复原貌 mostRight.right = null; } } // 最子树为空,则向右移动 curr = curr.right; } }
Spring AOP 源码分析:入门 中,梳理出来了 Spring AOP 的入口。 Spring AOP 源码分析:获得通知 中着重介绍了如何获取通知。上一篇文章 Spring AOP 源码分析:创建代理(一) 重点介绍了一下切面链的组装和基于 JDK 动态代理的 AOP 的实现,这篇文章介绍一下基于 cglib 的代理类是生成。
cglib 简介 CGLIB(Code Generator Library)是一个高性能的代码生成库,被广泛应用于 AOP 框架(Spring)中以提供方法拦截功能,主要以继承目标类的方式来进行拦截实现,因此 CGLIB 可以对无接口的类进行代理。
CGLIB代理主要通过操作字节码的方式为对象引入方法调用时访问操作,底层使用了ASM来操作字节码生成新的类,ASM是一个短小精悍的字节码操作框架。CGLIB的应用栈如下:
最新版的 Hibernate 已经把字节码库从 cglib 切换为 Byte Buddy。
Spring AOP 源码分析:入门 中,梳理出来了 Spring AOP 的入口。上一篇文章 Spring AOP 源码分析:获得通知 中着重介绍了如何获取通知。接着上一篇文章,这篇文章介绍一下如何创建代理。
AbstractAutoProxyCreator#createProxy protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) { if (this.beanFactory instanceof ConfigurableListableBeanFactory) { AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass); } // 创建代理工厂对象 ProxyFactory proxyFactory = new ProxyFactory(); // 获取当前类的属性 proxyFactory.copyFrom(this); //如果没有使用CGLib代理 if (!proxyFactory.isProxyTargetClass()) { // 是否可能使用CGLib代理 // 决定对于给定的 Bean 是否应该使用 targetClass 而不是他的接口代理 if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { // 查看beanClass对应的类是否含有InitializingBean.class/DisposableBean.class/Aware.class接口 // 无则采用JDK动态代理,有则采用CGLib动态代理 evaluateProxyInterfaces(beanClass, proxyFactory); } } // 获得所有关联的Advisor集合(该分支待补充) Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); proxyFactory.addAdvisors(advisors); // 此处的targetSource一般为SingletonTargetSource proxyFactory.setTargetSource(targetSource); // 定制代理,扩展点,空实现 customizeProxyFactory(proxyFactory); // 用来控制代理工厂被配置后,是否还允许修改通知 // 缺省为 false proxyFactory.setFrozen(this.freezeProxy); // 是否设置预过滤模式,此处针对本文为true if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } // 获取使用JDK动态代理或者cglib动态代理产生的对象 return proxyFactory.getProxy(getProxyClassLoader()); }
在文章 Spring AOP 处理流程概述 中,对 Spring AOP 有了一个整体认识。在文章 Spring AOP 源码分析:入门 中,对 Spring AOP 的相关入口做了分析。这篇文章就带大家看一看,Spring AOP 是如何获取通知的?
示例代码 在 如何阅读 Spring 源码?: 示例代码 中,已经给出了一个完整的 AOP 示例代码。为了节省篇幅,请直接参考那篇文章的示例代码,这里就不在赘述。
注册 Advice(通知/增强) 请根据 Spring AOP 源码分析:入门 中提到的关键方法入口处,打上断点,开始调试。
首先,需要明确一点的是:对于切面(使用 @Aspect 注解标注过的类)在 Spring 容器中,也是被统一f封装为 BeanDefinition 实例的,也需要通过一个方式,将其注册到 Spring 容器中。比如,就像 示例代码 那样,通过 ImportSelector 方式,使用类名,将其注册到容器中。这样,就可以利用 Spring 容器对 Bean 的 API 来统一处理了。
昨晚原计划给几个朋友简单介绍一下阅读 Spring 源码的方法。结果,大家因为各种原因没能及时参加。后来,就取消分享了。干脆写一篇文章出来,感兴趣欢迎自取。
代码准备 Spring Framework 是开源的,代码托管在 GitHub 上: Spring Framework。任何人都可以方便地获得它的源代码。所以,如果想阅读 Spring 的源代码,当然是直接把代码克隆到本地,然后直接在 IDE(推荐 IDEA)中进行调试了。另外,还需要存放自己写一些测试和文档。所以,最好把代码 fork 到自己的账户下,从 master 上切出一个新分支并 push 到自己的 Repo 中,这样自己就可以随意更新了。具体步骤如下:
克隆代码
# 直接克隆原始仓库为 origin git clone git@github.com:spring-projects/spring-framework.git fork 代码,D瓜哥直接 fork 到自己账户下了: diguage/spring-framework。
添加原创仓库地址:
# 添加自己仓库为 diguage # 这样就能在所有项目中保持命名的一致性,方便标识 git remote add diguage git@github.com:diguage/spring-framework.git