需求背景
项目需要前端直接与Rabbitmq创建连接,订阅故障上报、发布异常处理等。项目中有三个主题的消息,前端在不同主题的消息中分别需要publish、subscribe、publish&subscribe。
前端采用Vue3框架,Rabbitmq官网提供http/web-stomp协议应用于web端与Rabbitmq通讯,下面前端开发围绕着stomp协议展开。
官网文档参考 RabbitMQ Web STOMP Plugin — RabbitMQ
【注】Rabbitmq服务端配置文件需要添加端口15674.
Vue封装STOMP
stomp.js
stomp.js是一个stomp协议的代理库,本项目使用stomp.js创建rabbirmq连接实例,源码地址stomp-js/stompjs: Javascript and Typescript Stomp client for Web browsers and node.js apps (github.com)
配置文件
创建rabbitmq.config.js文件定义全局变量,存储连接rabbitmq的服务地址、交换机等。
/* rabbitmq 连接配置*/
export const RMQ_SERVER = // mq服务地址
export const RMQ_EXCHANGE = ''//交换机
export const RMQ_KEY = ''//交换机routing key
export const RMQ_ROBOT = ''//手动发布异常交换机
export const RMQ_ACCOUNT = '' // 用户名
export const RMQ_PASSWORD = '' // 密码
export const ERROR_KEY = ""//异常队列
export const SOLVE_KEY = ""//异常解决队列
export const RES_KEY = ""//启停响应队列
stompjs实例化
创建stomp-client.js文件,封装并初始化stomp实例
/* 初始化stomp client实例
* https://stomp-js.github.io/
* */
import {Client} from "@stomp/stompjs";
import {RMQ_ACCOUNT, RMQ_PASSWORD, RMQ_SERVER} from '@/config/stompjs/rabbitmq.config'
import {ElMessage} from "element-plus";
const conf = {
brokerURL: RMQ_SERVER,
connectHeaders: {
login: RMQ_ACCOUNT,
passcode: RMQ_PASSWORD
},
debug: (str) => {
console.log('STOMP: ' + str);
},
reconnectDelay: 5000,//断开重连
};//连接服务端的配置
let client = new Client(conf);//创建stomp客户端实例
export const connect = (...topics) => {
const callbacks = topics.reduce((obj, topic, index) => {
if (index % 2 === 0) {
obj[topic] = topics[index + 1];
}
return obj;
}, {})
client.onConnect = frame => {
topics.filter((topic, index) => index % 2 === 0).forEach(topic => {
// @ts-ignore
client.subscribe(topic, callbacks[topic], (err) => {
alert(err)
})
})
}
client.activate();
}
export const publishMsg=(destination, body)=> {
if (!client.connected) {
ElMessage.error("网络连接异常");
return -1;
}
client.publish({destination: destination, body: JSON.stringify(body)})
return 0;
}
/**
* @module: src\api\stomp-client.ts
* @desc: 断开连接
*/
export const disconnect=()=>{
client.deactivate();
}
订阅消息
使用subscribe()方法时必须包含两个参数:目的地destination,回调方法callback,还有一个可选参数headers;获取数据类型为String。
client.subscribe({destination:"",body:JSON.stringify(要发布的消息)})
【注】有多设备端访问,需要同时订阅的情况下需要开启fanout模式!
发布消息
使用publish()方法,必须包含参数: 目的地destination(String),消息体body。
在topic模式下destination为topic名称,存在交换机的模式下destination的格式如下拼接
"/exchang/exchange name/topic name"
应用
在入口文件index.vue中,创建连接以便于在启动项目时订阅相关消息
<template>
<el-container>
<el-header style="height: 10vh; border-bottom: 1px solid #CDD0D6">
<MainHeader></MainHeader>
</el-header>
<el-main style="height: 90vh; padding: 0">
<router-view></router-view>
</el-main>
</el-container>
</template>
<script setup>
import {nextTick, onMounted} from 'vue';
import {useHomeStore, useLogStore} from "@/store";
import {connect, publishMsg} from "@/config/stompjs/stomp-client.js";
import {ERROR_KEY, RES_KEY, RMQ_EXCHANGE, RMQ_KEY, RMQ_ROBOT, SOLVE_KEY} from "@/config/stompjs/rabbitmq.config";
import api from "@/api/exceptionLog/api";
import {storeToRefs} from "pinia";
import {useRoute, useRouter} from "vue-router";
const homeStore = useHomeStore();
const logStore = useLogStore();
const { errorList } = storeToRefs(logStore);
const tags = homeStore.tagList;
const router = useRouter();
const route = useRoute();
const getErrorMq = ()=>{
connect(
`/exchange/${ERROR_KEY}`, (x:any)=>{
//订阅异常
logStore.pushError(JSON.parse(x.body));
},
`/exchange/${SOLVE_KEY}`,(x:any)=>{
//异常消除
logStore.solveError(JSON.parse(x.body));
},
`/exchange/${RES_KEY}`, (x:any)=>{
//启停状态
let status = JSON.parse(x.body).systemStatus;
let type = JSON.parse(x.body).type;
console.log(status)
logStore.getRunningStatus(type, status);
},
`/exchange/${RMQ_ROBOT}`, (x:any)=>{
//手动消除
logStore.solveError(JSON.parse(x.body));
}
)
}
//向rabbitmq发布‘获取当前状态’消息
const publishGetStatus =()=>{
const body0 = {operate: 1, type: 0};//消息体
const body1 = {operate: 1, type: 1};
const destination = `/exchange/${RMQ_EXCHANGE}/${RMQ_KEY}`;
publishMsg(destination, body0);
publishMsg(destination, body1);
}
onMounted(()=>{
userListener();
api.getErrorData().then(res => {
errorList.value = res.resData;
})
//订阅mq队列
getErrorMq();
})
</script>