分布式系统——提交数据,写数据功能设计方案
声明
在系统功能完成几年后计划总结一下此方案,希望为别人提供一些参考价值。
一次偶然发现以前早有大神发表过文章。
中文版本未找到,英文看不太懂。猜测思路类似。可能是重复的造了个轮子。
http://queue.acm.org/detail.cfm?id=1394128
一次偶然发现以前早有大神发表过文章。
中文版本未找到,英文看不太懂。猜测思路类似。可能是重复的造了个轮子。
http://queue.acm.org/detail.cfm?id=1394128
如果文章中有错误或者表述不清的地方,请各位读者批评指正,多谢!
需求背景
实现高可用的写数据功能。(订单,评论,支付等)
数据结构复杂,涉及多个数据表。依赖外部第三方接口(接口无法保证可用性和性能)。
数据结构复杂,涉及多个数据表。依赖外部第三方接口(接口无法保证可用性和性能)。
解决方案一
设计方案图
流程描述
- 用户提交http请求,应用服务器接收数据。
- 应用服务处理业务数据,校验数据合法性,调用外部接口等。
- 应用服务写入数据库。
优点:
- 开发和维护成本低。开发时间短。
- 系统依赖组件少,开发难度不高。
缺点:
- 可用性:单点问题:数据库宕机后服务不可用。
- 可用性:业务处理集中在应用服务器的业务处理模块,如果依赖表结构复杂或者依赖外部接口不可用时,系统服务不可用。
- 性能:直接读写库,数据库压力大,数据库的最大连接数限制系统吞吐量。
解决方案二
设计方案图
流程描述
- 用户提交http请求,应用服务器接收数据。
- 应用服务处理业务数据,校验数据合法性,调用外部接口等。
- 应用服务写入分布式缓存。(新增)
- 应用服务写入数据库
优点:
- 开发和维护成本低。开发时间短。
- 性能相对有保障。缓存保证读服务性能。(新增)
- 系统依赖组件少,开发难度不高。
缺点:
- 可用性:单点问题:数据库宕机后服务不可用。
- 可用性:业务处理集中在应用服务器的业务处理模块,如果依赖表结构复杂,单个表写入失败或者依赖外部接口不可用时,系统服务不可用。
- 性能:直接写库,数据库写入压力大,数据库的最大连接数限制系统吞吐量。
解决方案三
设计方案图
流程描述
- 用户提交http请求,应用服务器接收数据。
- 应用服务处理业务数据,校验数据合法性等,保证数据合法,可进行后续操作。
- 应用服务生成全局唯一id(可参考Twitter-Snowflake)。(新增)
- 应用服务器将数据系统写入分布式缓存。
- 应用服务发送异步消息。例如:ActiveMq,RabbitMq。(新增)
- 异步服务接收mq。服务处理业务数据,调用外部接口等。(新增)
- 异步服务更新缓存。
- 异步服务写入数据库。
优点:
- 提高写服务性能。
缺点:
- 系统依赖mq,当mq不可用或者丢消息时,服务不可用。
注:
- 写入表和写入外部接口时需要先进行反查操作。
- mq在某一节点出错后可重试。
解决方案四-作者使用版本
设计方案图
流程描述
- 用户提交http请求,应用服务器接收数据。
- 应用服务处理业务数据,校验数据合法性等,保证数据合法,可进行后续操作。
- 应用服务器在业务数据上新增加字段,一致性状态,设置为0。(新增)
- 应用服务生成全局唯一id(可参考Twitter-Snowflake)。
- 应用服务器将数据系统写入分布式缓存。
- 应用服务器将数据写入本地。例如:本地缓存和本地文件。(新增)
- 应用服务发送异步消息。例如:ActiveMq,RabbitMq。
- 异步服务接收mq。服务处理业务数据,调用外部接口等。
- 异步服务在所有流程节点完成后将状态设置为1。(新增)
- 异步服务更新缓存。
- 异步服务写入数据库。
worker流程描述:
说明:应用服务worker的主要作用是保证数据的最终一致性。方案的思路主要通过本地存储和重试机制保证数据完整和最终一致。- 获取本地数据集合。
- 轮训数据集合,从缓存中获取数据的一致性状态。
- 判断状态是否为1, 如果为1,则证明此数据已经在异步服务中完成所有流程步骤处理。删除本地数据。
- 如果状态为0,则证明数据还未在异步服务中处理完毕。根据实际情况可发起重试。
报警设置:
- mq积压数量超过阈值报警。说明异步服务中某一节点故障或者网络异常。
- 本地数据积压数量超过阈值报警:说明单点服务器故障可能性比较大。
- 依赖的外部接口必须要监控,监控内容包括可用性和响应时间。便于排查问题。
降级方案:
- 单台服务器故障:可通过nginx反向代理服务器降权重,等本地数据处理完毕后恢复服务。
- mq故障:异步服务提供一个soa接口, 应用服务的发送mq的组件可直连异步服务的soa接口进行数据处理。
注意事项-汇总:
以下几步为功能实现的一些注意事项。 重要程度由上至下依次降低。重要程度以作者的开发经验和线上维护经验为判断依据。不适用所有业务场景。- 异步服务必须支持幂等。支持重试操作。
- 应用服务和异步服务通讯时,建议将业务数据当作消息体传递(消息体内容不宜过大)。说明:多处备份数据可增加系统可用性。
- 异步服务中建议首先反查分布式缓存。如果缓存数据不正确则更新缓存数据。说明:分布式缓存不可用或丢数据时。系统很多查询服务会回源数据库,增加数据库压力,可能产生雪崩效应。
- 异步服务中尽早写入数据库主表。说明:分布式缓存不可用或丢数据时,并且mq首次执行还没落库。可能会造成某些订单查询服务无数据。
- 所有写入数据库时必须先反查数据库是否已经插入过同样的业务数据。说明:防止数据重复写入。
- 依赖外部接口的组件在写入操作时先进行反查操作。说明:防止外部接口没做幂等,写入重复数据。
- 如果业务场景涉及二阶段或者三阶段提交。建议在应用服务器进行CanCommit和PreCommit。在异步处理服务中进行doCommit操作。
方案总结-杂谈,歪批
系统可用性论述:
系统共依赖了分布式缓存,mq,关系型数据库三种组件。下面分别说明当依赖的组件不可用时,系统的现象,读写服务的可用性和处理方案。
- 关系型数据库不可用:
- 系统现象:mq积压数量将增加。本地数据积压数量增加。
- 写服务:正常。数据存储本地就不会丢失数据。
- 读服务:部分正常。缓存提供的数据查询正常。回源数据库的请求不正常。
- 处理方案:恢复数据库服务,mq会逐步被消费。
- 缓存不可用:
- 系统现象: 读服务的请求全部回源到数据库,数据库压力增加。 接口性能下降。
- 写服务:正常,mq消息体内有业务数据,所以异步服务可直接使用消息体的业务数据做处理。
- 读服务:正常,性能低。请求全部回源到数据库。
- 处理方案:恢复缓存服务。
- mq不可用:
- 系统现象:本地数据积压数量将增加。
- 写服务:不正常,需要采用应急方案。
- 读服务:正常。
- 处理方案:应用服务和应用worker直连异步服务的rpc接口。
系统何时不能提供服务?
当数据库和分布式缓存不可用。本地数据积压过多,导致内存溢出或者本地磁盘写满时。服务不可用。