Springboot 整合Websocket+Stomp协议+RabbitMQ做消息代理 实例教程

}

@Bean

TopicExchange exchange() {

return new TopicExchange(“topicWebSocketExchange”,true,false);

}

//将firstQueue和topicExchange绑定,而且绑定的键值为topic.man

//这样只要是消息携带的路由键是topic.man,才会分发到该队列

@Bean

Binding bindingExchangeMessage() {

return BindingBuilder.bind(topicQueue()).to(exchange()).with(msgTopicKey);

}

@Bean

public ConnectionFactory connectionFactory() {

CachingConnectionFactory connectionFactory = new CachingConnectionFactory(“127.0.0.1”, 5672);

connectionFactory.setUsername(“root”);

connectionFactory.setPassword(“root”);

connectionFactory.setVirtualHost(“JCChost”);

connectionFactory.setPublisherConfirms(true); // 发送消息回调,必须要设置

connectionFactory.setPublisherReturns(true);

return connectionFactory;

}

@Bean

public RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory){

RabbitTemplate rabbitTemplate = new RabbitTemplate();

rabbitTemplate.setConnectionFactory(connectionFactory);

//设置开启Mandatory,才能触发回调函数,无论消息推送结果怎么样都强制调用回调函数

rabbitTemplate.setMandatory(true);

rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {

@Override

public void confirm(CorrelationData correlationData, boolean ack, String cause) {

System.out.println("ConfirmCallback: "+“相关数据:”+correlationData);

System.out.println("ConfirmCallback: "+“确认情况:”+ack);

System.out.println("ConfirmCallback: "+“原因:”+cause);

}

});

rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {

@Override

public void returnedMessage(org.springframework.amqp.core.Message message, int replyCode, String replyText, String exchange, String routingKey) {

System.out.println("ReturnCallback: "+“消息:”+message);

System.out.println("ReturnCallback: "+“回应码:”+replyCode);

System.out.println("ReturnCallback: "+“回应信息:”+replyText);

System.out.println("ReturnCallback: "+“交换机:”+exchange);

System.out.println("ReturnCallback: "+“路由键:”+routingKey);

}

});

return rabbitTemplate;

}

/**

  • 接受消息的监听,这个监听会接受消息队列topicQueue的消息

  • 针对消费者配置

  • @return

*/

@Bean

public SimpleMessageListenerContainer messageContainer() {

SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory());

container.setQueues(topicQueue());

container.setExposeListenerChannel(true);

container.setMaxConcurrentConsumers(1);

container.setConcurrentConsumers(1);

container.setAcknowledgeMode(AcknowledgeMode.MANUAL); //设置确认模式手工确认

container.setMessageListener(new ChannelAwareMessageListener() {

public void onMessage(Message message, com.rabbitmq.client.Channel channel) throws Exception {

byte[] body = message.getBody();

String msg = new String(body);

System.out.println("rabbitmq收到消息 : " +msg);

Boolean sendToWebsocket = chatService.sendMsg(msg);

if (sendToWebsocket){

System.out.println(“消息处理成功! 已经推送到websocket!”);

channel.basicAck(message.getMessageProperties().getDeliveryTag(), true); //确认消息成功消费

}

}

});

return container;

}

}

看到这么多代码是不是一下子有点懵,没事,看我的MyRabbitConfig代码简析 :

1.连接rabbitmq代码

2.简单使用一下主题交换机

(其实可以配置更多的交换机,对于使用rabbitmq方面,完全没接触过的小伙伴,不妨先看看这篇文章,《Springboot 整合RabbitMq ,用心看完这一篇就够了》 链接: https://blog.csdn.net/qq_35387940/article/details/100514134

3.配置生产者推送消息触发的回调函数

4.消费者监听器,采取开启手动确认

ok,到这里我们可以看到整合上rabbitmq的相关内容,完毕。

那么还有接收消息使用到的实体类,处理消息的业务类,也就是

ChatMessage.java:

这里实体类比较简单,大家可以自己扩展符合自己消息内容场景的。

/**

  • @Author : JCccc

  • @CreateTime : 2020/8/26

  • @Description :

**/

public class ChatMessage {

private MessageType type;

private String content;

private String sender;

private String to;

public enum MessageType {

CHAT,

JOIN,

LEAVE

}

@Override

public String toString() {

return “ChatMessage{” +

“type=” + type +

“, content='” + content + ‘’’ +

“, sender='” + sender + ‘’’ +

“, to='” + to + ‘’’ +

‘}’;

}

public MessageType getType() {

return type;

}

public void setType(MessageType type) {

this.type = type;

}

public String getContent() {

return content;

}

public void setContent(String content) {

this.content = content;

}

public String getSender() {

return sender;

}

public void setSender(String sender) {

this.sender = sender;

}

public String getTo() {

return to;

}

public void setTo(String to) {

this.to = to;

}

}

然后是ChatService.java:

import cn.jc.springbootwebsocketdemo.model.ChatMessage;

import com.alibaba.fastjson.JSONObject;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.messaging.MessagingException;

import org.springframework.messaging.simp.SimpMessageSendingOperations;

import org.springframework.stereotype.Service;

/**

  • @Author : JCccc

  • @CreateTime : 2020/8/26

  • @Description :

**/

@Service

public class ChatService {

@Autowired

private SimpMessageSendingOperations simpMessageSendingOperations;

public Boolean sendMsg(String msg) {

try {

JSONObject msgJson = JSONObject.parseObject(msg);

if (msgJson.getString(“to”).equals(“all”) && msgJson.getString(“type”).equals(ChatMessage.MessageType.CHAT.toString())){

simpMessageSendingOperations.convertAndSend(“/topic/public”, msgJson);

}else if (msgJson.getString(“to”).equals(“all”) && msgJson.getString(“type”).equals(ChatMessage.MessageType.JOIN.toString())) {

simpMessageSendingOperations.convertAndSend(“/topic/public”, msgJson);

}else if(msgJson.getString(“to”).equals(“all”) && msgJson.getString(“type”).equals(ChatMessage.MessageType.LEAVE.toString())) {

simpMessageSendingOperations.convertAndSend(“/topic/public”, msgJson);

}else if (!msgJson.getString(“to”).equals(“all”) && msgJson.getString(“type”).equals(ChatMessage.MessageType.CHAT.toString())){

simpMessageSendingOperations.convertAndSendToUser(msgJson.getString(“to”),“/topic/msg”, msgJson);

}

} catch (MessagingException e) {

e.printStackTrace();

return false;

}

return true;

}

}

这个很关键,该实例的点对点推送消息,群发的处理代码都在这个业务处理类里面了,

那么也是稍微做下代码简析:

简单点就是 convertAndSend 和 convertAndSendToUser的使用, 一个是群发,一个是单发(单发会在主题前拼接/user)

还有使用到的json转换工具类,其实也就是fastjosn的方法:

import com.alibaba.fastjson.JSON;

import com.alibaba.fastjson.JSONObject;

/**

  • @Author : JCccc

  • @CreateTime : 2020/8/26

  • @Description :

**/

public final class JsonUtil {

/**

  • 把Java对象转换成json字符串

  • @param object 待转化为JSON字符串的Java对象

  • @return json 串 or null

*/

public static String parseObjToJson(Object object) {

String string = null;

try {

string = JSONObject.toJSONString(object);

} catch (Exception e) {

System.out.println(e.getMessage());

}

return string;

}

/**

  • 将Json字符串信息转换成对应的Java对象

  • @param json json字符串对象

  • @param c 对应的类型

*/

public static T parseJsonToObj(String json, Class c) {

try {

JSONObject jsonObject = JSON.parseObject(json);

return JSON.toJavaObject(jsonObject, c);

} catch (Exception e) {

System.out.println(e.getMessage());

}

return null;

}

}

这一小段咱们完成了这几个文件:

好了,到这里,我们后端的代码也就完成了。

噢对了,还有我们的一个较为关键的,使用@MessageMapping 的 controller,

创建ChatController.java:

import cn.jc.springbootwebsocketdemo.model.ChatMessage;

import cn.jc.springbootwebsocketdemo.util.JsonUtil;

import org.springframework.amqp.rabbit.core.RabbitTemplate;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.messaging.handler.annotation.MessageMapping;

import org.springframework.messaging.handler.annotation.Payload;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.ResponseBody;

import java.security.Principal;

/**

  • @Author : JCccc

  • @CreateTime : 2020/8/26

  • @Description :

**/

@Controller

public class ChatController {

@Autowired

private RabbitTemplate rabbitTemplate;

/**

  • 服务端推送给单人的接口

  • @param uid

  • @param content

*/

@ResponseBody

@GetMapping(“/sendToOne”)

public void sendToOne(@RequestParam(“uid”) String uid,@RequestParam(“content”) String content ){

ChatMessage chatMessage=new ChatMessage();

chatMessage.setType(ChatMessage.MessageType.CHAT);

chatMessage.setContent(content);

chatMessage.setTo(uid);

chatMessage.setSender(“系统消息”);

rabbitTemplate.convertAndSend(“topicWebSocketExchange”,“topic.public”, JsonUtil.parseObjToJson(chatMessage));

}

/**

  • 接收 客户端传过来的消息 通过setSender和type 来判别时单发还是群发

  • @param chatMessage

  • @param principal

*/

@MessageMapping(“/chat.sendMessageTest”)

public void sendMessageTest(@Payload ChatMessage chatMessage, Principal principal) {

try {

String name = principal.getName();

chatMessage.setSender(name);

rabbitTemplate.convertAndSend(“topicWebSocketExchange”,“topic.public”, JsonUtil.parseObjToJson(chatMessage));

} catch (Exception e) {

System.out.println(e.getMessage());

}

}

/**

  • 接收 客户端传过来的消息 上线消息

  • @param chatMessage

*/

@MessageMapping(“/chat.addUser”)

public void addUser(@Payload ChatMessage chatMessage) {

System.out.println(“有用户加入到了websocket 消息室” + chatMessage.getSender());

try {

System.out.println(chatMessage.toString());

rabbitTemplate.convertAndSend(“topicWebSocketExchange”,“topic.public”, JsonUtil.parseObjToJson(chatMessage));

} catch (Exception e) {

System.out.println(e.getMessage());

}

}

}

我们也稍微做个代码简析:

那么为了做个简单的实力测试,我们也搞了点页面:

main.css:

  • {

-webkit-box-sizing: border-box;

-moz-box-sizing: border-box;

box-sizing: border-box;

}

html,body {

height: 100%;

overflow: hidden;

}

body {

margin: 0;

padding: 0;

font-weight: 400;

font-family: “Helvetica Neue”, Helvetica, Arial, sans-serif;

font-size: 1rem;

line-height: 1.58;

color: #333;

background-color: #f4f4f4;

height: 100%;

}

body:before {

height: 50%;

width: 100%;

position: absolute;

top: 0;

left: 0;

background: #128ff2;

content: “”;

z-index: 0;

}

.clearfix:after {

display: block;

content: “”;

clear: both;

}

.hidden {

display: none;

}

.form-control {

width: 100%;

min-height: 38px;

font-size: 15px;

border: 1px solid #c8c8c8;

}

.form-group {

margin-bottom: 15px;

}

input {

padding-left: 10px;

outline: none;

}

h1, h2, h3, h4, h5, h6 {

margin-top: 20px;

margin-bottom: 20px;

}

h1 {

font-size: 1.7em;

}

a {

color: #128ff2;

}

button {

box-shadow: none;

border: 1px solid transparent;

font-size: 14px;

outline: none;

line-height: 100%;

white-space: nowrap;

vertical-align: middle;

padding: 0.6rem 1rem;

border-radius: 2px;

transition: all 0.2s ease-in-out;

cursor: pointer;

min-height: 38px;

}

button.default {

background-color: #e8e8e8;

color: #333;

box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.12);

}

button.primary {

background-color: #128ff2;

box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.12);

color: #fff;

}

button.accent {

background-color: #ff4743;

box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.12);

color: #fff;

}

#username-page {

text-align: center;

}

.username-page-container {

background: #fff;

box-shadow: 0 1px 11px rgba(0, 0, 0, 0.27);

border-radius: 2px;

width: 100%;

max-width: 500px;

display: inline-block;

margin-top: 42px;

vertical-align: middle;

position: relative;

padding: 35px 55px 35px;

min-height: 250px;

position: absolute;

top: 50%;

left: 0;

right: 0;

margin: 0 auto;

margin-top: -160px;

}

.username-page-container .username-submit {

margin-top: 10px;

}

#chat-page {

position: relative;

height: 100%;

}

.chat-container {

max-width: 700px;

margin-left: auto;

margin-right: auto;

background-color: #fff;

box-shadow: 0 1px 11px rgba(0, 0, 0, 0.27);

margin-top: 30px;

height: calc(100% - 60px);

max-height: 600px;

position: relative;

}

#chat-page ul {

list-style-type: none;

background-color: #FFF;

margin: 0;

overflow: auto;

overflow-y: scroll;

padding: 0 20px 0px 20px;

height: calc(100% - 150px);

}

#chat-page #messageForm {

padding: 20px;

}

#chat-page ul li {

line-height: 1.5rem;

padding: 10px 20px;

margin: 0;

border-bottom: 1px solid #f4f4f4;

}

#chat-page ul li p {

margin: 0;

}

#chat-page .event-message {

width: 100%;

text-align: center;

clear: both;

}

#chat-page .event-message p {

color: #777;

font-size: 14px;

word-wrap: break-word;

}

#chat-page .chat-message {

padding-left: 68px;

position: relative;

}

#chat-page .chat-message i {

position: absolute;

width: 42px;

height: 42px;

overflow: hidden;

left: 10px;

display: inline-block;

vertical-align: middle;

font-size: 18px;

line-height: 42px;

color: #fff;

text-align: center;

border-radius: 50%;

font-style: normal;

text-transform: uppercase;

}

#chat-page .chat-message span {

color: #333;

font-weight: 600;

}

#chat-page .chat-message p {

color: #43464b;

}

#messageForm .input-group input {

float: left;

width: calc(100% - 85px);

}

#messageForm .input-group button {

float: left;

width: 80px;

height: 38px;

margin-left: 5px;

}

.chat-header {

text-align: center;

padding: 15px;

border-bottom: 1px solid #ececec;

}

.chat-header h2 {

margin: 0;

font-weight: 500;

}

.connecting {

padding-top: 5px;

text-align: center;

color: #777;

position: absolute;

top: 65px;

width: 100%;

}

@media screen and (max-width: 730px) {

.chat-container {

margin-left: 10px;

margin-right: 10px;

margin-top: 10px;

}

}

@media screen and (max-width: 480px) {

.chat-container {

height: calc(100% - 30px);

}

.username-page-container {

width: auto;

margin-left: 15px;

margin-right: 15px;

padding: 25px;

}

#chat-page ul {

height: calc(100% - 120px);

}

#messageForm .input-group button {

width: 65px;

}

#messageForm .input-group input {

width: calc(100% - 70px);

}

.chat-header {

padding: 10px;

}

.connecting {

top: 60px;

}

.chat-header h2 {

font-size: 1.1em;

}

}

main.js :

‘use strict’;

var usernamePage = document.querySelector(‘#username-page’);

var chatPage = document.querySelector(‘#chat-page’);

var usernameForm = document.querySelector(‘#usernameForm’);

var messageForm = document.querySelector(‘#messageForm’);

var messageInput = document.querySelector(‘#message’);

var messageArea = document.querySelector(‘#messageArea’);

var connectingElement = document.querySelector(‘.connecting’);

var stompClient = null;

var username = null;

var colors = [

‘#2196F3’, ‘#32c787’, ‘#00BCD4’, ‘#ff5652’,

‘#ffc107’, ‘#ff85af’, ‘#FF9800’, ‘#39bbb0’

];

function connect(event) {

username = document.querySelector(‘#name’).value.trim();

if(username) {

usernamePage.classList.add(‘hidden’);

chatPage.classList.remove(‘hidden’);

var socket = new SockJS(‘/ws’);

stompClient = Stomp.over(socket);

stompClient.connect({

username:username

}, onConnected, onError);

}

event.preventDefault();

}

function onConnected() {

// Subscribe to the Public Topic

stompClient.subscribe(‘/topic/public’, onMessageReceived);

//额外订阅了mike频道,暂时当做自己的频道,别人需要特意往这个频道发送消息,才能完成 ‘单对单’

stompClient.subscribe(‘/user/topic/msg’, onMessageReceived);

// Tell your username to the server

stompClient.send(“/app/chat.addUser”,

{},

JSON.stringify({sender: username, type: ‘JOIN’,to:‘all’})

)

connectingElement.classList.add(‘hidden’);

}

function onError(error) {

connectingElement.textContent = ‘Could not connect to WebSocket server. Please refresh this page to try again!’;

connectingElement.style.color = ‘red’;

}

function sendMessage(event) {

var messageContent = messageInput.value.trim();

if(messageContent && stompClient) {

var chatMessage = {

sender: username,

content: messageInput.value,

type: ‘CHAT’,

to:‘all’

};

console.log(“----- :”+ messageInput.value);

stompClient.send(“/app/chat.sendMessageTest”, {}, JSON.stringify(chatMessage));

messageInput.value = ‘’;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
img

前端框架

前端框架太多了,真的学不动了,别慌,其实对于前端的三大马车,Angular、React、Vue 只要把其中一种框架学明白,底层原理实现,其他两个学起来不会很吃力,这也取决于你以后就职的公司要求你会哪一个框架了,当然,会的越多越好,但是往往每个人的时间是有限的,对于自学的学生,或者即将面试找工作的人,当然要选择一门框架深挖原理。

以 Vue 为例,我整理了如下的面试题。

Vue部分截图

tContent = ‘Could not connect to WebSocket server. Please refresh this page to try again!’;

connectingElement.style.color = ‘red’;

}

function sendMessage(event) {

var messageContent = messageInput.value.trim();

if(messageContent && stompClient) {

var chatMessage = {

sender: username,

content: messageInput.value,

type: ‘CHAT’,

to:‘all’

};

console.log(“----- :”+ messageInput.value);

stompClient.send(“/app/chat.sendMessageTest”, {}, JSON.stringify(chatMessage));

messageInput.value = ‘’;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-ZTOVMUwm-1712109471280)]
[外链图片转存中…(img-1VxoGLEB-1712109471281)]
[外链图片转存中…(img-Z5ThSVdb-1712109471281)]
[外链图片转存中…(img-620mhHkW-1712109471282)]
[外链图片转存中…(img-APC0LKPH-1712109471282)]
[外链图片转存中…(img-8pGM0szI-1712109471282)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-9aJY8Qti-1712109471283)]

前端框架

前端框架太多了,真的学不动了,别慌,其实对于前端的三大马车,Angular、React、Vue 只要把其中一种框架学明白,底层原理实现,其他两个学起来不会很吃力,这也取决于你以后就职的公司要求你会哪一个框架了,当然,会的越多越好,但是往往每个人的时间是有限的,对于自学的学生,或者即将面试找工作的人,当然要选择一门框架深挖原理。

以 Vue 为例,我整理了如下的面试题。

Vue部分截图

CodeChina开源项目:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值