1. 引入pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>2.7.10</version>
</dependency>
2. 新建websocket配置文件
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
3. 新建websocket常量配置
public class WebsocketConst {
/**
* 消息类型 heartcheck
*/
public static final String CMD_CHECK = "heartcheck";
}
4. 编写websocket代码
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
@Slf4j
@ServerEndpoint("/websocket/{userId}")
public class WebSocketServer {
/**线程安全Map*/
private static ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<>();
//==========【websocket接受、推送消息等方法 —— 具体服务节点推送ws消息】=====================================================
@OnOpen
public void onOpen(Session session, @PathParam(value = "userId") String userId) {
try {
sessionPool.put(userId, session);
log.info("【系统 WebSocket】有新的连接,总数为:" + sessionPool.size());
} catch (Exception e) {
}
}
@OnClose
public void onClose(@PathParam("userId") String userId) {
try {
sessionPool.remove(userId);
log.info("【系统 WebSocket】连接断开,总数为:" + sessionPool.size());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* ws推送消息
*
* @param userId
* @param message
*/
public void pushMessage(String userId, String message) {
for (Map.Entry<String, Session> item : sessionPool.entrySet()) {
//userId key值= {用户id + "_"+ 登录token的md5串}
if (item.getKey().contains(userId)) {
Session session = item.getValue();
try {
synchronized (session){
log.info("【系统 WebSocket】推送单人消息:" + message);
session.getBasicRemote().sendText(message);
}
} catch (Exception e) {
log.error(e.getMessage(),e);
}
}
}
}
/**
* ws遍历群发消息
*/
public void pushMessage(String message) {
try {
for (Map.Entry<String, Session> item : sessionPool.entrySet()) {
try {
item.getValue().getAsyncRemote().sendText(message);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
log.info("【系统 WebSocket】广播消息:" + message);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
/**
* ws接受客户端消息
*/
@OnMessage
public void onMessage(String message, @PathParam(value = "userId") String userId) {
if(!"ping".equals(message) && !WebsocketConst.CMD_CHECK.equals(message)){
log.info("【系统 WebSocket】收到客户端消息:" + message);
}else{
log.debug("【系统 WebSocket】收到客户端消息:" + message);
}
}
/**
* 配置错误信息处理
*
* @param session
* @param t
*/
@OnError
public void onError(Session session, Throwable t) {
log.warn("【系统 WebSocket】消息出现错误");
t.printStackTrace();
}
//==========【系统 WebSocket接受、推送消息等方法 —— 具体服务节点推送ws消息】=================================================
}
5. ws连接测试
6. wss连接配置
-
生成ssl证书
openssl req -newkey rsa:2048 -nodes -keyout server.key -x509 -days 3650 -out server.crt
-
nginx配置
server { listen 8443 ssl; server_name localhost; ssl_certificate /etc/nginx/conf.d/certs/server.crt; ssl_certificate_key /etc/nginx/conf.d/certs/server.key; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; root html; charset 'utf-8'; location / { root /dsp/app/dsp-web/dist/; #你项目在系统中的路径 try_files $uri $uri/ /index.html; index index.html index.htm; } # 拦截API location ^~ /prod-api { proxy_pass http://dsp-gateway:9999/; proxy_redirect off; rewrite /prod-api/(.*)$ /$1 break; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 拦截websocket location /websocket/ { proxy_pass http://dsp-gateway:9999/; proxy_redirect off; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host:$server_port; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
-
wss连接测试
注意:使用postman测试wss时,可能会报证书验证失败的问题,如下:
可能是SSL认证拦截了请求,可以在postman的 设置 中将 SSL certificate verification关闭。