使用Springboot + netty 打造聊天服务之Nacos集群问题记录
目录
- 1、前言
- 1.1、方法一
- 1.2、方法二
- 2、方案二实战
- 2.1、在netty服务里加上ws连接、中断事件
- 2.2、在netty服务里加上消息服务
- 4、总结
使用Springboot + netty 打造聊天服务系列文章
第一章 初始搭建工程
第二章 Nacos集群问题记录
1、前言
在使用Springboot + Nacos + Netty(WebSocket) 集群后,发现了一个问题。
在集群环境下, X用户已经连接上了集群中的A服务器,这时Y用户发送给X用户的消息在B服务器,那么此时的消息应该如何处理呢?
1.1、方法一
通过广播的模式,把消息发送到MQ(且带有netty的channelId),netty集群的服务都订阅这个MQ。
通过对比channelId,不存在channelId的丢弃消息不处理。存在channelId的服务,处理此消息,并通过channel把消息推送给X用户。
1.2、方法二
发消息时,去寻找对应用户X的channel。
1、从缓存里获取用户X对应的channelId等信息,首先判断是否在缓存里,如果没有即用户X不在线,用户X下次连接netty服务时,再去推送消息;
2、如果缓存里有,判断此channel是否在当前服务中,
首先判断当前服务里是否有用户X对应的channelId,如果有,直接通过channel发送消息给用户X;
3、如果没有,则去组装IP、端口,去调用此服务的消息服务去发送消息。
2、方案二实战
方案一非常简单,订阅MQ即可实现,网上案例大多基于此。
我们今天重点讲解方案二,在netty服务里,加入消息服务,后续通过匹配用户X的channel去发送消息;
2.1、在netty服务里加上ws连接、中断事件
在ws连接时,本地服务器加入channelId、channel的缓存;
同时把channelId、本机IP、本机端口放入redis缓存(供远程消息服务调用)。
2.2、在netty服务里加上消息服务
1、通过传递来的channelId,从缓存里找到对应的服务(IP、端口);
2、调用对应的消息服务(IP、端口加上消息服务的地址)
3、消息服务里,铜鼓
import cn.hutool.core.bean.BeanUtil;
import com.qhkj.nettychatserver.bean.domain.Message;
import com.qhkj.nettychatserver.bean.request.MessageRequest;
import com.qhkj.nettychatserver.config.NettyConfig;
import com.qhkj.nettychatserver.config.http.HttpResult;
import com.qhkj.nettychatserver.config.http.HttpResultGenerator;
import com.qhkj.nettychatserver.config.http.HttpStatusEnum;
import com.qhkj.nettychatserver.constant.Common;
import com.qhkj.nettychatserver.constant.NettyCommon;
import com.qhkj.nettychatserver.netty.NettyHandler;
import com.qhkj.nettychatserver.service.MessageService;
import com.qhkj.nettychatserver.util.RedisUtil;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import com.qhkj.nettychatserver.bean.request.NettyMesaage;
import com.qhkj.nettychatserver.service.NettyService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSON;
import io.netty.channel.Channel;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;@Slf4j
@Service("chat")
public class ChatNettyServiceImpl implements NettyService {@Resourceprivate MessageService messageService;@Resourceprivate NettyConfig nettyConfig;@Resourceprivate RedisUtil redisUtil;@Resourceprivate RestTemplate restTemplate;// 确定channel之后,发送消息private void nettyHandler(NettyMesaage message, Channel channel) {log.info("message-> channelId:{} , nettyName: {}", channel.id(), nettyConfig.getNettyServerName());Date now = new Date();Message dbmsg = Message.builder().messageId(NettyCommon.getIdWorker().nextId()).createTime(now).modifyTime(now).build();BeanUtil.copyProperties(message, dbmsg, Common.options);boolean flag = messageService.insertOne(dbmsg);if (flag) {channel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(HttpResultGenerator.success(nettyConfig.getNettyServerName()))));} else {channel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(HttpResultGenerator.fail(HttpStatusEnum.INTERNAM_SERVER_ERROR.getCode(), nettyConfig.getNettyServerName() + ""))));}}@Overridepublic HttpResult nettyHandler(MessageRequest request) {NettyMesaage nettyMesaage = new MessageRequest();BeanUtil.copyProperties(request, nettyMesaage);String serverInfo = (String) redisUtil.get(request.getChannelId());if(StringUtils.isEmpty(serverInfo)) {log.info("用户不在线!");return HttpResultGenerator.success("用户不在线!");}Channel channel = NettyHandler.channelMap.get(request.getChannelId());// 本机与用户有连接if(null != channel) {this.nettyHandler(nettyMesaage, channel);} else {String url = "http://" + serverInfo + "/msg/send";HashMap jsonObject = restTemplate.postForObject(url, request, HashMap.class);if( !jsonObject.get("code").equals(200) ) {log.info("消息发送失败!");return HttpResultGenerator.fail(HttpStatusEnum.SERVER_BUSY.getCode(),"消息发送失败");}}return HttpResultGenerator.success("消息发送成功!");}
}
4、总结
文章写完之后,发现第二种方法问题特别多,需要在用户上下线(ws连接、掉线、netty服务销毁等)时,使用缓存记录用户与服务器的关系。在消息发送给接收方时,从缓存里取出接收方服务器信息,通过接收方服务器通知接收方有新消息。
最后给出2张消息服务架构简图:
1、集群版
2、单机版
分析单机版,客户端和netty之间的压力是相当小,万人同时在线,人均每秒2条消息,所需带宽也仅仅接近2MB,对应的内存消耗也是非常之小,几乎也是毫无压力。理论上来说,netty单机,10MB带宽、100MB内存就可以支撑5万用户(当然还得维护在线用户channel池、写数据到消息队列等等消耗,支撑2万在线用户肯定是没问题)。
而与之配套的数据服务系统(获取数据、解析消息并存库等),就可以做成集群,分配更大内存、更多的机器,去支撑快速增长的用户。
相关文章:
使用Springboot + netty 打造聊天服务之Nacos集群问题记录
目录 1、前言1.1、方法一1.2、方法二 2、方案二实战2.1、在netty服务里加上ws连接、中断事件2.2、在netty服务里加上消息服务 4、总结 使用Springboot netty 打造聊天服务系列文章 第一章 初始搭建工程 第二章 Nacos集群问题记录 1、前言 在使用Springboot Nacos Netty(Web…...
全网唯一!R语言顶刊配色包TheBestColors
与Matlab相比,R语言在绘图方面有着天然的优势。 比如在配色方面,R语言有各式各样现成的包,按理说配色这种事应该很方便才对。 但实际体验下来,发现似乎不是那么回事。 首先,你很难记住每个包的调用方法以及每种配色…...
链表题型思路错误总结
常见题目 206. 反转链表 关键点:定义前置指针。 在给cur.next复制前,需要定义好next节点防止断链。 public ListNode reverseList(ListNode head) {if (head null || head.next null) {return head;}ListNode pre null;ListNode cur head;while(cur…...
算法学习day28
一、寻找右区间(二分法) 题意:题目很容易理解 但是转换为二分法有点晦涩 给你一个区间数组 intervals ,其中 intervals[i] [starti, endi] ,且每个 starti 都 不同 。区间 i 的 右侧区间 可以记作区间 j ,并满足 startj > e…...
C语言基础题:迷宫寻路(C语言版)
1.题目描述 机器猫被困在一个矩形迷宫里。 迷宫可以视为一个n x m 矩阵,每个位置要么是空地,要么是墙。机器猫只能从一个空地走到其上、下、左、右的空地。 机器猫初始时位于(1,1)的位置,问能否走到(n,m)位置。 2.输入格式 第一行࿰…...
力扣-1两数之和2两数相加-2024/8/3
1、两数之和 解法一 暴力法(2个for循环) class Solution:def twoSum(self, nums: List[int], target: int) -> List[int]:for ii in range(len(nums)):for jj in range(ii1, len(nums)):if nums[ii]nums[jj] target:return [ii,jj]解法二 哈希表法…...
简站WordPress主题 专业的WordPress建站服务商
简站WordPress主题是一款备受推崇的WordPress主题,以其简洁、实用、无插件和更安全的特性脱颖而出。以下是关于简站WordPress主题的一些详细分析: 简站WordPress主题采用了扁平化设计风格,界面简洁明了,这使得网站看起来更加专业…...
Final Shell for Mac 虚拟机连接工具【简单易操作,轻松上手】【开发所需连接工具】
Mac分享吧 文章目录 效果一、下载软件二、安装软件三、运行测试安装完成!!! 效果 一、下载软件 下载软件 链接:http://www.macfxb.cn 二、安装软件 三、运行测试 安装完成!!!...
Oracle JDK:版本、支持与许可
文章目录 版本支持许可BCLOTNNFTCFAQ其他OpenJDK和其他的JDK实现JDK、JRE、JVMJava SE、Java EE、Java ME版本 Oracle JDK的最新版本和历史版本的官方下载地址(可查询版本发行说明等信息):https://www.oracle.com/cn/java/technologies/downloads/ 常规版本(非LTS):每隔…...
大模型学习笔记 - LLM 之RLHF人类对齐的简单总结
LLM - RLHF人类对齐的简单总结 LLM-人类对齐 1. RLHF(Reinforcement Learning from Human Feedback, RLHF),基于人类反馈的强化学习2 奖励模型训练3 强化学习训练 3.1 PPO介绍3.2 进阶的RLHF的介绍 3.2.1. 过程监督奖励模型3.2.2. 基于AI反馈的强化学习3.2.3. 非强化学习的对齐…...
【从零开始一步步学习VSOA开发】 概述
概述 概念 VSOA(Vehicle SOA)是翼辉为了解决任务关键型系统不能适用当前微服务通信架构问题而设计的⼀个轻量级适用于任务关键领域的微服务通信架构,以方便开发者构建大型分布式松耦合软件系统,且支持并行开发。 特点 其主要特…...
小程序背景图片无法通过 WXSS 获取
问题:pages/index/index.wxss 中的本地资源图片无法通过 WXSS 获取 可以使用网络图片,或者 base64,或者使用标签。 将图片转换为base64,地址 base64图片在线转换工具 - 站长工具 在这里把要使用的图片转换一把,然后将得…...
CC++内存魔术:掌控无形资源
hello,uu们,今天呢我们来详细讲解C&C的内存管理,好啦,废话不多讲,开干 1:C/C内存分布 2:C语言中动态内存管理方式:malloc/calloc/realloc/free 3:C内存管理方式 3.1:new/delete操作内置类型 3.1.1:代码1 3.1.2:代码2 3.2:new和delete操作自定义类型 3.2.1:C语言创建…...
算法--初阶
1、tips 1.1、set求交集 {1,2,3} & {2,3} & {1,2} {2} 其实就是位运算, 只有set可以这样使用, list没有这种用法 {1,2,3} | {2,3, 4} | {1,2} {1, 2, 3, 4} 并集 1.2、*与** * 序列(列表、元组)解包,如果是字典,那…...
通过Java实现插入排序(直接插入,希尔)与选择排序(直接选择,堆排)
目录 (一)插入排序 1.直接插入排序 (1)核心思想: (2)代码实现(以从小到大排序为例): (3)代码分析: 2.希尔排序(…...
大型分布式B2B2C多用户商城7.0企业版源码分享【java语言、方便二次开发】
项目介绍 项目基于SpringBoot开发,运营端和商户端采用ElementVue,买家使用采用VueIviewnuxt服务端渲染。使用到的中间件有Redis、RabbitMQ、ElasticSearch、FastDFS、Mongodb等。主要功能包括有运营管理、商品管理、订单管理、售后管理、会员管理、财务…...
C++的结构体、联合体、枚举类型(一)
1.C++的结构体 2.C++的联合体 3.C++的枚举类型 1.C++的结构体 (1)C++中定义结构体变量,可以省略struct关键字 struct XX{…}; XX x;//定义结构体变量直接省略struct(2)C++结构体中可以直接定义函数,谓之成员函数(又叫方法)(3)在成员函数中可以直接访问该结构体的成员变…...
搭建高可用OpenStack(Queen版)集群(一)之架构环境准备
一、搭建高可用OpenStack(Queen版)集群之架构环境准备 一、架构设计 二、初始化基础环境 1、管理节点创建密钥对(方便传输数据) 所有控制节点操作 # ssh-keygen #一路回车即可 Generating public/private rsa key pair. Enter f…...
通过Stack Overflow线程栈溢出的问题实例,详解C++程序线程栈溢出的诸多细节
目录 1、问题说明 2、从Visual Studio输出窗口中找到了线索,发生了Stack Overflow线程栈溢出的异常 3、发生Stack Overflow线程栈溢出的原因分析 4、线程占用的栈空间大小说明 5、引发线程栈溢出的常见原因和场景总结 6、在问题函数入口处添加return语句&…...
LeetCode刷题笔记 | 3 | 无重复字符的最长子串 | 双指针 | 滑动窗口 | 2025兴业银行秋招笔试题 | 哈希集合
🙋大家好!我是毛毛张! 🌈个人首页: 神马都会亿点点的毛毛张 这是一道银行的面试题,就是简单?! LeetCode链接:3. 无重复字符的最长子串 1.题目描述 给定一个字符串 s ,…...
验证cuda和pytorch都按照成功了
要验证您的PyTorch是否能够调用CUDA,您可以执行以下步骤: 1. **检查CUDA是否可用**: 在Python中运行以下代码来检查CUDA是否可用: python import torch print(torch.cuda.is_available()) 如果输出为 True&…...
iOS开发如何自己捕获Crash
为了在iOS中捕获和处理未捕获的Objective-C异常和系统信号引起的崩溃,可以使用NSSetUncaughtExceptionHandler和标准的Unix信号处理机制来实现。这能帮助你记录绝大部分的崩溃信息。以下是详细的实现步骤和代码示例: 一、系统崩溃处理 通过NSSetUncaug…...
雪花算法(Snowflake Algorithm)
雪花算法(Snowflake Algorithm)是一种分布式唯一ID生成算法,主要用于生成全球唯一的ID,广泛应用于分布式系统中,例如在数据库中作为主键。这个算法最初由Twitter提出,并且被广泛使用在很多大规模系统中。有…...
〖任务1〗ROS2 jazzy Linux Mint 22 安装教程
前言: 本教程在Linux系统上使用。 目录 一、linux安装二、linux VPN安装三、linux anaconda安装(可选)四、linux ROS2 安装五、rosdep init/update 解决方法六、安装GUI 一、linux安装 移动硬盘安装linux:[LinuxToGo教程]把ubunt…...
图像增强:使用周围像素填充掩码区域
制作图像需要填充的掩码区域,对需要填充的位置的mask赋值非0,不需要填充赋值为0使用cv2.inpaint对图像掩码mask中非0元素位置的图像像素进行修复。从而实现使用周围像素填充掩码区域cv2.inpaint 是 OpenCV 库中的一个函数,用于图像修复(inpainting),即填充图像中的损坏区…...
给虚拟机Ubuntu扩展硬盘且不丢数据
1.Ubuntu关机状态下先扩展,如扩展20GB 2.进入ubuntu,切换root登录,必须是root全选,否则启动不了分区工具gparted 将新的20GB创建好后,选择ext4,primary; 3.永久挂载 我的主目录在/并挂载到/dev/sda1 从图…...
Oracle(41)如何使用PL/SQL批量处理数据?
在PL/SQL中,批量处理数据是一种高效的方法,可以在数据库中处理大量数据,而无需逐行操作。批量处理数据的关键技术包括: PL/SQL表(索引表):在内存中存储数据以进行批量操作。FORALL语句…...
JavaEE 第2节 线程安全知识铺垫1
目录 一、通过jconsole.exe查看线程状态的方法 二、Thread类的几种常见属性 三、线程状态 一、通过jconsole.exe查看线程状态的方法 通过jconsole查看线程状态非常实用的方式 只要你安装了jdk,大致按照这个目录就可以找到这个可执行程序: 然后双击这…...
LeetCode Hot100 零钱兑换
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。 你可以认为每种硬币的数量是无限的。 示…...
微信小程序接口实现语音转文字
一、效果展示 我们有一个按钮,点击“开始录音”按钮,此时按钮变成“停止录音”并开始计时,点击停止录音后,界面上即可展示返回的文字 二、代码实现 完整代码实现见github 1.小程序端代码 // index.js const recorderManager…...
建设部网站核对编号/西安网站建设网络推广
你可以使用PHP函数的 mysqli_query() 及相同的 SQL SELECT 带上 WHERE…LIKE 子句的命令来获取数据。 该函数用于执行 SQL 命令,然后通过 PHP 函数 mysqli_fetch_assoc() 来输出所有查询的数据。 但是如果是 DELETE 或者 UPDATE 中使用 WHERE…LIKE 子句的S QL 语句…...
做网站需要电脑吗/站长查询工具
导读: 亚当理论是美国人威尔德(J.W.Wilder)所创立的投资理论。威尔德于1978年发明了著名的强弱指数RSI,还发明了其它分析工具如PAR、抛物线、动力指标MOM、摇摆指数、市价波幅等。 这些分析工具在当时的时代大行其道࿰…...
个人备案做视频网站/聊城网站seo
购买产品链接请点击“阅读原文”https://yao.jk.cn/index.html#/yao-item/498600/0?channelCodeduojin&promoCodeMERCENARY5279360301...
网站开发 上海/网络维护公司
手写vue响应式原理 首先我们看看原生 vue 做了什么 <script src"https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.js"></script> <script>const vm new Vue({data: {name: 小米,arr:[{a:2}] // 检测深度响应式}})console.log(vm, vm) </s…...
新格建站/上海网站建设咨询
机房的智能化建设与管理,随着各种用电设备对电源系统的稳定性、可靠性要求的进一步提高,能够提供不间断、稳定电力供应的UPS电源得到了越来越广泛的应用。目前市面上普遍的中大型UPS电源的因个别电池的劣化而造成UPS电源损坏的情况发生,因此&…...
做网站用asp还是php好/百度app下载官方
为什么80%的码农都做不了架构师?>>> OS: AIX7.1 运行ls,提示如下“ksh: There is not enough space in the file system.“ 1. 查看可用PPs: lsvg rootvg ->尚有free空间 2. 查看ls 所在file system /usr空间是否用完 ->尚有空间…...