Gobelieve解析

Gobelieve 架构

Gobelieve github地址

im 客户连接服务器 (可分布式部署,暂无负载均衡模块)

imr 路由查询服务器(主要解决im分布式部署的问题)

ims 存储服务器 (主从部署)

基础模块


1.数据包协议

包:header(12)|body

header:len(4),seq(4),cmd(1),version(1),空(2)

2.数据收发流程

accept收到一个连接
开启写线程和读线程

写线程:监听client.wt阻塞队列,一有数据就写入conn

读线程:按照数据包协议从conn读出数据包,由client.HandleMessage处理

3.几个方法

PushMessage 通过route_channel 发送 MSG_PUBLISH 给IMR
PushGroupMessage 通过route_channel 发送 MSG_PUBLISH_GROUP 给IMR
SaveMessage 通过IMS RPC服务 调用SavePeerMessage
SaveGroupMessage 通过IMS RPC服务 调用SaveGroupMessage
im_client.SendMessage
    1.PushMessage
    2.本地路由查询 并EnqueueMessage
im_client.SendGroupMessage
    1.PushGroupMessage
    2.group_manager查询group
    3.由group得到所有menber,对每个menber查询路由表,并EnqueueMessage
im_client.EnqueueMessage
    将数据写入client.wt,供发送出去

IM 模块


IM模块初始化

1.redis_pool

2.storage_pools 连接ims:3333

http服务器读取最近消息时调用

3.rpc_clients ims:13333

SyncMessage
SyncGroupMessage
SavePeerMessage
SaveGroupMessage

4.group_rpc_clients (可选)

5.route_channels 连接imr:4444

开启读写线程
写:从channel.wt管道取值并发送给imr
读:从imr接受消息,并分发给当前im节点连接用户

6.group_manager

1.load: 从mysql加载group,保存至 group_manager.groups
2.run: reids订阅 
case group_create、
    group_disband、
    group_member_add、
    group_member_remove、
    group_upgrade、
    回调处理 增删改查group_manager.groups
case ping
    脏数据检测
3.ping:
   每个五分钟发送ping

7.group_message_deliver:普通群消息分发

1.init:创建本地存储文件
2.run: 监听wt管道,有数据表示有新消息写入文件
        读取文件并发送

8.ListenRedis 禁言

redis订阅 speak_forbidden
接受事件推送,从本地路由查询到对应client,修改forbidden字段。

9.SyncKeyService

从 group_sync_c 和 sync_c 管道取值,保存至redis
( 客户端发送的 MSG_GROUP_SYNC_KEY 和 MSG_SYNC_KEY 消息会将消息内同步key写入对应的管道 group_sync_c 和 sync_c)

10.StartHttpServer :6666

web服务器

11.StartSocketIO :websocket

12.ListenClient :23000 处理客户端连接

ListenClient 处理流程


1.登录认证 cmd:MSG_AUTH_TOKEN

客户端将uid与token传给服务器,由redis_pool查询认证

认证成功: 1.由EnqueueMessage发送消息{cmd:MSG_AUTH_STATUS,status:0}
        2.client.AddClient() 缓存本连接到本机路由表
        3.client.IMClient.Login() 缓存本链接到IMR路由表
认证失败: 由EnqueueMessage发送消息{cmd:MSG_AUTH_STATUS,status:1}

2.IMClient 处理消息类型

MSG_IM: 处理IM 同步消息
MSG_GROUP_IM: 处理Group 同步消息
MSG_INPUTING: 处理Inputing消息
MSG_RT: 处理实时消息
MSG_UNREAD_COUNT: 设置未读消息数
MSG_SYNC: 客户端请求同步最新消息
MSG_SYNC_KEY: 客户端将SYNC_KEY 传至服务端
MSG_SYNC_GROUP: 客户端请求同步最新群消息
MSG_GROUP_SYNC_KEY: 客户端将GROUP_SYNC_KEY 传至服务端

RoomClient 消息类型

MSG_ENTER_ROOM:进入聊天室
MSG_LEAVE_ROOM:离开聊天室
MSG_ROOM_IM:聊天室IM消息

VOIPClient消息类型

MSG_VOIP_CONTROL: VOIP命令

CustomerClient消息类型

MSG_CUSTOMER: 顾客->客服
MSG_CUSTOMER_SUPPORT:客服->顾客

3.MSG_IM处理流程:
用户A -> B

1.SaveMessage:保存消息到目标用户B存储队列 (rpc->SavePeerMessage)
2.SaveMessage:保存消息到发送用户A存储队列(供多点登录同步消息)
3.PushMessage:外部推送消息给目标用户B(由IMR寻路由)MSG_IM
4.SendMessage:发送同步消息给目标用户B (外部推送+本地寻址发送) MSG_SYNC_NOTIFY
5.SendMessage:发送同步消息给发送用户A(多点登录)MSG_SYNC_NOTIFY
6.EnqueueMessage:给本连接回复MSG_ACK消息

4.MSG_GROUP_IM处理流程

1.由group_manager查询到指定group
2.根据Group类型:
    1.HandleSuperGroupMessage:
        SaveGroupMessage:保存MSG_GROUP_IM消息 (rpc->SaveGroupMessage)
        PushGroupMessage:外部推送群消息 MSG_GROUP_IM
        SendGroupMessage:发送群同步通知消息(外部推送+本地寻址推送) MSG_SYNC_GROUP_NOTIFY
    2.HandleGroupMessage:
        group_message_deliver:
            saveMessage:本地保存消息 MSG_PENDING_GROUP_MESSAGE
            ReadMessage:读取消息 MSG_PENDING_GROUP_MESSAGE
            对群每个成员:SaveMessage:保存MSG_GROUP_IM消息 
                        PushMessage:外部推送消息 MSG_GROUP_IM
                        SendMessage:发送同步消息 MSG_SYNC_NOTIFY
3.EnqueueMessage:给本连接回复MSG_ACK消息

5.MSG_INPUTING:

SendMessage:发送给目标用户

6.MSG_RT 实时消息处理流程

SendMessage:发送消息给目标用户

7.MSG_UNREAD_COUNT 设置用户未读数:

由redis_pool操作 hashkey:users_$appid_$uid field:unread

8.消息同步流程:

服务端->客户端 MSG_SYNC_NOTIFY:
    客户端:
    1.isSyncing==false:sendSync 发送旧syncKey,请求消息MSG_SYNC,状态切换为同步状态
    2.isSyncing==true: 同步状态中,新的newSyncKey 保存在pendingSyncKey中,this.pendingSyncKey = newSyncKey;

客户端->服务端:MSG_SYNC
    服务端:
    1.从客户端传来的sync_key得到last_id,(如果last_id==0,从redis取出最新sync_key)
    2.rpc->SyncMessage:根据last_id取出缓存的最近消息msgs
    3.EnqueueMessage:发送MSG_SYNC_BEGIN消息 (客户端不做处理)
    4.EnqueueMessage:循环发送msgs
    5.EnqueueMessage:发送MSG_SYNC_END消息 其中包含sync_key为最后一条msg的MsgID

服务端->客户端:MSG_SYNC_END
    客户端: 
    1.取出newSyncKey(如果newSyncKey>this.syncKey,客户端保存newSyncKey,并发送给服务端MSG_SYNC_KEY)
    2.切换同步状态isSyncing = false; 
    3.如果this.pendingSyncKey > this.syncKey ,
        即在上次同步中有新的MSG_SYNC_NOTIFY消息传给客户端,则再次同步,sendSync发送syncKey,pendingSyncKey置零

客户端->服务端: MSG_SYNC_KEY
    服务端:
    1.从sync_key得到last_id,
    2.包裹成SyncHistory,写入管道sync_c <- s
消息传输流程

9.超级群同步流程

服务端->客户端 MSG_SYNC_GROUP_NOTIFY
    客户端: 
    1.isSyncing==false:sendSync 发送旧syncKey,请求消息MSG_SYNC_GROUP,状态切换为同步状态
    2.isSyncing==true: 同步状态中,新的newSyncKey 保存在pendingSyncKey中,this.pendingSyncKey = newSyncKey;

客户端->服务端 MSG_SYNC_GROUP
    服务端: 
    1/从客户端传来的group_sync_key取出group_id,sync_key,last_id=sync_key,(如果last_id,从redis取出新的group_sync_key_$groupid)
    2.rpc->SyncGroupMessage:根据last_id取出最近的群消息 msgs
    3.EnqueueMessage:发送MSG_SYNC_GROUP_BEGIN 消息
    4.EnqueueMessage:循环发送msgs
    5.EnqueueMessage:发送MSG_SYNC_GROUP_END,其中sync_key为最后一条msg的MsgID

服务端->客户端 MSG_SYNC_GROUP_END
    客户端:
    1.取出GroupSyncKey.syncKey和当前syncKey(如果GroupSyncKey.syncKey较大,客户端保存更新,并发送给服务端 MSG_GROUP_SYNC_KEY)
    
    2.切换同步状态isSyncing = false;
        如果this.pendingSyncKey > this.syncKey ,
            即在上次同步中有新的MSG_SYNC_NOTIFY消息传给客户端,则再次同步,sendSync发送syncKey,pendingSyncKey置零

客户端->服务端 MSG_GROUP_SYNC_KEY
    服务端:
    1.取出group_id last_id
    2.包裹成SyncGroupHistory,写入管道group_sync_c <- s 

IMR模块

1.redis_pool

2.group_manager

3.ListenClient :4444

1.MSG_SUBSCRIBE
2.MSG_UNSUBSCRIBE
3.MSG_SUBSCRIBE_ROOM
4.MSG_UNSUBSCRIBE_ROOM
(以上四个均是对imr维护的路由表增删改查)
5.MSG_PUBLISH
    1.根据消息内容得到消息类型和目标用户
    2.查询路由表,用户如果离线则将消息放入第三方推送队列
    3.根据路由表得到用户连接所在im节点,并把消息推送至该节点
    
    第三部过滤条件:
        1.消息类型不为 MSG_IM、MSG_GROUP_IM、MSG_CUSTOMER、MSG_CUSTOMER_SUPPORT、MSG_SYSTEM
        2.目标IM节点和发送IM节点不是同一节点

6.MSG_PUBLISH_GROUP
    1.根据消息内容得到消息类型和群
    2.对群内每个成员查询路由表,用户如果离线则将消息放入第三方推送队列
    3.群发给所有IM节点
    
    第三部过滤条件:
        1.消息类型为 MSG_PUBLISH_GROUP

7.MSG_PUBLISH_ROOM
    1.根据消息内容得到roomid
    2.根据roomid查询路由表得到所有节点
    3.对每个节点发送消息

    第三部过滤条件:
        1.发送节点和目标节点不是同一节点
IMR架构

IMS模块 (主从)

1.NewMaster

1.init:创建容器存储 clients,创建队列ewt
2.run:监听队列ewt,将ewt队列内消息添加到缓存cache数组
    cache每满1000或者每隔1分钟,执行SendBatch,
    SendBatch:
        封装消息 MSG_STORAGE_SYNC_MESSAGE_BATCH ,写入每个从节点连接的消息队列client.ewt

2.NewSlaver 监听主节点(可选)

run:连接至主节点,连接成功发送 MSG_STORAGE_SYNC_BEGIN
    循环读取消息:
        MSG_STORAGE_SYNC_MESSAGE storage.SaveSyncMessage(emsg)
        MSG_STORAGE_SYNC_MESSAGE_BATCH storage.SaveSyncMessageBatch(mb)

3.waitSignal 处理中断 SIGINT SIGTERM

storage.FlushPeerIndex() 将每个人最近消息的msgid写入文件
storage.FlushGroupIndex()

4.ListenSyncClient master监听 3334

处理从节点连接 RunLoop:
    1.(初始化同步)接受 MSG_STORAGE_SYNC_BEGIN 消息,从中取得msgid,
    根据msgid LoadSyncMessagesInBackground 查询得到消息,并发送给从节点 
    2.将从节点连接添加至 clients
    3.进入for循环,监听消息队列client.ewt并发送给从节点
    4.循环break后 RemoveClient

5.ListenRPCClient :13333

SyncMessage
SyncGroupMessage
SavePeerMessage
SaveGroupMessage
GetNewCount
由im调用

6.ListenClient() 3333

对于每个连接:
    1.init:创建写管道wt
    2.run :写线程,从wt管道取数据并发送
            读线程,HandleMessage

7.HandleMessage

1.MSG_LOAD_OFFLINE  
    对IM: storage_client.LoadOfflineMessage响应
2.MSG_SAVE_AND_ENQUEUE 
    对IM:storage_client.SaveAndEnqueueMessage响应
3.MSG_DEQUEUE 
    对IM: storage_client.DequeueMessage响应
*4.MSG_LOAD_LATEST
    对IM-StartHttpServer-LoadLatestMessage响应
*5.MSG_LOAD_HISTORY 
    对IM-StartHttpServer-LoadHistoryMessage响应
6.MSG_SAVE_AND_ENQUEUE_GROUP  
    对IM: storage_client.SaveAndEnqueueGroupMessage响应
7.MSG_DEQUEUE_GROUP 
    对IM :storage_client.DequeueGroupMessage响应
8.MSG_LOAD_GROUP_OFFLINE 
    对IM: storage_client.LoadGroupOfflineMessage响应

(上述除了4,5,暂无被IM模块调用)
IMS架构
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • 一 典型的即时通讯架构可能是这样的 无论是IM消息通信系统还是客户消息系统,其本质都是一套消息发送与投递系统,或者...
    isgiker阅读 4,957评论 3 12
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 本文转载自http://dataunion.org/?p=9307 背景介绍Kafka简介Kafka是一种分布式的...
    Bottle丶Fish阅读 5,355评论 0 34
  • Python 面向对象Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对...
    顺毛阅读 4,165评论 4 16
  • 应是渺茫 惊醒在没有你的梦境 此刻 山也生长 水也生长 也叹沧桑 黎明压抑着夜的情绪 于是 风不思量 雨不思量 不...
    Whale小诺阅读 123评论 0 2