stomp.js+spring+sockjs+activemq实现websocket长连接,使用java配置。

pom.xml(只列出除了spring基本依赖意外的依赖,spring-version为4.3.3.RELEASE):

<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
<scope>provided</scope>
<!-- 注意,scope必须为provided,否则runtime会冲突,如果使用tomcat 8,还需要将TOMCAT_HOME/lib下的javax.websocket-api.jar一并删除 -->
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-spring</artifactId>
<version>3.16</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-core</artifactId>
<version>5.7.0</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
<version>5.12.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-net</artifactId>
<version>2.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.33.Final</version>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>2.0.8.RELEASE</version>
</dependency>

StompConfig.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry; /**
* @EnableWebSocketMessageBroker包含@EnableWebSocket
* @author TD
*
*/
@Configuration
@EnableWebSocketMessageBroker
@PropertySource("classpath:activemq.properties")
public class StompConfig extends AbstractWebSocketMessageBrokerConfigurer{ @Autowired
private Environment env;
/**
* 注册代理,暴露节点用于连接。
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// TODO Auto-generated method stub
registry.addEndpoint("/stompEndPoint.do").withSockJS();
} /**
* 修改消息代理的配置,默认处理以/topic为前缀的消息
* setApplicationDestinationPrefixes:配置请求的根路径,表示通过MessageMapping处理/td/*请求,不会发送到代理
* enableStompBrokerRelay:配置代理,匹配路径的请求会进入代理:mq等
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
/*基于内存实现的stomp代理,单机适用
registry.enableSimpleBroker("/queue","/topic");
*/
//以/td为目的地的消息使用MessageMapping控制器处理,不走代理
registry.setApplicationDestinationPrefixes("/td","/app");
/**
* 设置单独发送到某个user需要添加的前缀,用户订阅地址/user/topic/td1地址后会去掉/user,并加上用户名(需要springsecurity支持)等唯一标识组成新的目的地发送回去,
* 对于这个url来说 加上后缀之后走代理。发送时需要制定用户名:convertAndSendToUser或者sendtouser注解.
registry.setUserDestinationPrefix("/user")
*/
/*基于mq实现stomp代理,适用于集群。
* 以/topic和/queue开头的消息会发送到stomp代理中:mq等。
* 每个mq适用的前缀不一样且有限制。activemq支持stomp的端口为61613
*/
registry.enableStompBrokerRelay("/topic","/queue")
.setRelayHost(env.getProperty("mq.brokenHost"))
.setRelayPort(Integer.parseInt(env.getProperty("mq.brokenPort")))
.setSystemLogin(env.getProperty("mq.username"))
.setSystemPasscode(env.getProperty("mq.password"))
.setClientLogin(env.getProperty("mq.username"))
.setClientPasscode(env.getProperty("mq.password"));
/*
* systemLogin:设置代理所需的密码
* client:设置客户端连接代理所需的密码,默认为guest
*/
} }

JSP页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>stomp测试</title>
</head>
<body>
<button onclick="test()">stomp发送(服务端返回信息走订阅2)</button>
<button onclick="test2()">stomp订阅1@subscribeMapping,不过代理)</button>
<button onclick="test3()">stomp订阅2 :通过代理</button>
</body>
<script type="text/javascript" src="/spring15_socket/js/sockjs-0.3.4.min.js"></script>
<script type="text/javascript" src="/spring15_socket/js/stomp.js"></script>
<script type="text/javascript">
var url = 'http://localhost:8089/spring15_socket/stompEndPoint.do';
//创建sockjs链接
var sock = new SockJS(url);
//创建stomp客户端
var stomp = Stomp.over(sock);
var msg = JSON.stringify({'name':'td','age':13});
stomp.connect({},function(frame){
    console.log('connecting...'+frame)
    stomp.send('/app/stomp1.do',{},msg);
})
 
function test(){
    stomp.send('/app/stomp1.do',{},msg);
}
 
function test2(){
    stomp.subscribe('/app/stomp2.do',function(msg){
        console.log("subscribemapping:"+JSON.parse(msg.body).content);
    })
     
}
 
function test3(){
    stomp.subscribe('/topic/hello',function(msg){
        console.log("topicHello:"+JSON.parse(msg.body).content);
    })
     
}
 
</script>
</html>

  

控制器:

package spring15_socket.controller;

import java.sql.SQLException;

import org.springframework.messaging.handler.annotation.MessageExceptionHandler;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.annotation.SubscribeMapping;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping; import spring15_socket.bean.User; @Controller
public class TestController { @RequestMapping(value="/sockjs.do")
public String test0(Model model){
return "sockjs";
} @RequestMapping(value="/stompjs.do")
public String test1(Model model){
return "stomp";
} /**
* 表示该方法处理客户端发来的/td/stomp1.do或者/app/stomp1.do。
* sendTo:重新指定发送的位置,默认原路返回(url会加上/topic前缀)
* 需走代理
*/
@MessageMapping("/stomp1.do")
@SendTo("/topic/hello")
public User handleStomp(User user) {
System.out.println("stomp接收到客户端的请求:"+user);
user.setName("messagemapping返回user");
return user; } /**
* 用于处理messagemapping抛出的异常,类比exceptionhandler
* @return
*/
@MessageExceptionHandler({Exception.class,SQLException.class})
@SendTo("/topic/errorTopic")
public User errorHandler(Throwable t) {
System.out.println("异常统一处理");
User user = new User();
user.setName("异常统一处理:"+t.getMessage());;
return user;
} /**
* 触发方式和messagemapping一致。
* sendTo:重新指定发送的位置,默认原路返回(url会加上/topic前缀)
* 使用subscribemapping不走代理
*/
@SubscribeMapping("/stomp2.do")
@SendTo("/topic/hello")
public User subsTest() {
User user2 = new User();
user2.setName("订阅name");
user2.setPhone("subscribePhone");
return user2;
} }

进入activemq的控制台,点击connection可以看到stomp连接:(这里连接了两个客户端,可以点击超链接查看连接状态)

查看启动日志可以发现:表明启动成功

进入页面点击第一个按钮,会触发send:

同时控制台可以看到:

单独点击第二个按钮不会有返回消息,点击第三个按钮之后会订阅/topic/hello,此时点击第一个触发send。在服务端执行完毕后通过sendto注解发布到/topic/hello,此时浏览器控制台会输出该频道发布的内容

注意点:

1:基于activemq作为代理,连接的端口号为61613。

2:setSystemLogin表示设置服务器连接代理(activemq)的账号密码,setClientLogin表示连接过来的客户端连接代理所需要的账号密码。

Spring+Stomp+ActiveMq实现websocket长连接的更多相关文章

  1. 当web应用包含了websocket长连接,如何在web应用前加一层nginx转发

    1 通过在web应用的前面加一层nginx ,可以实现一台主机部署多个应用,每个应用都可以用不同的域名去访问,并且端口都是80 2 nignx 转发websocket长连接 1 每个web应用,他们运 ...

  2. 实现单台测试机6万websocket长连接

    本文由作者郑银燕授权网易云社区发布. 本文是我在测试过程中的记录,实现了单台测试机发起最大的websocket长连接数.在一台测试机上,连接到一个远程服务时的本地端口是有限的.根据TCP/IP协议,由 ...

  3. WebSocket长连接

    WebSocket长连接 1.概述 1.1 定义 1.2 原理 2.Django中配置WebSocket 2.1安装第三方法包 pip install channels 2.2 Django 中的配置 ...

  4. Python WebSocket长连接心跳与短连接

    python websocket 安装 pip install websocket-client 先来看一下,长连接调用方式: ws = websocket.WebSocketApp("ws ...

  5. WebSocket 长连接 及超时问题解决

    <?phpset_time_limit(0); class SocketService { private $address = 'localhost'; private $port = 80; ...

  6. 雨露均沾的OkHttp—WebSocket长连接的使用&源码解析

    前言 最近老板又来新需求了,要做一个物联网相关的app,其中有个需求是客户端需要收发服务器不定期发出的消息. 内心OS:

  7. Django websocket 长连接使用

    下载  pip install dwebsocket WebSocket是一种在单个TCP连接上进行全双工通信的协议 WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客 ...

  8. 微信小程序 实现websocket长连接 以及断开连接之后自动重连

    app.js let socketMsgQueue = [] let isLoading = false App({ globalData: { userInfo: null, localSocket ...

  9. Browser 與 Server 持續同步的作法介紹 (Polling, Comet, Long Polling, WebSocket)长连接

    對 Comet 的懵懂 記得兩年多前,第一次看到 Gmail 中的 GTalk 覺得很好奇:「咦?線上聊天且是 Google 的熱門系統,只用傳統的 AJAX 應該會操爆伺服器吧?」很幸運的,當時前公 ...

随机推荐

  1. bootstrap字体图标在IE上不显示

    最简单的办法就是直接下载最新的bootstrap.css替换掉旧的. 但是由于我做的项目直接替换会出现样式冲突问题,因此只好慢慢找是什么属性导致图标不显示,最后找到了解决办法: 1.首先保字体文件的位 ...

  2. linux命令行操作mysql数据库明细

    连接数据库==> mysql -uroot -p 输入root密码 进入mysql操作后 下面的命令不要忘了最后结尾的; 1.选择数据库命令: use <数据库名> 2.查看表的引擎 ...

  3. java 用户注册登陆Demo

    一个用户注册登陆的系统,用到了MD5加密处理密码,实现了一个简单的数据库连接池connectionPool, 实现了注册,登陆,登陆之后修改用户信息等功能,非常适合初学者 一.准备工作 数据库:MyS ...

  4. 通过原生JS打印一个空心菱形图案

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  5. JasperReport生命周期3

    JasperReports的主要目的是为了在一个简单而灵活的方式创建页面为导向,准备好打印文档.下面的流程图描述了一个典型的工作流程,同时创建报表. 如在图片的生命周期具有以下明显的阶段 设计报表在这 ...

  6. org.apache.ant实现压缩和解压

    <dependency> <groupId>org.apache.ant</groupId> <artifactId>ant</artifactI ...

  7. Chrome 地址栏如何设置显示 http/https 和 www

    首先在 chrome 地址栏输入以下地址 chrome://flags/#omnibox-ui-hide-steady-state-url-scheme-and-subdomains 然后使用 Ctr ...

  8. Luogu P1963 [NOI2009]变换序列(二分图匹配)

    P1963 [NOI2009]变换序列 题意 题目描述 对于\(N\)个整数\(0,1, \cdots ,N-1\),一个变换序列\(T\)可以将\(i\)变成\(T_i\),其中\(T_i \in ...

  9. <br>和换行符/n

    我们知道<br>是html的标签,表示文本另起一行.经常在html的body标签里面看到br,起到另起一行的作用. 而换行符\n是javascript的转义字符,表示将输出一个换行符,用于 ...

  10. SSH 框架controller向jsp传递List jsp中使用el表达式获取

    mvc可以使用ModelAndViev传递数据选择跳转的视图 controller中的代码, 把一个模拟的表单studentListSimulate传给ModelAndView @RequestMap ...