05服务幂等设计
幂等定义
请求层面幂等
保证请求重复执行和执行一次的结果一致
业务层面幂等
- 同一用户不重复下单
- 商品不超卖
- MQ消费端去重
幂等目的
交易、转账等操作在重试的时候不会出错。如果每个请求失败之后直接返回给用户,则不用考虑幂等。
数据访问层幂等
- Create操作禁止使用auto increment id,使用业务相关的唯一ID可以保证操作幂等
- Read操作本身幂等
- Update操作分为两种,绝对值更新是幂等的,相对值更新不是幂等的
- Delete操作分为两种,绝对值删除是幂等的,相对值删除不实幂等的
幂等案例
QQ离线消息的已读状态(请求层面幂等)
天然幂等操作
计数增加操作
增加Where操作,先查出当前值,再更新。但这种做法在高并发情况下性能不好。
电商平台购买商品
整个购买流程(整个请求)的幂等性由分布式事务来保证。即转换成分布式事务问题。
业务层面幂等
冗余部署多个进程,当MQ上游重复添加消息时,保证下游不重复消费。本质上是分布式锁问题。
06分布式锁设计
定义
分布式环境下锁定全局唯一资源(解决业务层面的幂等问题,解决消息的重复消费)
- 请求处理串行化
- 实际表现为互斥锁
****为什么不能解决消息的重复发送?
当没有收到ACK的时候,无法确定消息是否发送成功,只能重新发送
基于Redis分布式锁
实现方式
Set nx (SET if Not eXists)
存在的问题
- 锁的时间不可控:
- 难以确定过期时间
- 无法续租
- 单点问题:
- 单例情况下,进程一旦死掉,会彻底阻塞业务流程
- 主从方式,主从同步异步,当主从切换时会导致两个线程获取锁。
能不能用Redis实现分布式锁区别与业务场景:
- 交易场景要求绝对一致,不能使用
- 如果是社交或其他不极端要求严格的场景,可以使用
Redis主从复制半同步,本质上是AP模型,分布式锁是CP模型
Redis官方简历使用RedLock,类似自己实现Raft算法。实现上复杂性高。
高可用分布式锁设计目标
- 强一致性
- 服务高可用
- 锁自动续约及其自动释放
- 代码高度抽象,业务接入极间
- 可视化管理后台,监控和管理
存储层产品对比
image.pngTair也可用来做分布式锁的存储
Tair是淘宝开源的一个分布式key/value结构数据库。参考:深入理解Tair
美团点评内部开发的分布式锁Cerberus,使用了多种引擎实现(Tair、ZK),支持使用方自主选择一种或多种引擎。这样可以结合引擎的特点,选择符合实际业务需求和系统架构的方式。
选型(ETCD)
07分布式事务设计
数据不一致的原因
单独的数据库,可以依赖于数据的事务操作保证数据的一致性。在分布式系统中,数据的不一致性原因包括:
- 多个DB
- DB和缓存
举例
在一个业务流程中,比如:下单->减库存->支付
三个操作分布在不同的服务和不同的数据库上。此时无法使用本地事务保证数据的一致性。只能采用分布式事务来解决。
实际问题
在实际系统中,当系统中使用MQ的话,会进一步增加事务的复杂性。比如当消息发送者需要执行事务的回滚操作。
相关概念
刚性分布式事务
- 强一致性
- XA模型(实现了事务的ACID特性)。XA规范由AP(Application Program,应用程序)、RM(Resource Manager,资源管理器)、TM(Transaction Manager,事务管理器)组成。2PC
- CAP模型中实现CP
柔性分布式事务
- 最终一致性
- CAP、BASE理论,实现CAP中的AP。典型实现:TCC模型,Saga模型
BASE (Basically Availabe, Soft state, Eventual consistency)
柔性分布式事务场景
异步场景:基于MQ消息驱动的分布式事务
这个场景解决的问题是,本地操作和发送消息两个行为的事务问题。
在消息消费端:
- 消息幂等通过分布式锁来实现
- 执行失败,一般通过报警、日志、人工干预来解决。
方案一
MQ提供类似XA的分布式事务功能,通过MQ事务消息,等达到分布事务事务的最终一致。(RocketMQ)
缺点:业务方需要给MQ提供回查接口(对业务入侵大),发送消息非幂等(消费端需要处理幂等)
方案二
本地事务表。本质上是将本地操作和发消息转化成本地事务问题。通过本地事务操作表和本地事务消息表,两个表通过本地事务保证。
同步场景:基于异步补偿分布
同步场景描述的是在服务聚合处,串行执行下单、减库存、支付三个操作。
由业务网逻辑层驱动的同步场景。
如果下单执行成功,减库存执行失败,如果回滚下单操作?
可能地方案是在减库存执行失败时,服务聚合节点调用下单服务的回滚接口。理论上可以通过XA、TCC等来结局。但这里没讲。
08服务降级设计
目的
- 流量高峰期
- 短时请求量大
如果没有服务降级,可能导致:服务宕机、系统雪崩等。
手段
拒绝部分老请求
确保“新”请求正常 响应,因为老请求可能在前端以及等待超时了。
实现方式可以通过RPC队列:请求入队、出队时间,对于在队列中停留超过阈值的直接丢弃。
优先级请求
丢弃非核心请求,保证主要业务
随机拒绝
随机处理一定比例的请求,网站一会可用,一会不可用。
Integer.valueOf("1.23")的实现
方案
集中式:仅仅在网关层执行降级不靠谱,因为网关层无法确定应该过滤多少流量。只有底层才知道自己能处理多少个请求。
自治式:各个层都由自己的降级方案。
一般不需要对DB层进行降级,但新浪微博对于明星时间的突发流量,通过将瞬时数据写入缓存,然后在慢慢回放给数据库保证服务的可用性。
Log4j是阻塞的写日志,如果阻塞大量日志需要写的话,也会把主机资源用完。
09服务限流/熔断设计
目的
屏蔽性能下降时的非关键服务,防止导致雪崩。
平台化熔断系统需求分析
- 大部分熔断返回默认值
- 进入熔断时打印日志同时返回业务定制的Exception
- 通过服务治理平台能够对熔断进行查看、管理
- 可以报警
- 接口粒度
基于RPC客户端来实现熔断,升级时只需要升级RPC客户端就可以。
通过动态代理实现是否熔断。
业界方案
Hystrix、Resilience4J
10服务灰度发布设计
定义
一种发布规则,让一部分用户用新系统,另一部分仍用旧系统
金丝雀发布,A/B Test
目的
让一小部分用户来进行线上测试,如果发现问题可以快速回滚。
实现方案
通过配置中心对系统进行配置,包括:指定灰度服务器、灰度流量占比、是否允许灰度。
协议设计
数据协议:
确定Header中的灰度发布以来字段,比如:udi,Token,IP,网关中打Tag等。
灰度发布策略:仅根据uid/token/IP的单策略,基于上下文的灰度策略。
案例
- 多层服务进行灰度,通过nginx层打tag实现
- 涉及到数据的灰度服务分三步:部分灰度(数据全量复制、双写);全部灰度(双写);灰度完成(去除双写)
业界产品
Spring Cloud灰度发布神器:Nepxion Discovery,是服务注册和负载均和的增强中间件,包含了灰度发布功能。
11服务全链路压测设计
基于线上真实环境,模拟海量用户,对整个链路进行压测。
工具:
JMeter、TCPCopy(流量复制)、Apache ab
标准:
CPU、网卡、请求超时量、QPS
网友评论