一、websocket实现讨论组即时聊天的功能
1、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
2、写websocket配置类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* WebSocket 配置类
* @Author Niki_Ya
* @Date 2022/3/28 14:24
*/
@Configuration
public class WebSocketConfig {
/**
* 用于扫描和注册所有携带ServerEndPoint注解的实例 若部署到外部容器 则无需提供此类。
*
* @return org.springframework.web.socket.server.standard.ServerEndpointExporter
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
/**
* 在websocketServer服务注入讨论组服务接口,即时通讯的逻辑层
*
* @param dmtDiscussionService dmt讨论服务
*/
@Autowired
public void setMessageService(DmtDiscussionService dmtDiscussionService){
WebSocketServer.dmtDiscussionService = dmtDiscussionService;
}
}
3、websocket服务类实现
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* @Description
* @Author Niki_Ya
* @Date 2022/3/29 10:55
*/
@Slf4j
@ServerEndpoint(value = "/dmt/discussion/websocket")
@Component
@SuppressWarnings("all")
public class WebSocketServer {
/**
* 全部在线会话 PS: 基于场景考虑 这里使用线程安全的Map存储会话对象。
*/
private static final Map<String, List<Client>> ONLINE_SESSIONS = new ConcurrentHashMap<>();
//聊天逻辑层service
public static DmtDiscussionService dmtDiscussionService;
// 讨论组id
private String discussionId;
// 用户信息和session保存
private Client client;
/**
* 连接建立成功调用的方法,初始化
* 当客户端打开连接:1.添加会话对象
*/
@OnOpen
public void onOpen(Session session) {
//初始化数据
String[] split = session.getQueryString().split("&");
HashMap<String, String> map = new HashMap<>();
for (String s : split) {
String[] strings = s.split("=");
map.put(strings[0], strings[1]);
}
this.discussionId = map.get("discussionId");
log.info("用户 {} 打开了{} 讨论组的WebSocket连接", map.get("staffNo"), discussionId);
this.client = Client.builder()
.staffId(map.get("staffId"))
.staffNo(map.get("staffNo"))
.staffName(map.get("staffName"))
.session(session)
.build();
if (ONLINE_SESSIONS.containsKey(discussionId)) {
List<Client> clients = ONLINE_SESSIONS.get(discussionId);
clients.add(client);
ONLINE_SESSIONS.put(discussionId, clients);
} else {
List<Client> clients = new ArrayList<>();
clients.add(client);
ONLINE_SESSIONS.put(discussionId, clients);
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
log.info("用户 {} 关闭了 {} 的WebSocket连接", client.getStaffNo(), discussionId);
// 更新最后阅读时间
DmtDiscussionDTO.UpdateLastReadTimeDTO dto = new DmtDiscussionDTO.UpdateLastReadTimeDTO();
dto.setDiscussionId(discussionId);
dto.setTime(new Date());
dto.setStaffNo(client.getStaffNo());
dmtDiscussionService.updateLastReadTime(dto);
//将当前的session删除
List<Client> clients = ONLINE_SESSIONS.get(discussionId);
clients.remove(client);
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message) {
Long createTime = System.currentTimeMillis();
//联调:从客户端传过来的数据是json数据,所以这里使用jackson进行转换为chatMsg对象 todo
// DmtDiscussionDTO.Message message = JSON.parseObject(message, DmtDiscussionDTO.Message.class);
//对chatMsg进行装箱
DmtDiscussionDTO.MessageBaseDTO chatMsg = DmtDiscussionDTO.MessageBaseDTO.builder()
.sendFromStaffId(client.getStaffId())
.sendFromStaffNo(client.getStaffNo())
.sendFromStaffName(client.getStaffName())
.sendFromTime(createTime)
.discussionId(discussionId)
.content(message) // todo json数据
.type(MessageType.TEXT.getCode()) // todo 先默认为0
.build();
//声明一个map,封装直接发送信息数据返回前端
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("staffName", chatMsg.getSendFromStaffName());
resultMap.put("content", chatMsg.getContent());
resultMap.put("sendTime", chatMsg.getSendFromTime());
resultMap.put("type", chatMsg.getType());
JSONObject json = new JSONObject(resultMap);
//发送给接收者
List<Client> clients = ONLINE_SESSIONS.get(discussionId);
clients.forEach(e -> {
//异步发送消息.
e.getSession().getAsyncRemote().sendText(json.toString());
});
//保存聊天记录信息
dmtDiscussionService.saveMessage(chatMsg);
}
/**
* 发生错误时调用
*/
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
log.error("用户 {} 在{} 讨论组的通信发生异常 {}", client.getStaffNo(),discussionId, error.getMessage());
throw new BaseException(error.getMessage());
}
}
4、测试(测试网址:websocket在线测试)
测试地址示例:ws://本地IP地址:端口号/dmt/discussion/websocket?discussionId=1507261309312798722&staffId=1&staffNo=ceshi001&staffName=ceshi001
?
?二、通过网关转发webSocket
1、引入依赖(不确定需不需要)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
</exclusions>
</dependency>
2、配置网关路由
spring.cloud.gateway.routes[0].id = notice-websocket
spring.cloud.gateway.routes[0].uri = lb:ws://notice-service
spring.cloud.gateway.routes[0].predicates[0] = Path=/notice/websocket/dmt/**
spring.cloud.gateway.routes[0].filters[0] = StripPrefix=2
3、测试?(测试网址:websocket在线测试)
由于我的网关一定需要传token和timestamp才可以进入,大家可以按实际要求改地址
测试地址示例:ws://本地IP地址:端口号/notice/websocket/dmt/discussion/websocket?discussionId=1507261309312798722&staffId=1481884657660043266&staffNo=cshi019&staffName=cshi019&token=token内容&timeStamp=时间戳毫秒
?
?
|