IM服务器开发,从功能抽象的角度看可能非常简单,可以认为是管理大量的客户端连接和在不同的客户端之间传递消息,但具体到实现细节就比较复杂了。打个不恰当的比喻,OS的功能抽象也非常简单,无非是进程间的调度和硬件资源的管理,但要是自己去实现一个,一般人也就只能呵呵了。
由于IM服务器里面的内容比较多,这个可以是一个系列的内容,所以这里只介绍服务器的架构以及为什么选择这样的架构
我们的设计原则是保持简单,可以做一定的扩容,无单点故障。很多时候开发人员常犯的错误是过度设计和提前优化,"When in doubt, use brute force", 有时简单的方案才是最好的方案。
架构设计需要考虑到服务器的业务逻辑和预计的用户量,比如同样的IM服务器设计,最高并发在线人数百万和千万的肯定有很大的不同, 如果按千万级别来设计一个只有百万级别的系统,增加的复杂度和工作量是非常可观的.
目前我们的IM服务器架构设计的单机并发连接10万用户,总并发用户量可以达百万级,对于这样规模的服务器后台,可以采用很简单的架构来处理。有一个DispatchServer来分配客户端到一个消息服务器,消息服务器之间的数据交互通过一个中心的RouteServer来转发,和数据持久化层之间的交互由DBProxy服务器来处理.
由于IM消息服务器和客户端之间交互非常频繁,但处理单个数据包的逻辑比较简单,没有IO或CPU密集型的操作,所以消息服务器采用单线程来处理,这样比较简单,没有死锁,竞争条件,出现问题非常好定位。
分离出一个DBProxy服务器的的理由是,由于要操作DB和Redis,一个请求的回复会比较耗时,但是交互非常简单,一个请求对应一个回复,与消息服务器之间的长连接一般很稳定,不太需要处理太多的断连,所以这个可以用多线程的来处理大的IO等待。而且由于DBProxy服务器之间不需要交互,这样可以随着业务量的增加,添加很多实例来分散每个DBProxy的压力。
DispatchServer和RouteServer的逻辑都非常简单,而且可以启动多个实例,这样就不存在单点故障。两个DispatchServer或两个RouteServer之间的状态同步是通过消息服务器来实现的,比如用户上下线时,需要把这个状态通知所有的DispatchServer和RouteServer。
消息服务器支持TCP长连接和HTTP长轮询两种接入方式,目前的消息服务器承担了接入和业务逻辑处理两种角色,一般业界的做法是把这两种功能分解开,有一个接入服务器来处理客户端的接入,然后客户端的请求被分配到不同的业务逻辑处理服务器来处理,比如登陆服务器,状态通知服务器,好友管理服务器等。但这样运维起来比较麻烦,对于百万量级的IM来说,这样有点过度设计的感觉,所以我们把这些功能融合在一个消息服务器里面实现,但这样的缺点是,更新一个业务功能时,需要把重启消息服务器,这样客户端会有一个重新连接服务器的过程。以后可以调研是不是可以把业务逻辑写成动态加载库,这样修改业务逻辑时,只要Reload动态库就可以了
搭建这些只是开了一个头,以后可以做很多有挑战性的技术,比如用分布式的消息存储方案代替现在的MySQL,用分布式的小文件系统存储系统代替现在依赖蘑菇街主站的图片存储方案,用一些Unsupervised Learning的算法对用户消息进行分析,来获取一些用户的profile信息。
* 作者:蘑菇街子腾
* 作者简介:蘑菇街「SWAT」团队IM底层服务架构师,IM底层架构缔造者。
擅长领域:c++ 及网络编程相关技术,大神级别。
* 个人博客:(该家伙比较闷骚,至今未写博客。。)