思路其实也很简单了。要想无限扩展,跑Netty的服务器肯定是分布式的,机器数量会随着业务增长而增长,但是,谁来管理这些Netty的服务呢? 于是,第1个问题就产生了,通俗的问一句就是,万一Netty的服务挂了,谁管? 你可以说,我写个shell脚本来试试,如果项目小,只有几台机器,那也不妨这么做,但是,假定机器上到100左右,服务太多了,还玩shell脚本,不管是部署还是运维,真的有点麻烦了。所以,不如一开始就装个逼,让专业人干专业事,就交给zookeeper去做吧。 好,这个问题解决了。 第2个问题,假如跑Netty的机器很多,作为客户端,我们应该连哪台机器?总得有人告诉我吧。 可以的,用个http服务群的机器专门做这个事情,连接前,你就去请求这个接口,让这个接口给你分配Netty的机器。于是,这个问题也解决了。 第3个问题,既然是IM,就可以理解成必定是用户对用户,或者是管理者对众多用户,用通俗的话就是需要解决一对一和多对一的问题,这个问题,必然涉及到路由,张三发条消息给李四,我先不论你发的是什么内容,前提就是,你得告诉张三李四在哪里?这个路由表怎么维护?简单做也行,我觉得这个路由表肯定没有ip路由器的路由表那么复杂,但是,用什么组件呢? Redis集群当然是合适的,因为他可以无限扩,如果集群又主备,那当然机器越多越安全,数据也不会丢失,所以,可以用Redis去维护路由。 聊到这里,好像设计的问题都解决了,其实才刚刚开始。 第4个问题是,推送的身份的问题怎么解决? 既然是推送,谁推送给谁?这个谁,用什么来定义?不解决这个问题,就没得玩了。这里面需要用到两个东西,一个是硬件设备ID(理解成一台手机的唯一ID),一个是业务唯一ID(比如QQ号),你的QQ号可以换着用不同的手机登录。而业务唯一ID是不变的,但是硬件唯一ID可能会变。硬件唯一ID用来连接服务器,而业务唯一ID用来做什么?就是维持关系,维持路由!多个朋友多条路,原来这话是真的!因为业务ID是稳定的,不容易变的,所以用来做路由是最合适的。比如说,张三用手机1想登录qq1给用手机2登录qq2的李四发消息,那得怎么做?当然是张三的手机先让http服务群分配机器信息,然后连上机器Netty1,然后把消息发送到qq2,当消息发到Netty1服务器的时候,Netty1服务器拿着李四的qq号雄赳赳地去Redis集群查找qq2的路由信息,也就是,qq2是否登陆了,是哪台手机登录的,连接的是哪台Netty服务器?假定找到了李四用手机2连到Netty2服务器,他就把消息转到转给Netty2,Netty2就直接处理就行了,Netty2把消息推送给李四的手机。 那么,第5个问题来了,假如李四没有登录呢?那这个消息应该发给谁啊?那还得有个专门的模块来处理处理这些消息孤儿。 孤儿消息也可以用一群Netty服务来接收,但是,这个Netty服务群也应该可以扩展,否则,如果不在线的人数多了起来,处理孤儿消息的服务器就会撑不住。至于说,孤儿消息保留多久,多久去查一次状态然后决定是否重发,这个问题太细,就留在后续再展开讨论吧。 但是,第6个问题又来了,处理正常业务的那群Netty和处理孤儿消息的这群Netty之间,用来来连接呢?总得有个中心节点,其实不妨用个队列,比如active mq,这里最好持久化,就不担心有消息丢失了。然后处理孤儿消息的Netty服务,从mq里面把消息拿过来,然后做好持久化,这里的持久化,我觉得可以用mongodb,拿回来之后,存在mongodb里面,有空就去处理一下就可以了。 至于全局的基础参数,比如心跳的时间,比如孤儿消息的保存时间等等,可以放到redis里面去,隔一段时间就去装载一次就可以了。 整个设计就差不多出来了。这么这个就是大概的网络架构图。 可以说,这个架构虽然没有多复杂,但是可以支撑无限扩展,可以做到99.9%高可用应该没有问题,这里当然是不包括机房全军覆没的,之前挖断光纤的时候,微信不也出问题吗,所以,100%高可用是伪命题。 剩下的0.1%,只能交给业务层去处理了。 比如,上面的Redis机器,只要有三台或者以上,宕掉一台就没有关系的,如果三台都出事当然不行了。 比如上面的处理正常消息的Netty群,Netty服务的话,一般消息只是从其中经过而已,时间非常短,所以不必做持久化。如果真的机器出故障了被zookeeper摘了。用户可以去请求http服务器去要一台新的Netty机器,但是,这里会有个问题就是,所有的Netty机器不能跑得太满,否则,可能出现雪崩的情况。比如说,其中一台出事了,请求分配其它的Netty机器,然后其它的更满了,跑着跑着,就全军覆没了。 然后说到队列,队列要做持久化,是因为这是两个服务群之间的通道了,如果真的很多消息在这个地方,而且不持久化,他如果挂了,就会丢失很多消息,这就没有必要了。 然后处理孤儿消息节点群呢?这个地方,照理说不会有非常大的负荷,因为通过队列解耦了,这个地方不可能有洪峰。两个服务群之间用了队列,就是为了让大家都冷静一下,比如说,万一真的有很多用户不在线,也不至于会出大事,基本洪流到队列就会被削掉。 而处理孤儿消息的Netty服务为什么要弄个MongoDb呢?很简单,他得尝试很多次推送给用户,而且可能大多数都是失败的,这些消息,应该会存放在服务器一段时间,如果不给持久化,有点风吹草动,很容易丢失大量的孤儿消息。所以,处理孤儿消息都是长命功夫长命做的任务,如果能处理就处理,不能处理就直接存在MongDb,一直到超时就删除掉。 |
|
来自: liang1234_ > 《IM设计参考》