Spring WebSocket 与 STOMP 协议结合实现私聊私信功能

目录

    • 后端
      • pom.xml
      • Config配置类
      • Controller类
      • DTO
    • 前端
      • 安装相关依赖
      • websocketService.js接口
      • javascript
      • html
      • CSS
    • 效果展示
      • 简单测试连接:
    • 报错解决方法
      • 1、vue3 使用SockJS报错 ReferenceError: global is not defined
    • 功能补充拓展
      • 1. 安全性和身份验证
      • 2. 异常处理
      • 3. 消息广播的功能
      • 4. 配置 WebSocket 消息缓存和负载均衡
      • 5. 客户端连接管理
      • 6. WebSocket 消息格式和编码
      • 总结
    • 后面将继续完善,待更新...

后端

pom.xml

		<!-- Spring Boot WebSocket-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <!-- Spring Boot 数据库支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!-- MySQL 驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- Thymeleaf(如果你使用了模板) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!-- Spring Boot Web 支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
       <dependency>
      		<groupId>org.springframework.boot</groupId>
      		<artifactId>spring-boot-starter-data-jpa</artifactId>
      </dependency>

Config配置类

注意:允许源根据自己项目修改

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;


@Configuration
@EnableWebSocketMessageBroker
@Slf4j
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/queue", "/topic","/user");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws")
//        在 WebSocket 握手时,我们可以通过 URL 参数或者 HTTP headers 传递用户身份信息。
//                .addInterceptors(new MyHandshakeInterceptor())// 添加拦截器
                .setAllowedOrigins("http://127.0.0.1:8889", "http://localhost:8889", "http://localhost:8888", "http://127.0.0.1:8888", "http://localhost:8000","http://localhost:8890","http://127.0.0.1:8890")
                .withSockJS();  // 添加 SockJS 支持
    }
}

Controller类

import com.tianwen.mapper.UserMessagesMapper;
import com.tianwen.user.dtos.MessageDTO;
import com.tianwen.user.pojos.Messages;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;
import java.time.LocalDateTime;

@Controller
public class ChatController {

    @Autowired
    private UserMessagesMapper userMessagesMapper;

    @Autowired
    private SimpMessagingTemplate messagingTemplate; // 注入消息模板,用于发送消息到指定目的地

    @MessageMapping("/chat.sendMessage")//这个注解用来监听来自前端的 WebSocket 消息,路径是 /app/chat.sendMessage,当前端发送消息到这个路径时,sendMessage 方法会被触发。
//    @SendTo("/topic/messages")//这个注解表明处理完消息后,返回的消息将广播给订阅了 /topic/messages 路径的所有客户端。
    public MessageDTO sendMessage(MessageDTO messageDTO) throws Exception {
        System.out.println("接收到的message:"+messageDTO);
        // 1. 可以在这里进行私信存储到数据库操作
        Messages messages = new Messages();
        messages.setSenderId(messageDTO.getSenderId());
        messages.setReceiverId(messageDTO.getReceiverId());
        messages.setContent(messageDTO.getContent());
        messages.setCreateTime(LocalDateTime.now());

        // 2. 保存私信消息(插入操作)
        if (userMessagesMapper.insert(messages) <= 0) {
            // 如果插入失败,可以返回错误或做其他处理
            return null;
        }
        // 3. 实时将消息转发给接收者
        String receiverIdStr = String.valueOf(messageDTO.getReceiverId());  // 将 receiverId 转换为 String
        String receiverDestination = "/user/" + receiverIdStr + "/queue/messages";
        //通过 SimpMessagingTemplate 的 convertAndSendToUser 方法,将消息实时推送给接收者。
        //推送的目标是 /user/{receiverId}/queue/messages,该路径是给特定用户的私有消息队列。
        messagingTemplate.convertAndSendToUser(receiverIdStr, receiverDestination, messageDTO);

        return messageDTO;
    }
}

DTO

import lombok.Data;
@Data
public class MessageDTO {
    private Integer senderId;
    private Integer receiverId;
    private String content;
}

前端

安装相关依赖

npm install sockjs-client@latest
npm install @stomp/stompjs sockjs-client
npm install global    
npm i --save-dev @types/sockjs-client 

websocketService.js接口

注意:服务器地址根据自己的修改(application.yml)

// websocketService.js
// Stomp.js:用于处理 STOMP 协议,它在 WebSocket 基础上实现了消息订阅、发送等功能。
import { Stomp } from "@stomp/stompjs";
//SockJS:是一个用于实现 WebSocket 的库,它为 WebSocket 提供了回退机制(例如 HTTP 长轮询等),确保在不同浏览器和网络环境下的兼容性。
import SockJS from "sockjs-client/dist/sockjs.min.js";
export default {
  connect(onMessageReceived) {
    //使用 SockJS 和 Stomp 创建一个 WebSocket 客户端,连接到后端的 WebSocket 服务 http://localhost:8000/ws。
    const socket = new SockJS("http://localhost:8000/ws"); // 使用 SockJS 连接
    const stompClient = Stomp.over(socket);
    const userId = JSON.parse(localStorage.getItem("userId"));
    stompClient.connect({}, () => {
      console.log("本人消息队列ID:", userId);
      //在连接成功后,通过 stompClient.subscribe 订阅 /topic/messages,接收从后端广播过来的消息。
      // stompClient.subscribe("/topic/messages", (messageOutput) => {
      //   onMessageReceived(JSON.parse(messageOutput.body));
      // });
      // 订阅当前用户的私有消息队列
      stompClient.subscribe(
        "/user/" + userId + "/queue/messages",
        (messageOutput) => {
          onMessageReceived(JSON.parse(messageOutput.body)); // 处理接收到的私聊消息
        }
      );
      // 订阅当前用户的私有消息队列;
      // stompClient.subscribe(
      //   "/user/" + userId + "/queue/messages",
      //   function (messageOutput) {
      //     const message = JSON.parse(messageOutput.body);
      //     console.log("接收到私信:", message);
      //   }
      // );
    });
    // 连接到 WebSocket 后,订阅用户消息
    // stompClient.connect({}, function (frame) {
    //   // 获取当前用户ID
    //   const userId = getCurrentUserId(); // 假设这个方法能够获取当前用户的ID

    //   // 订阅接收者的消息队列
    // stompClient.subscribe(
    //   "/user/" + userId + "/queue/messages",
    //   function (messageOutput) {
    //     const message = JSON.parse(messageOutput.body);
    //     console.log("接收到私信:", message);
    //   }
    // );
    // });
  },

  sendMessage(message) {
    console.log("发送消息接口1:", message);
    const socket = new SockJS("http://localhost:8000/ws"); // 使用 SockJS 连接
    const stompClient = Stomp.over(socket);
    stompClient.connect({}, () => {
      //当用户输入消息时,通过 stompClient.send 方法将消息发送到 /app/chat.sendMessage,这个路径会将消息推送到后端进行处理。
      stompClient.send("/app/chat.sendMessage", {}, JSON.stringify(message));
    });
  },
};


javascript

<script setup lang="ts">
import '@wangeditor/editor/dist/css/style.css' // 引入 css
import { onBeforeUnmount, ref, shallowRef, onMounted } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import type { IToolbarConfig, IEditorConfig } from "@wangeditor/editor";
const editorRef = shallowRef();
import websocketService from "@/api/websocketService.js";
import {
  ref,
  onMounted,
} from "vue";
import websocketService from "@/api/websocketService.js";

const receiverIdAnswer = ref();
const sendPrivateMessage = () => {
  dialogVisible.value = true;
};
const sendPrivateMessage = async (userId) => {
  receiverIdAnswer.value = userId;
  dialogVisible.value = true;
  const response = await getAuthorDetailsByUserId(userId);
  console.log("response", userId);
  privateMessagesUser.value = response.data;
  console.log("privateMessagesUser", privateMessagesUser.value);
};

const dialogVisible = ref(false);

interface Message {
  id: string;
  senderId: string;
  receiverId: string;
  content: string;
}

const messages = ref<Message[]>([]); // 明确指定消息数组的类型

const newMessage = ref("");

onMounted(() => {
  websocketService.connect((message: Message) => {
    // 明确指定回调函数的参数类型
    messages.value.push(message);
  });
});

const sendMessage = () => {
  if (newMessage.value.trim()) {
    const message: Message = {
      // 明确声明消息类型
      id: Date.now().toString(), // 使用当前时间戳作为唯一 ID
      senderId: userInfo.value.id, // Example sender
      receiverId: receiverIdAnswer.value, // Example receiver
      content: newMessage.value,
    };
    websocketService.sendMessage(message);
    newMessage.value = "";
  }
};
const editorConfig: Partial<IEditorConfig> = {
  placeholder: "请输入...",
  MENU_CONF: {},
};
const handleCreated = (editor) => {
  editorRef.value = editor;
};
// 排除富文本的菜单项
const toolbarConfigPrivateMessages: Partial<IToolbarConfig> = {
  // toolbar 配置
  excludeKeys: [
    "headerSelect",
    "blockquote",
    "|",
    "bold",
    "underline",
    "italic",
    "group-more-style", // 排除菜单组,写菜单组 key 的值即可
    "color",
    "bgColor",
    "|",
    "fontSize",
    "fontFamily",
    "lineHeight",
    "bulletedList",
    "numberedList",
    "todo",
    "group-justify",
    "group-indent",
    "insertLink",
    "group-video",
    "insertTable",
    "codeBlock",
    "divider",
    "undo",
    "redo",
    "fullScreen",
  ],
};
</script>

html

 <!-- 私信聊天框 -->
  <el-dialog v-model="dialogVisible">
    <template #title>
      <div style="text-align: center; font-weight: bold">
        {{ privateMessagesUser.username }}
      </div>
      <hr class="line" />
    </template>
    <div class="chat-container">
      <div class="messages" ref="messagesContainer">
        <div
          v-for="message in messages"
          :key="message.id"
          :class="{
            'my-message': message.senderId === userInfo.id,
            'other-message': message.senderId !== userInfo.id,
          }"
        >
          <div style="display: flex; flex-direction: row">
            <div>
              <el-image
                :src="userInfo.avatarUrl"
                style="width: 45px; border-radius: 50%"
              ></el-image>
            </div>
            <div style="margin-top: 5px; margin-left: 10px">
              <div>
                <strong>{{ userInfo.username }}</strong>
              </div>
            <div
                class="message-bubble message-green"
                v-html="message.content"
              ></div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <div style="border: 1px solid #ccc">
      <Toolbar
        style="border-bottom: 1px solid #ccc"
        :editor="editorRef"
        :defaultConfig="toolbarConfigPrivateMessages"
        mode="default"
      />
      <Editor
        style="height: 200px; overflow-y: hidden"
        v-model="newMessage"
        @keyup.enter="sendMessage"
        :defaultConfig="editorConfig"
        mode="default"
        @onCreated="handleCreated"
      />
    </div>
    <template #footer>
      <el-button @click="dialogVisible = false">取消</el-button>
      <el-button type="primary" @click="sendMessage">发送</el-button>
    </template>
  </el-dialog>

CSS

<style scoped>
/* 私信样式 */
/* 标题居中 */
/* .private-message-dialog {
} */
.line {
  border-top: 1px solid #ccc; /* 直线的样式,可以修改颜色 */
}

/* 聊天框滚动 */
.chat-container {
  display: flex;
  flex-direction: column;
  height: 300px;
  overflow-y: auto;
  box-sizing: border-box; /* 让 padding 和 border 包含在宽度和高度内 */
}

.chat-container > * {
  width: 100%; /* 确保所有子元素不会超出容器宽度 */
  box-sizing: border-box; /* 确保子元素的宽度计算不受 padding 和 border 影响 */
}

/* 消息容器 */
.messages {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 10px;
  max-height: 250px;
  overflow-y: auto;
}

/* 发送方和接收方的消息样式 */
.my-message {
  /* text-align: right; */
  border-radius: 10px;
  height: auto;
  /* padding: 5px 10px; */
}

.other-message {
  /* text-align: left; */
  border-radius: 10px;
  /* padding: 5px 10px; */
}

.message-bubble {
  /* max-width: 70%; */
  padding: 0px 10px;
  border-radius: 10px;
  /* height: 30px; */
  /* margin: 0px 0px 0px 0px; */
  /* word-wrap: break-word; */
  /* line-height: 1.4; */
  font-size: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.message-green {
  background-color: #57c457; /* 微信消息绿色 */
  color: white;
  align-self: flex-end; /* 让气泡靠右显示 */
}
</style>

效果展示

在这里插入图片描述

简单测试连接:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

报错解决方法

1、vue3 使用SockJS报错 ReferenceError: global is not defined

解:

import SockJS from “sockjs-client”;

修改为:

import SockJS from “sockjs-client/dist/sockjs.min.js”;

并安装依赖

npm i --save-dev @types/sockjs-client

功能补充拓展

以下是一些可能的补充和优化,确保 WebSocket 能够顺利运行并且高效处理消息。

1. 安全性和身份验证

如果你的 WebSocket 服务需要进行身份验证(如用户登录),你可以考虑在 WebSocket 握手时验证用户身份。你可以在 WebSocketConfig 中添加一个 HandshakeInterceptor 来拦截握手请求,获取 HTTP header 或 URL 参数中的用户信息,确保只有经过身份验证的用户能够连接。

例如:

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/ws")
            .addInterceptors(new MyHandshakeInterceptor()) // 添加拦截器
            .setAllowedOrigins("http://localhost:8889")
            .withSockJS();
}

其中 MyHandshakeInterceptor 可以用来在 WebSocket 握手期间传递用户信息。

2. 异常处理

在 WebSocket 消息处理过程中,你可能会遇到一些异常,如数据库操作失败或消息传输失败等。在控制器中,你可以捕获这些异常并返回相应的错误消息,确保系统更加健壮。

例如:

@MessageMapping("/chat.sendMessage")
public MessageDTO sendMessage(MessageDTO messageDTO) {
    try {
        // 消息处理逻辑
    } catch (Exception e) {
        log.error("发送消息失败", e);
        return new MessageDTO("error", "消息发送失败");
    }
}

3. 消息广播的功能

目前,你的代码实现了将消息发送到指定用户的功能(私信)。如果你希望实现群聊功能或全局广播,可以进一步扩展 @SendTo 注解。这个注解的使用使得你可以将处理后的消息发送给所有订阅某个特定主题的客户端。

例如,广播消息给所有订阅 /topic/messages 的客户端:

@MessageMapping("/chat.sendMessage")
@SendTo("/topic/messages")
public MessageDTO sendMessage(MessageDTO messageDTO) {
    // 处理消息逻辑
    return messageDTO;  // 返回的消息会广播给所有订阅 /topic/messages 的客户端
}

4. 配置 WebSocket 消息缓存和负载均衡

如果你的系统需要处理大量的 WebSocket 连接,可能会面临性能和可扩展性的问题。在这种情况下,考虑使用 Redis 等消息队列作为消息代理,可以通过 @EnableWebSocketMessageBroker 配置远程消息代理。

例如,通过 Redis 实现消息广播:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/queue", "/topic", "/user");
        config.setApplicationDestinationPrefixes("/app");

        // 使用 Redis 消息代理
        config.setBrokerRegistry()
              .setApplicationDestinationPrefixes("/app")
              .enableStompBrokerRelay("/topic", "/queue")
              .setRelayHost("localhost")
              .setRelayPort(61613);  // 配置Redis(如ActiveMQ)等消息中间件
    }
}

5. 客户端连接管理

如果你希望在某个用户断开连接时进行一些清理工作(例如清理会话、推送通知等),你可以使用 @OnDisconnect 注解来捕获断开连接事件。

@MessageMapping("/chat.disconnect")
public void handleDisconnect(SessionDisconnectEvent event) {
    // 用户断开连接时执行的逻辑
    log.info("用户断开连接,sessionId: " + event.getSessionId());
}

6. WebSocket 消息格式和编码

确保你的客户端和服务端使用相同的消息格式。你可能需要为 WebSocket 消息提供适当的序列化和反序列化器,以确保消息能够正确地从客户端传输到服务端,以及从服务端传输回客户端。

例如,使用 Jackson 或其他库将 Java 对象序列化为 JSON 格式:

@MessageMapping("/chat.sendMessage")
@SendTo("/topic/messages")
public MessageDTO sendMessage(MessageDTO messageDTO) throws Exception {
    // 消息传输过程中确保使用适当的 JSON 格式
    return messageDTO;
}

总结

大体上,已经完成了 WebSocket 的配置和处理消息的核心部分,剩下的步骤主要是根据具体业务需求做扩展,如身份验证、消息缓存、广播支持等。如果你的应用规模较大,可能还需要考虑负载均衡和消息队列等高可用性的设计。

后面将继续完善,待更新…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/958453.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uniapp+Vue3(<script setup lang=“ts“>)模拟12306城市左右切换动画效果

效果图&#xff1a; 代码&#xff1a; <template><view class"container"><view class"left" :class"{ sliding: isSliding }" animationend"resetSliding">{{ placeA }}</view><view class"center…

缓存之美:万文详解 Caffeine 实现原理(下)

上篇文章&#xff1a;缓存之美&#xff1a;万文详解 Caffeine 实现原理&#xff08;上&#xff09; getIfPresent 现在我们对 put 方法有了基本了解&#xff0c;现在我们继续深入 getIfPresent 方法&#xff1a; public class TestReadSourceCode {Testpublic void doRead() …

Spring Security(maven项目) 3.0.2.6版本—总

通过实践而发现真理&#xff0c;又通过实践而证实真理和发展真理。从感性认识而能动地发展到理性认识&#xff0c;又从理性认识而能动地指导革命实践&#xff0c;改造主观世界和客观世界。实践、认识、再实践、再认识&#xff0c;这种形式&#xff0c;循环往复以至无穷&#xf…

C++函数——fill

在C中&#xff0c;std::fill 是标准库提供的一个算法适用于几乎所有类型的容器&#xff0c;只要这些容器支持迭代器操作。具体来说&#xff0c;std::fill 的适用性取决于容器是否提供了满足其要求的迭代器类型&#xff0c;用于将指定范围内的所有元素设置为某个特定值。它是一个…

jmeter中对接口进行循环请求后获取相应数据

1、工作中遇到一个场景就是对某个单一接口进行循环请求&#xff0c;并需要获取每次请求后返回的相应数据&#xff1b; 2、首先就在jmeter对接口相关组件进行配置&#xff0c;需要组件有&#xff1a;循环控制器、CSV数据文件设置、计数器、访问接口、HTTP信息头管理器、正则表达…

豆包MarsCode 蛇年编程大作战 | 高效开发“蛇年运势预测系统”

&#x1f31f; 嗨&#xff0c;我是LucianaiB&#xff01; &#x1f30d; 总有人间一两风&#xff0c;填我十万八千梦。 &#x1f680; 路漫漫其修远兮&#xff0c;吾将上下而求索。 豆包MarsCode 蛇年编程大作战 | &#x1f40d; 蛇年运势预测 在线体验地址&#xff1a;蛇年…

【豆包MarsCode蛇年编程大作战】花样贪吃蛇

目录 引言 展示效果 prompt提示信息 第一次提示&#xff08;实现基本功能&#xff09; 初次实现效果 第二次提示&#xff08;美化UI&#xff09; 第一次美化后的效果 第二次美化后的效果 代码展示 实现在线体验链接 码上掘金使用教程 体验地址&#xff1a; 花样贪吃蛇…

github汉化

本文主要讲述了github如何汉化的方法。 目录 问题描述汉化步骤1.打开github&#xff0c;搜索github-chinese2.打开项目&#xff0c;打开README.md3.下载安装脚本管理器3.1 在README.md中往下滑动&#xff0c;找到浏览器与脚本管理器3.2 选择浏览器对应的脚本管理器3.2.1 点击去…

K8S 启动探测、就绪探测、存活探测

先来思考一个问题&#xff1a; 在 Deployment 执行滚动更新 web 应用的时候&#xff0c;总会出现一段时间&#xff0c;Pod 对外提供网络访问&#xff0c;但是页面访问却发生404&#xff0c;这个问题要如何解决呢&#xff1f;学完今天的内容&#xff0c;相信你会有自己的答案。 …

如何使用CRM数据分析优化销售和客户关系?

嘿&#xff0c;大家好&#xff01;你有没有想过为什么有些公司在市场上如鱼得水&#xff0c;而另一些却在苦苦挣扎&#xff1f;答案可能就藏在他们的销售策略和客户关系管理&#xff08;CRM&#xff09;系统里。今天我们要聊的就是如何通过有效的 CRM 数据分析来提升你的销售额…

语音转文字的先驱-认识Buzz的前世今生

Buzz 是一款基于 OpenAI Whisper 模型开发的开源语音转文字工具&#xff0c;其历史可以追溯到 Whisper 模型的推出&#xff0c;并在之后逐渐发展为一个功能强大且广泛使用的工具。以下是关于 Buzz 的详细历史介绍&#xff1a; 1. Whisper 模型的背景 Buzz 的核心是 OpenAI 开…

宝塔Linux+docker部署nginx出现403 Forbidden

本文主要讲述了宝塔docker部署nginx出现403 Forbidden的原因&#xff0c;以及成功部署前端的方法步骤。 目录 1、问题描述2、问题检测2.1 检测监听端口是否异常2.2 检测Docker容器是否异常2.2.1 打开宝塔Linux的软件商店&#xff0c;找到Docker管理器&#xff0c;查看前端容器是…

LabVIEW项目中的工控机与普通电脑选择

工控机&#xff08;Industrial PC&#xff09;与普通电脑在硬件设计、性能要求、稳定性、环境适应性等方面存在显著差异。了解这些区别对于在LabVIEW项目中选择合适的硬件至关重要。下面将详细分析这两种设备的主要差异&#xff0c;并为LabVIEW项目中的选择提供指导。 ​ 硬件设…

QT6 + CMAKE编译OPENCV3.9

参考文档 [1] https://blog.csdn.net/rjkf_css/article/details/135676077 前提条件 配置好相关运行环境&#xff1a;QT6、OPENCV3.9的sources文件 OPENCV下载网页&#xff1a;https://opencv.org/releases/ QT6下载教程&#xff1a;https://blog.csdn.net/caoshangpa/article…

消息队列篇--基础篇(消息队列特点,应用场景、点对点和发布订阅工作模式,RabbmitMQ和Kafka代码示例等)

1、消息队列的介绍 消息&#xff08;Message&#xff09;是指在应用之间传送的数据&#xff0c;消息可以非常简单&#xff0c;比如只包含文本字符串&#xff0c;也可以更复杂&#xff0c;可能包含嵌入对象。 消息队列&#xff08;Message Queue&#xff0c;简称MQ&#xff09…

状态模式——C++实现

目录 1. 状态模式简介 2. 代码示例 3. 单例状态对象 4. 状态模式与策略模式的辨析 1. 状态模式简介 状态模式是一种行为型模式。 状态模式的定义&#xff1a;状态模式允许对象在内部状态改变时改变它的行为&#xff0c;对象看起来好像修改了它的类。 通俗的说就是一个对象…

GESP202309 三级【进制判断】题解(AC)

》》》点我查看「视频」详解》》》 [GESP202309 三级] 进制判断 题目描述 N N N 进制数指的是逢 N N N 进一的计数制。例如&#xff0c;人们日常生活中大多使用十进制计数&#xff0c;而计算机底层则一般使用二进制。除此之外&#xff0c;八进制和十六进制在一些场合也是常用…

汽车敏捷开发:项目经理如何精准跟进项目流程

在敏捷开发环境中&#xff0c;项目经理身兼协调者、推动者、决策者等关键角色。 作为协调者&#xff0c;需在团队及部门间搭建沟通桥梁&#xff0c;确保信息流畅。 作为推动者&#xff0c;面对迭代中的技术难题、资源短缺等阻碍&#xff0c;要主动寻找解决方案&#xff0c;为…

数据从前端传到后端入库过程分析

数据从前端传到后端入库过程分析 概述 积累了一些项目经验&#xff0c;成长为一个老程序员了&#xff0c;自认为对各种业务和技术都能得心应手的应对了&#xff0c;殊不知很多时候我们借助了搜索引擎的能力&#xff0c;当然现在大家都是通过AI来武装自己。 今天要分析的话题是…

Netty 实战

Netty实践 1 Netty 版本选择2 Netty 模版代码2.1 Server2.2 Client 3 组件3.1 EventLoop、EventLoopGroup3.1.1 EventLoop3.1.2 EventLoopGroup 3.2 Channel3.2.1 ChannelFuture3.2.2 CloseFuture 3.3 ChannelHandler3.2.1 常用的 ChannelInboundHandlerAdapter3.2.1.1 LineBas…