背景
微聊是58集团的即时通讯工具,承载了58同城、赶集网、移动经纪人、安居客、招才猫等产品线的用户在线沟通能力,支持PC、M、APP等多个端。为了满足58集团不断增长的业务需求,从设计之初到现在,微聊的架构经历了几个版本的演进,目前已经形成一套多服务协同处理、层次分明、结构清晰的微服务架构。
架构演进
最初的微聊架构很简单,server端短连接只有单一的php服务支撑,客户端所有请求通过短连接访问,php服务直接操作数据库。下行数据通过接口调用长连接下发,但查询用户在线状态并没有提供接口,短连接服务和长连接服务共享redis数据库,查询用户在线状态,服务间通讯采用的内网http接口;
这种架构有很多问题,首先长连接和短连接服务虽然隔离,但共享存储,这样一旦存储变更,两个服务都要改动,隔离的并不彻底,而且http协议本身在内网性能也不如长连接rpc调用。
其次,短连接服务大一统的架构会导致项目所有代码耦合在一起,既不利于分工协作,也会增加开发和维护难度,多个需求并行开发时,版本管理很复杂。
再次,PHP作为脚本语言性能本身就没有优势,服务选用的框架又不够简洁,导致整体服务的性能很差,不得不依靠增加机器数量支撑业务。
基于以上问题,微聊架构经历几个周期的服务化改造,最终形成了一个层次分明,逻辑清晰的微服务架构。
1、 长连接服务彻底服务化改造,长连接逻辑层服务改用58自研的java分布式rpc框架scf实现,不再共享存储,服务间彻底解耦,采用rpc接口提供服务,相比http接口也提升了性能;
2、基于代码耦合度过高的问题,将代码进行服务化改造,降低业务代码的耦合度,实现分工协作的同时,不同模块不会互相影响;
首先进行的是存储层服务化改造,将消息、会话、用户资料、好友关系等核心数据存储,做服务化改造,实现业务逻辑和数据存储的隔离;新的存储服务不在使用php开发,而是改用58自研的JAVA分布式RPC框架scf,底层存储采用分布式存储服务wtable,性能相较php也有了较大幅度的提升。同时,对外提供数据通过消息队列服务实现解耦,将消息、用户资料、用户关系数据写入消息队列,业务方根据需要自行订阅,减少了和业务方的耦合。
之后是对逻辑层进行拆分,存储服务化之后,业务逻辑代码依然很复杂,为了进一步降低业务耦合度,对业务逻辑比较独立的代码做了进一步的拆分,基于scf框架,独立开发了几个单独的服务,包括群服务、防垃圾服务、异步任务、用户资料同步等等。
经过几轮的拆分,微聊的微服务框架逐渐完善,最下面是存储层,提供消息、用户资料、好友关系等等的数据存储;中间是逻辑层,处理主要的业务逻辑比如防垃圾、群相关服务、长连接逻辑层服务等等;最上层为接入服务,提供短连接接口和长连接下发通道。
最后做了对内对外接口的隔离,在微聊架构整体框架形成的同时,将内部服务调用接口从php服务中独立出来,对内服务统一采用scf框架实现,独立出了ImBusiness服务,即方便业务方调用,又提升了性能。
3、最终,经历几个周期的服务化改造后,微聊的主要性能瓶颈集中在了PHP服务上。经过之前的服务化拆分,php服务本身业务逻辑相对减轻了不少,由于php语言和框架本身的性能问题,再加上基础服务采用scf后,scf框架的序列化方式对php也不友好,性能很差,虽然也有调整优化的空间,但相对于目前流行的golang,性能差距明显,故而直接用golang对原有的php服务进行了整体重构,作为短连接的接入层。
重构完成后,接口层性能有了很大的提升,在机器总量减少80%的同时,机器负载降低了50%以上,主要接口耗时至少减少了30%。
最终,微聊后端形成了一套层次分明、内外隔离、数据解耦的微服务架构。
- 底层存储采用58自研wtable分布式存储服务,业务代码不再考虑分库分表等策略;数据同步采用消息队列服务;
- 数据存储相关服务独立,存储结构的变更对上层业务透明;
- 逻辑层和接口层、存储层隔离,业务逻辑的变更不影响对外接口和底层存储;
- 接口层对内对外隔离,长连接短连接协同作用,为业务方提供稳定可靠服务。
业务模型
下面我们来看一下,在当前微聊架构下,两个主要的业务流程,用户数据同步和消息发送,微聊后端各个服务组件是如何协同处理的。
1、 用户数据同步
用户数据是IM系统中比较重要的数据,涉及用户的好友列表、好友昵称、头像等等数据的展示,微聊本身作为基础服务提供方,最初是没有业务方的用户资料的,需要业务方将用户资料同步到微聊,然后微聊在通过一系列的措施,实时同步到到用户端。
- 用户资料变更,因为微聊只是为58各业务线提供聊天服务,业务线的用户资料是在业务线自行维护的,用户资料的更改也是在业务线完成的,而微聊需要展示用户头像和昵称等信息,就需要业务线把用户资料同步到微聊,这一过程是通过消息队列异步完成的,微聊逻辑层用户资料同步服务主要负责这个工作;
- 用户资料服务在接收到资料变更通知后,会调用存储层用户资料服务存储数据,用户资料服务将数据存储在wtable;
- 数据存储完成后,调用异步任务服务,通过长连接逻辑层服务,判断用户是否在线,如果在线通过长连接服务下发数据;
- 如果用户不在线,那么当用户上线时,长连接服务会将用户登录事件写入消息总线,异步任务服务监听到登录事件后,会判断用户资料是否在下线时有过变更,将数据下发到用户端;
- 微聊长连接除了会下发用户资料外,还会下发好友资料,用户资料变更后,也会给相应的好友下发,但如果是陌生人建立的聊天(与微信不同,在58的业务中,这种情况很常见),客户端是没有对方的用户资料的,这时如果想要正确展示用户信息,客户端会通过短连接接口,主动拉取对应的用户资料。
这样,在微聊的架构下,就实现了一套推拉结合,实时性极强的用户资料同步体系。
2、 消息发送
消息是IM系统的核心,保证消息的实时触达是IM系统最重要的核心功能,微聊的消息流程如下图所示:
- client端调用接入层短连接接口,或者业务后端调用ImBusiness、广播服务发送消息;
- 微聊后端通过存储层消息服务将消息存储成功,写入消息队列,发消息接口返回成功;
- 逻辑层异步任务服务异步处理消息,首先通过长连接逻辑层服务判断用户是否在线,在线则通过长连接将消息下发,不在线则通过push服务发送push;
- 如果用户在线,收到消息后,会调用短连接ack接口,确认消息到达;
- 如果用户离线时收到消息,在下次登录时,长连接服务会触发登录事件,通知异步任务服务,下发用户离线时收到的消息(未ack的消息);
- 用户在更换设备,或者删除了本地消息后,也可以通过短连接拉取历史消息接口,重新拉取之前收到的消息;
- 业务后端可以通过订阅消息队列,来回放整个消息发送过程,以便做相应的业务处理或者数据存储、分析;
可以看到,整个消息处理流程中,各种模块分工协作,既能保证的消息的可靠性,也能保证消息实时触达客户端。
总结
微聊的架构走到今天,经历了多个版本的迭代,中间为了兼容业务的各种需求,也做了很多的妥协和改进,最终才有了现在这个层次分明、结构清晰、内外隔离的微服务架构;随着58集团业务的增长,微聊的架构还将持续的演进和发展,不断追求更高的性能,更强的可靠性,为了更好的支撑集团业务,持续改进,追求卓越。