目录
1.分布式系统的演进
2.分布式系统的简化架构
3.分布式系统的分布式事务
分布式系统解决方案
1.分布式系统的演进
首先说一下什么是分布式系统,那这里就需要相对的来描述下单机系统,传统的单机系统就是单个项目部署再一台服务器上,包括数据库。在系统初期用户访问量不大的时候并没有什么问题,但是,随着用户量的增加,并发量会逐渐增大,每个请求都是会占用服务器资源的(如保持连接的线程,以及系统运行时的堆栈内存占用,数据库读写的io资源占用),单一的服务器资源非常有限,在硬件没有革命性升级换代之前,单一的系统架构是无法解决大量并发问题的。
第二阶段就是集群式结构,就是同样的系统多个实例分别部署在不同的服务器上,并通过负载均衡设备进行请求的分配,这样就解决了一台服务器资源有限的问题。事实上很难去说集群模式和分布式模式的效率,一个经典的说法就是:分布式是以缩短单个任务的执行时间来提升效率的,而集群则是通过提高单位时间内执行的任务数来提升效率。例如:如果一个任务由 10 个子任务组成,每个子任务单独执行需 1 小时,则在一台服务器上执行该任务需 10 小时。 采用分布式方案,提供 10 台服务器(均为单核),每台服务器只负责处理一个子任务,不考虑子任务间的依赖关系,执行完这个任务只需一个小时。 而采用集群方案,同样提供 10 台服务器,每台服务器都能独立处理这个任务。假设有 10 个任务同时到达,10 个服务器将同时工作,1 小时后,10 个任务同时完成,这样,整身来看,还是 1 小时内完成一个任务!但是随着业务的不断发展变化,原来的简单的系统,变得异常复杂,代码越来越多,这就会面临几个问题,首先越庞大的系统运行在一台有限资源的服务器上效率会逐渐下降,其次与数据库放在的一台服务器,数据库没有良好的扩展性,数据量增多或者读写操作增多会对数据库造成压力,最后,系统的耦合性大大增加,扩展性降低,往往一个子业务的代码逻辑错误导致整个系统瘫痪。
第三阶段就是我们的分布式架构阶段,这种架构是将整体的系统分成多个不同的子系统,每个子系统单独部署,一般每个子系统都独立对应数据库。这样的好处显而易见,第一,业务耦合性大大降低,便于维护修改。第二,理论上不考虑网络通信的效率问题,扩展度是无限的,第三,架构的灵活度大大提高,其实就是更多的玩法去变换架构。
2.分布式系统的简化架构
说了分布式的好处这么多,那如何去实现一个分布式架构呢?根据上图,那我们就来聊聊如何去设计一个简单的分布式架构,这里的每个点都不去细讲因为每个点都会有非常多的内容,有机会再展开讲,这文章主要探讨分布式整体的解决方案,强调一点为了做到高可用性一般部署实例都不会是唯一的,无论是服务也好还是注册中心配置中心等等。 第一,网络通讯,首先大家思考下,分布式系统都是将原来一个完整的系统拆分成了多个子系统,或者子模块,并且还部署在了不同的独立服务器之上,这就使得原本一台服务器上的线程或者进程之间的调用方式没法使用了,那很自然就会想到网络通讯,这里不具体去说,目前用的最多的rpc远程过程调用,它是一种思想,一种屏蔽了底层网络通讯实现的调用方式。当然也用别的方式实现网络通讯的,比如springcloud的feign封装http请求的方式去联系各个子系统,这里提一句,提到网络通讯就涉及到一个很重要的问题就是序列化协议,本人倾向于二进制序列化协议效率更高,而不是为了可读性牺牲效率,因为对于用户角度来说通讯本就是透明的不存在可读性问题,如xml/json类型的协议。
第二,注册中心,有了网络通讯(大部分分布式实现通过tcp/ip协议),那就需要最起码的路径(ip端口)去连接对应的服务,由于分布式架构下服务繁杂,众多,且经常性的变换,不可能用配置去实现,那就需要一个可实时根据服务变化的记录服务及其路径的东西,注册中心就是干这个的,用来进行服务的暴漏与发现。
第三,网关,众多服务调用关系本就很复杂,现在又拆分成多个子模块,一个请求过来前端如何将这些请求对应到每个服务中呢?网关的主要作用就是用来请求分发的,它不需要前端记住请求的具体地址或者服务,请求由网关负责分发,另外,网关在架构中的位置决定他可以做整体的限流、熔断、降级等。
第四、监控,用于是跨节点的通讯,如果系统出现问题,抛出异常怎么办?多系统之间的成百上千的调用链路是什么样的?,这就有监控系统的由来,他用来跟踪请求的调用链路,同时收集各个节点上的应用日志用来分析,一般的稍微大点的公司这块都是自己来研发,但一般会参考Google的Dapper的一篇论文。
第五、devops系统,由于一个整体的系统拆分成多个子系统,每个子系统还有多个实例,部署发布如果像以前一样手动去做,工作量过大还容易错误,无法满足敏捷开发快速迭代的目的,因此,devops系统随之而生,实际上一个合格的devops系统应该满足代码仓库自动拉取代码、自动编译、打包、打版本标签上传版本仓库(用于版本回退)、版本对比、自动化测试、自动部署、灰度发布等等功能。以上实现可以是容器化的也可以是非容器化的但是起码要实现这些功能点,笔者做过相应实践,以后会详细讲下如何分别实现。当然还有第六、配置中心以及第七、分库分表中间件(这篇不细说了写的超出预期了已经),我觉得这算是一套比较简单相对完整的解决方案。
3.分布式系统的分布式事务
分布式系统一个比较大的难点就是如何解决分布式事务问题,在单一结构中,数据库都是单一节点的,不存在跨库问题出现,必然在一个是事务里,共同提交或者回滚,但是分布式系统中如上图是不同子系统之间是独立的数据库,甚至在同一个子系统中都需要分库分表。
首先解决单个子系统的分库分表的问题,那就是分片键,同样的分片键数据总是会落到同一个表中,一般实践中用客户作为分片键,没有分键的数据用一致性hash去落库。比如一个下单请求,先生成订单,订单落库,先根据下单客户找到所在库,这个库里有订单子系统所需要的全部表,再找到订单储存的表落库,这就是一个大概的落库过程。也就是说保证同一个子系统同一个客户请求是不会跨库执行的。
再来说说,不同子系统之间如何解决事务问题,一个请求在分布式系统中会产生调用多个子系统的来完成一个完整的请求,比如一个订单请求,会调用订单生成子系统,积分子系统以及仓库子系统等等。那怎么保证他们会具有共同提交共同回滚的特性呢?下面列举几个方法,同样只是概述,下个文章会展开具体说说实现方式。第一、基于xa协议的二段三段提交,不建议,第二、异步的可靠消息发送,适用于保证最终一直性的方案,需要编写消息系统。第三、补偿性方式,包括tcc思想的实现方式,以及目前本人实践中的一种方式,适用于大部分情况但是会对代码产生侵入性以及编写额外代码。第四、代码控制实现的分布式事务管理器,可以做到对使用者无感提交和回滚,不需要编写额外代码去补偿,大体想法是根据tcc的事务记录(记录整个分布式事务整体事务id用到哪些数据源分式务id记录下来),结合数据库类似binlog的方式进行统一补偿,还在研究,希望感兴趣的伙伴一同探讨一起聊聊。



