当前位置: 首页 > article >正文

如何给 Go 语言的 TCP 聊天服务加上 ACK 可靠送达机制

如何给 Go 语言的 TCP 聊天服务加上 ACK 可靠送达机制在我们学习 Go 语言网络编程时实现一个简单的 TCP 聊天室往往是入门的必经之路。原项目8h-GoIM通过建立 TCP 连接并将接收到的文本广播给所有在线用户非常直观地展示了 Go 语言在并发和通道设计上的优雅。然而如果要把这个“玩具”推向生产环境的“可用消息通道”单纯的文本广播就显得捉襟见肘了。最近我为这个项目引入了消息 ACK确认机制。本文将以技术分享的形式复盘这次重构的设计思考、实现细节以及踩过的坑。为什么纯文本广播不够用在最初的设计中客户端发来一行文本服务器原封不动地转发给其他 socket。虽然简单但在真实的弱网环境下存在以下致命缺陷重复推送不可控去重客户端因为网络超时触发重试服务端会把同一句话当作两条新消息广播消息时序混乱有序多客户端高并发发言时没有一个基准的序号各个接收端看到的消息顺序可能不一致无状态导致的丢失可靠网络瞬断或对端掉线期间的消息一旦错过就永远消失了服务器没有任何送达记录。因此我们的首要目标并不是一上来就搞定高可用的分布式架构而是立足单机先确保在线场景下消息“不重、不乱序、可确认”为以后的离线消息漫游打下结构化的基石。协议重塑从字符串到状态机要追踪一条消息的生命周期首先必须给它发“身份证”。为了兼顾现有代码里bufio.Reader按行读取的简便性我选用了单行 JSON作为传输协议。我们定义了一个包含类型和各种元数据的Message结构{type:send,client_msg_id:c-12345,chat_id:room1,from:alice,body:hello world!}这里面藏了几个关键字段的设计考量type我们把它分成了send(发送)、send_ack(发送确认)、deliver(投递)、deliver_ack(投递确认) 和sync(拉取同步)。client_msg_id这是去重的核心。客户端在本地生成一个唯一 ID如 UUID 或纳秒时间戳服务端据此判断是否为重复请求。server_msg_id服务端接收落盘后生成的全局唯一 ID作为系统内流转的凭证。seq服务端针对某个chat_id会话空间分配的严格单调递增序号用来保证业务侧的严格有序。当服务器收到上述的send请求并处理成功后会即刻响应{type:send_ack,client_msg_id:c-12345,server_msg_id:s-98765,seq:10}紧接着服务器打包deliver消息推给目标接收者接收者收到后必须回复deliver_ack。核心架构拆解在服务端要支撑这套状态扭转我引入了三个关键组件隔离了原先混在单一逻辑里的业务Store (内存状态管理器)目前的实现叫InMemoryStore它承担了三个职责防重内部用 Map 维护(from, client_msg_id)如果遇到历史出现过的键直接返回原有的send_ack极简实现幂等分配序列号通过对chat_id的原子操作自增生成连续的seq维护状态为每一条投递记录打上Pending待确认标签。DeliverQueue DeliverWorker (异步投递队列)客户端发完消息直接获得send_ack代表服务器“已揽收”随后这个server_msg_id会被扔进异步的DeliverQueue中。后台的DeliverWorker监听这个队列取出消息后从 Store 拉取目标接收人无论是私聊的一个人还是广播的所有人如果对方在线发送deliverJSON对方不仅收到了数据也知道了server_msg_id借此回传deliver_ack。如果对方不在线这条记录依然在 Store 中保持 Pending。业务入口聚合处理改造了原来的字符串处理流程HandleClientSend伪代码如下funcHandleClientSend(req*Message){// 1. 幂等拦截ifstore.Exist(from,client_msg_id){returnreply(send_ack)}// 2. 生成凭证seq:store.NextSeq(chat_id)serverMsgID:newID()// 3. 落库与投递列表store.SaveMessage(serverMsgID,req)store.SaveDelivery(serverMsgID,recipients)// 4. 回写成功响应reply(send_ack_with_seq)// 5. 送入后台投递enqueue(DeliverQueue,serverMsgID)}实践中的反思与权衡由于是迭代性质的升级过程中做了一些务实的取舍为什么把seq生成放在服务端如果依赖客户端维护序列号在多端并发发送同一个群组通道时必然产生冲突。服务端作为唯一权威的发号器保证了同一chat_id下的单调性接收端只需要依据seq就可以轻松识别出乱序或者缺失。关于拥塞控制原版项目一旦对端读的慢写入就会被卡死阻塞。现在因为投递操作变成了独立的异步任务并交给客户端专属的 GoroutineSelect处理配合定期的心跳或写超时机制拥塞引起的系统假死已被规避大不了断开那个死木头连接反正我们的 Store 仍会把发送状态定义为 Pending。测试验证要验证这个新机制非常直观。开两个终端连接服务器客户端 A 发送{type:send,client_msg_id:apple123,body:Hello!}客户端 A 收到服务器回应{type:send_ack,client_msg_id:apple123,server_msg_id:s-001,seq:1}客户端 B 收到被推过来的消息{type:deliver,server_msg_id:s-001,from:客户端A,seq:1,body:Hello!}客户端 B 按照规矩给服务器回传{type:deliver_ack,server_msg_id:s-001}整个闭环极其清晰。如果 B 故意不回发deliver_ack该记录将一直“挂起”这是我们后续补偿机制的基础。

相关文章:

如何给 Go 语言的 TCP 聊天服务加上 ACK 可靠送达机制

如何给 Go 语言的 TCP 聊天服务加上 ACK 可靠送达机制 在我们学习 Go 语言网络编程时,实现一个简单的 TCP 聊天室往往是入门的必经之路。原项目8h-GoIM通过建立 TCP 连接并将接收到的文本广播给所有在线用户,非常直观地展示了 Go 语言在并发和通道设计上…...

【万字文档+源码】基于springboot与vue个人财务管理系统-计算机设计学习项目

基于springboot与vue个人财务管理系统1.项目简介 基于springboot与vue个人财务管理系统管理员功能有个人中心,用户管理,账单类型管理。用户功能有个人中心,收入账单管理,支出账单管理,负债管理,财务报表管理…...

48tools:一站式多平台视频下载与直播录制高效解决方案

48tools:一站式多平台视频下载与直播录制高效解决方案 【免费下载链接】48tools 48工具,提供公演、口袋48直播录源,公演、口袋48录播下载,封面下载,B站直播抓取,B站视频下载,A站直播抓取&#x…...

三菱R系列PLC应用案例:远程操作与通信,涉及高端触摸屏、机器人连接及EPLAN电气设计

三菱R系列PLC案例程序 三菱R系列ST、RD77MS定位以及三菱触摸屏配方功能,此案例还提供两个触摸屏实现异地操作,使操作更加方便快捷。 此案例还通过CClink远程连接远程IO站以及机器人,将机器人作为远程设备站,实现跟机器人的快速通信…...

养殖场环境控制系统:大数据分析,优化养殖方案

一、应用背景 当前我国畜禽养殖正从传统散户养殖向规模化、集约化转型,而环境因素(温湿度、有害气体、光照等)是影响畜禽生长发育、繁殖效率、疫病防控的核心要素。据行业数据显示,2023年全球智慧农业市场规模达2200亿美元,畜牧养殖环境监控系…...

艾尔登法环存档安全迁移工具:解决跨设备数据流转难题的专业方案

艾尔登法环存档安全迁移工具:解决跨设备数据流转难题的专业方案 【免费下载链接】EldenRingSaveCopier 项目地址: https://gitcode.com/gh_mirrors/el/EldenRingSaveCopier 一、当数字冒险遭遇数据断层:玩家必须面对的存档危机 你是否经历过这样…...

工业级模拟量采集模块:给排水流量采集,工业级抗干扰更耐用

模拟量采集模块在水利水电行业应用广泛,其核心作用是将水位、压力、温度等非电的物理量转换成标准的电信号(如4-20mA电流或0-5V电压),供计算机系统进行分析、控制与决策。它主要被集成到PLC(可编程逻辑控制器)、RTU(远程终端单元)、MCU(数据采集单元) 等…...

工业级模拟量采集模块:空气温湿度采集,大棚环境全自动

模拟量采集模块在智慧农业中扮演着“神经末梢”的角色,负责将土壤/水体的温湿度、EC/pH、溶氧、光照等连续物理量转化为数字信号,为精准灌溉、水肥一体、水质调控提供可靠数据入口,直接决定生产决策的准确性与效率。一、系统架构感知层&#…...

GParted(磁盘分区工具)

链接:https://pan.quark.cn/s/6df06d1b7ed3GParted是一款非常专业开源的磁盘分区工具,通过该软件用户能够轻松的创建、删除分区,并且还可以调整分区的大小和移动分区的位置,软件支持创建15个磁盘的分区,然后用户可以在…...

Python实战:5种常用窗函数在音频信号处理中的对比与应用

Python实战:5种常用窗函数在音频信号处理中的对比与应用 当你第一次用麦克风录制一段钢琴曲时,可能会发现频谱图上出现了许多"不该存在"的频率成分。这种现象在专业术语中被称为频谱泄漏,而解决它的关键工具就是窗函数。作为音频处…...

2026 年膜结构车棚厂家怎么选?行业资深经验参考

2026 年,随着膜结构停车棚市场需求的不断增长,如何选择一家靠谱的膜结构车棚厂家,成为众多用户面临的重要问题。本文将详细介绍该行业的痛点,并分享选择膜结构车棚厂家的有效方法,为大家提供可落地的实用参考。当前膜结…...

IDimager Products Photo Supreme

链接:https://pan.quark.cn/s/ef9a346f6ac6IDimager Products Photo Supreme图像管理是一非常实用的图片管理应用程序,用户可以通过它来收集自己喜欢的图片,分类处理图片,以后想找图片就会更方便快捷一些,需要的可以来…...

保姆级教程:在CentOS 7.9上为SinoDB V16.8配置DBeaver连接(附sqlhosts文件详解)

从零构建SinoDB V16.8与DBeaver的高效连接体系:CentOS 7.9环境全流程精解 当企业级数据库SinoDB遇上可视化工具DBeaver,如何构建稳定高效的连接环境成为数据工程师的必修课。本文将带您深入CentOS 7.9系统,从底层配置到界面操作,打…...

第三章:面向对象编程

第三章:面向对象编程 【免费下载链接】wereader 一个浏览器扩展:主要用于微信读书做笔记,对常使用 Markdown 做笔记的读者比较有帮助。 项目地址: https://gitcode.com/gh_mirrors/wer/wereader 3.1 类与对象 面向对象编程的核心是类和…...

.NET 9边缘安全加固:如何用Minimal Hosting + Certificate Pinning + TPM2.0 attestation构建可信执行边界?

第一章:.NET 9边缘安全加固:Minimal Hosting Certificate Pinning TPM2.0 attestation构建可信执行边界 在边缘计算场景中,设备物理暴露、网络不可信及固件供应链风险显著提升了运行时攻击面。.NET 9通过Minimal Hosting模型精简启动路径&a…...

Python 3.14 JIT性能调优黄金三角:类型特化×循环展开×GC协同策略(附真实Web服务QPS从1.8K→3.4K压测报告)

第一章:Python 3.14 JIT编译器演进与性能调优全景图Python 3.14 引入了实验性但高度可配置的内置 JIT 编译器(代号“Torchlight”),标志着 CPython 首次在标准发行版中集成轻量级、函数粒度的即时编译能力。该 JIT 并非替代解释器…...

钻床夹具(说明书+装配图)

钻床夹具是机械加工中提升钻孔精度与效率的关键工具。其核心作用在于通过精准定位与可靠夹紧,确保工件在钻孔过程中保持稳定,避免因振动或位移导致的孔位偏差。传统钻孔作业依赖人工反复校准,不仅效率低下,且难以保证批量加工的一…...

低代码不是妥协,而是进化:.NET 9 AOT+Hot Reload双模引擎深度解析,上线周期压缩至72小时以内

第一章:低代码不是妥协,而是进化:.NET 9 AOTHot Reload双模引擎深度解析,上线周期压缩至72小时以内在传统认知中,“低代码”常被误读为牺牲可控性与性能的权宜之计。而.NET 9通过原生AOT编译与Hot Reload能力的深度融合…...

全能图像工具ImageGlass:免费开源的图像浏览颠覆体验

全能图像工具ImageGlass:免费开源的图像浏览颠覆体验 【免费下载链接】ImageGlass 🏞 A lightweight, versatile image viewer 项目地址: https://gitcode.com/gh_mirrors/im/ImageGlass ImageGlass是一款专为Windows用户设计的轻量级开源图像浏览…...

ARM内存操作指令实战:从LDR、STR到LDM、STM的嵌入式开发应用

1. ARM内存操作指令入门:从LDR/STR开始 第一次接触ARM汇编时,看到满屏的LDR和STR指令确实让人头大。但当我真正理解它们的作用后,才发现这些指令就像快递员一样,负责在寄存器和内存之间搬运数据。LDR(Load Register&am…...

欧姆龙NJ/NXPLC 全ST程序案例,全程序无加密,公司级框架,锂电新能源行业FB库文件

欧姆龙NJ/NXPLC 全ST程序案例,全程序无加密,公司级框架,锂电新能源行业FB库文件,NJ Socket功能PC端作为服务器,NJ作为客户端,汇川机械人控制,设备状态机,设备PPM,运行时间…...

一键清理Windows驱动垃圾:DriverStore Explorer帮你释放20GB磁盘空间

一键清理Windows驱动垃圾:DriverStore Explorer帮你释放20GB磁盘空间 【免费下载链接】DriverStoreExplorer Driver Store Explorer 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer 你的Windows电脑是否越用越慢?C盘空间总是莫…...

DOL-CHS-MODS整合包:2024一站式解决方案,3大优势助你轻松体验Degrees of Lewdity

DOL-CHS-MODS整合包:2024一站式解决方案,3大优势助你轻松体验Degrees of Lewdity 【免费下载链接】DOL-CHS-MODS Degrees of Lewdity 整合 项目地址: https://gitcode.com/gh_mirrors/do/DOL-CHS-MODS DOL-CHS-MODS整合包作为Degrees of Lewdity游…...

FireRedASR Pro功能体验:支持MP3/M4A/FLAC等全格式音频

FireRedASR Pro功能体验:支持MP3/M4A/FLAC等全格式音频 1. 音频识别新体验:告别格式转换烦恼 作为一名经常需要处理会议录音的技术博主,我深知音频格式兼容性带来的痛苦。上周我收到了三份不同格式的采访录音:市场部发来的MP3、…...

STM32F4标准库实战:用DMA+FSMC驱动TFT-LCD,让你的GUI刷新快人一步(附避坑指南)

STM32F4标准库实战:DMAFSMC驱动TFT-LCD的性能飞跃与避坑全攻略 在嵌入式GUI开发中,流畅的界面刷新体验往往决定着产品的第一印象。当你在STM32F4平台上使用LVGL或emWin时,是否遇到过这些场景:手指滑动列表时的明显卡顿、动画渲染…...

CANoe CAPL文件读写保姆级教程:从记录测试数据到读取配置文件

CANoe CAPL文件读写实战指南:从数据记录到动态配置 在汽车电子测试领域,数据记录和参数配置的自动化程度直接影响着测试效率和可靠性。想象这样一个场景:凌晨三点的耐久性测试实验室,测试工程师需要每隔15分钟手动记录一次总线报文…...

别再死磕复杂模型了!用Python+NumPy手把手教你从卫星J2000坐标算出经纬度

从卫星J2000坐标到经纬度:Python实战指南 当拿到卫星的J2000坐标数据时,如何快速将其转换为可在地图上显示的经纬度?本文将用Python和NumPy带你一步步实现这个转换过程,避开复杂的理论推导,专注于代码实现和实际问题解…...

2026届必备的五大降重复率工具实测分析

Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 人工智能论文工具正一步步重新塑造学术写作范式,当下主流平台整合自然语言处理和…...

安卓APP实时监控硬件数据?手把手教你用CH341库和串口通信实现

安卓工业级硬件监控APP开发实战:CH341库与高可靠串口通信实现 在工业自动化与物联网领域,实时采集设备数据是构建智能监控系统的关键环节。传统方案往往依赖昂贵的专用设备,而现代安卓设备配合USB转串口芯片(如CH341)&…...

PDE (Processing D Editor) 三维场景编辑器 · 软件白皮书 · 基于 v..汲

MySQL 中的 count 三兄弟:效率大比拼! 一、快速结论(先看结论再看分析) 方式 作用 效率 一句话总结 count(*) 统计所有行数 最高 我是专业的!我为统计而生 count(1) 统计所有行数 同样高效 我是 count(*) 的马甲兄弟…...