虾聊
1、最近做了两个临时需求,贼无语,就是一个做完需求说外部对接方不接入了,一个是做完后因为其他原因砍需求,导致最后没有实现业务方想要的内容。
emmm,这个之前有跟同事聊过,我认为是这样,一个项目、一个业务总得有牵头人、主导角色,要么是产品,要么是研发,关键取决于组织架构,或者哪个角色对业务逻辑、项目规划掌握更多。
在这个项目里面产品充当业务方的中间人,一般情况就是产品来当这个牵头人,如果需求型产品,就是没有自己对业务的理解或者规划,完全别人说啥就搞啥,导致项目后续对业务的扩展能力很差,而且需求会一直变动,浪费研发、测试资源。
作为研发角色的反思,如果产品做不了这个牵头人,那么研发同学需要去当这个角色,更多了解业务,在需求评审的时候就将问题抛出来,让项目更高效;我认为研发更多的职责是技术方案设计、扩展性、影响评估上。
稻盛和夫讲:只有当每个团队、每个人能够形成旋涡,这种主动性,才能带动团队向前。
2、应该多交流,让我看到了差距
有一次我跟杭州工作的朋友聊,他跟我年龄相仿,薪资月薪40k了,我他喵呆住了,原来这差距也太大了;薪资是表面问题,包括城市因素、学历、工作经历很多东西无法改变,只能着手自己能改变的东西。他发过一份别人的简历,大概是一位211学校的应届生,各种ACM算法比赛,手撸raft还有redis基本数据类型实现,我当年毕业就是什么管理系统,好家伙这就是差距。
前言
我最近在研究sofa相关框架,里面有sofa-jraft,主要是实现分布式一致性算法,作为一个新手去直接看源码会晕在里面的,所以我打算先去官网了解raft算法,然后自己手撸一套,最后看sofa-jraft框架跟我写的有什么不一样。
这样既能了解raft原理,通过动手实现加深印象,最后发现原本raft算法上问题,sofa-jraft相应的解决方案又是什么。
什么是raft
raft官网:raft.github.io/
简单讲:raft有两个核心流程:选举、日志同步,它的理念是这样的
选举
1、一个任期里面只有一个leader,强leader概念,就像一山只能一只老虎,不然就乱套了对吧;
2、其次每个节点都有机会变成候选人,逻辑就是每个节点本身有个定时器,当我这个定时器到期的时候,没有收到leader的心跳可以自立为王;(这很好理解,就是当leader宕机的时候,跟随者可以重新选举的,避免群龙无主的情况)
3、作为leader的逻辑:投给你票的数量,占整个集群的数量/2+1,你可以当老大啦,相当民主吧
4、节点数量不少于等于3
日志同步
1、有leader之后,它要干两件事,第一件是心跳,我还在你们别搞事,继续当follower,哈哈哈。第二件事是日志同步,当有数据更新的时候,leader节点会先写到日志log里面,然后同步到其他节点,当同步成功的数量占集群数量/2+1,说明集群大家赞同这次操作,那leader会最终将它应用到状态机
2、leader状态机应用之后,可以通过心跳让其他节点应用到各自状态机的。
上面我们提到几个东西:节点、日志、状态机,他们具体的关系是这样的
讲解,大概有5步,我们可以看到选举的结果就是有一个leader节点,若干跟随者,外部client设置数值的时候,会给leader去处理,如果发到follower的话,同样也需要转发给老大处理。
首先是应用到日志里面,就是跟大家讲有这么一回事,有人要往我这塞钱,硬塞那种哈哈,小弟们收到这个消息之后,也记录下这么一件事,然后大家决定记录好之后跟老大反馈,老大根据节点储存情况,来最终拍板这钱收的话,就应用到状态机,这就是板上钉钉的事了,然后再把这个同步给各个小弟。
如果以后有人来查账,直接从leader的状态机查,我们可以清晰的知道,节点是来选出老大的,日志是一种暂时储存的地方,而状态机是板上钉钉的数据,自此完成了分布式一致性。
至于整个过程,大家可以去raft官网玩一下,这样可以增加对概念的理解。
手撸raft-demo
我就按照我的理解,手撸了raft-demo,具体的地址:github.com/dajitui/jav… ,因为是简易版的,所以很多实现方式比较简单粗暴,比如说rpc通过map来储存数据,然后日志对比,如果不一致直接日志、状态机重新拷贝,去除一个一个对比的过程。
里面有两个demo,一个是3个节点选举,然后设置值的过程,另一个是4个节点,然后一个节点超时重新选举的demo。
介绍
里面我分为几个模块:一致性模块、日志模块、状态机模块、rpc模块、节点模块、定时器模块
一致性模块
1、选举、投票算法
日志模块
1、选型:rocksdb,初始化、新增、获取
2、lastlogindex,rocksdb并没有自带这个功能
3、批量重置,因为是暴力做法,就是只要节点数据不一致的话,直接全部重置,省去了对比的过程
状态机模块
1、选型:rocksdb,初始化、新增、获取
2、同上
3、批量应用,重置完日志之后,也得应用状态机对吧
rpc模块
1、通过本地map来储存,key是node唯一表示serviceid,value 是string类型,通过特定的契约来解码
2、节点定时搂数据
节点模块
1、定义节点属性,是否是leader节点、选举投票情况(候选人节点专属)、节点角色(leader、follower、候选人)、所有节点、日志模块、状态机模块、定时器模块...
定时器模块
1、选举定时器
2、心跳定时器(leader节点专属)
一套下来的感受
1、所有节点信息储存问题
这里我并没有说通过第三方中间件来解决这个问题,因为引入多一个中间件,那么框架的稳定性就越差,如果每个节点都存所有数据信息的时候,那他们怎么做到这个数据的同步呢?
答案也是很明显,就按照raft的算法作为数据在集群进行同步。
2、选举、心跳、新增日志操作缺点什么
没错,缺点同步,因为很多共享变量,这时就需要上锁,我们往后再看看jraft怎么解决的。
3、虽然我有分别打印各个节点的情况,但是很麻烦,因为开发或者演示的时候,需要定时打印这个集群的情况
带上上面几个问题,我们来看看sofa-jraft框架的实现
sofa-jraft
sofa-jraft 是蚂蚁下面sofastack社区的一个开源框架,主要实现分布式一致性的功能,官网地址:github.com/sofastack/s… ,大家感兴趣自行阅读。
优点
1、减少term数值叠加
我们通过raft算法可以知道,当一个节点失联的时候、网络分区的时候,那么它的任期会很大很大,最致命的是它会打断正常集群的作业,就像我们玩得好好的,忽然说你们先停下,我刚刚断网了,重新选个老大重玩,哈哈哈。
所以第一个理念,减少任期的叠加,为了实现这个功能就引出第二点preVote,预先投票,如果大家不同意你当老大,那么你乖乖回到之前的任期。
2、在第一点上需要预选举
3、投过谁的票有效期,raft一个任期只能投一票,导致任期一直叠加的结果
在raft算法里面,每个任期节点只能给一个人投票,而在jraft框架里面节点的投票,它是有有限期的,因为为了实现预选举,如果你一个任期只能投一个,那惨了,预选举失败那么你这个节点就投不了票了,所以投票结果也有过期时间。
4、快照,当一个节点起来、宕机的时候,快速恢复日志
这是额外的东西,就是当一个新节点加入的时候,如果一个日志一个日志同步很慢,这时就需要一个快照可以嗖~直接给干完。
5、心跳当日志新增传输,只是内容为空
在我自己开发的raft-demo里面,心跳是单独一个定时器,其实可以跟日志整合一个定时器,只不过data为空,这也是一个节省的方法
6、在操作的时候如果是对象最好再copy一个,技巧型
我们再回到上面几个问题,sofa-jraft是这么处理的。
所有节点信息储存问题
1、所有节点只在leader节点存,直接写到日志,通过日志复制,如果宕机可以从日志取所有节点,相当于所有节点是作为信息在集群同步
com.alipay.sofa.jraft.storage.impl.LogManagerImpl#appendEntries ConfigurationEntry
选举逻辑
2、prevote投票的时候,先投给自己,然后建立一个Ballot,quorum法定人数,如果它大于总node数/2+1则获取leader位置
逻辑优化
3、逻辑上修正:当选leader之后,leader节点选举定时器关掉
同步问题
4、采用读写锁,reentranreadwritelock、stamplock
总结
我们最后总结一下sofa-jraft的东西,它比raft额外加多了prevote+投票有效期+快照,使得raft算法有了一个提升,更加可靠;其次对于一些共享数据,比如所有节点的信息,其实也需要跟集群内部同步的,所以也是作为日志的一部分进行记录,靠谱滴~
这个raft-demo搞了3-4天吧,满满的成就感,接下来学paxos,下一篇文章见~