java springboot+rabbitmq+websocket 订阅展示
记录工作
需要的依赖
<!--fastjson坐标-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>
<!--RabbitMQ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!--WebSocket-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.46</version>
</dependency>
<!--JSON-->
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
</dependency>
rabbitmq:
host: 127.0.0.0.1 #(自己的服务地址)
port: 5672
username: admin #rabbitmq的用户名
password: admin #密码
virtual-host: /
# 开启消息发送确认
publisher-confirm-type: correlated
publisher-returns: true
listener:
simple:
acknowledge-mode: manual
rabbitmq创建队列以及路由配置类,这里用的是路由的方式
package com.example.demotest.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @title
* @description RabbitMQ 配置类
* @author admin issuser
* @updateTime 2022/11/10 15:44
* @throws
*/
@Slf4j
@Configuration
public class RabbitConfig {
//websocket 消息队列
public static final String msg_queue = "msg_queue";
//消息交换机
public static final String msg_exchang = "msg_exchang";
//消息路由键
public static final String msg_routing_key = "msg_routing_key";
/**
* 消息队列
* @return
*/
@Bean
public Queue msgQueue(){
return new Queue(msg_queue);
}
@Bean
public Queue newQueue(){
return new Queue("new_queue");
}
@Bean
public DirectExchange directExchange(){
return new DirectExchange(msg_exchang);
}
@Bean
public DirectExchange directExchange1(){
return new DirectExchange("new_exchang");
}
/**
* 消息队列绑定消息交换机
* @return
*/
@Bean
public Binding msgBinding(){
return BindingBuilder.bind(msgQueue()).to(directExchange()).with(msg_routing_key);
}
@Bean
public Binding meBinding(){
return BindingBuilder.bind(newQueue()).to(directExchange1()).with("me_key");
}
@Bean
public RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory){
RabbitTemplate rabbitTemplate = new RabbitTemplate();
rabbitTemplate.setConnectionFactory(connectionFactory);
//设置开启Mandatory,才能触发回调函数,无论消息推送结果怎么样都强制调用回调函数
rabbitTemplate.setMandatory(true);
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> log.info("发送订阅消息确认,确认情况:{}, 原因:{}", ack, cause));
rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> log.info("发送到订阅消息: {} , 回应码: {}, 回应信息: {}, 交换机: {}, 路由键: {}", message, replyCode, replyText, exchange, routingKey));
return rabbitTemplate;
}
}
发送消息的配置类,我这里稍微改造了一下直接变成了接口,如果不需要只需要删除接口有关的注解就可以了,返回类型其实是void
package com.example.demotest.controller;
import com.alibaba.fastjson.JSONObject;
import com.example.demotest.config.RabbitConfig;
import com.example.demotest.entity.ConfigurationTable;
import com.example.demotest.util.Redis;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.UUID;
/**
* @title
* @description 消息提供者
* @author admin issuser
* @updateTime 2022/11/10 15:44
* @throws
*/
@Slf4j
@Component
@RestController
@Api(tags = "发消息")
public class RabbitProduct {
@Resource
private RabbitTemplate rabbitTemplate;
/**
* 构造方法注入rabbitTemplate
*/
@Autowired
public RabbitProduct(RabbitTemplate rabbitTemplate){
this.rabbitTemplate = rabbitTemplate;
}
@Resource
private Redis redis;
//发送消息 推送到websocket 参数自定义 转为String发送消息
@GetMapping("/mm")
@ApiOperation(value = "发消息")
public String sendMSG(String msg,ConfigurationTable configurationTable){
CorrelationData correlationId = new CorrelationData(UUID.randomUUID().toString());
redis.selDateKey(1,"ConfigurationType",configurationTable.getConfigurationType(),500);
//把消息对象放入路由对应的队列当中去
rabbitTemplate.convertAndSend(configurationTable.getRoute(),configurationTable.getBindingKey(), JSONObject.toJSON(msg).toString(), correlationId);
return "操作成功!";
}
public String sendMsg(String msg,ConfigurationTable configurationTable){
CorrelationData correlationId = new CorrelationData(UUID.randomUUID().toString());
//把消息对象放入路由对应的队列当中去
rabbitTemplate.convertAndSend(configurationTable.getRoute(),configurationTable.getBindingKey(), JSONObject.toJSON(msg).toString(), correlationId);
return "操作成功!";
}
}
接下来就是接收消息了,我这里使用的是手动ACK的方式,想要简单一点的话,用注解直接监听,把rabbitConfig中的手动ack删了就好
package com.example.demotest.listener;
import com.example.demotest.config.WebSocketServerEndpoint;
import com.example.demotest.util.Redis;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
/**
* Description :
* Copyright : Copyright (c) 2022
* Company : 中移(成都)产业研究院 Co. Ltd.
*
* @author daisuhang
* @version 1.0.0
* @createTime 2022年11月14日 09:07:00
*/
@Slf4j
@Component
public class RabbitNewConsumer {
@Autowired
private Redis redis;
private static RabbitNewConsumer rabbitNewConsumer;
@Resource
private WebSocketServerEndpoint webSocketServerEndpoint; //引入WebSocket
/**
* 构造方法注入rabbitTemplate
*/
@PostConstruct
public void init() {
rabbitNewConsumer = this;
rabbitNewConsumer.webSocketServerEndpoint = webSocketServerEndpoint;
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "new_queue",durable = "true"),
exchange = @Exchange(name = "new_exchang",durable = "true",type = "direct"),
key = "me_key"
)) //监听队列
public void msgReceive(String content, Message message, Channel channel) throws IOException {
log.info("----------------接收到消息--new-queue------------------"+content);
//发送给WebSocket 由WebSocket推送给前端
String configurationType = redis.get("ConfigurationType");
if (configurationType.equals("事件上传")){
rabbitNewConsumer.webSocketServerEndpoint.sendMessageOnline(content);
}
// 确认消息已接收
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
}
下面这个是加了休眠时间的延时
package com.example.demotest.listener;
import com.example.demotest.config.RabbitConfig;
import com.example.demotest.config.WebSocketServerEndpoint;
import com.example.demotest.util.Redis;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import static com.example.demotest.config.RabbitConfig.*;
/**
* @title
* @description 定时消息队列 消费监听回调
* @author admin issuser
* @updateTime 2022/11/10 15:43
* @throws
*/
@Slf4j
@Component
public class RabbitConsumer {
private static RabbitConsumer rabbitConsumer;
@Resource
private WebSocketServerEndpoint webSocketServerEndpoint; //引入WebSocket
@Autowired
private Redis redis;
/**
* 构造方法注入rabbitTemplate
*/
@PostConstruct
public void init() {
rabbitConsumer = this;
rabbitConsumer.webSocketServerEndpoint = webSocketServerEndpoint;
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = msg_queue,durable = "true"),
exchange = @Exchange(name = msg_exchang,durable = "true",type = "direct"),
key = msg_routing_key
)) //监听队列
public void msgReceive(String content, Message message, Channel channel) throws IOException {
log.info("----------------接收到消息-msg_queue-------------------"+content);
//发送给WebSocket 由WebSocket推送给前端
String configurationType = redis.get("ConfigurationType");
if (configurationType.equals("属性上传")){
rabbitConsumer.webSocketServerEndpoint.sendMessageOnline(content);
// 确认消息已接收
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);
}
}
接下来就是socket的一些配置
package com.example.demotest.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* WebSocket配置类
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
连接前端的配置,以及一些方法封装
package com.example.demotest.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
/**
* WebSocket 服务配置类
* 定义 userId 为当前连接(在线) WebSocket 的用户
*/
@Slf4j
@Component
@ServerEndpoint(value = "/ws/{userId}")
public class WebSocketServerEndpoint {
private Session session; //建立连接的会话
private String userId; //当前连接用户id 路径参数
/**
* 存放存活的Session集合(map保存)
*/
private static ConcurrentHashMap<String , WebSocketServerEndpoint> livingSession = new ConcurrentHashMap<>();
/**
* 建立连接的回调
* session 建立连接的会话
* userId 当前连接用户id 路径参数
*/
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId){
this.session = session;
this.userId = userId;
livingSession.put(userId, this);
log.debug("----[ WebSocket ]---- 用户id为 : {} 的用户进入WebSocket连接 ! 当前在线人数为 : {} 人 !--------",userId,livingSession.size());
}
/**
* 关闭连接的回调
* 移除用户在线状态
*/
@OnClose
public void onClose(){
livingSession.remove(userId);
log.debug("----[ WebSocket ]---- 用户id为 : {} 的用户退出WebSocket连接 ! 当前在线人数为 : {} 人 !--------",userId,livingSession.size());
}
@OnMessage
public void onMessage(String message, Session session, @PathParam("userId") String userId) {
log.debug("--------收到用户id为 : {} 的用户发送的消息 ! 消息内容为 : ------------------",userId,message);
//sendMessageToAll(userId + " : " + message);
}
@OnError
public void onError(Session session, Throwable error) {
log.error("----------------WebSocket发生错误----------------");
log.error(error.getStackTrace() + "");
}
/**
* 根据userId发送给用户
* @param userId
* @param message
*/
public void sendMessageById(String userId, String message) {
livingSession.forEach((sessionId, session) -> {
//发给指定的接收用户
if (userId.equals(session.userId)) {
sendMessageBySession(session.session, message);
}
});
}
/**
* 根据Session发送消息给用户
* @param session
* @param message
*/
public void sendMessageBySession(Session session, String message) {
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
log.error("----[ WebSocket ]------给用户发送消息失败---------");
e.printStackTrace();
}
}
public void sendMessageBySessionObject(Session session, Object message) {
try {
session.getBasicRemote().sendObject(message);
} catch (IOException | EncodeException e) {
log.error("----[ WebSocket ]------给用户发送消息失败---------");
e.printStackTrace();
}
}
/**
* 给在线用户发送消息
* @param message
*/
public void sendMessageOnline(String message) {
livingSession.forEach((sessionId, session) -> {
if(session.session.isOpen()){
sendMessageBySession(session.session, message);
}
});
}
/**
* 给在线用户发送消息
* @param message
*/
public void sendMessageOnlineObject(Object message) {
livingSession.forEach((sessionId, session) -> {
if(session.session.isOpen()){
sendMessageBySessionObject(session.session, message);
}
});
}
}
最后就是前端页面了
<!DOCTYPE HTML>
<html>
<head>
<title>My WebSocket</title>
</head>
<body>
Welcome<br/>
<input id="text" type="text" /><button onclick="send()">Send</button> <button onclick="closeWebSocket()">Close</button>
<div id="message">
</div>
</body>
<script type="text/javascript">
var websocket = null;
//判断当前浏览器是否支持WebSocket
if('WebSocket' in window){
websocket = new WebSocket("ws://localhost:8001/ws/{123}");
}
else{
alert('当前浏览器 Not support websocket')
}
//连接发生错误的回调方法
websocket.onerror = function(){
setMessageInnerHTML("error");
};
//连接成功建立的回调方法
websocket.onopen = function(event){
setMessageInnerHTML("WebSocket连接成功");
}
//接收到消息的回调方法
websocket.onmessage = function(event){
setMessageInnerHTML(event.data);
}
//连接关闭的回调方法
websocket.onclose = function(){
setMessageInnerHTML("WebSocket连接关闭");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function(){
websocket.close();
}
//将消息显示在网页上
function setMessageInnerHTML(innerHTML){
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
//关闭连接
function closeWebSocket(){
websocket.close();
}
//发送消息
function send(){
var message = document.getElementById('text').value;
websocket.send(message);
}
</script>
</html>
到此本文就结束了
java springboot+rabbitmq+websocket 订阅展示的更多相关文章
- Java使用RabbitMQ之订阅分发(Topic)
使用RabbitMQ进行消息发布和订阅,生产者将消息发送给转发器(exchange),转发器根据路由键匹配已绑定的消息队列并转发消息,主题模式支持路由键的通配. 生产者代码: package org. ...
- Java Springboot webSocket简单实现,调接口推送消息到客户端socket
Java Springboot webSocket简单实现,调接口推送消息到客户端socket 后台一般作为webSocket服务器,前台作为client.真实场景可能是后台程序在运行时(满足一定条件 ...
- springboot+rabbitmq整合示例程
关于什么是rabbitmq,请看另一篇文: http://www.cnblogs.com/boshen-hzb/p/6840064.html 一.新建maven工程:springboot-rabbit ...
- 【Other】最近在研究的, Java/Springboot/RPC/JPA等
我的Springboot框架,欢迎关注: https://github.com/junneyang/common-web-starter Dubbo-大波-服务化框架 dubbo_百度搜索 Dubbo ...
- SpringBoot 整合 WebSocket
SpringBoot 整合 WebSocket(topic广播) 1.什么是WebSocket WebSocket为游览器和服务器提供了双工异步通信的功能,即游览器可以向服务器发送消息,服务器也可以向 ...
- SpringAOP+RabbitMQ+WebSocket实战
背景 最近公司的客户要求,分配给员工的任务除了有微信通知外,还希望PC端的网页也能实时收到通知.管理员分配任务是在我们的系统A,而员工接受任务是在系统B.两个系统都是现在已投入使用的系统. 技术选型 ...
- java~springboot~目录索引
回到占占推荐博客索引 最近写了不过关于java,spring,微服务的相关文章,今天把它整理一下,方便大家学习与参考. java~springboot~目录索引 Java~关于开发工具和包包 Java ...
- SpringBoot集成WebSocket【基于纯H5】进行点对点[一对一]和广播[一对多]实时推送
代码全部复制,仅供自己学习用 1.环境搭建 因为在上一篇基于STOMP协议实现的WebSocket里已经有大概介绍过Web的基本情况了,所以在这篇就不多说了,我们直接进入正题吧,在SpringBoot ...
- SpringBoot基于websocket的网页聊天
一.入门简介正常聊天程序需要使用消息组件ActiveMQ或者Kafka等,这里是一个Websocket入门程序. 有人有疑问这个技术有什么作用,为什么要有它?其实我们虽然有http协议,但是它有一个缺 ...
- springboot rabbitmq 死信队列应用场景和完整demo
何为死信队列? 死信队列实际上就是,当我们的业务队列处理失败(比如抛异常并且达到了retry的上限),就会将消息重新投递到另一个Exchange(Dead Letter Exchanges),该Exc ...
随机推荐
- Ant Design Table 如何动态自定义?Ant Popover 遮挡?
项目场景: 基于electron + Vue + node.js + express + mysql + evanpatchouli-mysql + Ant-Design-Vue,编写一款属于自己的轻 ...
- JS实现excel数据透析,形成关系图
网上查了好多例子,都没有找到答案,只能自己硬着头皮写了 想要的样子: 下面是DEMO,已经实现效果了!!!! 举例 导入 <!DOCTYPE html> <html lang=&q ...
- MRS_下载相关问题汇总
解决问题如下: MRS下载编译时,更改生成文件是HEX文件还是BIN文件 关于MounRiver下载时如何选择配置部分擦除 关于MounRiver下载起始地址配置问题 MRS下载编译时,更改生成文件是 ...
- RabbitMQ消息队列入门及解决常见问题
RabbitMQ消息队列 同步通讯和异步通讯 微服务间通讯有同步和异步两种方式: 同步通讯:就像打电话,需要实时响应. 异步通讯:就像发邮件,不需要马上回复. 两种方式各有优劣,打电话可以立即得到响应 ...
- 构建api gateway之 基于etcd实现动态配置同步
配置中心 在之前 tcp的yaml配置 介绍了如何监听yaml文件变化然后更新配置. 当然假如我们有很多实例,那么yaml改动将是非常痛苦的事情,那么如何做到配置文件统一管理,实时更新呢? 我们可以引 ...
- evil 控制窗口大小,比快捷键方便
下面是vim原本的支持的键 可以用于 emacs evil ,evil 用这个比用快捷键还方面些 1.纵向调整 :res[ize] num 指定当前窗口为num列num行 :res[ize] +num ...
- python 取整方法
1.向下取整: int() 2.向上取整:ceil() 使用ceil()方法时需要导入math模块,例如 3.四舍五入:round() 4.分别取 将整数部分和小数部分分别取出,可以使用math模块中 ...
- linux 基础(10)进程管理
使用 ps 观察程序 ps -l ps程序可以查询当前在运行的进程信息.ps -l可以列出详细的信息,默认仅列出当前 bash 相关的进程. sudo -i ps -l F S UID PID PPI ...
- (原创)【B4A】一步一步入门04:编译模式、打包为APK、私钥签名
一.前言 上篇 (原创)[B4A]一步一步入门03:APP名称.图标等信息修改 中我们将APP做成了标准的样子. 本篇文章会讲解如何将程序打包成APK文件以分发,同时讲解如何制作私钥并签名APP,以用 ...
- 跳板攻击之:NPS代理转发
跳板攻击之:NPS代理转发 目录 跳板攻击之:NPS代理转发 1 NPS介绍 2 NPS特点 3 实验环境 3.1 实验准备 3.2 实验拓扑 3.3 NPS配置 3.3.1 conf/nps.con ...