# Yi-IM
**Repository Path**: ghycn/Yi-IM
## Basic Information
- **Project Name**: Yi-IM
- **Description**: 即插即拔的IM聊天插件,基于netty-socketIO实现, 支持单机,集群模式。支持动消息持久化。
- **Primary Language**: Java
- **License**: GPL-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 5
- **Created**: 2022-07-26
- **Last Updated**: 2022-07-26
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
## 仓库地址 Gitee : https://gitee.com/cnm_1314/Yi-IM
## 功能特点
```
1. starter插件式,即插即拔
2. 基于netty-socketIO实现, 有良好的高并发抗压能力
3. 实现socket动态认证监听器
4. 支持单机版本(内存模式),集群版本(redis模式)
5. 消息自动存储,也可自定义重写
6. 支持web api方式通讯, 只需添加依赖im-api
7. 默认单机模式,集群模式只需添加依赖im-redis
8. 支持频道分组监听,添加@ChannelListener("group")进行注册
9. 支持注解形式消息处理器,可直接扩展
```
## 使用说明
### 单机模式
#### 1. 引入starter
```
com.itic
im-spring-boot-starter
1.0
im:
socket:
host: 127.0.0.1
port: 9901
# roomId: default
# channel: default
# namespace: /default
# 消息发送失败时,重试次数
retry: 3
# 默认是否插入消息到数据库中, 如果自己实现ImMsgHandler,该参数没用
insert-db: true
执行: t_im_msg.sql
```
#### 2. 继承DefaultEventHandler,实现自定义事件端点
```
@Component
public class ImEventHandler extends DefaultEventHandler {
/**
* 消息推送 自定义事件
*
* @param client
* @param request
* @param data
*/
@OnEvent("自定义")
public void onEvent2(SocketIOClient client, AckRequest request, SoMap data) {
}
}
```
#### 3. 实现自定义消息体处理, 如果不实现则使用DefaultImMsgHandler处理
```
/**
* 图片消息处理
* date: 2022/1/17 21:16
* author: yangwl
*/
@ImMsgType(value = MsgType.PICTURE)
@Component
public class ImPictureMsgHandler implements ImMsgHandler {
@Override
public void handler(Object o) {
// 以下可自定义
// 持久化数据库
if(null == o) {
throw new ImException("msg content is null");
}
SoMap data = (SoMap) o;
ImMsg imMsg = new ImMsg();
imMsg.setChatType(data.getString("groupType"));
imMsg.setSendUid(data.getString("fromUserId"));
imMsg.setReceiveUid(data.getString("toUserId"));
imMsg.setGroupId(data.getString("toGroupId"));
imMsg.setMsgType(data.getString("msgType"));
imMsg.setMsg(data.getString("msg"));
ImManager.getImMsgRepository().saveImMsg(imMsg);
}
}
```
#### 4. 各种默认监听器, 不实现则用默认的
```
/**
* 认证监听
* @author wanli.yang
* @version 1.0
* @date 2022/1/13 9:55
*/
@Component
public class ImAuthorizationListener implements AuthorizationListener {
@Override
public boolean isAuthorized(HandshakeData data) {
// 实现自己得监听逻辑
return true;
}
}
DefaultConnectListener 连接监听器
DefaultDisConnectListener 断开连接监听器
DefaultExceptionListener 异常监听器
DefaultPingListener Ping监听器
DefaultRegisterListener 注册监听器
```
#### 5. 启动
```
/**
* SocketIOServer启动器
* @author wanli.yang
*/
@Component
public class SocketServerRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(SocketServerRunner.class);
@Override
public void run(String... args) {
// 启动
ImManager.start();
logger.warn("IM实时通讯系统,启动成功.");
}
@PreDestroy
public void stop() {
ImManager.stop();
}
}
```
#### 6. 前端
```
im.js 中
// new 则开启连接
ws = new IM_SOCKET(function (clientId) {
// 注册成功回调
})
// 绑定事件,要和服务端定义的一样
ws.on(IM_EVENT.SEND_MSG_EVENT, function (data) {
console.log(data)
});
// 调用服务端某个监听事件端点
ws.emit(IM_EVENT.REGISTER_EVENT, {clientId: me.config.clientId, ......}, function(arg1, arg2) {
// 回调
});
// 客户端加入指定房间
ws.joinRoom(roomId, function(r) {
// 回调
console.log(r)
})
```
### 集群模式
#### 1. 引入starter
```
com.itic
im-spring-boot-starter
1.0
com.itic
im-redis
1.0
```
#### 2. 初始化房间与频道绑定
```
// 创建房间与频道对应关系, 根据实际业务进行绑定, 我这里根据群聊组
groupInfos.stream().forEach(e -> {
if( 1 == e.getGroupType()) {
RedisImManager.createChannel("room-"+e.getGroupId(), "group");
} else {
RedisImManager.createChannel("room-"+e.getGroupId(), "person");
}
});
```
#### 3. 自定义频道监听器 (如果不实现则使用默认频道监听器RedisSubMessageListener)
```
/**
* 自定义redis message监听器 群聊监听器
* @author wanli.yang
* @version 1.0
* @date 2022/1/13 18:22
*/
@Component
@ChannelListener("group")
public class ImMessageListener implements MessageListener {
private static final Logger logger = LoggerFactory.getLogger(ImMessageListener.class);
@Autowired
private ImRedisTemplateUtil imRedisTemplateUtil;
@Override
public void onMessage(Message message, byte[] pattern) {
SoMap map = JSONUtil.toBean((String) imRedisTemplateUtil.valueDeserialize(message.getBody()), SoMap.class);
BroadcastOperations bo = ImManager.defaultNamespace().getRoomOperations(map.getString("roomId"));
BroadcastAckCallback broadcastAckCallback = new BroadcastAckCallback(String.class) {
@Override
public void onClientSuccess(SocketIOClient client, String result) {
// logger.info("客户端{}发送成功,返回值={}", client.getSessionId(), result);
}
@Override
public void onClientTimeout(SocketIOClient client) {
logger.info("客户端{}, 发送超时,第{}次重试...", client.getSessionId(), 1);
ImManager.getImTemplate().retry(client, map, 1);
}
};
String excludedClientId = map.getString("excludedClientId");
if(StrUtil.isNotEmpty(excludedClientId)) {
Online online = (Online) ImManager.getDao().getOnline(excludedClientId);
SocketIOClient client = ImManager.defaultNamespace().getClient(online.getSessionId());
bo.sendEvent(map.getString("eventName"), map, client, broadcastAckCallback);
} else {
bo.sendEvent(map.getString("eventName"), map, broadcastAckCallback);
}
}
}
```
#### 4. 其他用法和单机版本就没区别了
```
1. 集群模式不通之处在于加入的redis,使用redis的发布/订阅模式,服务端收到消息,向redis频道pub消息, listener监听频道实现自动获取消息
2. 如果想在广播消息后做一些后置处理,可以实现PersistenceHandler接口,实现after方法
```
### WEB API调用
```
某些情况发送消息不能使用
// 调用服务端某个监听事件端点
ws.emit(IM_EVENT.REGISTER_EVENT, {clientId: me.config.clientId, ......}, function(arg1, arg2) {
// 回调
});
那么提供web api模式调用服务端事件,引入im-api模块, 目前只有一个方法
com.itic
im-api
1.0
/**
* 实时通讯API
* @author wanli.yang
* @version 1.0
* @date 2022/1/13 13:36
*/
@Api(tags = "实时通讯")
@RestController
@RequestMapping("/im")
public class ImController {
@ApiOperation("发送消息")
@PostMapping("/sendMsg")
public R sendMsg(SoMap map) {
ImManager.getImTemplate().handler(map);
return R.ok();
}
}
```
## 目前只有这些基础功能, 其他开发中............
## 更新
2022-01-13
```
1. 创建im-starter
2. ImConfig配置
3. 重写 SocketIOServer 支持动态添加认证监听
4. 添加SoMap
5. 添加ImTemplate 模板方法消息通讯, 支持直连模式,redis模式
6. 添加Dao,数据持久化,支持内存模式 redis模式
7. im-api 实现endpoint 公共api
8. 动态添加频道
9. 实现redis模式,一个房间对应一个频道,提高并发能力
```
2022-01-14
```
1. 扩展DefaultEventHandler 全局事件监听
```
2022-01-17
```
1. 添加redisMessageListener注解监听
如何绑定一个频道?
1. @ChannelListener("group")声明监听类
2. RedisImManager.createChannel("room-"+e.getGroupId(), "group"); 指定要绑定的频道
3. 如果不执行2, 那么底层会用默认的监听 @ChannelListener(IMConstants.IM_DEFAULT_CHANNEL)
2. 自定义注解消息类型处理起
```
##
QQ:[597392641]
##
技术交流QQ群:[570968411]