分布式系统中故障不可避免,所以为了提高可用性一定要做弹力设计,也就是容错设计。
常见的容错手段有:
- 隔离设计
- 异步通信
- 幂等设计
隔离设计
隔离分为两种,一种是以服务为种类来做隔离,另一种是以用户为种类来做分离。
服务维度的隔离是指不同的服务种类设计成独立的系统,比如电商平台,将用户注册登陆设计为一个系统,商品中心设计为另一系统,评论和社交设计为一个系统。这三个系统彼此相互独立,互不耦合,这三个系统的接入层、应用层和数据层都完全隔离的。这样一来,一个板块出了故障就不会影响到其他功能了。在分布式系统中,每个服务都应该有自己独立的数据库,多个服务间不应该共享数据库,否则出了问题很难定位。比如审核系统有很多字段,有些字段不同的服务都会去修改,若设计成多个服务共享一个hbase,那么一个字段状态不对了就很难排查到底是哪个服务把这个状态写错了。
带来的问题
- 如果我们需要获取多个板块的数据,那么需要协调多个服务,进行多次调用,这会降低性能,响应时间会增加。对此需要在展示的时候做好分页,每次展示有限数量的内容。
- 如果有大数据平台,就会有把各个服务的数据抽到一个数据仓库,这会增加数据聚合的难度。这个时候,需要一个中间件或者服务来聚合各个自服务的数据。
- 如果有些业务流程需要跨板块的话,某个板块出现问题会导致整个服务走不下去。一方面要保证服务的高可用,另一方面业务流程要做到step-by-step,保存每一步的处理结果(状态),这样如果某一步失败了,重试的时候就无需从头开始执行了。
- 多板块的交互也会变得复杂,需要消息中间件来打通各个板块之间的交互和信息交流
- 如果多板块存在分布式事务,需要引入两阶段提交
按照用户来做隔离
将不同的用户进行分组,部署不同的实例。这样某些服务实例挂掉后,只会影响其对应的用户组,不会影响其他用户。比如秒杀用户不会影响其他的网购用户。不同的用户组的应用层和数据层可以独立部署,也可以共享数据。如果该用户组是非常重要的大客户,可以单独部署服务层和数据库。一般来说都是折中方案。
异步通信设计
分布式系统很多时候是为了解决高吞吐的场景。此时,异步通信就比同步通信有更大的优势了。因为异步通信节省了很多调用时等待response的时间。
同步调用还会导致系统被最慢的那个服务拖垮,此外如果某个服务挂掉后会产生雪崩,导致其他服务也不能正常使用。异步调用可以将各个服务彻底解偶。
异步通信有三种方式:
业务方不断轮训、服务方回调通知、broker方式。前两种方式有一定偶尔,broker方式则完全解偶。
Broker方式就是EDA(事件驱动模式),服务间通过消息来完成交流和整个流程的驱动。
带来的问题
- 无法保证时间的顺序,会出现乱序
- 事务处理会变的很复杂,需要用两阶段提交来保证数据的一致性
幂等设计
网络调用种有三种状态:成功、失败和超时。前两者是明确的,而超时是一种未知状态。
面对超时,client重试可能会导致副作用。为了解决副作用,一般有两种处理方式:
- 服务方提供查询接口,给调用方查询结果是否被执行了;
- 提供幂等接口,让client放心重试;
有些操作是有副作用的,比如http post表示新建。为了实现幂等,我们一般会把每个任务用唯一的识别码(雪花算法)表示,然后将结果存到存储种。这样不管是查询还是提供幂等接口都需要,也便于服务的扩展。
服务的状态
将服务从有状态到无状态的迁移是微服务的设计理念之一。这样做可以提高服务的伸缩性。将服务的状态存到第三方存储是通用的方法;而有状态服务的好处是数据本地化,响应时间比较快。有状态服务的数据可以通过gossip协议还有就是session-sticky。
此外
此外分布式系统还要做好业务补偿,流程到某一步实在做不下去了要做好补偿。重试、限流、熔断、降级等也是必须的。