传输协议和幂等性

一. TCP和UDP

在OSI七层模型和TCP/IP协议栈中,传输层(Transport Layer)负责为运行在网络中的主机上的进程之间提供逻辑通信(logical communication)。

传输层的两个主要协议分别是:

TCP(Transmission Control Protocol):传输控制协议,是一种面向连接、可靠的、基于字节流的传输层协议。在通信过程中,TCP 提供了完整的数据传输控制流程,包括连接建立、数据传输和连接终止。

UDP(User Datagram Protocol):用户数据报协议,无连接的、尽最大努力交付的传输协议。

1. TCP协议

1.1 协议特点

1.面向连接

TCP 是一种面向连接的协议,通信前需通过三次握手建立连接,通信结束后通过四次挥手释放连接。提高传输可靠性,确保通信双方状态一致;

2.可靠传输

TCP 提供可靠性保障机制,确保数据不丢失、不重复、按顺序到达。具体机制包括:

1.数据确认(ACK):每个分组需收到接收方确认;

2.超时重传:若未收到确认,则在超时后自动重发;

3.数据校验:对传输数据进行校验和检测,确保数据未被篡改;

4.数据排序:接收方根据序列号对乱序数据进行重排;

5.重复数据丢弃:接收方能识别并丢弃重复到达的数据段。

3.面向字节流

1.应用层发送的数据会被 TCP 看作一串连续的字节流;

2.传输层不保留原始消息边界,而是将数据拆分为适合网络传输的报文段(Segment);

3.接收方按序拼接后交付给应用层。

4.该特性便于高效的数据流传输,但也要求应用层自行定义消息的边界(例如使用定长、分隔符或长度字段协议格式)。

4.流量控制

TCP 采用滑动窗口机制进行流量控制,防止发送方发送速度过快,导致接收方缓存溢出。核心机制为:

1.每个 ACK 包含一个接收窗口大小(Window Size);

2.发送方根据窗口大小控制数据的发送速率;

3.接收方可以根据处理能力动态调整窗口大小。

4.该机制实现了通信两端之间的流速匹配,保障数据接收的稳定性。

5. 拥塞控制

为应对网络拥塞,TCP 内置了多种拥塞控制机制,主要包括:慢启动、拥塞避免、快重传、快恢复。这些算法通过动态调整拥塞窗口,在检测到网络拥堵时自动降低数据发送速率,避免网络进一步恶化。

拥塞控制是 TCP 与 UDP 的重要区别之一,保证了在复杂网络环境中的传输公平性和系统稳定性。

6.全双工通信

TCP 支持全双工通信,即通信双方可以同时进行数据的发送与接收。这得益于:

1.每个方向都维护独立的发送与接收缓冲区;

2.各自使用独立的序列号与确认号;

这种机制适用于大多数现代通信场景,如即时通讯、数据同步等。

7.面向连接的流控制状态机

TCP 协议具有明确的状态机模型,涵盖从连接建立、数据传输到连接释放的各个阶段典型状态包括:

1.LISTEN、SYN_SENT、SYN_RECEIVED;

2.ESTABLISHED(连接建立完成);

3.FIN_WAIT_1、FIN_WAIT_2、TIME_WAIT;

4.CLOSE_WAIT、LAST_ACK、CLOSED。

状态机的精确管理,是 TCP 能正确处理各种复杂网络条件的关键。

8.可靠的端到端通信保障

TCP 实现了端到端的传输保障能力,其特性包括连接管理、错误检测与纠正、顺序控制、流量管理、拥塞避免这使得 TCP 成为构建企业级、面向事务、安全可靠系统(如 Web、FTP、SMTP、数据库交互等)的首选传输协议。

1.2 TCP 协议的工作流程

1.2.1 连接的建立-三次握手

在开始数据传输之前,通信双方需通过三次握手建立可靠的连接。此过程旨在确认彼此的接收与发送能力正常,初始化必要参数。

握手过程:

1.第一次握手:客户端发送 SYN

客户端向服务器发送一个 SYN 报文段(SYN=1,Seq=x),请求建立连接;

此时客户端进入 SYN-SENT 状态。

2.第二次握手:服务器回复 SYN+ACK

服务器收到 SYN 后,回应一个 SYN+ACK 报文(SYN=1,ACK=1,Seq=y,Ack=x+1)

表示同意连接,并确认客户端的 SYN;

服务器进入 SYN-RECEIVED 状态。

3.第三次握手:客户端发送 ACK

客户端收到 SYN+ACK 后,发送 ACK 报文(ACK=1,Seq=x+1,Ack=y+1);

连接建立,客户端进入 ESTABLISHED 状态;

服务器收到该 ACK 后也进入 ESTABLISHED 状态。

三次握手完成后,双方就可以开始可靠的数据传输了。

1.2.2 数据传输阶段

在连接建立之后,双方通过 TCP 报文段交换数据

此过程具有以下机制保障:

1.序列号(Sequence Number):用于标识字节流中的位置;

2.确认号(Acknowledgment Number):接收方对收到数据的确认;

3.滑动窗口:实现流量控制;

4.超时重传机制:保证数据可靠;

5.校验和(Checksum):校验数据完整性。

TCP 将数据视为连续的字节流,每个数据段都附有序列号,确保接收端能按顺序组装数据,并通过 ACK 报文进行确认。

1.2.3 连接的终止-四次挥手

TCP 连接断开需要通过四次挥手来保证双方都能完全关闭通信,且数据不丢失。

挥手过程:

1.第一次挥手:主动关闭方发送 FIN

客户端(或服务器)发送 FIN 报文(FIN=1,Seq=u);

表示“我已经没有数据要发送了”;

对方收到后,进入 CLOSE-WAIT 状态。

2.第二次挥手:被动关闭方回复 ACK

被动方发送 ACK 报文(ACK=1,Ack=u+1);

表示“我知道你准备关闭连接”;

主动方进入 FIN-WAIT-2 状态。

3.第三次挥手:被动方也发送 FIN

被动方在发送完剩余数据后,发出自己的 FIN 报文;

表示“我也没数据了,准备关闭”;

状态变为 LAST-ACK。

4.第四次挥手:主动方发送 ACK

主动方回应一个 ACK 报文(Ack=w+1);

并进入 TIME-WAIT 状态,等待 2 倍最大报文段生命周期(MSL)后关闭连接;

确保对方最后的 ACK 能被可靠接收。

1.2.4 为什么是三次握手?

三次握手的设计,旨在确保双方都能收发数据,连接状态同步。两次握手是不够的,原因如下:

第一次:客户端发送 SYN 请求;

第二次:服务器响应 SYN+ACK;

若客户端不回 ACK,服务器无法确认客户端是否已收到 ACK,可能误判连接状态。

就好像小扬子给小呆萌打电话, 小呆萌接了电话问能不能听见声音, 小扬子听到了声音但是没确认小呆萌能不能听见, 就开始说话了, 结果线路坏了, 小扬子说了半天小呆萌也没听见

第三次握手解决了此问题:客户端回应 ACK,明确告诉服务器“我收到了你的 SYN+ACK,并准备通信”,至此双方状态一致,连接才真正建立。

1.2.5 为什么是四次挥手?

连接终止的复杂性在于TCP 是双向通信的,发送和接收必须分别关闭。

一个 FIN 仅表示我这边不再发送数据,但不能代表对方也没有数据要发;

所以必须有两个 FIN,分别关闭各自的发送通道两个 ACK 是对 FIN 的确认。

此外,主动关闭方要进入 TIME-WAIT 状态,防止最后一个 ACK 丢失导致连接残留在对方系统中。

1.2.6 打电话模拟三次握手四次挥手

我们以两个人打电话来举例三次握手的过程:

小扬子给小呆萌打电话:

阶段

谁说话

内容

TCP行为模拟

第一次握手

小扬子

"我要说话,你听得到吗?"

发送SYN,表示想要建立连接

第二次握手

小呆萌

"我听到了,你能听我的吗?"

发送SYN+ACK,一端已接通,等待确认是否双方都通畅

第三次握手

小扬子

"确认听见你啦,我们开始聊!"

发送ACK,TCP连接正式建立,进入ESTABLISHED状态

第一次挥手

小扬子

"我说完了,我准备挂电话了"

发送FIN,表示准备断开连接

第二次挥手

小呆萌

"知道了,我继续说几句"

发送ACK,表示收到

第三次挥手

小呆萌

"我也说完了,也准备挂电话了"

发送FIN,表示准备断开连接

第四次挥手

小扬子

"好,我们都说完了,挂电话!"等待一段时间,确认对方挂了再挂

发送ACK,进入TIME-WAIT,然后断开连接

1.3 TCP应用场景

应用类别

协议或应用

原因

网页浏览

HTTP / HTTPS

页面数据完整、支持 TLS 加密

文件传输

FTP / SFTP / SCP

大量数据传输需高可靠性

电子邮件

SMTP / POP3 / IMAP

邮件与附件需按顺序、完整传输

远程控制

SSH / Telnet

实时命令交互,确保反馈准确

数据库访问

MySQL / Oracle

数据一致性、事务安全

即时通讯

XMPP / MQTT over TCP

高可靠、状态同步

控制信令传输

RTSP / SIP(信令部分)

控制信道高可靠性,不能丢包

2.UDP协议

UDP(用户数据报协议,User Datagram Protocol) 是一种无连接的、面向报文的传输层协议。与 TCP 不同,UDP 不提供数据可靠性保障,不建立连接,传输过程简单高效。

2.1 协议特点

UDP 被设计为一种轻量级协议,适用于那些对实时性要求高、容忍一定数据丢失的场景。

1.无连接

UDP 是一种无连接(Connectionless)协议,在通信双方之间不需要建立连接即可直接传输数据。发送方不需要预先通知接收方,接收方也不对连接状态进行维护。

优点:建立传输开销小,启动快,适合高频、短时间交互

缺点:由于缺乏连接状态管理,无法检测对方是否准备好接收数据。

2.不可靠传输

UDP 不提供数据传输的可靠性保障,即:

1.无确认机制:接收方不会发送 ACK 来确认收到数据;

2.无重传机制:即使数据丢失或顺序错乱,也不会进行重发或纠正;

3.无顺序保证:多个数据报可能乱序到达;

4.无连接状态跟踪:不会维护连接状态或会话上下文。

UDP 的不可靠并不意味着“必定丢包”,而是其本身不具备纠错与恢复能力,如果需要保障可靠性,需由上层协议或应用程序自行实现。

3.报文边界明确

UDP 是面向报文的协议,每个 UDP 报文(Datagram)都是独立传输的单元。

1.每个应用层数据包作为一个完整的数据报进行发送;

2.接收方每次接收的是一个完整的数据报,不进行拆分或合并。

这一特性使得 UDP 特别适用于需要保持消息完整性的应用,如 DNS 查询、语音数据包等。

4.传输效率高

UDP 的协议头部结构极为简洁,仅占 8 字节,相较于 TCP 至少 20 字节的头部,UDP 开销更小,处理效率更高,更适合对性能和时延要求敏感的应用场景。

5.支持广播与组播

1.广播(Broadcast):向同一网络内的所有主机发送数据;

2.组播(Multicast):向特定组成员广播数据,仅限加入该组的主机接收;

该能力使得 UDP 成为局域网发现服务、视频分发、在线教学、语音会议等典型多对多通信场景的首选协议。

6.无拥塞控制

UDP 不具备 TCP 的拥塞控制机制。在网络拥堵情况下,UDP 不会自动减少发送速率。这种行为使其具有以下特性:

优点:实时传输性能好,适用于低延迟需求场景

缺点:可能加剧网络拥堵,不适合在对网络资源需谨慎控制的环境中大量使用。

现代 UDP 应用如 QUIC 协议,通过引入应用层或用户空间的流控逻辑,弥补了这一不足。

7.适合实时应用

由于 UDP 无需握手建立连接、无延迟控制、无重传等待,具备极高的实时性。尤其在以下场景中尤为重要:

音视频实时通信(如 RTP over UDP);

实时在线游戏、直播;

快速 DNS 查询与响应;

物联网(IoT)低功耗数据交换。

2.2 UDP工作流程

2.2.1 UDP 发送端工作流程

1.应用层调用套接字接口

应用程序(如 DNS 客户端、语音传输模块等)通过套接字接口(如 sendto())将数据交给操作系统。

2.传输层封装 UDP 报文

操作系统内核将应用层数据封装为 UDP 报文段(Datagram),格式如下:

[UDP首部 (8字节)] + [用户数据]

1.源端口(Source Port)

2.目标端口(Destination Port)

3.报文长度(Length)

4.校验和(Checksum)

3.交由网络层处理

UDP 报文段被交给网络层(如 IP 协议)进行进一步封装,附加 IP 头部,形成 IP 数据报

4.经过物理链路发送至网络

数据报通过链路层和物理层发送到网络,最终到达目标主机。

2.2.2 UDP 接收端工作流程

1.IP 层接收数据报

接收端 IP 层收到数据报后,分析其头部,将目的地址匹配本机后,判断数据负载为 UDP 协议,则交由传输层处理

2.UDP 解封装

UDP 协议解析首部信息,并从数据报中提取用户数据

校验和用于检测数据是否损坏(非强制);

长度字段确保报文边界不出错。

3.根据端口号分发给对应进程

操作系统根据目标端口号,将数据交给监听该端口的应用程序(如 DNS 服务器、VoIP 处理程序等)。

4.应用程序处理数据

应用程序读取数据进行处理,流程结束。无需向发送方返回 ACK(确认)。

2.3 UDP应用场景

应用类型

协议/应用

原因

域名服务

DNS

快速查询、数据小、可重试

流媒体

IPTV、直播、HLS

实时性强、可容忍丢包

实时语音

VoIP、SIP

音频流畅、低延迟

在线游戏

FPS、MOBA 网络通信

快速响应、不需确认机制

广播与组播

DHCP、mDNS、IPTV组播

一发多收、无需点对点连接

简单请求应答

SNMP、TFTP

请求量大、容错能力依赖上层逻辑

工业与物联网

传感器、PLC 数据上传

数据量小、实时更新、高频低延迟传输

二. HTTP 协议

HTTP 协议的全称是 HyperText Transfer Protocol(超文本传输协议),它是基于 TCP/IP 之上的应用层协议用于客户端(通常是浏览器)和服务器之间传输文本、图片、视频(超文本)等资源。

1. HTTP 特性

1.无状态每个请求都是独立的,服务器不会记住上一个请求发生了什么。

2.基于请求/响应客户端主动发起请求,服务器被动响应。

3.明文传输(HTTP)HTTP 本身是明文的,数据不加密,容易被中间人监听,但可以采用HTTP + SSL/TLS (HTTPS)加密层,更安全,防窃听、防篡改。

4.可扩展性强通过增加 Header 可以扩展很多功能,如认证、缓存、代理控制等,可以利用Session、Token、Cookie 等机制变为"有状态"。

2.HTTP 协议的工作流程

以 HTTP/1.1 为例,HTTP 的通信过程通常包括以下五个阶段:

1. 建立 TCP 连接(三次握手)

HTTP 运行于 TCP 之上,通信前必须通过 TCP 三次握手 建立可靠连接。

2. 客户端发送 HTTP 请求

连接建立后,客户端(如浏览器)构造 HTTP 请求报文并发送给服务器,请求包含:

请求行(方法、资源路径、协议版本)

请求头(User-Agent、Host、Accept 等)

请求体(仅某些方法如 POST、PUT 会包含)

3. 服务器处理请求并发送响应

服务器接收请求后,进行相应处理(如读取资源、查询数据库),并构造 HTTP 响应报文返回给客户端,报文结构如下:

状态行(协议版本、状态码、状态描述)

响应头(Content-Type、Content-Length、Server 等)

响应体(实际返回内容,如 HTML 页面、JSON 数据等)

4. 客户端解析响应并呈现内容

客户端收到响应后,会根据响应头和响应体内容进行解析与渲染

5. 断开连接(或保持连接)

在 HTTP/1.0 中,默认每次请求-响应后断开 TCP 连接;

在 HTTP/1.1 中,默认启用 Connection: keep-alive,允许复用连接,提升性能;

可由任一方通过设置 Connection: close 显式关闭连接。

3.HTTP 的长连接和短连接

HTTP 长连接和短连接是指:客户端和服务器之间的 TCP 连接的保持时长和复用方式不同。

HTTP 的“长连接”机制,其实是基于传输层 TCP 的长连接实现的!HTTP 是一个应用层协议,它本身并不负责建立连接。它只是通过设置一些头部字段(如 Connection: keep-alive)告诉 TCP 层“我希望复用连接”。而实际的连接管理、复用、保持活动等操作,都是由TCP(传输层)来负责的。

1.短连接每一次请求响应之后就立即断开连接。下一次请求还要重新建立一次 TCP 连接(三次握手)和断开连接(四次挥手)。HTTP/1.0 默认短连接。适用于请求极少或对连接复用要求不高的场景

2.长连接(Keep-Alive): 建立一次 TCP 连接后,可以复用这条连接来发送多个 HTTP 请求。连接不会马上关闭,保持一段时间。HTTP/1.1 开始默认长连接。适用于高并发系统、大量请求场景(如网页加载)

 小注:我们通过 curl -v 接口的方式查看我们开发的接口是不是长连接模式

4.HTTP/1.0、HTTP/1.1、HTTP/2 和 HTTP/3区别

对比项

HTTP/1.0

HTTP/1.1

HTTP/2

HTTP/3

发布年份

1996

1997

2015

2022

连接方式

每次请求新建 TCP 连接

支持 Keep-Alive 长连接

单个 TCP 连接支持并发请求(多路复用)

基于 UDP 的 QUIC 协议,天然支持并发

多路复用

不支持

不支持,串行处理

支持 Stream 多路复用

更高效的多路复用(无 TCP 队头阻塞)

编码格式

纯文本

纯文本

二进制帧结构

二进制帧结构

请求并发

一个连接一次只能传一个请求

串行排队传多个请求

多个请求并发(每个 Stream 有 ID)

多个请求并发,且每个 Stream 独立

队头阻塞

严重

严重

TCP 层可能发生

基于 QUIC,无队头阻塞

头部压缩

HPACK 压缩

QPACK 压缩(优化丢包处理)

性能提升机制

管线化(Pipeline),效果差

多路复用、压缩、服务器推送

所有 HTTP/2 优化 + 低延迟 + 0-RTT

加密支持

可选(依赖 HTTPS)

强烈推荐使用 TLS

内建 TLS 1.3(加密是基础)

服务端推送

支持 Server Push

支持 Server Push(但使用受限)

TCP/UDP 协议

TCP

TCP

TCP

QUIC(基于 UDP)

建连延迟

高,每次新建连接

中,支持连接复用

中,但仍有 TCP/TLS 握手

最低,支持 0-RTT

丢包处理

TCP 重传

TCP 重传

TCP 重传(会影响所有流)

每个流独立丢包重传,不互相影响

支持情况

老旧系统、教学用途

依然广泛使用

主流浏览器和 CDN 均支持

逐步发展部署,现代浏览器已支持

典型使用场景

老旧 HTTP 客户端

中小型 Web 服务

现代 Web、API、高并发系统

视频、移动网络、CDN、低延迟需求场景

5.HTTP 请求与响应方法

方法

含义

适用场景

GET

获取资源

浏览网页、读取数据

POST

提交数据创建资源

表单提交、用户注册

PUT

修改或替换资源

更新配置、修改数据

DELETE

删除资源

删除用户、注销账号

HEAD

获取资源头部信息

检测资源是否存在

OPTIONS

查询服务器支持哪些方法

跨域请求预检

PATCH

局部更新资源(非全替换)

编辑部分字段

三. 接口幂等性

1. 什么是接口幂等性(Idempotency)?

一个接口在被客户端调用一次和多次时,产生的效果是一样的,不会因为多次调用造成多次执行的副作用。

2. 哪些接口必须幂等?

有些接口一旦重复调用,就可能引起严重后果,比如数据重复、资金重复扣款、状态错乱、安全漏洞(如重复发券)等问题因此有些接口必须具有幂等性。这些问题经常出现在用户重复操作、网络抖动的场景(如客户端重试)

1.支付接口 : 重复扣款,直接经济损失。

2.订单创建接口 : 重复下单,库存混乱。

3.发放优惠券/积分接口 : 重复发放,用户领取。

4.账号注册 / 绑定接口 : 重复账号、账号绑定混乱。

5.提现、转账接口 : 重复执行,造成资金泄漏。

6.消息推送接口(短信、邮件) : 多次通知,骚扰用户,浪费资源。

7.评论/点赞接口(视业务定制) : 重复点赞或灌水式评论。

3.天然具有幂等性接口

有些接口哪怕调用多次,结果也不会改变系统状态或产生副作用。这是在设计上带来的幂等性也叫弱幂等接口。

 1.查询类接口  : 查询特定信息,纯读操作,不会改变数据状态。

 2.状态设置类接口  : 比如“设置开启状态”,重复设值没副作用。

3.幂等删除接口 : 如 DELETE 特定id的数据,即使删除多次,最后系统仍是删除此数据状态。

4.更新字段类接口 : 如更新头像接口,传同样的图多次仍然是该头像。

5.PUT 接口(标准 Restful) :   设计本身就要求是幂等的。

 6.幂等消息消费接口  : MQ 消费端处理幂等,可以重复消费但不重复处理数据。

这些接口本质上是 “读” 或 “覆盖写”状态不会因为调用次数改变就算没有针对性的加上幂等性验证,也不会出错。

 4.不需要幂等的接口(或幂等性不重要)

有些接口本身多次执行的副作用可接受或者幂等性并不会显著提升安全性或用户体验。

1. 获取验证码接口 : 只要控制频率,无需考虑幂等。

2. 日志上报/行为统计接口 : 多报几次无所谓,服务端聚合即可。

 3. 推荐接口  : 查询结果每次都可能不同,没必要强求幂等。

5. 解决幂等性问题

5.1. 幂等key机制

幂等Key(Idempotent Key) 是一个在客户端生成的唯一标识符,用来标记一次请求的唯一性。

幂等Key机制可以解决:

1. 避免业务副作用的重复发生。

2. 让服务端识别并跳过重复请求。

3. 提升系统健壮性:可以应对网络抖动、客户端重试、幂等测试、异常恢复等问题。

幂等Key 要满足的条件:

1.唯一性 : 每次不同操作请求必须有不同的 Key。

2.有时效性 : 避免 Redis / DB 中 Key 永久堆积。

3.可追踪性 : 最好能标记出请求来源 / 用户 / 业务类型。

4.幂等性绑定粒度合适 : 粒度太大会误判重复,太小会放过重复。

常见的幂等Key 设计方式:

1.客户端传 UUID : 该方案最灵活、比较通用。

2.订单号作为 Key : 对于下单类接口, 以订单号作为key值, 做唯一性校验。

3.用户ID + 操作类型 + 时间戳哈希 : 例如 user:123:create:xxxhash  进行幂等性校验。

4.签名摘要内容(body + token hash) : 接口请求幂等性校验(内容型幂等)。

幂等Key的常用存储方式:

1. Redis : 查询快速快速、支持原子性、自动过期, 比如 setIfAbsent() 方法。

2. 数据库 : 可持久化、可审计,对于高价值交易类行为比较适用(如支付流水)。

注: 建议采用AOP切面 / 拦截器统一处理提升效率。

注意: 要由客户端生成唯一key而不能由服务端去自动生成或者采用自增key这时要考验客户端能不能识别用户“点两次”发起的是两个不同的请求以及客户端如何管理“幂等 ID”生命周期。

常用方式:

1. 客户端显式生成并持久化幂等 ID 

1. 适用于业务操作明确、一次性动作, 如下单、提交支付、申请退款、表单提交。

2.用户刷新前都用同一个请求 ID,多次点击只发送该 ID。

3.客户端要负责缓存生命周期,本地存储依赖浏览器机制。

2.由前端操作模型决定 UUID 生命周期

1. UUID 在点击前生成。

2. 多次点击不会重复生成 UUID。

3. 用户手动刷新页面或切换 tab 才重新生成。

3.由业务模型生成幂等ID

1.根据业务信息构造一个稳定幂等 ID,如 hash(user_id + 商品id + 当前时间段)。

2.只要用户在相同时间内提交同一个商品请求,生成的 ID 是一样的。

3.客户端存下来这个 ID,用于控制幂等。

5.2.基于业务唯一约束

直接依赖数据库的唯一性约束(unique key)来强行拒绝重复创建数据 此方式适用于创建型接口, 对于更新操作无效, 该方式简单直接,可靠性高,但需要注意,此时的唯一Key 不能在业务中自动生成UUID或采用自增主键, 需由客户端生成并管理。

5.3.防重令牌机制

防重令牌(Idempotency Token)是由服务端提前生成的一种“唯一标识符”,交给客户端,用来标识一类幂等操作。客户端每次请求必须携带Token,服务端校验这个 Token 是否存在且未使用,后续相同 Token 的请求会被拒绝或返回缓存结果。

1.客户端先从服务端请求一个唯一 Token, 这个 Token 服务端会同时存到 Redis 中。

2.客户端提交表单时携带该 Token。

3.服务端验证该 Token 是否存在并且立刻删除(原子操作)。

4.验证成功则正常提交,失败则拒绝执行。

防重 Token 的校验必须是 Redis 层的原子操作,否则在并发下会出现逻辑漏洞。

String lua = """
    if redis.call('GET', KEYS[1]) then
        return redis.call('DEL', KEYS[1])
    else
        return 0
    end
    """;

Long result = stringRedisTemplate.execute(
    new DefaultRedisScript<>(lua, Long.class),
    Collections.singletonList("idempotent:token:" + token)
);


if (result == null || result == 0) {
    return error("重复或非法请求");
}
5.4. 幂等性中间件 / API 网关处理

把幂等性校验从业务服务内部抽离出来,放到服务调用链的前置层(如网关、Filter、拦截器、中间件),在请求到达业务服务之前,先判断是不是重复请求,是的话直接拦截掉。

5.4.1 API 网关层统一处理

在微服务中, 我们可以采用以下方案, NGINX + Lua 模块Kong/APISIX/Envoy, Spring Cloud Gateway + Redis 等方案。

1. 判断是否携带幂等 key(或 Token)。

2. 使用 Redis 等缓存判断是否已经处理。

3. 拦截掉重复请求,或者直接返回上次结果(如存储响应缓存)。

4. 合法请求则将 key 记录为“已处理”,放行到后端服务。

5.4.2 统一中间件 / SDK 封装

多服务内统一接入 SDK(比如拦截器、注解),比如写一个 @Idempotent 注解,开发者只需要加在方法上。

四.WebSocket 协议

WebSocket 是一种全双工、长连接的网络通信协议,由 IETF 于 2011 年发布(RFC 6455)。它设计用于在客户端(浏览器)与服务器之间建立持久连接,从而支持低延迟、双向数据通信,弥补传统 HTTP 协议在实时通信方面的不足。

WebSocket 属于 应用层协议,运行于 TCP 之上,是 HTML5 的重要组成部分。

1. WebSocket 特性

1.基于连接的双向通信(全双工)

WebSocket 支持客户端与服务器之间的全双工通信,即双方可以同时主动发送和接收数据,摆脱了传统 HTTP 协议严格的请求-响应限制

1.客户端无需等待服务器响应即可继续发送;

2.服务器可随时主动向客户端推送数据。

2.单一持久连接(长连接)

与传统 HTTP 请求每次都建立和关闭连接不同,WebSocket 通过一次握手建立后,连接在会话期间持续有效。

1.避免频繁连接创建和销毁,降低系统开销。

2.提高传输效率,支持持续的数据流交换。

3.轻量级协议头,节省带宽

WebSocket 报文头部结构精简,仅需少量字节,相较于 HTTP 的冗长头部,能有效降低通信带宽和网络负载,尤其在小数据包高频传输场景中效果显著。

4. 支持文本与二进制数据

WebSocket 原生支持:

文本数据(UTF-8 编码);

二进制数据(如 Blob、ArrayBuffer);

允许传输包括音频、视频、图片、文件等各种数据格式,满足多种业务需求。

5.跨平台兼容性强

WebSocket 协议由 W3C 和 IETF 联合制定,已被所有主流浏览器和服务端框架广泛支持(如 Node.js、Java、Python、Go 等)。

6.支持心跳检测与断线重连机制

WebSocket 提供 ping/pong 帧作为心跳机制,用于检测连接活性,防止空闲连接长期占用资源。

7.安全性可控

WebSocket 协议支持通过 wss:// 使用 TLS 加密传输(即 WebSocket Secure),具备与 HTTPS 等效的安全强度。

1.支持身份认证(可通过 Cookie、Token、JWT 等);

2.可结合访问控制策略、限流机制抵御连接攻击(如 DoS)。

8.支持扩展协议(Extensions)

WebSocket 协议允许在握手阶段协商附加的扩展功能,如:

1.压缩(Per-Message Deflate);

2.多路复用;

3.自定义协议子集(Subprotocol)。

9.易于与 HTTP 协议集成

WebSocket 协议的连接初始化阶段通过 HTTP/1.1 的升级机制完成(HTTP Upgrade),可以无缝集成到现有的 Web 系统中。

1.使用同一服务器端口(如 80/443);

2.便于在已有 HTTP 基础设施上部署。

2. WebSocket 协议的工作流程

WebSocket 协议的工作流程包括:连接建立(握手)、数据传输、连接关闭 三个主要阶段。整个过程以一次 HTTP 请求为入口,最终转化为持久的全双工通信通道。

2.1 连接建立

WebSocket 连接的创建始于一次特殊的 HTTP 请求-响应过程,该过程称为握手(Handshake),主要完成协议升级与双方能力协商。

1. 客户端发起 HTTP 升级请求

客户端通过标准 HTTP/1.1 请求发起连接请求,其中包含关键头字段,表示希望将当前连接升级为 WebSocket

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Origin: http://example.com

关键字段:

Upgrade: websocket:声明希望切换协议;

Sec-WebSocket-Key:客户端提供的 Base64 编码随机字符串,用于验证;

Sec-WebSocket-Version:协议版本(当前主流为13);

2. 服务器响应并升级协议

若服务器支持 WebSocket,返回状态码 101 Switching Protocols,表示协议切换成功,并包含计算后的 Sec-WebSocket-Accept 值:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

握手成功后,HTTP 通信通道升级为 WebSocket 连接。

2.2 数据传输阶段

一旦连接建立成功,客户端与服务器之间可在此通道上进行 双向、实时、低开销的数据通信。此阶段的特点如下:

1. 数据帧(Frame)结构

WebSocket 传输数据采用帧(Frame)机制,消息可以被拆分成一个或多个帧传输,帧支持两种类型:

1.文本帧(Text Frame): 传输 UTF-8 编码文本;

2.二进制帧(Binary Frame): 传输二进制数据,如音频、图片等;

3.WebSocket 还支持控制帧:如 ping(心跳)、pong(响应)、close(关闭)。

2. 双向通信(全双工)

1.客户端可以主动向服务器发送消息;

2.服务器也可主动推送消息给客户端,无需客户端先发请求。

3. 保持连接(心跳检测)

为保证连接状态可用,WebSocket 协议定义了 ping/pong 控制帧,用于周期性检测连接是否仍然活跃。

若一段时间内未收到响应,可判定连接中断,触发断线重连

2.3 连接关闭阶段

WebSocket 连接可由客户端或服务器任何一方发起关闭操作,通过发送 Close 帧完成连接释放。

关闭过程说明:

1.一方发送关闭帧,标识关闭原因与状态码;

2.对方返回确认关闭帧;

3.连接终止,释放资源。

常见关闭状态码:

1000:正常关闭;

1001:终端离开;

1006:异常中断(如断电、网络崩溃);

五.WebSocket 消息可靠性

1.防止消息丢失

WebSocket 本身是基于 TCP 的,因此它天然具备 可靠传输 机制(如 数据包确认、重传),但这并不意味着 WebSocket 一定不会丢消息。消息丢失的常见原因包括:

1.网络断连(导致消息未能成功送达)

2.服务器或客户端崩溃(导致消息未处理或未存储)

为了确保 WebSocket 消息不丢失,可以采取以下措施:

1. 客户端 ACK 机制

1.服务器发送消息时附加唯一 ID(如 message_id)。

2.客户端收到消息后,主动发送 ACK(确认收到该 message_id)。

3.服务器维护一个已发送但未确认的消息队列,若在一定时间内未收到 ACK,则重发该消息。

2. 断线重连与消息恢复

1.客户端检测 WebSocket 断开时自动重连(可使用 指数退避策略 避免频繁重连)。

2.服务器维护一个消息队列,对于已发送但未确认的消息,在客户端重连后重新推送。

3. WebSocket + 消息队列

在 高并发环境(例如 负载均衡后多个 WebSocket 服务器),可以使用消息队列(Kafka / RabbitMQ / Redis Stream) 确保消息可靠投递:

1.客户端 WebSocket 连接到不同服务器节点。

2.所有消息先进入消息队列(MQ),保证存储可靠性。

3.WebSocket 服务器从 MQ 中拉取数据后推送给客户端。

4.客户端 ACK 机制确保消息不会丢失。

4. WebSocket 分片传输

为防止大消息丢失WebSocket 默认支持数据分片(fragmentation)。对于大数据包,建议:

1.开启 WebSocket 分片,防止因单个大消息丢失导致整个传输失败。

2.客户端拼接数据,确保完整收到。

 2.防止消息堆积

WebSocket 消息堆积(Backpressure)通常发生在 消息生产速度 > 消费速度 时,导致服务器或客户端缓存过多数据,从而增加延迟、占用大量内存,甚至导致崩溃。

常见的消息堆积原因:

1.客户端处理速度太慢(如 UI 渲染卡顿、消息处理逻辑过重)。

2.网络延迟或不稳定(导致 TCP 发送窗口收缩)。

3.服务器推送速度过快(高频率推送数据,客户端处理不过来)。

4.未进行消息丢弃或限流处理(所有消息都缓存,导致堆积)。

解决方案:

1.采用消息丢弃策略

适用于 实时性高、但不要求每条消息都被处理的场景(如股票行情、弹幕系统)。

1.只保留最新消息,丢弃旧消息,防止堆积历史数据。

2.基于 Sliding Window(滑动窗口)策略,让客户端始终处理最新的数据,而不是堆积消息。

2. 客户端阻塞队列

适用于服务器端消息推送过快,客户端来不及处理的情况。

客户端添加阻塞队列,将数据先放入阻塞队列中, 然后客户端根据实际业务拉取数据。

3.使用消息队列进行缓冲

适用于 高并发 WebSocket 服务器,避免瞬时流量过载。

1.使用 Kafka / RabbitMQ / Redis Stream 作为消息缓冲层。

2.让 WebSocket 服务器从 MQ 拉取消息,避免消息直接推送导致积压。

4. 客户端批量拉取(Pull)模式

适用于 数据量大、实时性要求较低的场景(如日志推送)。

1.改为客户端定期拉取(Pull),而不是服务器主动推送(Push),需要依靠自己在服务端代码中实现。

2.减少每次 WebSocket 传输数据量,比如 客户端只拉取未读数据。

5. 使用数据压缩

适用于 消息体积过大,导致带宽占用过多的情况。

1.开启 WebSocket 的 permessage-deflate 压缩,减少传输数据大小。

2.在消息层面压缩数据,如 JSON 压缩、Protobuf、MessagePack 等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

纯洁的小魔鬼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值