Browse Source

README, im-entry-http monitor

master
tangmingyou 4 years ago
parent
commit
b3beaa7697
  1. 21
      LICENSE
  2. 203
      README.md
  3. BIN
      doc/image/README/image-20220612130234826.png
  4. BIN
      doc/image/README/image-20220612150740804.png
  5. BIN
      doc/image/README/image-20220612152931599.png
  6. BIN
      doc/image/README/image-20220612153031702.png
  7. BIN
      doc/image/README/soim.gif
  8. BIN
      doc/image/README/soim.jpg
  9. BIN
      doc/image/README/soim.mp4
  10. 782
      doc/soim_db.sql
  11. 1
      im-client/src/main/java/net/sopod/soim/client/cmd/CmdEnum.java
  12. 2
      im-client/src/main/java/net/sopod/soim/client/cmd/args/ArgsSend.java
  13. 6
      im-client/src/main/java/net/sopod/soim/client/config/ClientConfig.java
  14. 11
      im-client/src/main/java/net/sopod/soim/client/handler/cmd/LoginHandler.java
  15. 5
      im-client/src/main/java/net/sopod/soim/client/handler/cmd/SendHandler.java
  16. 7
      im-client/src/main/java/net/sopod/soim/client/protocol/ImMessageInboundHandler.java
  17. 1
      im-client/src/main/java/net/sopod/soim/client/session/SoImSession.java
  18. 25
      im-client/src/main/java/net/sopod/soim/client/util/HttpClient.java
  19. 2
      im-common/src/main/java/net/sopod/soim/common/constant/AppConstant.java
  20. 5
      im-das/im-das-group/pom.xml
  21. 4
      im-das/im-das-group/src/main/java/net/sopod/soim/das/group/DasGroupApplication.java
  22. 10
      im-das/im-das-group/src/main/resources/application.properties
  23. 3
      im-das/im-das-group/src/main/resources/application.yml
  24. 5
      im-das/im-das-message/pom.xml
  25. 4
      im-das/im-das-message/src/main/java/net/sopod/soim/das/message/DasMessageApplication.java
  26. 8
      im-das/im-das-message/src/main/resources/application.properties
  27. 8
      im-das/im-das-message/src/main/resources/application.yml
  28. 5
      im-das/im-das-user/pom.xml
  29. 7
      im-das/im-das-user/src/main/java/net/sopod/soim/das/user/DasUserApplication.java
  30. 40
      im-das/im-das-user/src/main/java/net/sopod/soim/das/user/cache/FriendRelationCache.java
  31. 33
      im-das/im-das-user/src/main/java/net/sopod/soim/das/user/service/FriendDasImpl.java
  32. 8
      im-das/im-das-user/src/main/resources/application.properties
  33. 3
      im-das/im-das-user/src/main/resources/application.yml
  34. 14
      im-entry-http/pom.xml
  35. 3
      im-entry-http/src/main/java/net/sopod/soim/entry/http/EntryHttpApplication.java
  36. 176
      im-entry-http/src/main/java/net/sopod/soim/entry/http/client/EntryClient.java
  37. 48
      im-entry-http/src/main/java/net/sopod/soim/entry/http/client/ImMessageInboundHandler.java
  38. 65
      im-entry-http/src/main/java/net/sopod/soim/entry/http/config/ApplicationOnReady.java
  39. 175
      im-entry-http/src/main/java/net/sopod/soim/entry/http/config/ImEntryMonitor.java
  40. 7
      im-entry-http/src/main/java/net/sopod/soim/entry/http/controller/AuthController.java
  41. 37
      im-entry-http/src/main/java/net/sopod/soim/entry/http/controller/MonitorController.java
  42. 21
      im-entry-http/src/main/java/net/sopod/soim/entry/http/service/ImEntryMonitorService.java
  43. 58
      im-entry-http/src/main/java/net/sopod/soim/entry/http/service/ImMessageReqHolder.java
  44. 3
      im-entry-http/src/main/resources/application.yml
  45. 5
      im-entry/Dockerfile
  46. 5
      im-entry/pom.xml
  47. 3
      im-entry/src/main/java/net/sopod/soim/entry/EntryApplication.java
  48. 4
      im-entry/src/main/java/net/sopod/soim/entry/config/EntryServerConfig.java
  49. 2
      im-entry/src/main/java/net/sopod/soim/entry/config/ImEntryAppOnReady.java
  50. 2
      im-entry/src/main/java/net/sopod/soim/entry/handlers/chat/ReqGroupMessageHandler.java
  51. 5
      im-entry/src/main/java/net/sopod/soim/entry/handlers/chat/ReqTextChatHandler.java
  52. 33
      im-entry/src/main/java/net/sopod/soim/entry/handlers/monitor/ReqEntryStatusHandler.java
  53. 38
      im-entry/src/main/java/net/sopod/soim/entry/handlers/monitor/ReqMonitorAuthHandler.java
  54. 2
      im-entry/src/main/java/net/sopod/soim/entry/handlers/task/NetUserDelayCloseHandler.java
  55. 11
      im-entry/src/main/java/net/sopod/soim/entry/server/AccountRegistry.java
  56. 40
      im-entry/src/main/java/net/sopod/soim/entry/server/EntryServerRunner.java
  57. 4
      im-entry/src/main/java/net/sopod/soim/entry/server/ImEntryInitializer.java
  58. 13
      im-entry/src/main/java/net/sopod/soim/entry/server/InboundImMessageHandler.java
  59. 2
      im-entry/src/main/java/net/sopod/soim/entry/server/handler/AccountMessageHandler.java
  60. 30
      im-entry/src/main/java/net/sopod/soim/entry/server/handler/MonitorMessageHandler.java
  61. 32
      im-entry/src/main/java/net/sopod/soim/entry/server/session/Monitor.java
  62. 14
      im-entry/src/main/java/net/sopod/soim/entry/server/session/NetUser.java
  63. 2
      im-entry/src/main/java/net/sopod/soim/entry/worker/WorkerGroup.java
  64. 11
      im-entry/src/main/resources/application.yml
  65. 27
      im-launcher/pom.xml
  66. 15
      im-launcher/src/main/java/net/sopod/soim/launcher/LauncherService.java
  67. 34
      im-launcher/src/main/java/net/sopod/soim/launcher/SoimApplication.java
  68. 52
      im-launcher/src/main/java/net/sopod/soim/launcher/service/PropertiesLauncherService.java
  69. 21
      im-launcher/src/main/java/net/sopod/soim/launcher/util/PropsUtil.java
  70. 1
      im-launcher/src/main/resources/META-INF/services/net.sopod.soim.launcher.LauncherService
  71. 55
      im-launcher/src/main/resources/launcher.properties
  72. 103
      im-service-api/im-entry-protocol/src/main/java/net/sopod/soim/data/msg/group/Group.java
  73. 2195
      im-service-api/im-entry-protocol/src/main/java/net/sopod/soim/data/msg/monitor/EntryMonitor.java
  74. 29
      im-service-api/im-entry-protocol/src/main/resources/proto/monitor/EntryMonitor.proto
  75. 60
      im-service-api/im-entry-protocol/src/main/resources/protoSerialNoTable.txt
  76. 5
      im-service/im-logic-group/pom.xml
  77. 4
      im-service/im-logic-group/src/main/java/net/sopod/soim/logic/group/LogicGroupApplication.java
  78. 3
      im-service/im-logic-group/src/main/resources/application.yml
  79. 5
      im-service/im-logic-message/pom.xml
  80. 4
      im-service/im-logic-message/src/main/java/net/sopod/soim/logic/message/LogicMessageApplication.java
  81. 18
      im-service/im-logic-message/src/main/java/net/sopod/soim/logic/message/service/ImUserChatServiceImpl.java
  82. 8
      im-service/im-logic-message/src/main/resources/application.yml
  83. 5
      im-service/im-logic-user/pom.xml
  84. 4
      im-service/im-logic-user/src/main/java/net/sopod/soim/logic/user/LogicUserApplication.java
  85. 3
      im-service/im-logic-user/src/main/resources/application.yml
  86. 21
      im-service/im-router/pom.xml
  87. 4
      im-service/im-router/src/main/java/net/sopod/soim/router/RouterApplication.java
  88. 11
      im-service/im-router/src/main/java/net/sopod/soim/router/datasync/server/SyncServer.java
  89. 20
      im-service/im-router/src/main/resources/application.yml
  90. 5
      im-service/im-segment-id/pom.xml
  91. 4
      im-service/im-segment-id/src/main/java/net/sopod/soim/logic/segmentid/SegmentIdApplication.java
  92. 21
      im-service/im-segment-id/src/main/resources/application.yml
  93. 60
      pom.xml

21
LICENSE

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 tangmingyou
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

203
README.md

@ -1,121 +1,86 @@
## 基本介绍
`so-im` 是一款简单的可水平扩展的 `IM(即时通讯)`系统。
### 开发计划
- (05-08~05-11)router 一致性 hash,新增、删除、(备份处理)[滞后]
- 数据同步删除后,保留id一段时间,有请求进行重定向/转发
- 新增:重算hash,发起数据同步,接收其他节点推送数据及数据更改日志,注册服务,其他节点删除数据
- 正常删除节点:重算 hash,数据和更改日志推送给其他节点,取消注册,关闭服务
- 宕机:备份服务接收全量同步数据和更改日志,服务探测到主服务不可用,注册到注册中心提供服务
- (05-12~05-12) 备用计划:router 层 id 号段负载均衡
- (05-13~05-14) dubbo 服务异步处理
- 功能开发:
- entry-http entry 节点获取功能(05-13~05-13)
- (05-13~05-13) 用户注册功能
- (05-14~05-14) 好友列表(在线状态:批量uid一致性hash, router查询)
- 用户查询
- 添加好友
- 好友在线状态列表
- (05-15~05-15) 消息群发(im-router群消息路由,批量uid一致性哈希路由)
- 创群:
- 加群:发送申请,推送申请,推送回复
- 删群
- 群列表
- (05-16~05-16) 聊天记录查询
- (05-17~05-18) das 部分接口消息队列异步写
- (05-19~05-20) 集群部署:docker swarm
- 监控:log4j2完善 + prometheus + grafana + loki + arthas
- (05-21~05-21) prometheus 服务发现,exporter(服务发现) 开发
- (05-22~05-22) 容器监控,主机监控,数据库监控,日志收集
- (05-23~05-24) 应用监控
- (Dubbo线程池监控)[https://cloud.tencent.com/developer/article/1800906]
- 通讯指标监控,请求量,吞吐量,延时,请求节点分布,链路追踪
- 数据库连接池监控
- (05-25~05-25) 压测:压测开发
- (05-26~05-26) client: 控制台完善,grallvm 打包
- 后续:
- 通讯加密,服务链路SSL
- websocket 网关
- web 页面开发
- 异/同设备,多地登录
### 资料
一致性hash算法能否解决数据迁移的问题?
https://www.zhihu.com/question/521159623
dubbo + protobuf 兼容到 2.7.15
### TODO
- router -> entry 负载均衡
- router 新增节点顺时针相邻节点数据一致性哈希迁移
- router 冗余节点存储数据不提供服务
- dubbo 服务异步处理提升吞吐量
- das 消息队列异步写
- entry 监控,http 查询 entry 地址返回接口
- 功能开发:
- 消息群发(im-router 群消息路由,批量uid一致性哈希路由)
- 好友列表(在线状态:批量uid一致性hash, router查询)
- 聊天记录查询
- 异/同设备,多地登录
- 集群部署, docker swarm, k8s, jenkens
- 服务监控
- websocket 网关
- 考虑 dubbo 使用 grpc service
模块列表
- 接入层 entry
- 逻辑层 logic
- 内存存储层 router
- 固化存储层 das
功能组件:
1.0
- IOC: guice
- 通信层: netty
- 序列化: protobuf
- 内存存储:
- Caffeine
- Memcache
- 分布式缓存:redis
- 注册中心:etcd、直连
- 处理队列:disruptor
- 持久化:
- mysql 存储全量历史消息
- mongodb 存储直接拉取的未读消息
- 日志:log4j2
- 监控:prometheus、grafana
- ID生成器
2.0
- quarkus
集群负载均衡:
子网1,子网2,公网
子网1和2不相通
dubbo native image
https://dubbo.apache.org/zh/docs/references/graalvm/support-graalvm/
entry <--> client 通信
- serviceId --> paramClass --> serviceHandler
entry <--> logic dubbo service
client jconsle cmd
router <--> cache
das <--> db
router <--> das
das shardingjdbc
table struct, sharding roles
logic biz
请求响应消息队列异步处理,减少线程 cpu 占用, dubbo async
客户端和TCP接入层通过 netty+protobuf 通信。
后端服务通过 dubbo 服务间调用。
## 功能演示
好友、群、单聊、群聊、内置命令
![](doc/image/README/soim.gif)
<video id="video" controls="" preload="none" poster="doc/image/README/soim.jpg">
<source id="mp4" src="doc/image/README/soim.mp4" type="video/mp4">
</videos>
## 消息转发流程
![image-20220612152931599](doc/image/README/image-20220612152931599.png)
## 内置命令
| 命令 | 选项 | 描述 |
| ------- | ------------------------------------------------------------ | ---------------------------------------------------- |
| login | -u 账号<br />-p 密码 | 登录并连接tcp接入层 |
| send | -u 好友id 单聊消息<br />-g 群id 群消息 | 发送消息 |
| users | | 在线用户列表 |
| friends | | 我的好友列表 |
| group | | 查询我的群聊列表 |
| group | create 群聊名称<br />search 群聊名称(右模糊)<br />join 群聊id<br />users | 创建群聊<br />搜索群聊<br />加入群聊<br />群用户列表 |
| search | -u 用户账号(右模糊) | 搜索用户 |
| add | -u 用户id | 添加好友 |
| me | | 我的信息 |
| exit | | 退出客户端 |
## TODO LIST
- [x] 好友、基础单聊
- [x] 群搜索、添加、列表、基础群聊
- [x] 消息通过MQ持久化到MySQL分表库
- [x] 启动路由层新节点根据一致性hash迁移其他节点内存数据
- [x] 接入层负载均衡
- [x] 分布式分段id生成服务
- [ ] 聊天记录查询
- [ ] 优化命令行客户端打印输出
- [ ] 群聊,单聊 消息已读确认
- [ ] 未读离线消息推送
- [ ] 客户端接入层消息加密
- [ ] dubbo service SSL 加密
- [ ] 路由层状态服务冗余节点备份,自动切换
## 启动步骤
### 创建数据库
![image-20220612153031702](doc/image/README/image-20220612153031702.png)
### 中间件配置
配置 nacos、mysql、rabbitmq、redis 服务地址
![image-20220612150740804](doc/image/README/image-20220612150740804.png)
### 启动如下服务
![image-20220612130234826](doc/image/README/image-20220612130234826.png)

BIN
doc/image/README/image-20220612130234826.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
doc/image/README/image-20220612150740804.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
doc/image/README/image-20220612152931599.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
doc/image/README/image-20220612153031702.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
doc/image/README/soim.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

BIN
doc/image/README/soim.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

BIN
doc/image/README/soim.mp4

Binary file not shown.

782
doc/soim_db.sql

@ -0,0 +1,782 @@
/*
Navicat Premium Data Transfer
Source Server : mysql
Source Server Type : MySQL
Source Server Version : 80022
Source Host : 127.0.0.1:3306
Source Schema : soim_db
Target Server Type : MySQL
Target Server Version : 80022
File Encoding : 65001
Date: 12/06/2022 15:11:15
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for im_friend_0
-- ----------------------------
DROP TABLE IF EXISTS `im_friend_0`;
CREATE TABLE `im_friend_0` (
`id` bigint(0) NOT NULL,
`relation_id` bigint(0) NOT NULL COMMENT '好友关系id(消息存储路由分片)',
`uid` bigint(0) NOT NULL COMMENT '用户id',
`fid` bigint(0) NOT NULL COMMENT '好友id',
`status` tinyint(0) NOT NULL COMMENT '状态:0删除,1正常,2临时好友(群单聊)',
`unread_num` int(0) NOT NULL COMMENT '群未读消息数量',
`unread_offset_id` bigint(0) NOT NULL COMMENT '群未读消息id偏移量(大于等于该id的都未读)',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '好友关系表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_friend_1
-- ----------------------------
DROP TABLE IF EXISTS `im_friend_1`;
CREATE TABLE `im_friend_1` (
`id` bigint(0) NOT NULL,
`relation_id` bigint(0) NOT NULL COMMENT '好友关系id(消息存储路由分片)',
`uid` bigint(0) NOT NULL COMMENT '用户id',
`fid` bigint(0) NOT NULL COMMENT '好友id',
`status` tinyint(0) NOT NULL COMMENT '状态:0删除,1正常,2临时好友(群单聊)',
`unread_num` int(0) NOT NULL COMMENT '群未读消息数量',
`unread_offset_id` bigint(0) NOT NULL COMMENT '群未读消息id偏移量(大于等于该id的都未读)',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '好友关系表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_friend_2
-- ----------------------------
DROP TABLE IF EXISTS `im_friend_2`;
CREATE TABLE `im_friend_2` (
`id` bigint(0) NOT NULL,
`relation_id` bigint(0) NOT NULL COMMENT '好友关系id(消息存储路由分片)',
`uid` bigint(0) NOT NULL COMMENT '用户id',
`fid` bigint(0) NOT NULL COMMENT '好友id',
`status` tinyint(0) NOT NULL COMMENT '状态:0删除,1正常,2临时好友(群单聊)',
`unread_num` int(0) NOT NULL COMMENT '群未读消息数量',
`unread_offset_id` bigint(0) NOT NULL COMMENT '群未读消息id偏移量(大于等于该id的都未读)',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '好友关系表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_friend_3
-- ----------------------------
DROP TABLE IF EXISTS `im_friend_3`;
CREATE TABLE `im_friend_3` (
`id` bigint(0) NOT NULL,
`relation_id` bigint(0) NOT NULL COMMENT '好友关系id(消息存储路由分片)',
`uid` bigint(0) NOT NULL COMMENT '用户id',
`fid` bigint(0) NOT NULL COMMENT '好友id',
`status` tinyint(0) NOT NULL COMMENT '状态:0删除,1正常,2临时好友(群单聊)',
`unread_num` int(0) NOT NULL COMMENT '群未读消息数量',
`unread_offset_id` bigint(0) NOT NULL COMMENT '群未读消息id偏移量(大于等于该id的都未读)',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '好友关系表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_friend_4
-- ----------------------------
DROP TABLE IF EXISTS `im_friend_4`;
CREATE TABLE `im_friend_4` (
`id` bigint(0) NOT NULL,
`relation_id` bigint(0) NOT NULL COMMENT '好友关系id(消息存储路由分片)',
`uid` bigint(0) NOT NULL COMMENT '用户id',
`fid` bigint(0) NOT NULL COMMENT '好友id',
`status` tinyint(0) NOT NULL COMMENT '状态:0删除,1正常,2临时好友(群单聊)',
`unread_num` int(0) NOT NULL COMMENT '群未读消息数量',
`unread_offset_id` bigint(0) NOT NULL COMMENT '群未读消息id偏移量(大于等于该id的都未读)',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '好友关系表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_friend_5
-- ----------------------------
DROP TABLE IF EXISTS `im_friend_5`;
CREATE TABLE `im_friend_5` (
`id` bigint(0) NOT NULL,
`relation_id` bigint(0) NOT NULL COMMENT '好友关系id(消息存储路由分片)',
`uid` bigint(0) NOT NULL COMMENT '用户id',
`fid` bigint(0) NOT NULL COMMENT '好友id',
`status` tinyint(0) NOT NULL COMMENT '状态:0删除,1正常,2临时好友(群单聊)',
`unread_num` int(0) NOT NULL COMMENT '群未读消息数量',
`unread_offset_id` bigint(0) NOT NULL COMMENT '群未读消息id偏移量(大于等于该id的都未读)',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '好友关系表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_friend_6
-- ----------------------------
DROP TABLE IF EXISTS `im_friend_6`;
CREATE TABLE `im_friend_6` (
`id` bigint(0) NOT NULL,
`relation_id` bigint(0) NOT NULL COMMENT '好友关系id(消息存储路由分片)',
`uid` bigint(0) NOT NULL COMMENT '用户id',
`fid` bigint(0) NOT NULL COMMENT '好友id',
`status` tinyint(0) NOT NULL COMMENT '状态:0删除,1正常,2临时好友(群单聊)',
`unread_num` int(0) NOT NULL COMMENT '群未读消息数量',
`unread_offset_id` bigint(0) NOT NULL COMMENT '群未读消息id偏移量(大于等于该id的都未读)',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '好友关系表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_friend_7
-- ----------------------------
DROP TABLE IF EXISTS `im_friend_7`;
CREATE TABLE `im_friend_7` (
`id` bigint(0) NOT NULL,
`relation_id` bigint(0) NOT NULL COMMENT '好友关系id(消息存储路由分片)',
`uid` bigint(0) NOT NULL COMMENT '用户id',
`fid` bigint(0) NOT NULL COMMENT '好友id',
`status` tinyint(0) NOT NULL COMMENT '状态:0删除,1正常,2临时好友(群单聊)',
`unread_num` int(0) NOT NULL COMMENT '群未读消息数量',
`unread_offset_id` bigint(0) NOT NULL COMMENT '群未读消息id偏移量(大于等于该id的都未读)',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '好友关系表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_0
-- ----------------------------
DROP TABLE IF EXISTS `im_group_0`;
CREATE TABLE `im_group_0` (
`id` bigint(0) NOT NULL COMMENT '群聊id',
`master_uid` bigint(0) NOT NULL COMMENT '群主id',
`status` int(0) NOT NULL COMMENT '状态:0删除,1.正常,3禁用,4.解散',
`group_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '群聊名称',
`user_limit` int(0) NOT NULL COMMENT '群聊人数限制',
`user_num` int(0) NOT NULL COMMENT '当前群聊人数',
`create_time` datetime(0) NOT NULL COMMENT '创建时间',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_1
-- ----------------------------
DROP TABLE IF EXISTS `im_group_1`;
CREATE TABLE `im_group_1` (
`id` bigint(0) NOT NULL COMMENT '群聊id',
`master_uid` bigint(0) NOT NULL COMMENT '群主id',
`status` int(0) NOT NULL COMMENT '状态:0删除,1.正常,3禁用,4.解散',
`group_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '群聊名称',
`user_limit` int(0) NOT NULL COMMENT '群聊人数限制',
`user_num` int(0) NOT NULL COMMENT '当前群聊人数',
`create_time` datetime(0) NOT NULL COMMENT '创建时间',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_2
-- ----------------------------
DROP TABLE IF EXISTS `im_group_2`;
CREATE TABLE `im_group_2` (
`id` bigint(0) NOT NULL COMMENT '群聊id',
`master_uid` bigint(0) NOT NULL COMMENT '群主id',
`status` int(0) NOT NULL COMMENT '状态:0删除,1.正常,3禁用,4.解散',
`group_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '群聊名称',
`user_limit` int(0) NOT NULL COMMENT '群聊人数限制',
`user_num` int(0) NOT NULL COMMENT '当前群聊人数',
`create_time` datetime(0) NOT NULL COMMENT '创建时间',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_3
-- ----------------------------
DROP TABLE IF EXISTS `im_group_3`;
CREATE TABLE `im_group_3` (
`id` bigint(0) NOT NULL COMMENT '群聊id',
`master_uid` bigint(0) NOT NULL COMMENT '群主id',
`status` int(0) NOT NULL COMMENT '状态:0删除,1.正常,3禁用,4.解散',
`group_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '群聊名称',
`user_limit` int(0) NOT NULL COMMENT '群聊人数限制',
`user_num` int(0) NOT NULL COMMENT '当前群聊人数',
`create_time` datetime(0) NOT NULL COMMENT '创建时间',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_message_0
-- ----------------------------
DROP TABLE IF EXISTS `im_group_message_0`;
CREATE TABLE `im_group_message_0` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`sender` bigint(0) NOT NULL COMMENT '发送者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_message_1
-- ----------------------------
DROP TABLE IF EXISTS `im_group_message_1`;
CREATE TABLE `im_group_message_1` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`sender` bigint(0) NOT NULL COMMENT '发送者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_message_2
-- ----------------------------
DROP TABLE IF EXISTS `im_group_message_2`;
CREATE TABLE `im_group_message_2` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`sender` bigint(0) NOT NULL COMMENT '发送者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_message_3
-- ----------------------------
DROP TABLE IF EXISTS `im_group_message_3`;
CREATE TABLE `im_group_message_3` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`sender` bigint(0) NOT NULL COMMENT '发送者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_message_4
-- ----------------------------
DROP TABLE IF EXISTS `im_group_message_4`;
CREATE TABLE `im_group_message_4` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`sender` bigint(0) NOT NULL COMMENT '发送者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_message_5
-- ----------------------------
DROP TABLE IF EXISTS `im_group_message_5`;
CREATE TABLE `im_group_message_5` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`sender` bigint(0) NOT NULL COMMENT '发送者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_message_6
-- ----------------------------
DROP TABLE IF EXISTS `im_group_message_6`;
CREATE TABLE `im_group_message_6` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`sender` bigint(0) NOT NULL COMMENT '发送者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_message_7
-- ----------------------------
DROP TABLE IF EXISTS `im_group_message_7`;
CREATE TABLE `im_group_message_7` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`sender` bigint(0) NOT NULL COMMENT '发送者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_message_8
-- ----------------------------
DROP TABLE IF EXISTS `im_group_message_8`;
CREATE TABLE `im_group_message_8` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`sender` bigint(0) NOT NULL COMMENT '发送者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_message_9
-- ----------------------------
DROP TABLE IF EXISTS `im_group_message_9`;
CREATE TABLE `im_group_message_9` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`sender` bigint(0) NOT NULL COMMENT '发送者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_message_10
-- ----------------------------
DROP TABLE IF EXISTS `im_group_message_10`;
CREATE TABLE `im_group_message_10` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`sender` bigint(0) NOT NULL COMMENT '发送者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_message_11
-- ----------------------------
DROP TABLE IF EXISTS `im_group_message_11`;
CREATE TABLE `im_group_message_11` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`sender` bigint(0) NOT NULL COMMENT '发送者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_message_12
-- ----------------------------
DROP TABLE IF EXISTS `im_group_message_12`;
CREATE TABLE `im_group_message_12` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`sender` bigint(0) NOT NULL COMMENT '发送者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_message_13
-- ----------------------------
DROP TABLE IF EXISTS `im_group_message_13`;
CREATE TABLE `im_group_message_13` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`sender` bigint(0) NOT NULL COMMENT '发送者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_message_14
-- ----------------------------
DROP TABLE IF EXISTS `im_group_message_14`;
CREATE TABLE `im_group_message_14` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`sender` bigint(0) NOT NULL COMMENT '发送者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_message_15
-- ----------------------------
DROP TABLE IF EXISTS `im_group_message_15`;
CREATE TABLE `im_group_message_15` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`sender` bigint(0) NOT NULL COMMENT '发送者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_user_0
-- ----------------------------
DROP TABLE IF EXISTS `im_group_user_0`;
CREATE TABLE `im_group_user_0` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`uid` bigint(0) NOT NULL COMMENT '用户id',
`unread_num` int(0) NOT NULL COMMENT '群未读消息数量',
`unread_offset_id` bigint(0) NULL DEFAULT NULL COMMENT '群未读消息id偏移量(大于等于该id的都未读)',
`last_active` datetime(0) NULL DEFAULT NULL COMMENT '最后活跃时间',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊用户列表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_user_1
-- ----------------------------
DROP TABLE IF EXISTS `im_group_user_1`;
CREATE TABLE `im_group_user_1` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`uid` bigint(0) NOT NULL COMMENT '用户id',
`unread_num` int(0) NOT NULL COMMENT '群未读消息数量',
`unread_offset_id` bigint(0) NULL DEFAULT NULL COMMENT '群未读消息id偏移量(大于等于该id的都未读)',
`last_active` datetime(0) NULL DEFAULT NULL COMMENT '最后活跃时间',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊用户列表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_user_2
-- ----------------------------
DROP TABLE IF EXISTS `im_group_user_2`;
CREATE TABLE `im_group_user_2` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`uid` bigint(0) NOT NULL COMMENT '用户id',
`unread_num` int(0) NOT NULL COMMENT '群未读消息数量',
`unread_offset_id` bigint(0) NULL DEFAULT NULL COMMENT '群未读消息id偏移量(大于等于该id的都未读)',
`last_active` datetime(0) NULL DEFAULT NULL COMMENT '最后活跃时间',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊用户列表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_user_3
-- ----------------------------
DROP TABLE IF EXISTS `im_group_user_3`;
CREATE TABLE `im_group_user_3` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`uid` bigint(0) NOT NULL COMMENT '用户id',
`unread_num` int(0) NOT NULL COMMENT '群未读消息数量',
`unread_offset_id` bigint(0) NULL DEFAULT NULL COMMENT '群未读消息id偏移量(大于等于该id的都未读)',
`last_active` datetime(0) NULL DEFAULT NULL COMMENT '最后活跃时间',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊用户列表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_user_4
-- ----------------------------
DROP TABLE IF EXISTS `im_group_user_4`;
CREATE TABLE `im_group_user_4` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`uid` bigint(0) NOT NULL COMMENT '用户id',
`unread_num` int(0) NOT NULL COMMENT '群未读消息数量',
`unread_offset_id` bigint(0) NULL DEFAULT NULL COMMENT '群未读消息id偏移量(大于等于该id的都未读)',
`last_active` datetime(0) NULL DEFAULT NULL COMMENT '最后活跃时间',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊用户列表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_user_5
-- ----------------------------
DROP TABLE IF EXISTS `im_group_user_5`;
CREATE TABLE `im_group_user_5` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`uid` bigint(0) NOT NULL COMMENT '用户id',
`unread_num` int(0) NOT NULL COMMENT '群未读消息数量',
`unread_offset_id` bigint(0) NULL DEFAULT NULL COMMENT '群未读消息id偏移量(大于等于该id的都未读)',
`last_active` datetime(0) NULL DEFAULT NULL COMMENT '最后活跃时间',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊用户列表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_user_6
-- ----------------------------
DROP TABLE IF EXISTS `im_group_user_6`;
CREATE TABLE `im_group_user_6` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`uid` bigint(0) NOT NULL COMMENT '用户id',
`unread_num` int(0) NOT NULL COMMENT '群未读消息数量',
`unread_offset_id` bigint(0) NULL DEFAULT NULL COMMENT '群未读消息id偏移量(大于等于该id的都未读)',
`last_active` datetime(0) NULL DEFAULT NULL COMMENT '最后活跃时间',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊用户列表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_group_user_7
-- ----------------------------
DROP TABLE IF EXISTS `im_group_user_7`;
CREATE TABLE `im_group_user_7` (
`id` bigint(0) NOT NULL COMMENT 'id',
`group_id` bigint(0) NOT NULL COMMENT '群聊id',
`uid` bigint(0) NOT NULL COMMENT '用户id',
`unread_num` int(0) NOT NULL COMMENT '群未读消息数量',
`unread_offset_id` bigint(0) NULL DEFAULT NULL COMMENT '群未读消息id偏移量(大于等于该id的都未读)',
`last_active` datetime(0) NULL DEFAULT NULL COMMENT '最后活跃时间',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '群聊用户列表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_message_0
-- ----------------------------
DROP TABLE IF EXISTS `im_message_0`;
CREATE TABLE `im_message_0` (
`id` bigint(0) NOT NULL COMMENT 'id',
`relation_id` bigint(0) NULL DEFAULT NULL,
`sender` bigint(0) NOT NULL COMMENT '发送人id',
`receiver` bigint(0) NOT NULL COMMENT '接受者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_message_1
-- ----------------------------
DROP TABLE IF EXISTS `im_message_1`;
CREATE TABLE `im_message_1` (
`id` bigint(0) NOT NULL COMMENT 'id',
`relation_id` bigint(0) NULL DEFAULT NULL,
`sender` bigint(0) NOT NULL COMMENT '发送人id',
`receiver` bigint(0) NOT NULL COMMENT '接受者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_message_2
-- ----------------------------
DROP TABLE IF EXISTS `im_message_2`;
CREATE TABLE `im_message_2` (
`id` bigint(0) NOT NULL COMMENT 'id',
`relation_id` bigint(0) NULL DEFAULT NULL,
`sender` bigint(0) NOT NULL COMMENT '发送人id',
`receiver` bigint(0) NOT NULL COMMENT '接受者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_message_3
-- ----------------------------
DROP TABLE IF EXISTS `im_message_3`;
CREATE TABLE `im_message_3` (
`id` bigint(0) NOT NULL COMMENT 'id',
`relation_id` bigint(0) NULL DEFAULT NULL,
`sender` bigint(0) NOT NULL COMMENT '发送人id',
`receiver` bigint(0) NOT NULL COMMENT '接受者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_message_4
-- ----------------------------
DROP TABLE IF EXISTS `im_message_4`;
CREATE TABLE `im_message_4` (
`id` bigint(0) NOT NULL COMMENT 'id',
`relation_id` bigint(0) NULL DEFAULT NULL,
`sender` bigint(0) NOT NULL COMMENT '发送人id',
`receiver` bigint(0) NOT NULL COMMENT '接受者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_message_5
-- ----------------------------
DROP TABLE IF EXISTS `im_message_5`;
CREATE TABLE `im_message_5` (
`id` bigint(0) NOT NULL COMMENT 'id',
`relation_id` bigint(0) NULL DEFAULT NULL,
`sender` bigint(0) NOT NULL COMMENT '发送人id',
`receiver` bigint(0) NOT NULL COMMENT '接受者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_message_6
-- ----------------------------
DROP TABLE IF EXISTS `im_message_6`;
CREATE TABLE `im_message_6` (
`id` bigint(0) NOT NULL COMMENT 'id',
`relation_id` bigint(0) NULL DEFAULT NULL,
`sender` bigint(0) NOT NULL COMMENT '发送人id',
`receiver` bigint(0) NOT NULL COMMENT '接受者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_message_7
-- ----------------------------
DROP TABLE IF EXISTS `im_message_7`;
CREATE TABLE `im_message_7` (
`id` bigint(0) NOT NULL COMMENT 'id',
`relation_id` bigint(0) NULL DEFAULT NULL,
`sender` bigint(0) NOT NULL COMMENT '发送人id',
`receiver` bigint(0) NOT NULL COMMENT '接受者id',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`content` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '消息内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '聊天消息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_segment_id
-- ----------------------------
DROP TABLE IF EXISTS `im_segment_id`;
CREATE TABLE `im_segment_id` (
`biz_tag` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '业务标签',
`current_id` bigint(0) NOT NULL COMMENT '当前id值',
`init_step` bigint(0) NOT NULL COMMENT '业务标签初始步长',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
`version` bigint(0) NOT NULL COMMENT '版本号',
PRIMARY KEY (`biz_tag`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '分段id' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for im_segment_id
-- ----------------------------
DROP TABLE IF EXISTS `im_segment_id`;
CREATE TABLE `im_segment_id` (
`biz_tag` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '业务标签',
`current_id` bigint(0) NOT NULL COMMENT '当前id值',
`init_step` bigint(0) NOT NULL COMMENT '业务标签初始步长',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
`version` bigint(0) NOT NULL COMMENT '版本号',
PRIMARY KEY (`biz_tag`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '分段id' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of im_segment_id
-- ----------------------------
INSERT INTO `im_segment_id` VALUES ('im_friend', 12002, 1000, '2022-06-03 19:45:15', '2022-06-12 13:08:59', 2);
INSERT INTO `im_segment_id` VALUES ('im_friend_relation_id', 12002, 1000, '2022-06-03 19:45:15', '2022-06-12 13:08:59', 2);
INSERT INTO `im_segment_id` VALUES ('im_group', 12002, 1000, '2022-06-05 09:52:41', '2022-06-07 17:39:39', 2);
INSERT INTO `im_segment_id` VALUES ('im_group_message', 16006, 1000, '2022-06-07 16:45:41', '2022-06-12 13:09:48', 6);
INSERT INTO `im_segment_id` VALUES ('im_group_user', 13003, 1000, '2022-06-05 09:52:42', '2022-06-07 17:39:39', 3);
INSERT INTO `im_segment_id` VALUES ('im_message', 35025, 1000, '2022-06-03 19:55:01', '2022-06-12 13:09:10', 25);
-- ----------------------------
-- Table structure for im_user_0
-- ----------------------------
DROP TABLE IF EXISTS `im_user_0`;
CREATE TABLE `im_user_0` (
`id` bigint(0) NOT NULL COMMENT 'id',
`status` int(0) NOT NULL COMMENT '状态:0删除,1.正常,2.禁用',
`account` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '登录账号',
`nickname` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户昵称',
`password` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '密码',
`phone` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '手机号',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`last_login_time` datetime(0) NULL DEFAULT NULL COMMENT '最后登录时间',
`last_active_time` datetime(0) NULL DEFAULT NULL COMMENT '最后活跃时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_account_status_0`(`account`, `status`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of im_user_0
-- ----------------------------
INSERT INTO `im_user_0` VALUES (728220, 1, 'zeus', '宙斯', '123456', '17882451901', '2022-04-14 10:50:55', NULL, NULL);
-- ----------------------------
-- Table structure for im_user_1
-- ----------------------------
DROP TABLE IF EXISTS `im_user_1`;
CREATE TABLE `im_user_1` (
`id` bigint(0) NOT NULL COMMENT 'id',
`status` int(0) NOT NULL COMMENT '状态:0删除,1.正常,2.禁用',
`account` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '登录账号',
`nickname` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户昵称',
`password` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '密码',
`phone` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '手机号',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`last_login_time` datetime(0) NULL DEFAULT NULL COMMENT '最后登录时间',
`last_active_time` datetime(0) NULL DEFAULT NULL COMMENT '最后活跃时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_account_status`(`account`, `status`) USING BTREE,
INDEX `idx_account_status_1`(`account`, `status`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of im_user_1
-- ----------------------------
INSERT INTO `im_user_1` VALUES (727717, 1, 'prometheus', '普罗米修斯', '123456', '17882451908', '2022-04-14 10:46:23', NULL, NULL);
-- ----------------------------
-- Table structure for im_user_2
-- ----------------------------
DROP TABLE IF EXISTS `im_user_2`;
CREATE TABLE `im_user_2` (
`id` bigint(0) NOT NULL COMMENT 'id',
`status` int(0) NOT NULL COMMENT '状态:0删除,1.正常,2.禁用',
`account` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '登录账号',
`nickname` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户昵称',
`password` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '密码',
`phone` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '手机号',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`last_login_time` datetime(0) NULL DEFAULT NULL COMMENT '最后登录时间',
`last_active_time` datetime(0) NULL DEFAULT NULL COMMENT '最后活跃时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_account_status`(`account`, `status`) USING BTREE,
INDEX `idx_account_status_2`(`account`, `status`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of im_user_2
-- ----------------------------
INSERT INTO `im_user_2` VALUES (729726, 1, 'poros', '波罗斯', '123456', '17882451902', '2022-04-14 10:53:39', NULL, NULL);
-- ----------------------------
-- Table structure for im_user_3
-- ----------------------------
DROP TABLE IF EXISTS `im_user_3`;
CREATE TABLE `im_user_3` (
`id` bigint(0) NOT NULL COMMENT 'id',
`status` int(0) NOT NULL COMMENT '状态:0删除,1.正常,2.禁用',
`account` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '登录账号',
`nickname` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户昵称',
`password` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '密码',
`phone` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '手机号',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`last_login_time` datetime(0) NULL DEFAULT NULL COMMENT '最后登录时间',
`last_active_time` datetime(0) NULL DEFAULT NULL COMMENT '最后活跃时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_account_status_3`(`account`, `status`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of im_user_3
-- ----------------------------
SET FOREIGN_KEY_CHECKS = 1;

1
im-client/src/main/java/net/sopod/soim/client/cmd/CmdEnum.java

@ -1,6 +1,5 @@
package net.sopod.soim.client.cmd;
import net.sopod.soim.client.cmd.args.ArgsGroup;
import net.sopod.soim.client.cmd.handler.CmdHandler;
import net.sopod.soim.client.handler.cmd.*;

2
im-client/src/main/java/net/sopod/soim/client/cmd/args/ArgsSend.java

@ -18,7 +18,7 @@ public class ArgsSend {
private List<String> parameters;
@Parameter(names = {"-u", "account"}, required = false, description = "接收者")
private String account;
private Long receiverUid;
@Parameter(names = {"-g", "group"}, required = false, description = "接收者")
private Long groupId;

6
im-client/src/main/java/net/sopod/soim/client/config/ClientConfig.java

@ -13,10 +13,6 @@ import lombok.Data;
@Data
public class ClientConfig {
private String loginUrl = "http://localhost:3021/auth/pwdAuth";
private String host = "127.0.0.1";
private Integer port = 8089;
private String entryHttpHost = "http://localhost:3021";
}

11
im-client/src/main/java/net/sopod/soim/client/handler/cmd/LoginHandler.java

@ -11,6 +11,7 @@ import net.sopod.soim.client.session.SoImSession;
import net.sopod.soim.client.util.HttpClient;
import net.sopod.soim.data.msg.auth.Auth;
import java.util.Collections;
import java.util.HashMap;
/**
@ -39,18 +40,20 @@ public class LoginHandler implements CmdHandler<ArgsLogin> {
params.put("account", args.getAccount());
params.put("password", args.getPassword());
Console.info("登录中...");
LoginResDTO loginRes = HttpClient.restPost(clientConfig.getLoginUrl(), params, LoginResDTO.class);
LoginResDTO loginRes = HttpClient.restPost(clientConfig.getEntryHttpHost() + "/auth/pwdAuth", params, LoginResDTO.class);
if (!Boolean.TRUE.equals(loginRes.getSuccess())) {
Console.error("登录失败: {}", loginRes.getMessage());
return;
}
Console.info("登录成功: {}", loginRes.getUid());
Auth.ReqTokenAuth reqTokenAuth = Auth.ReqTokenAuth.newBuilder()
.setUid(loginRes.getUid())
.setToken(loginRes.getAuthToken())
.build();
soImSession.connect(clientConfig.getHost(), clientConfig.getPort(), reqTokenAuth, args.getAccount());
// 获取 im-entry host
String entryHost = HttpClient.restGet(clientConfig.getEntryHttpHost() + "/monitor/entryHost", String.class);
Console.info("登录成功: uid={}, entryHost={}", loginRes.getUid(), entryHost);
String[] hostPort = entryHost.split(":");
soImSession.connect(hostPort[0], Integer.parseInt(hostPort[1]), reqTokenAuth, args.getAccount());
}
}

5
im-client/src/main/java/net/sopod/soim/client/handler/cmd/SendHandler.java

@ -38,10 +38,11 @@ public class SendHandler implements CmdHandler<ArgsSend> {
Console.info("请输入要发送的内容");
return;
}
if (args.getAccount() != null) {
if (args.getReceiverUid() != null) {
Chat.TextChat textChat = Chat.TextChat.newBuilder()
.setMessage(message)
.setReceiverAccount(args.getAccount())
.setReceiver(args.getReceiverUid())
//.setReceiverAccount(args.get())
.setSender(soImSession.getUid())
.build();
soImSession.justSend(textChat);

7
im-client/src/main/java/net/sopod/soim/client/protocol/ImMessageInboundHandler.java

@ -6,6 +6,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import net.sopod.soim.client.logger.Console;
import net.sopod.soim.data.serialize.ImMessage;
import net.sopod.soim.data.serialize.ImMessageCodec;
@ -22,6 +23,12 @@ public class ImMessageInboundHandler extends SimpleChannelInboundHandler<ByteBuf
@Inject
private ImMessageReqHolder imMessageReqHolder;
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Console.info("与服务器断开连接...");
super.channelInactive(ctx);
}
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf bytebuf) throws Exception {
ImMessage imMessage = ImMessageCodec.decodeImMessage(bytebuf);

1
im-client/src/main/java/net/sopod/soim/client/session/SoImSession.java

@ -123,6 +123,7 @@ public class SoImSession {
}
if (clientChannel == null
|| !clientChannel.isActive()) {
// TODO 尝试重连
throw new ClientException("连接已关闭");
}
ImMessage imMessage = ImMessageCodec.encodeImProto(message);

25
im-client/src/main/java/net/sopod/soim/client/util/HttpClient.java

@ -5,12 +5,14 @@ import kong.unirest.HttpResponse;
import kong.unirest.JsonNode;
import kong.unirest.Unirest;
import kong.unirest.json.JSONElement;
import net.sopod.soim.client.config.ClientConfig;
import net.sopod.soim.common.util.Jackson;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -28,6 +30,8 @@ public class HttpClient {
.body(Jackson.json().serialize(params))
.contentType("application/json")
.charset(StandardCharsets.UTF_8)
.connectTimeout(1000)
.socketTimeout(1000)
.asString();
if (String.class.equals(resType)) {
return (T) res.getBody();
@ -35,12 +39,21 @@ public class HttpClient {
return Jackson.json().deserialize(res.getBody(), resType);
}
public static void main(String[] args) throws IOException {
HashMap<String, String> params = new HashMap<>();
params.put("account", "prometheus");
params.put("password", "123456");
String res = restPost("http://localhost:3021/auth/pwdAuth", params, String.class);
System.out.println(res);
public static <T> T restGet(String url, Class<T> resType) {
return restGet(url, Collections.emptyMap(), resType);
}
@SuppressWarnings("unchecked")
public static <T> T restGet(String url, Map<String, Object> queryString, Class<T> resType) {
HttpResponse<String> res = Unirest.get(url)
.queryString(queryString)
.connectTimeout(1000)
.socketTimeout(1000)
.asString();
if (String.class.equals(resType)) {
return (T) res.getBody();
}
return Jackson.json().deserialize(res.getBody(), resType);
}
}

2
im-common/src/main/java/net/sopod/soim/common/constant/AppConstant.java

@ -30,4 +30,6 @@ public interface AppConstant {
*/
int IM_ROUTER_SYNC_SERVER_OFFSET = 1000;
String IM_ENTRY_MONITOR_SECURITY = "b699d74ae95011ec8373bc97e1e99320";
}

5
im-das/im-das-group/pom.xml

@ -12,6 +12,11 @@
<artifactId>im-das-group</artifactId>
<dependencies>
<dependency>
<groupId>net.sopod</groupId>
<artifactId>im-launcher</artifactId>
<version>${soim.version}</version>
</dependency>
<dependency>
<groupId>net.sopod</groupId>
<artifactId>im-common</artifactId>

4
im-das/im-das-group/src/main/java/net/sopod/soim/das/group/DasGroupApplication.java

@ -1,8 +1,8 @@
package net.sopod.soim.das.group;
import net.sopod.soim.launcher.SoimApplication;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
@ -17,7 +17,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
public class DasGroupApplication {
public static void main(String[] args) {
SpringApplication.run(DasGroupApplication.class);
SoimApplication.run(DasGroupApplication.class, args);
}
}

10
im-das/im-das-group/src/main/resources/application.properties

@ -1,13 +1,7 @@
# sharding-jdbc 配置
# ÅäÖÃÊý¾ÝÔ´
spring.shardingsphere.datasource.names=ds1
spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://cd-cdb-mrz9fw80.sql.tencentcdb.com:61843/soim_db?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=sopod@2347#
# 配置数据源...launcher.properties
# 5.1.0 分表
# https://github.com/apache/shardingsphere/blob/master/examples/shardingsphere-jdbc-example/single-feature-example/sharding-example/sharding-raw-jdbc-example/src/main/resources/META-INF/sharding-tables.yaml

3
im-das/im-das-group/src/main/resources/application.yml

@ -5,9 +5,6 @@ spring:
dubbo:
application:
name: ${spring.application.name}
registry:
address: nacos://124.222.131.236:3848
group: so-im
protocol:
port: 3014
consumer:

5
im-das/im-das-message/pom.xml

@ -12,6 +12,11 @@
<artifactId>im-das-message</artifactId>
<dependencies>
<dependency>
<groupId>net.sopod</groupId>
<artifactId>im-launcher</artifactId>
<version>${soim.version}</version>
</dependency>
<dependency>
<groupId>net.sopod</groupId>
<artifactId>im-common</artifactId>

4
im-das/im-das-message/src/main/java/net/sopod/soim/das/message/DasMessageApplication.java

@ -1,7 +1,7 @@
package net.sopod.soim.das.message;
import net.sopod.soim.launcher.SoimApplication;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
@ -15,7 +15,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
public class DasMessageApplication {
public static void main(String[] args) {
SpringApplication.run(DasMessageApplication.class);
SoimApplication.run(DasMessageApplication.class, args);
}
}

8
im-das/im-das-message/src/main/resources/application.properties

@ -1,11 +1,5 @@
# sharding-jdbc 配置
# ÅäÖÃÊý¾ÝÔ´
spring.shardingsphere.datasource.names=ds1
spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://cd-cdb-mrz9fw80.sql.tencentcdb.com:61843/soim_db?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=sopod@2347#
# 配置数据源...launcher.properties
# 分表
#spring.shardingsphere.sharding.tables.im_user.actual-data-nodes=db1.im_user_$->{0..2}

8
im-das/im-das-message/src/main/resources/application.yml

@ -1,18 +1,10 @@
spring:
application:
name: im-das-message
rabbitmq:
host: 124.222.131.236
port: 3672
username: soim
password: sopod@rabbit#
dubbo:
application:
name: ${spring.application.name}
registry:
address: nacos://124.222.131.236:3848
group: so-im
protocol:
port: 3011
consumer:

5
im-das/im-das-user/pom.xml

@ -12,6 +12,11 @@
<artifactId>im-das-user</artifactId>
<dependencies>
<dependency>
<groupId>net.sopod</groupId>
<artifactId>im-launcher</artifactId>
<version>${soim.version}</version>
</dependency>
<dependency>
<groupId>net.sopod</groupId>
<artifactId>im-common</artifactId>

7
im-das/im-das-user/src/main/java/net/sopod/soim/das/user/DasUserApplication.java

@ -1,13 +1,10 @@
package net.sopod.soim.das.user;
import net.sopod.soim.launcher.SoimApplication;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.HashMap;
import java.util.Map;
/**
* DasUserApplication
*
@ -20,7 +17,7 @@ import java.util.Map;
public class DasUserApplication {
public static void main(String[] args) {
SpringApplication.run(DasUserApplication.class, args);
SoimApplication.run(DasUserApplication.class, args);
}
}

40
im-das/im-das-user/src/main/java/net/sopod/soim/das/user/cache/FriendRelationCache.java vendored

@ -0,0 +1,40 @@
package net.sopod.soim.das.user.cache;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import org.springframework.stereotype.Service;
import java.util.function.BiFunction;
/**
* FriendRelationCache
*
* @author tmy
* @date 2022-06-12 09:38
*/
@Service
public class FriendRelationCache {
private final Table<Long, Long, Long> friendRelationTable = HashBasedTable.create();;
public Long computeIfAbsent(Long uid, Long fid, BiFunction<Long, Long, Long> mappingFunction) {
Long relationId;
if (null != (relationId = friendRelationTable.get(uid, fid))) {
return relationId;
}
// TODO lockFunc(uid, fid)
synchronized (FriendRelationCache.class) {
if (null != (relationId = friendRelationTable.get(uid, fid))) {
return relationId;
}
relationId = mappingFunction.apply(uid, fid);
friendRelationTable.put(uid, fid, relationId);
}
return relationId;
}
public Long remove(Long uid, Long fid) {
return friendRelationTable.remove(uid, fid);
}
}

33
im-das/im-das-user/src/main/java/net/sopod/soim/das/user/service/FriendDasImpl.java

@ -9,6 +9,7 @@ import net.sopod.soim.das.common.config.LogicTables;
import net.sopod.soim.das.user.api.model.entity.ImFriend;
import net.sopod.soim.das.user.api.model.entity.ImUser;
import net.sopod.soim.das.user.api.service.FriendDas;
import net.sopod.soim.das.user.cache.FriendRelationCache;
import net.sopod.soim.das.user.dao.FriendMapper;
import net.sopod.soim.das.user.dao.UserMapper;
import net.sopod.soim.logic.api.segmentid.core.SegmentIdGenerator;
@ -31,12 +32,14 @@ public class FriendDasImpl implements FriendDas {
private static Logger logger = LoggerFactory.getLogger(FriendDasImpl.class);
SegmentIdGenerator segmentIdGenerator;
private SegmentIdGenerator segmentIdGenerator;
private FriendMapper friendMapper;
private UserMapper userMapper;
private FriendRelationCache friendRelationCache;
@Override
public int saveFriendRelation(Long uid, Long fid) {
long id = segmentIdGenerator.nextId(LogicTables.IM_FRIEND);
@ -69,19 +72,21 @@ public class FriendDasImpl implements FriendDas {
*/
@Override
public Long getRelationId(Long uid, Long fid) {
LambdaQueryWrapper<ImFriend> friendQuery = new QueryWrapper<ImFriend>().lambda()
.select(ImFriend::getId, ImFriend::getRelationId)
.eq(ImFriend::getUid, uid)
.eq(ImFriend::getFid, fid)
.eq(ImFriend::getStatus, LogicTables.STATUS_NORMAL);
List<ImFriend> imFriends = friendMapper.selectList(friendQuery);
if (Collects.isEmpty(imFriends)) {
return null;
}
if (imFriends.size() > 1) {
logger.warn("重复的好友数据: user={}, friend={}", uid, fid);
}
return imFriends.get(0).getRelationId();
return friendRelationCache.computeIfAbsent(uid, fid, (userId, friendId) -> {
LambdaQueryWrapper<ImFriend> friendQuery = new QueryWrapper<ImFriend>().lambda()
.select(ImFriend::getId, ImFriend::getRelationId)
.eq(ImFriend::getUid, userId)
.eq(ImFriend::getFid, friendId)
.eq(ImFriend::getStatus, LogicTables.STATUS_NORMAL);
List<ImFriend> imFriends = friendMapper.selectList(friendQuery);
if (Collects.isEmpty(imFriends)) {
return null;
}
if (imFriends.size() > 1) {
logger.warn("重复的好友数据: user={}, friend={}", userId, friendId);
}
return imFriends.get(0).getRelationId();
});
}
@Override

8
im-das/im-das-user/src/main/resources/application.properties

@ -1,11 +1,5 @@
# sharding-jdbc 配置
# ÅäÖÃÊý¾ÝÔ´
spring.shardingsphere.datasource.names=ds1
spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://cd-cdb-mrz9fw80.sql.tencentcdb.com:61843/soim_db?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=sopod@2347#
# 配置数据源...launcher.properties
# 分表
#spring.shardingsphere.sharding.tables.im_user.actual-data-nodes=db1.im_user_$->{0..2}

3
im-das/im-das-user/src/main/resources/application.yml

@ -5,9 +5,6 @@ spring:
dubbo:
application:
name: ${spring.application.name}
registry:
address: nacos://124.222.131.236:3848
group: so-im
protocol:
port: 3011
consumer:

14
im-entry-http/pom.xml

@ -13,11 +13,25 @@
<description>提供im-entry监控,节点负载分配,http登录服务,后面用dubbo http网关替代</description>
<dependencies>
<dependency>
<groupId>net.sopod</groupId>
<artifactId>im-launcher</artifactId>
<version>${soim.version}</version>
</dependency>
<dependency>
<groupId>net.sopod</groupId>
<artifactId>im-logic-user-api</artifactId>
<version>${soim.version}</version>
</dependency>
<dependency>
<groupId>net.sopod</groupId>
<artifactId>im-entry-protocol</artifactId>
<version>${soim.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>

3
im-entry-http/src/main/java/net/sopod/soim/entry/http/EntryHttpApplication.java

@ -1,5 +1,6 @@
package net.sopod.soim.entry.http;
import net.sopod.soim.launcher.SoimApplication;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@ -15,7 +16,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
public class EntryHttpApplication {
public static void main(String[] args) {
SpringApplication.run(EntryHttpApplication.class, args);
SoimApplication.run(EntryHttpApplication.class, args);
}
}

176
im-entry-http/src/main/java/net/sopod/soim/entry/http/client/EntryClient.java

@ -0,0 +1,176 @@
package net.sopod.soim.entry.http.client;
import com.google.protobuf.MessageLite;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.AttributeKey;
import net.sopod.soim.common.constant.AppConstant;
import net.sopod.soim.common.dubbo.exception.SoimException;
import net.sopod.soim.common.util.netty.Varint32FrameCodec;
import net.sopod.soim.data.msg.monitor.EntryMonitor;
import net.sopod.soim.data.serialize.ImMessage;
import net.sopod.soim.data.serialize.ImMessageCodec;
import net.sopod.soim.entry.http.service.ImMessageReqHolder;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
/**
* EntryClient
*
* @author tmy
* @date 2022-06-11 13:17
*/
public class EntryClient {
private static final Logger logger = LoggerFactory.getLogger(EntryClient.class);
public static AttributeKey<EntryClient> IM_ENTRY_CLIENT_KEY = AttributeKey.valueOf(EntryClient.class, "IM_ENTRY_CLIENT_KEY");
private final String entryAddress;
private final ImMessageReqHolder imMessageReqHolder;
private Channel clientChannel;
private NioEventLoopGroup eventLoopGroup;
private int entryConnections;
private long updateTime;
public EntryClient(String entryAddress) {
this.entryAddress = entryAddress;
this.imMessageReqHolder = new ImMessageReqHolder();
}
public boolean connect() {
this.eventLoopGroup = new NioEventLoopGroup(2);
Bootstrap b = new Bootstrap()
.group(this.eventLoopGroup)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
.addLast(new Varint32FrameCodec())
.addLast(new ImMessageCodec.ImMessage2ByteEncoder())
.addLast(new ImMessageInboundHandler());
}
})
.option(ChannelOption.TCP_NODELAY, true);
String[] hostPort = this.entryAddress.split(":");
try {
this.clientChannel = b.connect(hostPort[0], Integer.parseInt(hostPort[1])).await().channel();
} catch (InterruptedException e) {
logger.error("im-entry 连接失败: ", e);
return false;
}
// 设置 MessageHolder 到 channel
this.clientChannel.attr(ImMessageReqHolder.IM_MESSAGE_HOLDER_KEY).set(this.imMessageReqHolder);
// 设置 client 到 channel
this.clientChannel.attr(EntryClient.IM_ENTRY_CLIENT_KEY).set(this);
EntryMonitor.ReqMonitorAuth req = EntryMonitor.ReqMonitorAuth.newBuilder()
.setSecurity(AppConstant.IM_ENTRY_MONITOR_SECURITY)
.build();
CompletableFuture<?> future = this.send0(req);
EntryMonitor.ResMonitorAuth res = (EntryMonitor.ResMonitorAuth) future.join();
if (!res.getSuccess()) {
logger.error("{} 认证失败", this.entryAddress);
// 关闭连接
this.close();
return false;
}
logger.info("认证成功: {}", this.entryAddress);
return true;
}
private CompletableFuture<?> send0(MessageLite message) {
if (this.clientChannel == null
|| !this.clientChannel.isActive()) {
// TODO 尝试重连
throw new SoimException("连接已关闭");
}
// 构建 ImMessage
ImMessage imMessage = ImMessageCodec.encodeImProto(message);
// serialNo, Future
Pair<Integer, CompletableFuture<Object>> futurePair = imMessageReqHolder.nextSerialNo();
imMessage.setSerialNo(futurePair.getLeft());
try {
Throwable cause = this.clientChannel.writeAndFlush(imMessage).await().cause();
if (cause != null) {
throw new SoimException(cause);
}
} catch (InterruptedException e) {
throw new SoimException("发送消息失败", e);
}
return futurePair.getRight();
}
/**
* 发送消息
*/
@SuppressWarnings("unchecked")
public <T> CompletableFuture<T> send(MessageLite message) {
return (CompletableFuture<T>) send0(message);
}
public String getEntryAddress() {
return entryAddress;
}
public void updateEntryStatus(int connections, long time) {
// logger.info("{} entry status: conns={}", this.entryAddress, connections);
this.entryConnections = connections;
this.updateTime = time;
}
/**
* 关闭 monitor client 连接
*/
public void close() {
if (this.clientChannel != null) {
this.clientChannel.close();
this.clientChannel.attr(ImMessageReqHolder.IM_MESSAGE_HOLDER_KEY).set(null);
this.clientChannel.attr(EntryClient.IM_ENTRY_CLIENT_KEY).set(null);
logger.info("entry monitor client {} closed.", this.entryAddress);
}
if (this.eventLoopGroup != null) {
this.eventLoopGroup.shutdownGracefully();
}
}
public int getEntryConnections() {
return entryConnections;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EntryClient that = (EntryClient) o;
return Objects.equals(entryAddress, that.entryAddress);
}
@Override
public int hashCode() {
return Objects.hash(entryAddress);
}
@Override
public String toString() {
return "EntryClient{" +
"entryAddress='" + entryAddress + '\'' +
", entryConnections=" + entryConnections +
", updateTime=" + updateTime +
'}';
}
}

48
im-entry-http/src/main/java/net/sopod/soim/entry/http/client/ImMessageInboundHandler.java

@ -0,0 +1,48 @@
package net.sopod.soim.entry.http.client;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import net.sopod.soim.data.serialize.ImMessage;
import net.sopod.soim.data.serialize.ImMessageCodec;
import net.sopod.soim.entry.http.service.ImMessageReqHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ImMessageInboundHandler
*
* @author tmy
* @date 2022-06-11 15:12
*/
public class ImMessageInboundHandler extends SimpleChannelInboundHandler<ByteBuf> {
private static final Logger logger = LoggerFactory.getLogger(ImMessageInboundHandler.class);
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
logger.info("channel active...");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
EntryClient entryClient = ctx.channel().attr(EntryClient.IM_ENTRY_CLIENT_KEY).get();
if (entryClient != null) {
entryClient.close();
}
super.channelInactive(ctx);
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
ImMessage imMessage = ImMessageCodec.decodeImMessage(byteBuf);
// 请求序列号,complete 对应 CompletableFuture
int serialNo = imMessage.getSerialNo();
ImMessageReqHolder imMessageReqHolder = ctx.channel().attr(ImMessageReqHolder.IM_MESSAGE_HOLDER_KEY).get();
imMessageReqHolder.complete(serialNo, imMessage.getDecodeBody());
}
}

65
im-entry-http/src/main/java/net/sopod/soim/entry/http/config/ApplicationOnReady.java

@ -1,65 +0,0 @@
package net.sopod.soim.entry.http.config;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.listener.Event;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;
import net.sopod.soim.common.constant.AppConstant;
import org.apache.dubbo.common.URL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import java.util.List;
import java.util.Properties;
/**
* ApplicationOnReady
*
* @author tmy
* @date 2022-06-09 11:16
*/
@Configuration
public class ApplicationOnReady implements ApplicationListener<ApplicationReadyEvent>, Ordered {
private static final Logger logger = LoggerFactory.getLogger(ApplicationOnReady.class);
private final String serverAddress;
public ApplicationOnReady(@Value("${dubbo.registry.address}") String serverAddress) {
this.serverAddress = URL.valueOf(serverAddress).getAddress();
}
@Override
public void onApplicationEvent(ApplicationReadyEvent readyEvent) {
// NamingServer 获取 im-entry 实例,连接上,
Properties properties = new Properties();
properties.put("serverAddr", serverAddress);
NamingService namingService = null;
try {
namingService = NacosFactory.createNamingService(properties);
List<Instance> allInstances = namingService.getAllInstances(AppConstant.APP_IM_ENTRY_NAME);
logger.info("im-entry instance...: {}", allInstances);
namingService.subscribe(AppConstant.APP_IM_ENTRY_NAME, (Event event) -> {
NamingEvent ne = (NamingEvent) event;
logger.info("namingEvent: {}", ne);
});
} catch (NacosException e) {
e.printStackTrace();
}
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}

175
im-entry-http/src/main/java/net/sopod/soim/entry/http/config/ImEntryMonitor.java

@ -0,0 +1,175 @@
package net.sopod.soim.entry.http.config;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.listener.Event;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;
import net.sopod.soim.common.constant.AppConstant;
import net.sopod.soim.common.dubbo.exception.SoimException;
import net.sopod.soim.common.util.Collects;
import net.sopod.soim.common.util.netty.FastThreadLocalThreadFactory;
import net.sopod.soim.data.msg.monitor.EntryMonitor;
import net.sopod.soim.entry.http.client.EntryClient;
import org.apache.dubbo.common.URL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;
/**
* ApplicationOnReady
*
* @author tmy
* @date 2022-06-09 11:16
*/
@Component
// @Configuration
public class ImEntryMonitor implements ApplicationListener<ApplicationReadyEvent>, Ordered {
private static final Logger logger = LoggerFactory.getLogger(ImEntryMonitor.class);
private final String nacosServerAddress;
private NamingService namingService;
private final Map<String, EntryClient> entryClientMap = new ConcurrentHashMap<>(8);
private final ScheduledExecutorService scheduledExecutorService;
public ImEntryMonitor(@Value("${dubbo.registry.address}") String nacosServerAddress) {
this.nacosServerAddress = URL.valueOf(nacosServerAddress).getAddress();
this.scheduledExecutorService = Executors.newScheduledThreadPool(1,
new FastThreadLocalThreadFactory("entry-monitor-%d", Thread.NORM_PRIORITY));
this.scheduledExecutorService.scheduleWithFixedDelay(() -> {
try {
for (Map.Entry<String, EntryClient> entry : entryClientMap.entrySet()) {
try {
EntryClient entryClient = entry.getValue();
// 定时获取状态信息
EntryMonitor.ReqEntryStatus req = EntryMonitor.ReqEntryStatus.newBuilder().build();
CompletableFuture<EntryMonitor.ResEntryStatus> future = entryClient.send(req);
EntryMonitor.ResEntryStatus res = future.join();
entryClient.updateEntryStatus(res.getConnections(), res.getStatusTime());
} catch (SoimException e) {
logger.error("获取状态信息失败", e);
entryClientMap.remove(entry.getKey(), entry.getValue());
}
}
} catch (Exception e) {
logger.error("定时拉取状态信息错误", e);
}
}, 5, 5, TimeUnit.SECONDS);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
if (namingService != null) {
try {
namingService.shutDown();
} catch (NacosException e) {
logger.error("nacos naming server shutdown exception", e);
}
scheduledExecutorService.shutdownNow();
entryClientMap.values().forEach(EntryClient::close);
logger.info("entry monitor shutdown.");
}
}));
}
@Override
public void onApplicationEvent(ApplicationReadyEvent readyEvent) {
// NamingServer 获取 im-entry 实例,连接上,
Properties properties = new Properties();
properties.put("serverAddr", nacosServerAddress);
try {
this.namingService = NacosFactory.createNamingService(properties);
// 初次启动获取所有 im-entry 服务节点
List<Instance> imEntryInstances = this.namingService.getAllInstances(AppConstant.APP_IM_ENTRY_NAME);
this.buildEntryMonitorMap(imEntryInstances);
// 监听 im-entry 服务变化
this.namingService.subscribe(AppConstant.APP_IM_ENTRY_NAME, (Event event) -> {
if (!(event instanceof NamingEvent)) {
return;
}
NamingEvent ne = (NamingEvent) event;
try {
this.buildEntryMonitorMap(ne.getInstances());
}catch (Exception e) {
logger.error("构建im-entry服务节点失败:", e);
}
});
} catch (NacosException e) {
logger.error("nacos exception", e);
}
}
/**
* 重新构建 im-entry 服务节点记录
*/
private synchronized void buildEntryMonitorMap(List<Instance> imEntryInstances) {
HashSet<String> instanceAddresses = new HashSet<>(Collects.mapCapacity(imEntryInstances.size()));
imEntryInstances.forEach(instance -> {
String address = instance.getIp() + ":" + (instance.getPort() + AppConstant.IM_ENTRY_SERVER_OFFSET);
instanceAddresses.add(address);
});
logger.info("im-entry instances: {}", instanceAddresses);
// 关闭失效im-entry
List<String> invalidateAddresses = new ArrayList<>();
for (Map.Entry<String, EntryClient> entry : entryClientMap.entrySet()) {
if (!instanceAddresses.contains(entry.getKey())) {
invalidateAddresses.add(entry.getKey());
}
}
for (String invalidateAddress : invalidateAddresses) {
EntryClient invalidClient = entryClientMap.remove(invalidateAddress);
invalidClient.close();
logger.info("invalid im-entry {} closed.", invalidateAddress);
}
// 添加新的 im-entry 连接
for (String instanceAddress : instanceAddresses) {
if (entryClientMap.containsKey(instanceAddress)) {
continue;
}
// nacos 注册时, entry server 还未启动完成,延时 5s 连接
this.scheduledExecutorService.schedule(() -> {
EntryClient entryClient = new EntryClient(instanceAddress);
logger.info("连接: {}", instanceAddress);
boolean connected = entryClient.connect();
if (!connected) {
logger.error("im-entry {} 连接失败", instanceAddress);
return;
}
entryClientMap.put(instanceAddress, entryClient);
}, 5, TimeUnit.SECONDS);
}
}
/**
* 获取一个活跃的 im-entry 服务器地址
*/
public String getOneActiveImEntryAddress() {
if (entryClientMap.size() == 0) {
return null;
}
Collection<EntryClient> clients = entryClientMap.values();
List<EntryClient> sortedClients = clients.stream()
.sorted(Comparator.comparing(EntryClient::getEntryConnections))
.collect(Collectors.toList());
logger.info("entrys: {}", sortedClients);
return sortedClients.get(0).getEntryAddress();
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}

7
im-entry-http/src/main/java/net/sopod/soim/entry/http/controller/AuthController.java

@ -4,10 +4,9 @@ import net.sopod.soim.entry.http.model.ao.PwdAuthAO;
import net.sopod.soim.logic.api.user.auth.model.ImAuth;
import net.sopod.soim.logic.api.user.auth.service.UserAuthService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* AuthController

37
im-entry-http/src/main/java/net/sopod/soim/entry/http/controller/MonitorController.java

@ -0,0 +1,37 @@
package net.sopod.soim.entry.http.controller;
import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Http;
import lombok.AllArgsConstructor;
import net.sopod.soim.entry.http.client.EntryClient;
import net.sopod.soim.entry.http.config.ImEntryMonitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.Iterator;
/**
* MonitorController
*
* @author tmy
* @date 2022-06-11 16:13
*/
@RestController
@AllArgsConstructor
@RequestMapping("/monitor")
public class MonitorController {
private static final Logger logger = LoggerFactory.getLogger(EntryClient.class);
private ImEntryMonitor imEntryMonitor;
@GetMapping("/entryHost")
public String getEntryHost() {
return imEntryMonitor.getOneActiveImEntryAddress();
}
}

21
im-entry-http/src/main/java/net/sopod/soim/entry/http/service/ImEntryMonitorService.java

@ -1,13 +1,30 @@
package net.sopod.soim.entry.http.service;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.listener.Event;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;
import net.sopod.soim.common.constant.AppConstant;
import net.sopod.soim.entry.http.config.ImEntryMonitor;
import org.apache.dubbo.common.URL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Properties;
import java.util.stream.Collectors;
/**
* ImMonitorService
*
* @author tmy
* @date 2022-04-13 23:27
*/
@Service
public class ImEntryMonitorService {
}

58
im-entry-http/src/main/java/net/sopod/soim/entry/http/service/ImMessageReqHolder.java

@ -0,0 +1,58 @@
package net.sopod.soim.entry.http.service;
import com.google.protobuf.MessageLite;
import io.netty.util.AttributeKey;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* MessageQueueHolder
* 发送一条消息产生一个序列号在队列中等待响应消息
*
* @author tmy
* @date 2022-06-02 17:29
*/
public class ImMessageReqHolder {
private static final Logger logger = LoggerFactory.getLogger(ImMessageReqHolder.class);
public static AttributeKey<ImMessageReqHolder> IM_MESSAGE_HOLDER_KEY = AttributeKey.valueOf(ImMessageReqHolder.class, "IM_MESSAGE_HOLDER_KEY");
private final AtomicInteger serialNoGen;
// TODO 超时处理
private final ConcurrentHashMap<Integer, CompletableFuture<Object>> futureMap;
public ImMessageReqHolder() {
this.serialNoGen = new AtomicInteger();
this.futureMap = new ConcurrentHashMap<>();
}
public Pair<Integer, CompletableFuture<Object>> nextSerialNo() {
serialNoGen.compareAndSet(Integer.MAX_VALUE, 0);
int serialNo = serialNoGen.incrementAndGet();
CompletableFuture<Object> future = new CompletableFuture<>();
futureMap.put(serialNo, future);
logger.debug("put future: {}", serialNo);
return Pair.of(serialNo, future);
}
/**
* TODO complete fail
*/
public void complete(int serialNo, MessageLite msg) {
CompletableFuture<Object> completableFuture = futureMap.remove(serialNo);
if (completableFuture != null) {
completableFuture.complete(msg);
} else {
// TODO dispatch im-entry 主动消息
logger.warn("no future wait for msg: {}", msg);
}
}
}

3
im-entry-http/src/main/resources/application.yml

@ -4,9 +4,6 @@ spring:
dubbo:
application:
name: ${spring.application.name}
registry:
address: nacos://124.222.131.236:3848
group: so-im
provider:
register: false # 不向注册中心注册服务
protocol: injvm # 服务提供只供jvm内部使用, 不暴露在外面 (不启动dubbo provider服务)

5
im-entry/Dockerfile

@ -0,0 +1,5 @@
FROM openjdk:11
ARG JAR_FILE
ENV JAR_FILE ${JAR_FILE}
ADD target/${JAR_FILE} /
CMD java -jar $JAVA_OPTS /${JAR_FILE}

5
im-entry/pom.xml

@ -16,6 +16,11 @@
</properties>
<dependencies>
<dependency>
<groupId>net.sopod</groupId>
<artifactId>im-launcher</artifactId>
<version>${soim.version}</version>
</dependency>
<dependency>
<groupId>net.sopod</groupId>
<artifactId>im-entry-api</artifactId>

3
im-entry/src/main/java/net/sopod/soim/entry/EntryApplication.java

@ -1,5 +1,6 @@
package net.sopod.soim.entry;
import net.sopod.soim.launcher.SoimApplication;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@ -15,7 +16,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
public class EntryApplication {
public static void main(String[] args) {
SpringApplication.run(EntryApplication.class, args);
SoimApplication.run(EntryApplication.class, args);
}
}

4
im-entry/src/main/java/net/sopod/soim/entry/config/EntryServerConfig.java

@ -18,9 +18,8 @@ import org.springframework.stereotype.Component;
@Data
public class EntryServerConfig {
private String name = AppConstant.APP_IM_ENTRY_NAME;
/** entry 所在服务器 ip */
@Deprecated
private String ip = "127.0.0.1";
/**
@ -29,6 +28,7 @@ public class EntryServerConfig {
@Deprecated
private Integer port = 8088;
@Deprecated
private String nacosAddr;
/** 消息消费者线程数 */

2
im-entry/src/main/java/net/sopod/soim/entry/config/ImEntryAppOnReady.java

@ -32,7 +32,7 @@ public class ImEntryAppOnReady implements ApplicationListener<ApplicationReadyEv
EntryServerConfig config = ImEntryAppContext.getBean(EntryServerConfig.class);
EntryServer entryServer = new EntryServer(
config.getName(),
AppConstant.APP_IM_ENTRY_NAME,
// 使用dubbo 服务端口偏移量
ImEntryAppContext.getAppPort() + AppConstant.IM_ENTRY_SERVER_OFFSET);
entryServer.startServer(err -> {

2
im-entry/src/main/java/net/sopod/soim/entry/handlers/chat/ReqGroupMessageHandler.java

@ -41,7 +41,7 @@ public class ReqGroupMessageHandler extends AccountMessageHandler<Group.ReqGroup
future.whenComplete((res, err) -> {
Group.ResGroupMessage.Builder reqBuilder = Group.ResGroupMessage.newBuilder();
if (err != null) {
logger.error("群消息发送失败: {}, {}, {}", req.getGid(), req.getSender(), req.getMessage(), err);
logger.error("群消息发送失败: {}, {}, {}", req.getGid(), account.getUid(), req.getMessage(), err);
account.writeNow(ctx, reqBuilder.setSuccess(false)
.setMessage(Func.nullSo(err.getMessage(), ""))
.build());

5
im-entry/src/main/java/net/sopod/soim/entry/handlers/chat/ReqTextChatHandler.java

@ -34,11 +34,10 @@ public class ReqTextChatHandler extends AccountMessageHandler<Chat.TextChat> {
UserMessage textChat = new UserMessage()
.setSenderUid(msg.getSender())
.setReceiverUid(msg.getReceiver())
.setReceiverName(msg.getReceiverAccount())
.setTime(msg.getTime())
.setMessage(msg.getMessage());
CompletableFuture<String> stringCompletableFuture = imUserChatService.userMessage(textChat);
stringCompletableFuture.whenCompleteAsync((res, e) -> {
CompletableFuture<String> future = imUserChatService.userMessage(textChat);
future.whenCompleteAsync((res, e) -> {
if (e != null) {
logger.error("发送失败:", e);
return;

33
im-entry/src/main/java/net/sopod/soim/entry/handlers/monitor/ReqEntryStatusHandler.java

@ -0,0 +1,33 @@
package net.sopod.soim.entry.handlers.monitor;
import com.google.protobuf.MessageLite;
import lombok.AllArgsConstructor;
import net.sopod.soim.common.util.ImClock;
import net.sopod.soim.data.msg.monitor.EntryMonitor;
import net.sopod.soim.entry.server.AccountRegistry;
import net.sopod.soim.entry.server.handler.ImContext;
import net.sopod.soim.entry.server.handler.MonitorMessageHandler;
import net.sopod.soim.entry.server.session.Monitor;
import org.springframework.stereotype.Service;
/**
* ReqEntryStatusHandler
*
* @author tmy
* @date 2022-06-11 14:39
*/
@Service
@AllArgsConstructor
public class ReqEntryStatusHandler extends MonitorMessageHandler<EntryMonitor.ReqEntryStatus> {
private final AccountRegistry accountRegistry;
@Override
public MessageLite handle(ImContext ctx, Monitor monitor, EntryMonitor.ReqEntryStatus req) {
return EntryMonitor.ResEntryStatus.newBuilder()
.setConnections(accountRegistry.getRegistryAccountSize())
.setStatusTime(ImClock.millis())
.build();
}
}

38
im-entry/src/main/java/net/sopod/soim/entry/handlers/monitor/ReqMonitorAuthHandler.java

@ -0,0 +1,38 @@
package net.sopod.soim.entry.handlers.monitor;
import com.google.protobuf.MessageLite;
import net.sopod.soim.common.constant.AppConstant;
import net.sopod.soim.common.util.ImClock;
import net.sopod.soim.data.msg.monitor.EntryMonitor;
import net.sopod.soim.data.msg.task.Tasks;
import net.sopod.soim.entry.delay.NetUserDelayTaskManager;
import net.sopod.soim.entry.server.handler.ImContext;
import net.sopod.soim.entry.server.handler.NetUserMessageHandler;
import net.sopod.soim.entry.server.session.Monitor;
import net.sopod.soim.entry.server.session.NetUser;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
/**
* ReqMonitorAuthHandler
*
* @author tmy
* @date 2022-06-11 14:30
*/
@Service
public class ReqMonitorAuthHandler extends NetUserMessageHandler<EntryMonitor.ReqMonitorAuth> {
@Override
public MessageLite handle(ImContext ctx, NetUser netUser, EntryMonitor.ReqMonitorAuth msg) {
EntryMonitor.ResMonitorAuth.Builder resBuilder = EntryMonitor.ResMonitorAuth.newBuilder();
if (!AppConstant.IM_ENTRY_MONITOR_SECURITY.equals(msg.getSecurity())) {
// 认证失败 6 秒后会关闭连接
return resBuilder.setSuccess(false).build();
}
Monitor monitor = new Monitor(netUser.channel());
netUser.upgradeMonitor(monitor);
return resBuilder.setSuccess(true).build();
}
}

2
im-entry/src/main/java/net/sopod/soim/entry/handlers/task/NetUserDelayCloseHandler.java

@ -18,7 +18,7 @@ public class NetUserDelayCloseHandler extends NetUserMessageHandler<Tasks.NetUse
@Override
public MessageLite handle(ImContext ctx, NetUser netUser, Tasks.NetUserDelayCloseTask msg) {
if (netUser.isAccount()) {
if (netUser.isAccount() || netUser.isMonitor()) {
return null;
}
// 用户还未登录关闭连接

11
im-entry/src/main/java/net/sopod/soim/entry/server/AccountRegistry.java

@ -30,4 +30,15 @@ public class AccountRegistry {
return accounts.get(uid);
}
public void remove(Long uid) {
Account account = accounts.remove(uid);
if (account != null) {
logger.info("remove account: {}", account);
}
}
public int getRegistryAccountSize() {
return accounts.size();
}
}

40
im-entry/src/main/java/net/sopod/soim/entry/server/EntryServerRunner.java

@ -1,40 +0,0 @@
package net.sopod.soim.entry.server;
import net.sopod.soim.entry.config.EntryServerConfig;
import net.sopod.soim.entry.worker.WorkerGroup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
/**
* ServerRunner
*
* @author tmy
* @date 2022-03-29 00:22
*/
// @Component
@Deprecated
public class EntryServerRunner implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(EntryServerRunner.class);
private final EntryServerConfig config;
public EntryServerRunner(EntryServerConfig config) {
this.config = config;
}
@Override
public void run(ApplicationArguments args) {
EntryServer entryServer = new EntryServer(this.config.getName(), this.config.getPort());
entryServer.startServer(err -> {
logger.error("EntryServer 启动失败:", err);
});
Runtime.getRuntime().addShutdownHook(new Thread(entryServer::shutdown));
// 启动消息消费队列组
WorkerGroup.init(config.getWorkerSize());
}
}

4
im-entry/src/main/java/net/sopod/soim/entry/server/ImEntryInitializer.java

@ -22,9 +22,9 @@ public class ImEntryInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) {
LogLevel logLevel = logger.isDebugEnabled() ? LogLevel.DEBUG : LogLevel.INFO;
// LogLevel logLevel = logger.isDebugEnabled() ? LogLevel.DEBUG : LogLevel.INFO;
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new LoggingHandler(logLevel))
pipeline//.addLast(new LoggingHandler(LogLevel.WARN))
.addLast(new Varint32FrameCodec())
.addLast(new ImMessageCodec.ImMessageDecoder())
.addLast(new ImMessageCodec.ImMessage2ByteEncoder())

13
im-entry/src/main/java/net/sopod/soim/entry/server/InboundImMessageHandler.java

@ -6,7 +6,9 @@ import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.Attribute;
import net.sopod.soim.common.util.ImClock;
import net.sopod.soim.data.serialize.ImMessage;
import net.sopod.soim.entry.config.ImEntryAppContext;
import net.sopod.soim.entry.server.handler.ImContext;
import net.sopod.soim.entry.server.session.Account;
import net.sopod.soim.entry.server.session.NetUser;
import net.sopod.soim.data.msg.task.Tasks;
import net.sopod.soim.entry.delay.NetUserDelayTaskManager;
@ -38,15 +40,20 @@ public class InboundImMessageHandler extends SimpleChannelInboundHandler<ImMessa
Tasks.NetUserDelayCloseTask taskMsg = Tasks.NetUserDelayCloseTask.newBuilder()
.setTime(ImClock.millis())
.build();
NetUserDelayTaskManager.addTask(netUser, taskMsg, 10, TimeUnit.SECONDS);
NetUserDelayTaskManager.addTask(netUser, taskMsg, 6, TimeUnit.SECONDS);
ctx.fireChannelActive();
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
Attribute<NetUser> netUser = ctx.channel().attr(NetUser.NET_USER_KEY);
NetUser netUser = ctx.channel().attr(NetUser.NET_USER_KEY).get();
// 取消登录用户状态
if (netUser != null && netUser.isAccount()) {
AccountRegistry registry = ImEntryAppContext.getBean(AccountRegistry.class);
registry.remove(((Account) netUser).getUid());
}
// TODO 离线状态同步到 im-router,等待30s不重连,删除im-router用户缓存数据
ctx.fireChannelInactive();
}

2
im-entry/src/main/java/net/sopod/soim/entry/server/handler/AccountMessageHandler.java

@ -17,7 +17,7 @@ public abstract class AccountMessageHandler<T> implements MessageHandler<T> {
@Override
public final void exec(ImContext ctx, NetUser netUser, T req) {
if (!netUser.isAccount()) {
throw new SoimException("NetUser is not account!" + netUser);
throw new SoimException("NetUser is not Account!" + netUser);
}
MessageLite res = handle(ctx, (Account) netUser, req);
if (res != null) {

30
im-entry/src/main/java/net/sopod/soim/entry/server/handler/MonitorMessageHandler.java

@ -0,0 +1,30 @@
package net.sopod.soim.entry.server.handler;
import com.google.protobuf.MessageLite;
import net.sopod.soim.common.dubbo.exception.SoimException;
import net.sopod.soim.entry.server.session.Account;
import net.sopod.soim.entry.server.session.Monitor;
import net.sopod.soim.entry.server.session.NetUser;
/**
* MonitorMessageHandler
*
* @author tmy
* @date 2022-06-11 14:41
*/
public abstract class MonitorMessageHandler<T> implements MessageHandler<T> {
@Override
public void exec(ImContext ctx, NetUser netUser, T req) {
if (!netUser.isMonitor()) {
throw new SoimException("NetUser is not Monitor!" + netUser);
}
MessageLite res = this.handle(ctx, (Monitor) netUser, req);
if (res != null) {
netUser.writeNow(ctx, res);
}
}
public abstract MessageLite handle(ImContext ctx, Monitor monitor, T req);
}

32
im-entry/src/main/java/net/sopod/soim/entry/server/session/Monitor.java

@ -0,0 +1,32 @@
package net.sopod.soim.entry.server.session;
import io.netty.channel.Channel;
/**
* Monitor
*
* @author tmy
* @date 2022-06-11 14:31
*/
public class Monitor extends NetUser {
private long lastActive;
public Monitor(Channel channel) {
super(channel);
}
@Override
public boolean isMonitor() {
return true;
}
public void setLastActive(long lastActive) {
this.lastActive = lastActive;
}
public long getLastActive() {
return lastActive;
}
}

14
im-entry/src/main/java/net/sopod/soim/entry/server/session/NetUser.java

@ -28,6 +28,10 @@ public class NetUser {
return false;
}
public boolean isMonitor() {
return false;
}
public boolean isActive() {
Channel chan = this.channel.get();
return chan != null && chan.isActive();
@ -47,6 +51,16 @@ public class NetUser {
}
}
/**
* 设置连接为 monitor
*/
public void upgradeMonitor(Monitor monitor) {
Channel chan = this.channel.get();
if (chan != null) {
chan.attr(NET_USER_KEY).set(monitor);
}
}
/**
* 客户端通过 dispatcher 消费
*/

2
im-entry/src/main/java/net/sopod/soim/entry/worker/WorkerGroup.java

@ -25,7 +25,7 @@ public class WorkerGroup {
}
WORKERS = new Worker[coreSize];
for (int i = 0; i < coreSize; i++) {
WORKERS[i] = new Worker("group-worker-" + i);
WORKERS[i] = new Worker("group-worker-%d" + i);
}
counter = new AtomicInteger(-1);
logger.info("worker group initialed, worker size {}", coreSize);

11
im-entry/src/main/resources/application.yml

@ -4,16 +4,11 @@ spring:
dubbo:
application:
name: ${spring.application.name}
registry:
address: nacos://124.222.131.236:3848
group: so-im
protocol:
name: dubbo
# 启动参数 -DDUBBO_IP_TO_REGISTRY=192.168.51.7 或环境变量指定服务注册ip
# host: 192.168.56.8 # 规则 NetUtil.isInvalidLocalHost,不能是127.x、localhost本机ip
# provider:
# protocol: injvm # 服务提供只供jvm内部使用, 不暴露在外面 (不启动dubbo provider服务)
port: 3009
port: 3012
consumer:
check: false
filter: pre_invoke_filter
@ -23,7 +18,3 @@ dubbo:
timeout: 2000
retries: 0
register: true
entry-server:
nacos-addr: 124.222.131.236:3848
port: 8089

27
im-launcher/pom.xml

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>so-im</artifactId>
<groupId>net.sopod</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>im-launcher</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions><!-- 去掉springboot默认logback日志配置 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

15
im-launcher/src/main/java/net/sopod/soim/launcher/LauncherService.java

@ -0,0 +1,15 @@
package net.sopod.soim.launcher;
import org.springframework.core.Ordered;
/**
* LauncherService
*
* @author tmy
* @date 2022-06-10 21:33
*/
public interface LauncherService extends Ordered {
void launcher();
}

34
im-launcher/src/main/java/net/sopod/soim/launcher/SoimApplication.java

@ -0,0 +1,34 @@
package net.sopod.soim.launcher;
import net.sopod.soim.launcher.util.PropsUtil;
import org.springframework.boot.SpringApplication;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
/**
* SoimApplication
*
* @author tmy
* @date 2022-06-10 19:48
*/
public class SoimApplication {
public static void run(Class<?> primarySource, String...args) {
buildApplicationProperties();
SpringApplication.run(primarySource, args);
}
private static void buildApplicationProperties() {
List<LauncherService> launcherServices = new ArrayList<>();
ServiceLoader.load(LauncherService.class).forEach(launcherServices::add);
launcherServices.stream().sorted(Comparator.comparing(LauncherService::getOrder))
.collect(Collectors.toList())
.forEach(LauncherService::launcher);
}
}

52
im-launcher/src/main/java/net/sopod/soim/launcher/service/PropertiesLauncherService.java

@ -0,0 +1,52 @@
package net.sopod.soim.launcher.service;
import net.sopod.soim.launcher.LauncherService;
import net.sopod.soim.launcher.util.PropsUtil;
import org.springframework.core.Ordered;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Properties;
/**
* PropertiesLauncherService
*
* @author tmy
* @date 2022-06-10 21:34
*/
public class PropertiesLauncherService implements LauncherService {
@Override
public void launcher() {
System.out.println("启动参数初始化...");
PathMatchingResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
Resource resource = resourceResolver.getResource("classpath:launcher.properties");
try {
InputStream launcherPropsIn = resource.getInputStream();
// InputStream launcherPropsIn = this.getClass().getResourceAsStream("launcher.properties");
InputStreamReader launcherPropsReader = new InputStreamReader(launcherPropsIn, StandardCharsets.UTF_8);
Properties properties = new Properties();
Properties sysProps = System.getProperties();
properties.load(launcherPropsReader);
// 设置 properties
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
String key = String.valueOf(entry.getKey());
String value = String.valueOf(entry.getValue());
PropsUtil.setProperty(sysProps, key, value);
}
} catch (IOException e) {
throw new IllegalStateException("启动参数初始化失败", e);
}
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}

21
im-launcher/src/main/java/net/sopod/soim/launcher/util/PropsUtil.java

@ -0,0 +1,21 @@
package net.sopod.soim.launcher.util;
import org.springframework.util.StringUtils;
import java.util.Properties;
/**
* PropsUtil
*
* @author tmy
* @date 2022-06-10 20:03
*/
public class PropsUtil {
public static void setProperty(Properties props, String key, String value) {
if (StringUtils.isEmpty(props.getProperty(key))) {
props.setProperty(key, value);
}
}
}

1
im-launcher/src/main/resources/META-INF/services/net.sopod.soim.launcher.LauncherService

@ -0,0 +1 @@
net.sopod.soim.launcher.service.PropertiesLauncherService

55
im-launcher/src/main/resources/launcher.properties

@ -0,0 +1,55 @@
# dubbo registry nacos
dubbo.registry.address=nacos://ip:port
dubbo.registry.group=so-im
# shardingsphere datasource (before DataSourceAutoConfiguration)
spring.shardingsphere.datasource.names=ds1
spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://ip:port/soim_db?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.ds1.username=username
spring.shardingsphere.datasource.ds1.password=password
spring.shardingsphere.datasource.ds1.minimumIdle=1
spring.shardingsphere.datasource.ds1.maximumPoolSize=4
spring.shardingsphere.datasource.ds1.connectionTimeout=2000
spring.shardingsphere.datasource.ds1.idleTimeout=300000
spring.shardingsphere.datasource.ds1.maxLifeTime=600000
spring.shardingsphere.datasource.ds1.keepaliveTime=30000
spring.shardingsphere.datasource.ds1.validateTimeout=2000
# spring datasource DataSourceAutoConfiguration
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.url=jdbc:mysql://ip:port/soim_db?serverTimezone=GMT%2B8
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.hikari.minimum-idle=1
spring.datasource.hikari.maximum-pool-size=8
spring.datasource.hikari.connection-timeout=2000
# 5分钟空闲关闭
spring.datasource.hikari.idle-timeout=300000
# 10分钟最大存活时间
spring.datasource.hikari.max-lifetime=600000
spring.datasource.hikari.validation-timeout=2000
# datasource.hikari.connection-init-sql: select 1
# 连接存活时间,小于maxLifetime, 最小30秒, 空闲30秒后移除连接测试通过再添加回池
spring.datasource.hikari.keepalive-time=30000
# rabbitmq
spring.rabbitmq.host=ip
spring.rabbitmq.port=port
spring.rabbitmq.username=username
spring.rabbitmq.password=password
# redis
redis.host=ip
redis.port=port
redis.password=password
redis.lettuce.pool.max-active=100
redis.lettuce.pool.max-idle=100
redis.lettuce.pool.max-wait=1000
redis.lettuce.pool.min-idle=2

103
im-service-api/im-entry-protocol/src/main/java/net/sopod/soim/data/msg/group/Group.java

@ -6944,16 +6944,6 @@ public final class Group {
// @@protoc_insertion_point(interface_extends:net.sopod.soim.data.msg.group.ReqGroupMessage)
com.google.protobuf.MessageOrBuilder {
/**
* <pre>
* 发送者uid
* </pre>
*
* <code>int64 sender = 1;</code>
* @return The sender.
*/
long getSender();
/**
* <pre>
* 群聊id
@ -7044,11 +7034,6 @@ public final class Group {
case 0:
done = true;
break;
case 8: {
sender_ = input.readInt64();
break;
}
case 16: {
gid_ = input.readInt64();
@ -7097,21 +7082,6 @@ public final class Group {
net.sopod.soim.data.msg.group.Group.ReqGroupMessage.class, net.sopod.soim.data.msg.group.Group.ReqGroupMessage.Builder.class);
}
public static final int SENDER_FIELD_NUMBER = 1;
private long sender_;
/**
* <pre>
* 发送者uid
* </pre>
*
* <code>int64 sender = 1;</code>
* @return The sender.
*/
@java.lang.Override
public long getSender() {
return sender_;
}
public static final int GID_FIELD_NUMBER = 2;
private long gid_;
/**
@ -7202,9 +7172,6 @@ public final class Group {
@java.lang.Override
public void writeTo(com.google.protobuf.CodedOutputStream output)
throws java.io.IOException {
if (sender_ != 0L) {
output.writeInt64(1, sender_);
}
if (gid_ != 0L) {
output.writeInt64(2, gid_);
}
@ -7223,10 +7190,6 @@ public final class Group {
if (size != -1) return size;
size = 0;
if (sender_ != 0L) {
size += com.google.protobuf.CodedOutputStream
.computeInt64Size(1, sender_);
}
if (gid_ != 0L) {
size += com.google.protobuf.CodedOutputStream
.computeInt64Size(2, gid_);
@ -7253,8 +7216,6 @@ public final class Group {
}
net.sopod.soim.data.msg.group.Group.ReqGroupMessage other = (net.sopod.soim.data.msg.group.Group.ReqGroupMessage) obj;
if (getSender()
!= other.getSender()) return false;
if (getGid()
!= other.getGid()) return false;
if (!getMessage()
@ -7272,9 +7233,6 @@ public final class Group {
}
int hash = 41;
hash = (19 * hash) + getDescriptor().hashCode();
hash = (37 * hash) + SENDER_FIELD_NUMBER;
hash = (53 * hash) + com.google.protobuf.Internal.hashLong(
getSender());
hash = (37 * hash) + GID_FIELD_NUMBER;
hash = (53 * hash) + com.google.protobuf.Internal.hashLong(
getGid());
@ -7420,8 +7378,6 @@ public final class Group {
@java.lang.Override
public Builder clear() {
super.clear();
sender_ = 0L;
gid_ = 0L;
message_ = "";
@ -7454,7 +7410,6 @@ public final class Group {
@java.lang.Override
public net.sopod.soim.data.msg.group.Group.ReqGroupMessage buildPartial() {
net.sopod.soim.data.msg.group.Group.ReqGroupMessage result = new net.sopod.soim.data.msg.group.Group.ReqGroupMessage(this);
result.sender_ = sender_;
result.gid_ = gid_;
result.message_ = message_;
result.time_ = time_;
@ -7506,9 +7461,6 @@ public final class Group {
public Builder mergeFrom(net.sopod.soim.data.msg.group.Group.ReqGroupMessage other) {
if (other == net.sopod.soim.data.msg.group.Group.ReqGroupMessage.getDefaultInstance()) return this;
if (other.getSender() != 0L) {
setSender(other.getSender());
}
if (other.getGid() != 0L) {
setGid(other.getGid());
}
@ -7548,49 +7500,6 @@ public final class Group {
return this;
}
private long sender_ ;
/**
* <pre>
* 发送者uid
* </pre>
*
* <code>int64 sender = 1;</code>
* @return The sender.
*/
@java.lang.Override
public long getSender() {
return sender_;
}
/**
* <pre>
* 发送者uid
* </pre>
*
* <code>int64 sender = 1;</code>
* @param value The sender to set.
* @return This builder for chaining.
*/
public Builder setSender(long value) {
sender_ = value;
onChanged();
return this;
}
/**
* <pre>
* 发送者uid
* </pre>
*
* <code>int64 sender = 1;</code>
* @return This builder for chaining.
*/
public Builder clearSender() {
sender_ = 0L;
onChanged();
return this;
}
private long gid_ ;
/**
* <pre>
@ -8696,11 +8605,11 @@ public final class Group {
"qJoinGroup\022\013\n\003gid\030\001 \001(\003\022\013\n\003uid\030\002 \001(\003\"\034\n\r" +
"ReqGroupUsers\022\013\n\003gid\030\001 \001(\003\"G\n\rResGroupUs" +
"ers\0226\n\005users\030\003 \003(\0132\'.net.sopod.soim.data" +
".msg.group.UserInfo\"M\n\017ReqGroupMessage\022\016" +
"\n\006sender\030\001 \001(\003\022\013\n\003gid\030\002 \001(\003\022\017\n\007message\030\003" +
" \001(\t\022\014\n\004time\030\004 \001(\003\"B\n\017ResGroupMessage\022\017\n" +
"\007success\030\001 \001(\010\022\017\n\007message\030\002 \001(\t\022\r\n\005msgId" +
"\030\003 \001(\003B\tB\005GroupP\000b\006proto3"
".msg.group.UserInfo\"=\n\017ReqGroupMessage\022\013" +
"\n\003gid\030\002 \001(\003\022\017\n\007message\030\003 \001(\t\022\014\n\004time\030\004 \001" +
"(\003\"B\n\017ResGroupMessage\022\017\n\007success\030\001 \001(\010\022\017" +
"\n\007message\030\002 \001(\t\022\r\n\005msgId\030\003 \001(\003B\tB\005GroupP" +
"\000b\006proto3"
};
descriptor = com.google.protobuf.Descriptors.FileDescriptor
.internalBuildGeneratedFileFrom(descriptorData,
@ -8771,7 +8680,7 @@ public final class Group {
internal_static_net_sopod_soim_data_msg_group_ReqGroupMessage_fieldAccessorTable = new
com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
internal_static_net_sopod_soim_data_msg_group_ReqGroupMessage_descriptor,
new java.lang.String[] { "Sender", "Gid", "Message", "Time", });
new java.lang.String[] { "Gid", "Message", "Time", });
internal_static_net_sopod_soim_data_msg_group_ResGroupMessage_descriptor =
getDescriptor().getMessageTypes().get(11);
internal_static_net_sopod_soim_data_msg_group_ResGroupMessage_fieldAccessorTable = new

2195
im-service-api/im-entry-protocol/src/main/java/net/sopod/soim/data/msg/monitor/EntryMonitor.java

File diff suppressed because it is too large Load Diff

29
im-service-api/im-entry-protocol/src/main/resources/proto/monitor/EntryMonitor.proto

@ -0,0 +1,29 @@
syntax="proto3";
option java_multiple_files = false;
option java_outer_classname = "EntryMonitor";
package net.sopod.soim.data.msg.monitor;
// im-entry
message ReqMonitorAuth {
// string host = 1;
string security = 2;
}
message ResMonitorAuth {
bool success = 1;
}
message ReqEntryStatus {
}
message ResEntryStatus {
//
int64 statusTime = 5;
//
int32 connections = 6;
// TODO...其他信息
}

60
im-service-api/im-entry-protocol/src/main/resources/protoSerialNoTable.txt

@ -1,30 +1,34 @@
net.sopod.soim.data.msg.group.Group$ReqUserGroups=10000
net.sopod.soim.data.msg.group.Group$GroupInfo=10001
net.sopod.soim.data.msg.user.UserMsg$UserInfo=10000
net.sopod.soim.data.msg.group.Group$ReqGroupUsers=10001
net.sopod.soim.data.msg.group.Group$ResSearchGroup=10002
net.sopod.soim.data.msg.hello.HelloPB$Hello=10003
net.sopod.soim.data.msg.user.Friend$ReqAddFriend=10004
net.sopod.soim.data.msg.auth.Auth$ResTokenAuth=10005
net.sopod.soim.data.msg.chat.Chat$TextChat=10006
net.sopod.soim.data.msg.user.AccountSearch$ReqAccountSearch=10007
net.sopod.soim.data.msg.net.HeartBeat$Pong=10008
net.sopod.soim.data.msg.auth.Auth$ReqTokenAuth=10009
net.sopod.soim.data.msg.group.Group$ReqGroupUsers=10010
net.sopod.soim.data.msg.user.UserGroup$ResOnlineUserList=10011
net.sopod.soim.data.msg.user.AccountSearch$ResAccountSearch=10012
net.sopod.soim.data.msg.group.Group$ReqCreateGroup=10013
net.sopod.soim.data.msg.user.Friend$ResFriendList=10014
net.sopod.soim.data.msg.group.Group$ReqSearchGroup=10015
net.sopod.soim.data.msg.group.Group$ReqGroupMessage=10016
net.sopod.soim.data.msg.group.Group$ResGroupUsers=10017
net.sopod.soim.data.msg.hello.HelloPB$World=10018
net.sopod.soim.data.msg.group.Group$ReqJoinGroup=10019
net.sopod.soim.data.msg.user.UserGroup$ReqOnlineUserList=10020
net.sopod.soim.data.msg.group.Group$UserInfo=10021
net.sopod.soim.data.msg.group.Group$ResGroupMessage=10022
net.sopod.soim.data.msg.user.UserMsg$UserInfo=10023
net.sopod.soim.data.msg.group.Group$ResUserGroups=10024
net.sopod.soim.data.msg.user.Friend$ResAddFriend=10025
net.sopod.soim.data.msg.net.HeartBeat$Ping=10026
net.sopod.soim.data.msg.task.Tasks$NetUserDelayCloseTask=10027
net.sopod.soim.data.msg.user.Friend$ReqFriendList=10028
net.sopod.soim.data.msg.common.Res$ResState=10029
net.sopod.soim.data.msg.user.UserGroup$ReqOnlineUserList=10004
net.sopod.soim.data.msg.group.Group$ResUserGroups=10005
net.sopod.soim.data.msg.user.Friend$ResFriendList=10006
net.sopod.soim.data.msg.task.Tasks$NetUserDelayCloseTask=10007
net.sopod.soim.data.msg.group.Group$ReqSearchGroup=10008
net.sopod.soim.data.msg.net.HeartBeat$Pong=10009
net.sopod.soim.data.msg.chat.Chat$TextChat=10010
net.sopod.soim.data.msg.group.Group$ReqGroupMessage=10011
net.sopod.soim.data.msg.user.UserGroup$ResOnlineUserList=10012
net.sopod.soim.data.msg.common.Res$ResState=10013
net.sopod.soim.data.msg.user.AccountSearch$ResAccountSearch=10014
net.sopod.soim.data.msg.group.Group$ReqJoinGroup=10015
net.sopod.soim.data.msg.monitor.EntryMonitor$ResEntryStatus=10016
net.sopod.soim.data.msg.hello.HelloPB$World=10017
net.sopod.soim.data.msg.user.Friend$ReqFriendList=10018
net.sopod.soim.data.msg.net.HeartBeat$Ping=10019
net.sopod.soim.data.msg.monitor.EntryMonitor$ReqEntryStatus=10020
net.sopod.soim.data.msg.user.AccountSearch$ReqAccountSearch=10021
net.sopod.soim.data.msg.user.Friend$ResAddFriend=10022
net.sopod.soim.data.msg.group.Group$ResGroupUsers=10023
net.sopod.soim.data.msg.group.Group$ReqCreateGroup=10024
net.sopod.soim.data.msg.group.Group$ResGroupMessage=10025
net.sopod.soim.data.msg.monitor.EntryMonitor$ReqMonitorAuth=10026
net.sopod.soim.data.msg.group.Group$ReqUserGroups=10027
net.sopod.soim.data.msg.group.Group$UserInfo=10028
net.sopod.soim.data.msg.user.Friend$ReqAddFriend=10029
net.sopod.soim.data.msg.auth.Auth$ReqTokenAuth=10030
net.sopod.soim.data.msg.monitor.EntryMonitor$ResMonitorAuth=10031
net.sopod.soim.data.msg.group.Group$GroupInfo=10032
net.sopod.soim.data.msg.auth.Auth$ResTokenAuth=10033

5
im-service/im-logic-group/pom.xml

@ -12,6 +12,11 @@
<artifactId>im-logic-group</artifactId>
<dependencies>
<dependency>
<groupId>net.sopod</groupId>
<artifactId>im-launcher</artifactId>
<version>${soim.version}</version>
</dependency>
<dependency>
<groupId>net.sopod</groupId>
<artifactId>im-logic-group-api</artifactId>

4
im-service/im-logic-group/src/main/java/net/sopod/soim/logic/group/LogicGroupApplication.java

@ -1,7 +1,7 @@
package net.sopod.soim.logic.group;
import net.sopod.soim.launcher.SoimApplication;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
@ -15,7 +15,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
public class LogicGroupApplication {
public static void main(String[] args) {
SpringApplication.run(LogicGroupApplication.class);
SoimApplication.run(LogicGroupApplication.class);
}
}

3
im-service/im-logic-group/src/main/resources/application.yml

@ -5,9 +5,6 @@ spring:
dubbo:
application:
name: ${spring.application.name}
registry:
address: nacos://124.222.131.236:3848
group: so-im
protocol:
port: 3013
consumer:

5
im-service/im-logic-message/pom.xml

@ -12,6 +12,11 @@
<artifactId>im-logic-message</artifactId>
<dependencies>
<dependency>
<groupId>net.sopod</groupId>
<artifactId>im-launcher</artifactId>
<version>${soim.version}</version>
</dependency>
<dependency>
<groupId>net.sopod</groupId>
<artifactId>im-common</artifactId>

4
im-service/im-logic-message/src/main/java/net/sopod/soim/logic/message/LogicMessageApplication.java

@ -1,7 +1,7 @@
package net.sopod.soim.logic.message;
import net.sopod.soim.launcher.SoimApplication;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
@ -15,7 +15,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
public class LogicMessageApplication {
public static void main(String[] args) {
SpringApplication.run(LogicMessageApplication.class);
SoimApplication.run(LogicMessageApplication.class);
}
}

18
im-service/im-logic-message/src/main/java/net/sopod/soim/logic/message/service/ImUserChatServiceImpl.java

@ -56,15 +56,15 @@ public class ImUserChatServiceImpl implements ImUserChatService {
@Override
public CompletableFuture<String> userMessage(UserMessage msg) {
if (msg.getReceiverUid() == null
|| Objects.equals(msg.getReceiverUid(), 0L)) {
ImUser receiverUser = userDas.getNormalUserByAccount(msg.getReceiverName());
if (receiverUser == null) {
// TODO receiverUid 由客户端传递
throw new LogicException("聊天对象不存在");
}
msg.setReceiverUid(receiverUser.getId());
}
// if (msg.getReceiverUid() == null
// || Objects.equals(msg.getReceiverUid(), 0L)) {
// ImUser receiverUser = userDas.getNormalUserByAccount(msg.getReceiverName());
// if (receiverUser == null) {
// throw new LogicException("聊天对象不存在");
// }
// msg.setReceiverUid(receiverUser.getId());
// }
// receiverUid 由客户端传递
// 查询好友关系
Long relationId = friendDas.getRelationId(msg.getSenderUid(), msg.getReceiverUid());
// 不是好友

8
im-service/im-logic-message/src/main/resources/application.yml

@ -1,18 +1,10 @@
spring:
application:
name: im-logic-message
rabbitmq:
host: 124.222.131.236
port: 3672
username: soim
password: sopod@rabbit#
dubbo:
application:
name: ${spring.application.name}
registry:
address: nacos://124.222.131.236:3848
group: so-im
protocol:
port: 3007
consumer:

5
im-service/im-logic-user/pom.xml

@ -12,6 +12,11 @@
<artifactId>im-logic-user</artifactId>
<dependencies>
<dependency>
<groupId>net.sopod</groupId>
<artifactId>im-launcher</artifactId>
<version>${soim.version}</version>
</dependency>
<dependency>
<groupId>net.sopod</groupId>
<artifactId>im-logic-user-api</artifactId>

4
im-service/im-logic-user/src/main/java/net/sopod/soim/logic/user/LogicUserApplication.java

@ -1,7 +1,7 @@
package net.sopod.soim.logic.user;
import net.sopod.soim.launcher.SoimApplication;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@ -17,7 +17,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
public class LogicUserApplication {
public static void main(String[] args) {
SpringApplication.run(LogicUserApplication.class, args);
SoimApplication.run(LogicUserApplication.class, args);
}
}

3
im-service/im-logic-user/src/main/resources/application.yml

@ -5,9 +5,6 @@ spring:
dubbo:
application:
name: ${spring.application.name}
registry:
address: nacos://124.222.131.236:3848
group: so-im
protocol:
port: 3004
consumer:

21
im-service/im-router/pom.xml

@ -12,6 +12,11 @@
<artifactId>im-router</artifactId>
<dependencies>
<dependency>
<groupId>net.sopod</groupId>
<artifactId>im-launcher</artifactId>
<version>${soim.version}</version>
</dependency>
<dependency>
<groupId>net.sopod</groupId>
<artifactId>im-router-api</artifactId>
@ -85,22 +90,14 @@
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.msgpack</groupId>-->
<!-- <artifactId>jackson-dataformat-msgpack</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.msgpack</groupId>
<artifactId>jackson-dataformat-msgpack</artifactId>
</dependency>
<dependency>
<groupId>org.xerial.snappy</groupId>
<artifactId>snappy-java</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-amqp</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.mybatis</groupId>-->
<!-- <artifactId>mybatis</artifactId>-->
<!-- </dependency>-->
</dependencies>
</project>

4
im-service/im-router/src/main/java/net/sopod/soim/router/RouterApplication.java

@ -1,7 +1,7 @@
package net.sopod.soim.router;
import net.sopod.soim.launcher.SoimApplication;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
@ -15,7 +15,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
public class RouterApplication {
public static void main(String[] args) {
SpringApplication.run(RouterApplication.class, args);
SoimApplication.run(RouterApplication.class, args);
}
}

11
im-service/im-router/src/main/java/net/sopod/soim/router/datasync/server/SyncServer.java

@ -46,13 +46,12 @@ public class SyncServer {
.childHandler(new ChannelInitializer<>() {
@Override
protected void initChannel(Channel channel) {
LogLevel logLevel = logger.isDebugEnabled() ? LogLevel.DEBUG
: logger.isInfoEnabled() ? LogLevel.INFO
: logger.isWarnEnabled() ? LogLevel.WARN
: logger.isErrorEnabled() ? LogLevel.ERROR : LogLevel.INFO;
// LogLevel logLevel = logger.isDebugEnabled() ? LogLevel.DEBUG
// : logger.isInfoEnabled() ? LogLevel.INFO
// : logger.isWarnEnabled() ? LogLevel.WARN
// : logger.isErrorEnabled() ? LogLevel.ERROR : LogLevel.INFO;
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new LoggingHandler(logLevel))
pipeline.addLast(new LoggingHandler(LogLevel.WARN))
// 接收字节可能会分段到达,添加帧编解码器
.addLast(new Varint32FrameCodec())
.addLast(new SyncCmdCodec())

20
im-service/im-router/src/main/resources/application.yml

@ -1,31 +1,13 @@
spring:
application:
name: im-router
redis:
host: 124.222.131.236
port: 3379
password: sopod@redis#
lettuce:
pool:
max-active: 100
max-idle: 100
max-wait: 1000
min-idle: 2
rabbitmq:
host: 124.222.131.236
port: 3672
username: soim
password: sopod@rabbit#
dubbo:
application:
name: ${spring.application.name}
registry:
address: nacos://124.222.131.236:3848
group: so-im
protocol:
name: dubbo
port: 3035
port: 3036
# serialization: protobuf
consumer:
check: false

5
im-service/im-segment-id/pom.xml

@ -12,6 +12,11 @@
<artifactId>im-segment-id</artifactId>
<dependencies>
<dependency>
<groupId>net.sopod</groupId>
<artifactId>im-launcher</artifactId>
<version>${soim.version}</version>
</dependency>
<dependency>
<groupId>net.sopod</groupId>
<artifactId>im-segment-id-api</artifactId>

4
im-service/im-segment-id/src/main/java/net/sopod/soim/logic/segmentid/SegmentIdApplication.java

@ -1,8 +1,8 @@
package net.sopod.soim.logic.segmentid;
import net.sopod.soim.launcher.SoimApplication;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
@ -17,7 +17,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
public class SegmentIdApplication {
public static void main(String[] args) {
SpringApplication.run(SegmentIdApplication.class);
SoimApplication.run(SegmentIdApplication.class);
}
}

21
im-service/im-segment-id/src/main/resources/application.yml

@ -1,31 +1,10 @@
spring:
application:
name: segment-id
datasource:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://cd-cdb-mrz9fw80.sql.tencentcdb.com:61843/soim_db?serverTimezone=GMT%2B8
username: root
password: sopod@2347#
hikari:
minimum-idle: 1
maximum-pool-size: 8
connection-timeout: 2000
idle-timeout: 300000 # 5分钟空闲关闭
max-lifetime: 600000 # 10分钟最大存活时间
validation-timeout: 2000
connection-init-sql: select 1
keepalive-time: 30000 # 连接存活时间,小于maxLifetime, 最小30秒, 空闲30秒后移除连接测试通过再添加回池
redis:
host: 124.222.131.236
port: 3379
password: sopod@redis#
dubbo:
application:
name: ${spring.application.name}
registry:
address: nacos://124.222.131.236:3848
group: so-im
protocol:
port: 3001

60
pom.xml

@ -17,6 +17,7 @@
<module>im-service</module>
<module>im-service-api</module>
<module>im-entry-http</module>
<module>im-launcher</module>
</modules>
<properties>
@ -27,6 +28,8 @@
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.compilerVersion>11</maven.compiler.compilerVersion>
<docker.registry.name.prefix>itbaba-docker.pkg.coding.net/repo/docker</docker.registry.name.prefix>
<jackson.version>2.12.5</jackson.version>
<logback.version>1.2.11</logback.version>
<log4j.version>2.17.2</log4j.version>
@ -300,32 +303,11 @@
<build>
<plugins>
<!-- maven 打包集成插件
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<finalName>${project.build.finalName}</finalName>
<archive>
<manifest>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin> -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
@ -336,6 +318,34 @@
<encoding>utf-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.12</version>
<dependencies>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
<configuration>
<!--上下文路径配置,此处设置为项目根路径-->
<contextDirectory>${project.basedir}</contextDirectory>
<!--Dockerfile 路径-->
<dockerfile>im-entry/Dockerfile</dockerfile>
<!--使用 maven 配置文件中的账号密码认证-->
<useMavenSettingsForAuth>true</useMavenSettingsForAuth>
<!--镜像名-->
<repository>${docker.registry.name.prefix}/${project.artifactId}</repository>
<!--生成的镜像标签-->
<tag>${project.version}</tag>
<buildArgs>
<!--Dockerfile 构建时 ARG 参数-->
<JAR_FILE>${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>

Loading…
Cancel
Save