业务背景
某些业务场景:需要定时返回前端大量数据,这时候就需要WebSocket长连接
整合webSocket
导入依赖pom.xml
<!--日志工具类-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.5.0</version>
</dependency>
<!--日志 需要移除依赖-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
<optional>true</optional>
</dependency>
<!--webSocket-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
配置WebSocketConfig
import com.ruoyi.system.service.IShieldAuthService;
import org.apache.ibatis.javassist.tools.web.Webserver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* 开启WebSocket支持
* @author zhengkai.blog.csdn.net
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
编写WebSocketServer
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.system.domain.ProjectZone;
import com.ruoyi.system.domain.ShieldAuth;
import com.ruoyi.system.service.IProjectZoneService;
import com.ruoyi.system.service.IShieldAuthService;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
/**
* @author zhengkai.blog.csdn.net
*/
@ServerEndpoint("/webSocket/{userId}")
@Component
public class WebSocketServer {
static Log log=LogFactory.get(WebSocketServer.class);
/**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/
private static int onlineCount = 0;
/**concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。*/
private static ConcurrentHashMap<String,WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
/**与某个客户端的连接会话,需要通过它来给客户端发送数据*/
private Session session;
/**接收userId*/
private String userId="";
/**
* 连接建立成功调用的方法*/
@OnOpen
public void onOpen(Session session,@PathParam("userId") String userId) {
this.session = session;
this.userId=userId;
if(webSocketMap.containsKey(userId)){
webSocketMap.remove(userId);
webSocketMap.put(userId,this);
//加入set中
}else{
webSocketMap.put(userId,this);
//加入set中
addOnlineCount();
//在线数加1
}
log.info("用户连接:"+userId+",当前在线人数为:" + getOnlineCount());
try {
sendMessage("连接成功");
} catch (IOException e) {
log.error("用户:"+userId+",网络异常!!!!!!");
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
if(webSocketMap.containsKey(userId)){
webSocketMap.remove(userId);
//从set中删除
subOnlineCount();
}
log.info("用户退出:"+userId+",当前在线人数为:" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息*/
@OnMessage
public void onMessage(String message, Session session) throws IOException {
log.info("用户消息:"+userId+",报文:"+message);
}
/**
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("用户错误:"+this.userId+",原因:"+error.getMessage());
error.printStackTrace();
}
/**
* 实现服务器主动推送
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
/**
* 发送自定义消息
* */
public static void sendInfo(String message,@PathParam("userId") String userId) throws IOException {
log.info("发送消息到:"+userId+",报文:"+message);
if(StringUtils.isNotBlank(userId)&&webSocketMap.containsKey(userId)){
webSocketMap.get(userId).sendMessage(message);
}else{
log.error("用户"+userId+",不在线!");
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
测试WebSoket连接,测试地址:在线websocket测试-在线工具-postjson
WebSocket实战业务处理
WebSocket的业务处理与返回在onMessage方法中处理,但是会存在Service不能注入的,这个时候需要在容器启动的时候注入静态的Service;
@ServerEndpoint("/webSocket/{userId}")
@Component
public class WebSocketServer {
public static IShieldAuthService shieldAuthService;
//解决webSocket不能注入Service的问题,不能使用@Autowired
public static RedisTemplate<String, Object> redisTemplate;
public static IProjectZoneService projectZoneService;
//定时器
public static Timer timer;
/**
* 解决webSocket不能注入Service的问题,在启动中注入静态的service
*/
@Autowired
public void setShieldAuthService(IShieldAuthService shieldAuthService,RedisTemplate<String, Object> redisTemplate,
IProjectZoneService projectZoneService){
WebSocketServer.shieldAuthService = shieldAuthService;
WebSocketServer.redisTemplate = redisTemplate;
WebSocketServer.projectZoneService = projectZoneService;
}
注意:最后返回需要调用sendMessage方法,返回数据类型需要修改方法里.sendText()改为.senObject();
具体详见实现代码。