分布式系统技术

通信

既然是分布式系统,系统间通信的技术就不可避免的要掌握。

首先要掌握一些基础知识,例如网络通信协议(诸如TCP/UDP等等)、网络IO(Blocking-IO,NonBlocking-IO、Asyn-IO)、网卡(多队列等);更偏应用的层面,需要了解例如连接复用、序列化/反序列化、RPC、负载均衡等。

学了这些基本知识后,基本上可以写一个简单的分布式系统里的通信模块,但这其实远远不够,既然进入了分布式领域,对规模其实就已经有了不低的要求,通常也就意味着需要的是能支持大量连接、高并发、低资源消耗的通信程序。

大量的连接通常会有两种方式:

大量client连一个server

在现如今NonBlocking-IO这么成熟的情况下,一个支持大量client的server已经不那么难写了,但在大规模,并且通常长连接的情况下,有一个点要特别注意,就是当server挂掉的时候,不能出现所有client都在一个时间点发起重连,那样基本就是灾难,在没有经验的情况下我看过好几起类似的case,到client规模上去后,server一重启基本就直接被冲进来的大量建连冲垮了(当然,server的backlog队列首先应该稍微设置大一些),通常可以采用的方法是client重连前都做随机时间的sleep,另外就是重连的间隔采取避让算法。

一个client连大量的server

有些场景也会出现需要连大量server的现象,在这种情况下,同样要注意的也是不要并发同时去建所有的连接,而是在能力范围内分批去建。

除了建连接外,另外还要注意的地方是并发发送请求也同样,一定要做好限流,否则很容易会因为一些点慢导致内存爆掉。

这些问题在技术风险上得考虑进去,并在设计和代码实现上体现,否则一旦随着规模上去了,问题一时半会还真不太好解。

高并发这个点需要掌握CAS、常见的lock-free算法、读写锁、线程相关知识(例如线程交互、线程池)等,通信层面的高并发在NonBlocking-IO的情况下,最重要的是要注意在整体设计和代码实现上尽量减少对io线程池的时间占用。

低资源消耗这点的话NonBlocking-IO本身基本已经做到。

伸缩性

分布式系统基本就意味着规模不小了,对于这类系统在设计的时候必须考虑伸缩性问题,架构图上画的任何一个点,如果请求量或者是数据量不断增大,怎么做到可以通过加机器的方式来解决,当然,这个过程也不用考虑无限大的场景,如果经历过从比较小到非常大规模的架构师,显然优势是不小的,同样也会是越来越稀缺的。

横向可扩展性(Scale Out)是指通过增加服务器数量来提升集群整体性能。纵向可扩展性(Scale Up)是指提升每台服务器性能进而提升集群整体性能。纵向可扩展性的上限非常明显,分布式系统强调横向可扩展性。

分布式系统应用服务最好做成无状态的

应用服务的状态是指运行时程序因为处理服务请求而存在内存的数据。分布式应用服务最好是设计成无状态。因为如果应用程序是有状态的,那么一旦服务器宕机就会使得应用服务程序受影响而挂掉,那存在内存的数据也就丢失了,这显然不是高可靠的服务。把应用服务设计成无状态的,让程序把需要保存的数据都保存在专门的存储上(eg. 数据库),这样应用服务程序可以任意重启而不丢失数据,方便分布式系统在服务器宕机后恢复应用服务。

伸缩性的问题围绕着以下两种场景在解决:

无状态场景

对于无状态场景,要实现随量增长而加机器支撑会比较简单,这种情况下只用解决节点发现的问题,通常只要基于负载均衡就可以搞定,硬件或软件方式都有;

无状态场景通常会把很多状态放在db,当量到一定阶段后会需要引入服务化,去缓解对db连接数太多的情况。

有状态场景

所谓状态其实就是数据,通常采用Sharding来实现伸缩性,Sharding有多种的实现方式,常见的有这么一些:

2.1 规则Sharding

基于一定规则把状态数据进行Sharding,例如分库分表很多时候采用的就是这样的,这种方式支持了伸缩性,但通常也带来了很复杂的管理、状态数据搬迁,甚至业务功能很难实现的问题,例如全局join,跨表事务等。

2.2 一致性Hash

一致性Hash方案会使得加机器代价更低一些,另外就是压力可以更为均衡,例如分布式cache经常采用,和规则Sharding带来的问题基本一样。

2.3 Auto Sharding

Auto Sharding的好处是基本上不用管数据搬迁,而且随着量上涨加机器就OK,但通常Auto Sharding的情况下对如何使用会有比较高的要求,而这个通常也就会造成一些限制,这种方案例如HBase。

2.4 Copy

Copy这种常见于读远多于写的情况,实现起来又会有最终一致的方案和全局一致的方案,最终一致的多数可通过消息机制等,全局一致的例如zookeeper/etcd之类的,既要全局一致又要做到很高的写支撑能力就很难实现了。

即使发展到今天,Sharding方式下的伸缩性问题仍然是很大的挑战,非常不好做。

上面所写的基本都还只是解决的方向,到细节点基本就很容易判断是一个解决过多大规模场景问题的架构师,

稳定性

作为分布式系统,必须要考虑清楚整个系统中任何一个点挂掉应该怎么处理(到了一定机器规模,每天挂掉一些机器很正常),同样主要还是分成了无状态和有状态:

无状态场景

对于无状态场景,通常好办,只用节点发现的机制上具备心跳等检测机制就OK,经验上来说无非就是纯粹靠4层的检测对业务不太够,通常得做成7层的,当然,做成7层的就得处理好规模大了后的问题。

有状态场景

对于有状态场景,就比较麻烦了,对数据一致性要求不高的还OK,主备类型的方案基本也可以用,当然,主备方案要做的很好也非常不容易,有各种各样的方案,对于主备方案又觉得不太爽的情况下,例如HBase这样的,就意味着挂掉一台,另外一台接管的话是需要一定时间的,这个对可用性还是有一定影响的;

全局一致类型的场景中,如果一台挂了,就通常意味着得有选举机制来决定其他机器哪台成为主,常见的例如基于paxos的实现。

可维护性

维护性是很容易被遗漏的部分,但对分布式系统来说其实是很重要的部分,例如整个系统环境应该怎么搭建,部署,配套的维护工具、监控点、报警点、问题定位、问题处理策略等等。

尽管Java架构师学习路线已经分享给大家,但有多少学员能认真的去践行,这个就难说了。互联网寒冬已经到来,作为程序员,更应在此时提高自己,有着更高远的追求。