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 ...
随机推荐
- Serilog日志同步到redis中和自定义Enricher来增加额外的记录信息
Serilog 日志同步到redis队列中 后续可以通过队列同步到数据库.腾讯阿里等日志组件中,这里redis库用的新生命团队的NewLife.Redis组件 可以实现轻量级消息队列(轻量级消息队列R ...
- 移动端安卓开发学习记录--Android Studio打断点调试操作步骤记录
今天我发现一个很迷的事情,我是安卓初学者,发现打印对象的话,打印不出来,但是打印对象的属性值,却可以打印出来,迷啊!!!我好迷茫 咨询大佬后,得知,JAVA打印对象的话,打印出来的不是它自己的内容,而 ...
- Flutter 3.7 新特性:介绍后台isolate通道
Flutter 3.7 发布,本人对其中后台 isolate 通道比较感兴趣,迫不及待翻译了下Aaron Clarke文章,第一次翻译,有不足地方欢迎各位大佬们评论区指正,我将持续更新到本文,谢谢. ...
- 【ccc】为了ds的ccc2
作业: #include <stdio.h> #include<string.h> int main(){ char s[100]; gets(s); int len; len ...
- C++练习2 强制类型转换
const可以把有关的数据定义为常量. const类型可以修饰:对象,指针,引用 使用const_cast为强制类型转换,将常量强制转换非常量. 1 #include <iostream> ...
- TS在实际开发中的使用
TS的基础使用 // 数字 let num = ref<number>(100) // 文字 let str = rer<string>('文字') // boolean le ...
- xmind使用分享
Xmind 介绍 1. 特点 是表达发散性思维的有效工具,图文并重,把各主题的隶属关系和相关的层级表现出来,把关键字与图像,文字,颜色建立起链接记忆. 2.安装,下载与破解 参考:https://bl ...
- 三分钟实战手写Spring Boot Starter
1 背景 在平时的开发中,开发的同学会把一些通用的方法,写成一个工具类,例如日期转换的,JSON转换的等等,方便业务后续调用,使代码更容易维护. 如果一些更常用的方法,例如鉴权的,加解密的等等,几乎每 ...
- 三元运算符整体执行情况:Object obj = true? Integer.valueOf(1): Double.valueOf(2.0);
/* * @author YAM */public class Test01 { public static void main(String[] args) { Object obj = true? ...
- ctfshow_web入门 xss
额,怎么说呢,对xss理解不深刻,虽然做了XSS-LAB,但是感觉不会用,看了群主的视频,知道了原因,用群主的话来说就是,X的是自己... 这个文章写得比较潦草... 准备一个带nc的工具: 无vps ...