应用场景
前端客户端(PC、移动)希望能及时的接收到后端消息的反馈。
传统解决方案
在以前购买一件商品,你需要知道物流进度时候你需要不断的向物流公司进行查询,这个是非常消耗时间的操作而且做上许多无用功,这种操作称之为“拉取”。
新的解决方案
所以现在需要购买一件商品后,你希望物流每到一个物流点就给你发送一条消息,那么这样你就能准确的知道商品的位置和到达时间,这种操作称之为“推送”。
很显然,这种场景下,推送比拉取好太多了,双赢的操作;
好了废话不多说,直接贴代码
基本架构
Spring4.3.7
maven 依赖
<spring.version>4.3.7.RELEASE</spring.version>
<!-- redis cache related.....start -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.6.0.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.7.3</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>1.2.1.RELEASE</version>
</dependency>
<!-- redis cache related.....end -->
<!-- 添加Spring依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- begin 添加对websocket的支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- 防止单元测试websocket报错 -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-websocket</artifactId>
<version>7.0.52</version>
<scope>test</scope>
</dependency>
<!-- end 添加对websocket的支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>${spring.version}</version>
</dependency>
实现原理
集群服务器ServerA、ServerB同时订阅了主题topicA;
同时有两个客户分别对ServerA、B发起长连接。
消息(msg)发送到ServerA时,topicA先将消息(msg)发布到ServerA、B上,这时ServerA就能给clinetB发送msg,ServerB就能给clinetA发送msg。
这样就实现了分布式消息推送
配置websocket端点 WebSocketConfig :
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
private static final Logger log = LoggerFactory.getLogger("SAFE");
@Autowired
private SimpMessagingTemplate messagingTemplate;
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
config.setPathMatcher(new AntPathMatcher("."));
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket")
./* addInterceptors(new HandshakeInterceptor()). */setAllowedOrigins("*").withSockJS();
}
}
简单写一个控制器
WebSocketController
@Controller
@RequestMapping("/socket")
public class WebSocketController extends BaseController{
@Autowired
SimpMessagingTemplate simpMessagingTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 给某人发送消息
* @param name
* @param msg
*/
@GetMapping("/messagePush/{name}")
public void userMsg(@PathVariable("name") String name,String msg) {
Message message = new Message();
message.setMessageInterfaceType(MessageCode.USER_PUSH);
message.setBody(name);
message.setMessage(HtmlUtils.htmlEscape(msg));
stringRedisTemplate.convertAndSend("serverBroadcast",JSON.toJSON(message).toString());
}
/**
* 消息推送
* @param msg
*/
@GetMapping("/messagePush")
public void pushMsg(String msg) {
Message message = new Message();
message.setMessageInterfaceType(MessageCode.ALL_PUSH);
message.setMessage(HtmlUtils.htmlEscape(msg));
stringRedisTemplate.convertAndSend("serverBroadcast",JSON.toJSON(message).toString());
}
}
新建一个测试页面 websocket.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello WebSocket</title>
<link href="bootstrap.css" rel="stylesheet">
<script src="websocket/jquery.min.js" charset="utf-8"></script>
<script src="websocket/sockjs.min.js" charset="utf-8"></script>
<script src="websocket/stomp.js" charset="utf-8"></script>
<script src="app.js" charset="utf-8"></script>
<style>
body {
background-color: #f5f5f5;
}
#main-content {
max-width: 940px;
padding: 2em 3em;
margin: 0 auto 20px;
background-color: #fff;
border: 1px solid #e5e5e5;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
</style>
</head>
<body>
<noscript>
<h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being
enabled. Please enable
Javascript and reload this page!</h2>
</noscript>
<div id="main-content" class="container">
<div class="row">
<div class="col-md-6">
<form class="form-inline">
<div class="form-group">
<label for="connect">WebSocket 连接:</label>
<button id="connect" class="btn btn-default" type="submit">连接</button>
<button id="disconnect" class="btn btn-default" type="submit" disabled="disabled">断开连接
</button>
</div>
</form>
</div>
<div class="col-md-12">
<div class="form-group">
<label for="sendname">你的工号:</label>
<input type="text" id="sendname" class="form-control" placeholder="send name ..." readonly="readonly">
</div>
<div class="form-group">
<label for="receivername">对方工号:</label>
<input type="text" id="receivername" class="form-control" placeholder="receiver name ...">
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<button id="sendName" class="btn btn-default" type="button" onclick="sendName()">给指定工号发送消息</button>
<button id="sendGroup" class="btn btn-default" type="button" onclick="sendGroup()">给所有工号发送消息</button>
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<label for="content">发送的内容:</label>
<input type="text" id="content" class="form-control" placeholder="content ...">
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table id="conversation" class="table table-striped">
<thead>
<tr>
<th>信息</th>
</tr>
</thead>
<tbody id="msg">
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>
app.js
var stompClient = null;
function setConnected(connected) {
$("#connect").prop("disabled", connected);
$("#disconnect").prop("disabled", !connected);
if (connected) {
$("#conversation").show();
} else {
$("#conversation").hide();
}
$("#greetings").html("");
}
function connect() {
var socket = new SockJS(otnurl + 'websocket');
stompClient = Stomp.over(socket);
//stompClient._setupHeartbeat(header);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
//订阅 前端消息推送
stompClient.subscribe('/topic/webMessagePush', function(data) {
showMsg(data.body);
});
//订阅 专属的前端消息推送
stompClient.subscribe('/topic/' + $("#sendname").val() + '/webMessagePush', function(data) {
showMsg(data.body);
});
});
}
function disconnect() {
if (stompClient !== null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
function sendGroup() {
//stompClient.send("/app.hello", {}, JSON.stringify({'name': $("#name").val()}));
$.ajax({
headers: JSON.parse(localStorage.getItem("headers")),
url: otnurl + 'socket/messagePush',
type: 'get',
data: {
msg: $("#content").val()
}
})
}
function sendName() {
//stompClient.send("/app.hello", {}, JSON.stringify({'name': $("#name").val()}));
var staffCode = $("#receivername").val();
if ('' != staffCode) {
$.ajax({
headers: JSON.parse(localStorage.getItem("headers")),
url: otnurl + 'socket/messagePush/' + staffCode,
type: 'get',
data: {
msg: $("#content").val()
}
})
} else {
alert("send name can't empty!");
}
}
function showMsg(message) {
$("#msg").prepend("<tr><td>" + getLocalTime((new Date()).valueOf()) + ":<br />" + message + "</td></tr>");
}
function getLocalTime(nS) {
return new Date(parseInt(nS)).toLocaleString().replace(/:\d{1,2}$/, ' ');
}
$(function() {
$("form").on('submit', function(e) {
e.preventDefault();
});
$("#connect").click(function() {
$('#sendname').val("TY" + (new Date()).valueOf());
connect();
});
$("#disconnect").click(function() {
disconnect();
});
});
测试结果
输入内容 点击给所有人发送,结果如下
输入内容 点击给指定人发送,结果如下
结果运行ok
本文章转载请标明出处