对于一个即时通讯服务器来讲,在用户量少的时候,一台服务器就足以提供全部的服务。而这种架构也最简单,举个例子,用户A与用户B互为好友,A向B发消息,服务器接收到消息时,解析出接收消息的人,直接转发给B便可。但是当用户数量愈来愈多时,一台服务器已经没法全部用户的需求,这时就要进行服务扩容,进行分布式部署redis
如图所示,不一样的用户可能登陆到不一样的服务器上,那么用户A给用户B发消息时,服务器收到消息,首先判断B是否也登陆在本服务器上,若是是,那么直接转发消息便可。若是B不在本服务器上,那应该往哪里转发这条消息呢?最简单的作法就是向服务器集群中的其余服务器广播这条消息,对于每一个收到这条消息的服务器,首先判断消息的目的用户是否登陆在本身身上,若是不是,直接忽略该消息。若是是,那么向目的用户转发该消息。当然,这种暴力粗犷的作法是最简单直接的,可是会产生不少无效的消息转发,对于服务器性能产生很大的影响。曾看过蘑菇街开源的即时通讯软件Teamtalk的代码,服务器就是这种实现方式。其服务器架构以下:数据库
不一样的msg服务器链接到同一台route server上,全部msg服务器之间的转发所有经过route server。这无疑会加剧route server的负载。即时msg server部署的再多,根据木桶理论,一个系统的性能是由其最薄弱的环节所决定的。因此也注定这样的架构,其系统容量也是有限的。那么如何改善这种系统呢,很明显服务器之间的消息转发不能直接所有广播,而应该有一套明确的路由系统,即服务器在转发消息时,应该知道这条消息应该转发到哪一台服务器,这样就不须要每条消息都在全部服务器之间广播了。缓存
那么如何实现这样一套路由系统呢?服务器
简单的作法是,每一个用户上线时,经过其链接的msg server向其余全部msg server广播本身的登陆信息,告知其余服务器本身登陆在哪台服务器上面。这样当某个用户向其好友发消息时,首先经过好友id查看其登陆的msg server。若是好友与本身是同一台服务器,那么直接转发便可;若是不是,服务器向route server发送转发该消息,而且带上目标msg server的id.这样route server 收到消息后,解析出目标的msg server,进行一次转发便可,省去了大量的广播消息。这种方式虽然解决了广播消息的问题,可是在每台msg server上都要保存全部用户的路由信息。当全部用户都登陆时,几乎就退化成了单点模型,msg server确定承受不了。架构
那么如何解决这个问题呢?试想一下,既然全部的msg server上都保存着一样的路由信息,那么咱们能够把这些数据从msg server剥离出来,存在一个单独的服务器上,供msg server查询。咱们暂且把这个服务器叫作route info server(路由信息服务器).对于一个用户要存储的数据为分布式
{性能
userid,spa
msgserverid设计
}server
假设这两个数据都是32Byte,那么存储一亿个用户须要的内存32B*10^8=3.2G。目前好点的服务器都有50G的内存,很显然内存不是问题。那么就剩访问量的问题。若是全部的msg server都从这一台服务器上读取数据, 确定会影响整个系统的性能。因此路由信息服务器不能采用这种单点模型。考虑到这种路由信息的特色,很明显是一种读多写少的数据。一个用户只有在登陆的时候才会写一次路由信息,其余时候就是转发消息的时候读取路由信息了。那么能够采用相似数据库的主备模型,主服务器用来写路由信息,备服务器用于查询路由信息。并且能够设置多台备服务器,分担msg server的读压力。其实咱们也可使用一些成熟的缓存系统来完成路由信息服务器的功能,好比redis. redis拥有现成的主备方案,只是像这种通用的缓存服务器在存储数据时,消耗的内存会大些。研究过redis源码的,大多都能理解。其key value都存储在redisobject的结构体当中,有一些附加的信息,因此比本身写一个这样的服务所消耗的内存确定会大些。
OK,说完了路由信息服务器,咱们再回到msg server上来。那么msg server在转发消息时,首先根据目的用户的id 到路由信息服务器上查找其所在的msg server用于消息转发。可是这也会存在一个,每次转发消息时,都要查询一次路由信息,这无疑会影响消息的转发速度,并且也会增大路由信息服务器的访问压力。若是在发送消息以后,将路由信息保存到本地,那么下次发送消息,就无需再去路由信息服务器重复查询了。可是也不能把全部的路由所有保存到本地,那样又会严重消耗msg server的内存。因而,就有咱们想到一种折中的方案,使用一个lru的缓存队列,在须要保存新的路由信息时,首先查看缓存队列是否已满,若是未满,直接插入到队首,若是队列已满,淘汰到队尾的数据。缓存列队大小可根据内存大小灵活设置。考虑到在咱们平时在使用qq时,大部分人都登陆着,可是发消息的人并很少。对于路由信息,在其首次转发消息是,从路由信息服务器查询一次路由,在其整个回话过程当中,路由信息都缓存在本地。在其会话结束后,将最近最久未使用的路由数据淘汰出去,这种作法再考虑到内存使用的同时,又大大减小了服务器的访问次数,算是一种较好的折中方案. 在完成了路由信息系统以后,route server也能够进行水平扩展,route server要作的仅仅是转发消息,并不须要存储数据,扩展起来很是方便。最终的系统架构以下:
总结:
1. 本文所描述的即时通讯服务器架构,着重讨论的是消息如何路由的问题,但这并不表明一个完整的即时通讯服务器系统,诸如注册,登陆,离线消息,文件等功能这些都未在本文的讨论范围之类
2. 本文中所提的方案也是一种设想,并未真正进行实现,确定也有不少细节问题没有考虑到。欢迎你们留言讨论
3. 对于本文所提的系统,可称之为一个服务集群。而像qq这样数量用户的系统,在全国分布了不少个集群。本文所讨论的也仅仅局限于一个集群内的通讯设计,而集群之间的通讯又如何通讯呢。每一个集群的路由数据,若是全同步到其余集群,这种作法显然不是最优。若是有更好的想法,也欢迎留言讨论