日处理20亿数据,实时用户行为服务系统架构实践(2)
另外,数据发生积压的情况下,可以调整Worker的消费游标,从最新的数据重新开始消费,保证最新数据得到处理.中间未经处理的一段数据则启动backupWorker,指定起止游标,在消费完指定区间的数据之后,backupWorker会自动停止.(如下图) 图6:积压数据消解 三、可用性?作为基础服务,对可用性的要求比一般的服务要高得多,因为下游依赖的服务多,一旦出现故障,有可能会引起级联反应影响大量业务.项目从设计上对以下问题做了处理,保障系统的可用性:
首先是系统层面上做了全栈集群化.kafka和storm本身比较成熟地支持集群化运维;web服务支持了无状态处理并且通过负载均衡实现集群化;Redis和DB方面携程已经支持主备部署,使用过程中如果主机发生故障,备机会自动接管服务;通过全栈集群化保障系统没有单点. 另外系统在部分模块不可用时通过降级处理保障整个系统的可用性.先看看正常数据处理流程:(如下图) 图7:正常数据流程 在系统正常状态下,storm会从kafka中读取数据,分别写入到redis和mysql中.服务从redis拉取(取不到时从db补偿),输出给客户端.DB降级的情况下,数据流程也随之改变(如下图) 图8:系统降级-DB 当mysql不可用时,通过打开db降级开关,storm会正常写入redis,但不再往mysql写入数据.数据进入reids就可以被查询服务使用,提供给客户端.另外storm会把数据写入一份到kafka的retry队列,在mysql正常服务之后,通过关闭db降级开关,storm会消费retry队列中的数据,从而把数据写入到mysql中.redis和mysql的数据在降级期间会有不一致,但系统恢复正常之后会通过retry保证数据最终的一致性.redis的降级处理也类似(如下图) 图9:系统降级-Redis 唯一有点不同的是Redis的服务能力要远超过MySQL.所以在Redis降级时系统的吞吐能力是下降的.这时我们会监控db压力,如果发现MySQL压力较大,会暂时停止数据的写入,降低MySQL的压力,从而保证查询服务的稳定. 为了降低故障情况下对下游的影响,查询服务通过Netflix的Hystrix组件支持了熔断模式(如下图). 图10:Circuit Breaker Pattern 在该模式下,一旦服务失败请求在给定时间内超过一个阈值,就会打开熔断开关.在开关开启情况下,服务对后续请求直接返回失败响应,不会再让请求经过业务模块处理,从而避免服务器进一步增加压力引起雪崩,也不会因为响应时间延长拖累调用方. 开关打开之后会开始计时,timeout后会进入Half Open的状态,在该状态下会允许一个请求通过,进入业务处理模块,如果能正常返回则关闭开关,否则继续保持开关打开直到下次timeout.这样业务恢复之后就能正常服务请求. 另外,为了防止单个调用方的非法调用对服务的影响,服务也支持了多个维度限流,包括调用方AppId/ip限流和服务限流,接口限流等. 四、性能&扩展?由于 在线旅游 行业近几年的高速增长,携程作为行业领头羊也蓬勃发展,因此访问量和数据量也大幅提升.公司对业务的要求是可以支撑10倍容量扩展,扩展最难的部分在数据层,因为涉及到存量数据的迁移. 实时用户行为系统的数据层包括Redis和MySQL,Redis因为实现了一致性哈希,扩容时只要加机器,并对分配到新分区的数据作读补偿就可以. MySQL方面,我们也做了水平切分作为扩展的准备,分片数量的选择考虑为2的n次方,这样做在扩容时有明显的好处.因为携程的mysql数据库现在普遍采用的是一主一备的方式,在扩容时可以直接把备机拉平成第二台(组)主机.假设原来分了2个库,d0和d1,都放在服务器s0上,s0同时有备机s1.扩容只需要如下几步:
迁移过程利用MySQL的复制分发特性,避免了繁琐易错的人工同步过程,大大降低了迁移成本和时间.整个操作过程可以在几分钟完成,结合DB降级的功能,只有在DNS切换的几秒钟时间会产生异常. 整个过程比较简单方便,降低了运维负担,一定程度也能降低过多操作造成类似GitLab式悲剧的可能性. 五、部署?前文提到Storm部署是比较方便的,只要上传重启就可以完成部署.部署之后由于程序重新启动上下文丢失,可以通过Kafka记录的游标找到之前处理位置,恢复处理. 作者:陈清渠,毕业于武汉大学,多年软件及互联网行业开发经验.14年加入携程,先后负责了订单查询服务重构,实时用户行为服务搭建等项目的架构和研发工作,目前负责携程技术中心基础业务研发部订单中心团队. 文章来自微信公众号:互联网架构师 (编辑:ASP站长网) |