由于要完成一个聊天的项目,因此借鉴了goim,第一篇分析打算半原版,先摘抄http://www.jianshu.com/p/8bd96a9a473d他的一些理解,写这些仍是为了让本身更好的理解这个项目,若是有什么不对的,请你们给我留言。git
在整个架构中,系统被分红 Comet, Logic, Job, Router 四大模块,各个模块经过 RPC 通讯,参考官方中文文档,Comet 程序是链接层,暴露给公网,全部的业务处理推给 Logic 模块,经过 RPC 通讯。这样设计的好处在于,长链接逻辑不多变更,稳定的保持公网链接,然后端 Logic, Router 模块常常变更,重启不会影响链接层。github
作为典型代码即注释的开源项目,goim 基本无太多阅读障碍,几个逻辑点梳理下很快就会明白。后端
Bucket: 每一个 Comet 程序拥有若干个 Bucket, 能够理解为 Session Management, 保存着当前 Comet 服务于哪些 Room 和 Channel. 长链接具体分布在哪一个 Bucket 上呢?根据 SubKey 一致性 Hash 来选择。网络
Room: 能够理解为房间,群组或是一个 Group. 这个房间内维护 N 个 Channel, 即长链接用户。在该 Room 内广播消息,会发送给房间内的全部 Channel.架构
Channel: 维护一个长链接用户,只能对应一个 Room. 推送的消息能够在 Room 内广播,也能够推送到指定的 Channel.并发
Proto: 消息结构体,存放版本号,操做类型,消息序号和消息体。优化
Goim 支持 Tcp, Http, WebSocket, TLS WebSocket. 很是强大,底层原理同样,下面的分析都是基于 Tcp 协议。spa
先来看看结构体的定义.net
定义很明了,维护当前消息通道和房间的信息,方法也很简单,加减 Channel 和 Room. 一个 Comet Server 默认开启 1024 Bucket, 这样作的好处是减小锁 ( Bucket.cLock ) 争用,在大并发业务上尤为明显。最新的版本增长了routineamout,routinesize这两个选项主要是用来作progress推送消息的做用,第一个是作消息推送协程个数,第二个是管道长度。比旧版buket取消了之前的roomoptions直接使用room的参数设计
Room 结构体稍显复杂一些,不但要维护所属的消息通道 Channel, 还要消息广播的合并写,即 Batch Write, 若是不合并写,每来一个小的消息都经过长链接写出去,系统 Syscall 调用的开销会很是大,Pprof 的时候会看到网络 Syscall 是大头。新版本room改动很大,之前这么同窗说辞不知道是否管用,可是从结构体定义来看,room增长了下个channel和一个在线状态。
Logic Server 经过 RPC 调用,将广播的消息发给 Room.Push, 数据会被暂存在proto这个结构体 里,每一个 Room 在初始化时会开启一个 groutine 用来处理暂存的消息,达到 Batch Num 数量或是延迟必定时间后,将消息批量 Push 到 Channel 消息通道。
Channel
这是一个通道。Writer/Reader 就是对网络 Conn 的封装,cliProto 是一个 Ring Buffer,保存 Room 广播或是直接发送过来的消息体。
这里只分析 Comet 代码,因此消息生成暂时不提
1. Client 链接到 Comet Server, 握手认证
2. 新建当前长链接的 Channel, 因为 Comet 服务不处理业务逻辑,须要 RPC 去 Logic Server 获取该 Channel 的订阅信息。同时 Channel 开启一个 dispatchTCP groutine, 阻塞等待 Ring Buffer 数据可用,发送到 Client。
3. Logic 服务经过 RPC, 将消息写到 Room (广播)或是直接写到指定 Channel (单播)。注意这里,广播是有写合并 BatchWrite, 而单播没有,消息生成后马上发送。
4. Room 里的广播消息到达必定数量 Batch Num, 或是延迟等待必定时间后,将消息写到 Channel Ring Buffer。
新版本跟之前那位同窗的很不同,初版本因为时间关系,基本大多数是摘抄这位同窗的,感受新版本不少不对,可是阅读代码还没到那个程度,因此初版就到这里,这位同窗对goim大致架子分析的很清楚,细节在我后续章节会有,若是有不对的,请及时留言纠正
1. bucket按照key的cityhash求余命中的,没有用一致性hash,由于这里不涉及迁移
2. 私信发送其实也有合并的,和room合并不一样的是,在ringbuffer取消息饥饿时候才会真正flush
3. 还有一个优化能够改进,由于room有个特色你们消息可能都同样,因此在room提早合并成字节buffer,而后广播全部人,避免每一个人都序列化一次,而后利用gc来处理这个buffer的释放,这样能够节省大量cpu,目前这个优化还没作