应用场景:
-项目中XX功能运行最新情况需服务端主动推送至浏览器.spring Maven项目.
目的:
-浏览器与服务端建立全双工的通信方式,解决http请求-响应带来过多的资源消耗.
POM.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
webSocket 配置
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
/**
* webSocket 配置
*/
@Configuration
@EnableWebMvc
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer{
@Autowired
private SocketHandler socketHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(socketHandler, "/socketServer").addInterceptors(new WebSocketInterceptor());
registry.addHandler(socketHandler, "/sockjs/socketServer").addInterceptors(new WebSocketInterceptor()).withSockJS();
}
}
webSocket 拦截器
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
import javax.servlet.http.HttpSession;
import java.util.Map;
/**
* webSocket 拦截器
*/
public class WebSocketInterceptor implements HandshakeInterceptor{
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler handler, Exception exception) {
}
/**
* @desp 将HttpSession中对象放入WebSocketSession中
*/
@Override
public boolean beforeHandshake(ServerHttpRequest request,
ServerHttpResponse response, WebSocketHandler handler,
Map<String, Object> map) throws Exception {
if(request instanceof ServerHttpRequest){
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpSession session = servletRequest.getServletRequest().getSession();
if(session!=null){
String user = (String) session.getAttribute("user");
map.put("user", user);
if (user == null){
map.put("user","minerUser");
}
}
}
return true;
}
}
socket 处理器
import com.alibaba.fastjson.JSONObject;
import com.lemon.miner.flowdesigner.exec.dao.WorkflowRunlogDao;
import com.lemon.miner.flowdesigner.exec.entity.WorkflowRunlogEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.*;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
/**
* socket 处理器
*/
@Service
public class SocketHandler implements WebSocketHandler {
private static final Logger logger;
private static final ArrayList<WebSocketSession> users;
@Resource
private WorkflowRunlogDao workflowRunlogDao;
static {
users = new ArrayList<WebSocketSession>();
logger = LoggerFactory.getLogger(SocketHandler.class);
}
@Override
public void afterConnectionEstablished(WebSocketSession session)
throws Exception {
logger.info("成功建立socket连接");
users.add(session);
}
@Override
public void handleMessage(WebSocketSession arg0, WebSocketMessage<?> arg1)
throws Exception {
}
@Override
public void handleTransportError(WebSocketSession session, Throwable error)
throws Exception {
if (session.isOpen()) {
session.close();
}
logger.error("连接出现错误:" + error.toString());
users.remove(session);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus arg1)
throws Exception {
logger.debug("连接已关闭");
users.remove(session);
}
@Override
public boolean supportsPartialMessages() {
return false;
}
/**
* 给所有在线用户发送消息
*
* @param message
*/
public void sendMessageToUsers(TextMessage message) {
for (WebSocketSession user : users) {
try {
if (user.isOpen()) {
user.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 给某个用户发送消息
*
* @param userName
* @param message
*/
public void sendMessageToUser(String userName, TextMessage message) {
for (WebSocketSession user : users) {
if (user.getAttributes().get("user").equals(userName)) {
try {
if (user.isOpen()) {
user.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}
/**
* 发送最新一条日志
*/
public void sendLogs(){
WorkflowRunlogEntity newestLog = workflowRunlogDao.getNewestLog();
String message = JSONObject.toJSONString(newestLog);
sendMessageToUsers(new TextMessage(message));
}
}
webSocket Controller
import com.lemon.miner.flowdesigner.utils.R;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.io.IOException;
/**
* webSocket Controller
* Created by Even on 2017/6/14.
*/
@RestController
@RequestMapping("/websocket")
public class WebSocketController {
@Resource
private SocketHandler socketHandler;
@RequestMapping("/sendRunlogs")
@ResponseBody
public R testWebSocket() throws IOException {
socketHandler.sendLogs();
return R.success();
}
}
websocket.html
<!DOCTYPE html>
<html>
<head>
<title>webSocket管理</title>
#parse("sys/header.html")
<style>
th,td{
border: 1px solid #00a7d0;
text-align: center;
width: 150px;
}
</style>
</head>
<body>
<div>
<div>
<div class="grid-btn">
<a class="btn btn-primary" id="sendRequestToController" onclick="sendRunlogs()">模拟服务器发送最新日志</a>
</div>
</div>
<div>
<table>
<thead>
<tr>
<th>流程ID</th>
<th>流程名称</th>
<th>节点ID</th>
<th>节点名称</th>
<th>状态</th>
<th>开始时间</th>
<th>结束时间</th>
<th>耗时(ms)</th>
<th>日志内容</th>
</tr>
</thead>
<tbody id="message">
<tr>
<td>10055</td>
<td>XY</td>
<td>NODE0</td>
<td>线性回归</td>
<td>TaskRunSuccess</td>
<td>2017-06-14 16:13:52</td>
<td>2017-06-14 16:13:53</td>
<td>47</td>
<td>任务运行完毕</td>
</tr>
</tbody>
</table>
</div>
</div>
<script src="${rc.contextPath}/js/generator/websocket/websocket.js?_${date.systemTime}"></script>
</body>
</html>
websocket.js
$(function () {
initSocket();
});
function initSocket() {
var thisURl=location.host;
$(function(){
var sock;
if ('WebSocket' in window) {
sock = new WebSocket("ws://"+thisURl+"/miner/socketServer");
} else if ('MozWebSocket' in window) {
sock = new MozWebSocket("ws://"+thisURl+"/miner/socketServer");
} else {
sock = new SockJS("http://"+thisURl+"/miner/sockjs/socketServer");
}
sock.onopen = function (e) {
console.log(e);
};
sock.onmessage = function (e) {
console.log(e);
var obj = JSON.parse(e.data);
var trObj = '<tr>' +
'<td>' + obj.workflowId + '</td>' +
'<td>' + obj.workflowName + '</td>' +
'<td>' + obj.nodeId + '</td>' +
'<td>' + obj.nodeName + '</td>' +
'<td>' + obj.status + '</td>' +
'<td>' + obj.startDatetime + '</td>' +
'<td>' + obj.endDatetime + '</td>' +
'<td>' + obj.costTime + '</td>' +
'<td>' + obj.msg + '</td>' +
'</tr>';
$("#message").prepend(trObj);
};
sock.onerror = function (e) {
console.log(e);
$("#message").prepend("<p><font color='red'>"+e.data+"</font>");
};
sock.onclose = function (e) {
console.log(e);
}
});
}
function sendRunlogs() {
var thisURl=location.host;
$.ajax({
type: "POST",
url: "http://"+thisURl + "/miner/websocket/sendRunlogs",
success: function(result){
}
});
}