Spring4.3.7整合Websocket+Redis实现分布式消息推送

应用场景

前端客户端(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

本文章转载请标明出处

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值