This commit is contained in:
2025-12-27 11:44:50 +08:00
commit ccd43fac1f
1193 changed files with 384161 additions and 0 deletions
+549
View File
@@ -0,0 +1,549 @@
{
"edges":[
{
"id":"5311072484566988",
"styleAttributes":{"pathfindingMethod":"square"},
"toFloating":false,
"fromFloating":false,
"fromNode":"a088e85e91293434",
"fromSide":"bottom",
"toNode":"24c1312b4fc764d7",
"toSide":"top"
},
{
"id":"2f6ce6e78eebc535",
"styleAttributes":{"pathfindingMethod":"square"},
"toFloating":false,
"fromFloating":false,
"fromNode":"7e034ab4587534f8",
"fromSide":"bottom",
"toNode":"24c1312b4fc764d7",
"toSide":"top"
},
{
"id":"ca86ea0bc78e362d",
"styleAttributes":{},
"toFloating":false,
"fromNode":"bb32b85e68a3c5a6",
"fromSide":"bottom",
"toNode":"24c1312b4fc764d7",
"toSide":"top"
},
{
"id":"dcb49779f1396c86",
"styleAttributes":{"pathfindingMethod":"square"},
"toFloating":false,
"fromNode":"f9483b9c68b4ca95",
"fromSide":"bottom",
"toNode":"24c1312b4fc764d7",
"toSide":"top"
},
{
"id":"fa48c88118e041cd",
"styleAttributes":{"pathfindingMethod":"square"},
"toFloating":false,
"fromNode":"d541ad6e1481d9b3",
"fromSide":"bottom",
"toNode":"24c1312b4fc764d7",
"toSide":"top"
},
{
"id":"d203c1ec3edcb103",
"styleAttributes":{"pathfindingMethod":"square"},
"toFloating":false,
"fromFloating":false,
"fromNode":"4f62d8a7660f5155",
"fromSide":"bottom",
"toNode":"6f1d4aa0e394c3fc",
"toSide":"top"
},
{
"id":"50735bd49c8ebf40",
"styleAttributes":{"pathfindingMethod":"square"},
"toFloating":false,
"fromFloating":false,
"fromNode":"7d2f4e279def45da",
"fromSide":"bottom",
"toNode":"6f1d4aa0e394c3fc",
"toSide":"top"
},
{
"id":"1af01a2210ca6691",
"styleAttributes":{"pathfindingMethod":"square"},
"toFloating":false,
"fromNode":"6286d89761a9c395",
"fromSide":"bottom",
"toNode":"6f1d4aa0e394c3fc",
"toSide":"top"
},
{
"id":"021fff7e70488500",
"styleAttributes":{},
"toFloating":false,
"fromFloating":false,
"fromNode":"2f7f8d771e76ac3d",
"fromSide":"bottom",
"toNode":"28adebc518af8f41",
"toSide":"top",
"label":"ws://url/uri+token"
},
{
"id":"157a8b8b3d433ec7",
"styleAttributes":{},
"toFloating":false,
"fromNode":"566be66ace698d58",
"fromSide":"bottom",
"toNode":"78ea03c7259f97a0",
"toSide":"top"
},
{
"id":"f8c655b3c2eb9131",
"styleAttributes":{},
"toFloating":false,
"fromNode":"8fe0b6419e0b01b1",
"fromSide":"bottom",
"toNode":"e637376395b8424b",
"toSide":"top"
},
{
"id":"534d1591d591ab16",
"styleAttributes":{},
"toFloating":false,
"fromNode":"a7cd89568ff94edd",
"fromSide":"bottom",
"toNode":"aaecfc8d2cd8987a",
"toSide":"top"
},
{
"id":"88420432ef7d7cba",
"styleAttributes":{},
"toFloating":false,
"fromFloating":false,
"fromNode":"a7cd89568ff94edd",
"fromSide":"right",
"toNode":"2f7f8d771e76ac3d",
"toSide":"left",
"label":"token"
},
{
"id":"d47b295180f5154e",
"styleAttributes":{},
"toFloating":false,
"fromNode":"a5311e2ceb1af6ff",
"fromSide":"bottom",
"toNode":"5eef7d3ff512845b",
"toSide":"top"
},
{
"id":"11bfa021e47bee6a",
"styleAttributes":{"pathfindingMethod":"square"},
"toFloating":false,
"fromNode":"1d968add45109fc4",
"fromSide":"bottom",
"toNode":"778d251815154128",
"toSide":"left"
},
{
"id":"9ec97e188133650d",
"styleAttributes":{"pathfindingMethod":"square"},
"toFloating":false,
"fromFloating":false,
"fromNode":"778d251815154128",
"fromSide":"top",
"toNode":"1d968add45109fc4",
"toSide":"right"
},
{
"id":"8497a83162464f1f",
"styleAttributes":{"pathfindingMethod":"square"},
"toFloating":false,
"fromFloating":false,
"fromNode":"9bc530c3107092b1",
"fromSide":"bottom",
"toNode":"1d968add45109fc4",
"toSide":"left"
},
{
"id":"c9fbcb78c7de38b9",
"styleAttributes":{"pathfindingMethod":"square"},
"toFloating":false,
"fromNode":"9bc530c3107092b1",
"fromSide":"bottom",
"toNode":"e3b18695fa20f63e",
"toSide":"left"
},
{
"id":"4d8a4d511cdaee2b",
"styleAttributes":{},
"toFloating":false,
"fromNode":"5989527a0beba624",
"fromSide":"right",
"toNode":"9bc530c3107092b1",
"toSide":"left",
"fromEnd":"arrow",
"label":"Websocket长连接"
},
{
"id":"eec3fe18d6abba52",
"styleAttributes":{},
"toFloating":false,
"fromNode":"5989527a0beba624",
"fromSide":"bottom",
"toNode":"e59f51d6cc055628",
"toSide":"top"
},
{
"id":"5510d08807d7b4e5",
"styleAttributes":{"pathfindingMethod":"square"},
"toFloating":false,
"fromNode":"e59f51d6cc055628",
"fromSide":"bottom",
"toNode":"0160569168edd2f4",
"toSide":"top"
},
{
"id":"d18ce4da305bb2e7",
"styleAttributes":{"pathfindingMethod":"square"},
"toFloating":false,
"fromFloating":false,
"fromNode":"e59f51d6cc055628",
"fromSide":"bottom",
"toNode":"1d2ec3b7b7a40c8f",
"toSide":"top"
}
],
"metadata":{
"frontmatter":{},
"version":"1.0-1.0"
},
"nodes":[
{"id":"a1a0f1f7f1faf671","type":"group","x":1280,"y":100,"width":670,"height":640,"label":"MsgGateway服务"},
{"id":"e6cf0f9ce7031bb7","type":"group","x":-500,"y":-400,"width":760,"height":380,"label":"服务端"},
{"id":"32f5104e2e1236c4","type":"group","x":-500,"y":-820,"width":760,"height":320,"label":"SDK"},
{"id":"6f1d4aa0e394c3fc","type":"group","x":-480,"y":-220,"width":720,"height":180,"label":"下层服务"},
{"id":"a5311e2ceb1af6ff","type":"group","x":400,"y":-340,"width":560,"height":100,"label":"Auth服务"},
{"id":"96073594a2eb838c","type":"group","x":400,"y":-680,"width":560,"height":100,"label":"客户端SDK"},
{"id":"aaecfc8d2cd8987a","type":"group","x":400,"y":-500,"width":280,"height":100,"label":"APIService"},
{"id":"28adebc518af8f41","type":"group","x":700,"y":-500,"width":260,"height":100,"label":"MsgGateway"},
{
"id":"a088e85e91293434",
"styleAttributes":{},
"text":"Android",
"type":"text",
"x":-480,
"y":-800,
"width":140,
"height":60
},
{
"id":"2f7f8d771e76ac3d",
"type":"text",
"text":"WebSocket客户端",
"styleAttributes":{"textAlign":"center"},
"x":740,
"y":-660,
"width":180,
"height":60
},
{
"id":"7e034ab4587534f8",
"styleAttributes":{},
"text":"iOS",
"type":"text",
"x":-328,
"y":-800,
"width":114,
"height":60
},
{
"id":"f9483b9c68b4ca95",
"styleAttributes":{},
"text":"Browser",
"type":"text",
"x":-26,
"y":-800,
"width":126,
"height":60
},
{
"id":"bb32b85e68a3c5a6",
"styleAttributes":{},
"text":"MiniApp",
"type":"text",
"x":-184,
"y":-800,
"width":128,
"height":60
},
{
"id":"d541ad6e1481d9b3",
"styleAttributes":{},
"text":"Other",
"type":"text",
"x":126,
"y":-800,
"width":114,
"height":60
},
{
"id":"a7cd89568ff94edd",
"type":"text",
"text":"Http连接",
"styleAttributes":{"textAlign":"center"},
"x":440,
"y":-660,
"width":200,
"height":60
},
{
"id":"7e54ebcf5ca41a90",
"styleAttributes":{"textAlign":"center"},
"text":"长连接(WebSocket",
"type":"text",
"x":-230,
"y":-580,
"width":220,
"height":60
},
{
"id":"24c1312b4fc764d7",
"styleAttributes":{"textAlign":"center"},
"text":"Core SDK With Golang",
"type":"text",
"x":-480,
"y":-680,
"width":720,
"height":60
},
{
"id":"643fefa30582c725",
"styleAttributes":{"textAlign":"center"},
"text":"短连接(Http",
"type":"text",
"x":-480,
"y":-580,
"width":220,
"height":60
},
{
"id":"275361f8bd185882",
"styleAttributes":{"textAlign":"center"},
"text":"用户信息",
"type":"text",
"x":-440,
"y":-200,
"width":140,
"height":60
},
{
"id":"7d2f4e279def45da",
"styleAttributes":{"textAlign":"center"},
"text":"API服务\n微服务远程调用",
"type":"text",
"x":-480,
"y":-360,
"width":220,
"height":60
},
{
"id":"81daaa946bed5ffc",
"styleAttributes":{"textAlign":"center"},
"text":"朋友/群组",
"type":"text",
"x":-440,
"y":-120,
"width":140,
"height":60
},
{
"id":"3e6f641292cc4dd2",
"styleAttributes":{"textAlign":"center"},
"text":"会话信息",
"type":"text",
"x":-190,
"y":-120,
"width":140,
"height":60
},
{
"id":"4f62d8a7660f5155",
"styleAttributes":{"textAlign":"center"},
"text":"MsgGateway\nWebSocket长连接服务",
"type":"text",
"x":-230,
"y":-360,
"width":220,
"height":60
},
{
"id":"6286d89761a9c395",
"styleAttributes":{"textAlign":"center"},
"text":"其他服务",
"type":"text",
"x":20,
"y":-360,
"width":220,
"height":60
},
{
"id":"8fe0b6419e0b01b1",
"type":"text",
"text":"API服务",
"styleAttributes":{"textAlign":"center"},
"x":440,
"y":-480,
"width":200,
"height":60
},
{
"id":"566be66ace698d58",
"type":"text",
"text":"发起连接",
"styleAttributes":{"textAlign":"center"},
"x":740,
"y":-480,
"width":180,
"height":60
},
{
"id":"afadf110737841fe",
"styleAttributes":{"textAlign":"center"},
"text":"用户认证",
"type":"text",
"x":-190,
"y":-200,
"width":140,
"height":60
},
{
"id":"e6a84d96ead95498",
"styleAttributes":{"textAlign":"center"},
"text":"信息",
"type":"text",
"x":60,
"y":-200,
"width":140,
"height":60
},
{
"id":"782074c3ea044381",
"styleAttributes":{"textAlign":"center"},
"text":"三方",
"type":"text",
"x":60,
"y":-120,
"width":140,
"height":60
},
{
"id":"78ea03c7259f97a0",
"type":"text",
"text":"ParseToken\n解析Token",
"styleAttributes":{"textAlign":"center"},
"x":740,
"y":-320,
"width":180,
"height":60
},
{
"id":"e637376395b8424b",
"type":"text",
"text":"创建一个Token",
"styleAttributes":{"textAlign":"center"},
"x":440,
"y":-320,
"width":200,
"height":60
},
{
"id":"5eef7d3ff512845b",
"type":"text",
"text":"AuthDatabase\n基于Redis的缓存服务",
"styleAttributes":{"textAlign":"center"},
"x":440,
"y":-200,
"width":480,
"height":60
},
{
"id":"5989527a0beba624",
"type":"text",
"text":"客户端SDK",
"styleAttributes":{},
"x":300,
"y":120,
"width":260,
"height":60
},
{
"id":"e59f51d6cc055628",
"type":"text",
"text":"客户端类型",
"styleAttributes":{},
"x":361,
"y":210,
"width":139,
"height":60
},
{
"id":"0160569168edd2f4",
"type":"text",
"text":"GoBaseSDK-GOB",
"styleAttributes":{"textAlign":"center"},
"x":261,
"y":300,
"width":169,
"height":60
},
{
"id":"1d2ec3b7b7a40c8f",
"type":"text",
"text":"Others-JSON",
"styleAttributes":{"textAlign":"center"},
"x":431,
"y":300,
"width":169,
"height":60
},
{
"id":"9bc530c3107092b1",
"type":"text",
"text":"WebSocket Client",
"styleAttributes":{},
"x":1310,
"y":120,
"width":260,
"height":60
},
{
"id":"e3b18695fa20f63e",
"type":"text",
"text":"ticker drived heartbeat",
"styleAttributes":{},
"x":1500,
"y":240,
"width":240,
"height":60
},
{
"id":"1d968add45109fc4",
"type":"text",
"text":"readMessage",
"styleAttributes":{},
"x":1500,
"y":400,
"width":200,
"height":60
},
{
"id":"778d251815154128",
"type":"text",
"text":"处理消息",
"styleAttributes":{"textAlign":"center"},
"x":1720,
"y":520,
"width":200,
"height":60
}
]
}
@@ -0,0 +1,54 @@
---
opp_publish_id: "944175079304876032"
opp_publish_status: published
opp_publish_title: OpenIM源码分析之一---项目部署与结构
---
# 开始
部署一套体验环境,参考[官方指引](https://docs.openim.io/zh-Hans/guides/gettingStarted/dockerCompose),Docker方式当然最便捷的。不过我们的目的当然不仅是,体验一下发个消息,对于项目结构本身依然还是0认知,通过源码部署,依照[官方文档-源码部署](https://docs.openim.io/zh-Hans/guides/gettingStarted/imSourceCodeDeployment),完成部署也不是什么难事。那么让我们从这里开始,一起走进OpenIM项目。
# 部署结构
## 依赖服务安装
OpenIM已经准备好的依赖组件的Docker服务配置,位于项目[open-im-server](https://github.com/openimsdk/open-im-server)下。
`$ git clone https://github.com/openimsdk/open-im-server && cd open-im-server
| 组件类别 | OpenIM依赖以下组件 | 运维组件 | 测试组件 |
| ---- | ------------------------------------------ | ------------------------------------- | ------------------------ |
| 组件列表 | mongodb<br>redis<br>kafka<br>MinIO<br>etcd | prometheus<br>alertmanager<br>grafana | admin-front<br>web-front |
| 可选 | 必选 | 可选 | 可选 |
| 启动命令 | `$ docker compose up -d` | `$ docker compose --profile m up -d` | 跟随依赖组件启动 |
### 文档补充说明:
* [Minior的访问配置](https://docs.openim.io/zh-Hans/guides/gettingStarted/imSourceCodeDeployment#23-%E8%AE%BE%E7%BD%AE%E5%A4%96%E7%BD%91-ip-%E6%88%96%E5%9F%9F%E5%90%8D%E5%8F%82%E8%80%83-nginx-%E9%85%8D%E7%BD%AE)Minio主要用于存储用户对话过程中的文件,而这些请求是直接进minio的,因此需要配置域名,或用户可以访问的IP地址。若是内网,给Minio一个内网IP即可。端口号默认Docker启动Minio使用的端口,映射到了主体的10005,具体查看minio.yml配置确认。
* [Go的代理](https://docs.openim.io/zh-hans/guides/gettingstarted/imsourcecodedeployment#24-%EF%B8%8F-%E5%88%9D%E5%A7%8B%E5%8C%96)golang语言没限制,不过golang依赖库的机制对github有非常强的依赖,大部分的依赖库都在github上,源码运行,需要配置go依赖的代理。
## 核心服务
在上面的准备基础完成之后,使用openim的指定的编译工具,mage start就可以直接启动核心服务了,这里看原文,不赘述。
如果自己的系统需要IM,那么到此为止,便可以基于OpenIM,来进行二次开发了。
但如果你想直接上手试一下消息,就需要下面的AppServer演示服务接口。
## AppServer演示接口服务
核心服务中,有USER的逻辑,但是却没有账号体系,因此上面的web-front和admin-front项目,可以打开,但却没办法登录,这需要另外的实现账号机制的后端的API服务。这个服务即AppServer,需要另外部署。
服务获取:`$ git clone https://github.com/openimsdk/chat&& cd chat`
如果您是在本地部署,那么admin-front的默认端口应该是11002,浏览器打开http://localhost:11002即可。
系统初始的管理员账号与密码是:chatAdmin/chatAdmin。
# 核心服务结构
* 主服务:
* openim-api:对客户端曝露的核心服务,http协议,信息查询,与消息发送请求。
* openim-msggateway:对客户端曝露的核心服务,ws协议,处理客户端实时消息发送与监听。
* openim-cmdutil
* openim-crontask
* openim-msgtransfer
* openim-push
* 底层服务
* openim-rpc-auth
* openim-rpc-conversation
* openim-rpc-friend
* openim-rpc-group
* openim-rpc-msg
* openim-rpc-third
* openim-rpc-user
主服务与底层服务之间的关系
![|center|800](https://picbed-1253586264.cos.ap-guangzhou.myqcloud.com/uPic/2025062316-5Yrpqe.jpg)
# SDK结构
OpenIM的SDK支持多平台,核心使用golang编写,封装了对API服务请求,与长链接的处理逻辑。而各端的封装主要完成了客户端参数配置,与桥接Golang的部分。
![800](https://picbed-1253586264.cos.ap-guangzhou.myqcloud.com/uPic/2025062316-QpVmoW.jpg)
@@ -0,0 +1,42 @@
##### OpenIM五种系统消息类型
* TextMessagePing/Pone Message
* BinaryMessageChat Message)
* CloseMessage(客户端关闭连接)
* PingMessage
* PongMessage
##### BinaryMessageReq/Resp
```golang
type Req struct {
ReqIdentifier int32 `json:"reqIdentifier" validate:"required"`
Token string `json:"token"`
SendID string `json:"sendID" validate:"required"`
OperationID string `json:"operationID" validate:"required"`
MsgIncr string `json:"msgIncr" validate:"required"`
Data []byte `json:"data"`
}
type Resp struct {
ReqIdentifier int32 `json:"reqIdentifier"`
MsgIncr string `json:"msgIncr"`
OperationID string `json:"operationID"`
ErrCode int `json:"errCode"`
ErrMsg string `json:"errMsg"`
Data []byte `json:"data"`
}
```
##### ReqIdentifier
```golang
WSGetNewestSeq = 1001
WSPullMsgBySeqList = 1002
WSSendMsg = 1003 // 一般聊天消息
WSSendSignalMsg = 1004
WSPullMsg = 1005
WSGetConvMaxReadSeq = 1006
WsPullConvLastMessage = 1007
WSPushMsg = 2001
WSKickOnlineMsg = 2002
WsLogoutMsg = 2003
WsSetBackgroundStatus = 2004
WsSubUserOnlineStatus = 2005
WSDataError = 3001
```
@@ -0,0 +1,68 @@
# 概括:
客户端交互的接口都在API服务中以http接口方式实现,而消息收发都在msggateway中以websocket协议实现。以下API定义在OpenIMAPI服务中,OpenIMAPI本身只起到接口转接的作用,具体的逻辑定义在下面的RPC服务,user&auth中。
# 接口
* 用户接口
```golang
u := NewUserApi(user.NewUserClient(userConn), client, cfg.Discovery.RpcService)
{
userRouterGroup := r.Group("/user")
userRouterGroup.POST("/user_register", u.UserRegister)
userRouterGroup.POST("/update_user_info", u.UpdateUserInfo)
userRouterGroup.POST("/update_user_info_ex", u.UpdateUserInfoEx)
userRouterGroup.POST("/set_global_msg_recv_opt", u.SetGlobalRecvMessageOpt)
userRouterGroup.POST("/get_users_info", u.GetUsersPublicInfo)
userRouterGroup.POST("/get_all_users_uid", u.GetAllUsersID)
userRouterGroup.POST("/account_check", u.AccountCheck)
userRouterGroup.POST("/get_users", u.GetUsers)
userRouterGroup.POST("/get_users_online_status", u.GetUsersOnlineStatus)
userRouterGroup.POST("/get_users_online_token_detail", u.GetUsersOnlineTokenDetail)
userRouterGroup.POST("/subscribe_users_status", u.SubscriberStatus)
userRouterGroup.POST("/get_users_status", u.GetUserStatus)
userRouterGroup.POST("/get_subscribe_users_status", u.GetSubscribeUsersStatus)
userRouterGroup.POST("/process_user_command_add", u.ProcessUserCommandAdd)
userRouterGroup.POST("/process_user_command_delete", u.ProcessUserCommandDelete)
userRouterGroup.POST("/process_user_command_update", u.ProcessUserCommandUpdate)
userRouterGroup.POST("/process_user_command_get", u.ProcessUserCommandGet)
userRouterGroup.POST("/process_user_command_get_all", u.ProcessUserCommandGetAll)
userRouterGroup.POST("/add_notification_account", u.AddNotificationAccount)
userRouterGroup.POST("/update_notification_account", u.UpdateNotificationAccountInfo)
userRouterGroup.POST("/search_notification_account", u.SearchNotificationAccount)
}
```
* 认证相关
```golang
{
a := NewAuthApi(pbAuth.NewAuthClient(authConn))
authRouterGroup := r.Group("/auth")
authRouterGroup.POST("/get_admin_token", a.GetAdminToken)
authRouterGroup.POST("/get_user_token", a.GetUserToken)
authRouterGroup.POST("/parse_token", a.ParseToken)
authRouterGroup.POST("/force_logout", a.ForceLogout)
}
```
# 认证逻辑
![|600](https://picbed-1253586264.cos.ap-guangzhou.myqcloud.com/uPic/2025062409-cKhzvC.jpg)
## Token说明
TokenKey格式:`前缀:用户ID:登录平台` = `UID_PID_TOKEN_STATUS:4834513665:Web`
TokenValue说明:{Token:TokenFlag}
TokenFlag取值如下,也就是说,TokenValue将以JsonMap的形式保存,最里可以看到最多4种情况
* NormalToken = 0
* InValidToken = 1
* KickedToken = 2
* ExpiredToken = 3
## AuthDatabase
Auth作为一个底层RPC服务,在启动是,会实例化AuthDatabase,Auth服务实现的4四认证相关接口,便是依赖authDatabase实现认证数据的管理。
authDatabase实现的四个方法如下,即操作上面Token的方法:
```
GetTokensWithoutError(ctx context.Context, userID string, platformID int) (map[string]int, error)
CreateToken(ctx context.Context, userID string, platformID int) (string, error)
BatchSetTokenMapByUidPid(ctx context.Context, tokens []string) error
SetTokenMapByUidPid(ctx context.Context, userID string, platformID int, m map[string]int) error
```
# 三方系统整合逻辑
即替换AuthDatabase的Token逻辑,比如改为依赖三方系统提供的Token获取与校验接口。当前出租屋的逻辑倒是权益之计,需要前端系统主动设置token.可以解决问题,但逻辑有漏洞。