WebSocket与gRPC:全双工通信与高效RPC

WebSocket:全双工实时通信 HTTP的局限 HTTP请求-响应模式: 客户端 → 服务器: 请求 客户端 ← 服务器: 响应 问题: - 服务器无法主动推送 - 实时性差(需要轮询) 轮询(Polling): 客户端每隔1秒请求一次: GET /api/messages(1秒后) GET /api/messages(2秒后) GET /api/messages(3秒后) ... 问题:大量无效请求,浪费资源 长轮询(Long Polling): 客户端请求,服务器挂起直到有新消息: GET /api/messages ... 服务器等待30秒 ... ← 返回新消息 问题:仍然是请求-响应模式,连接频繁断开重连 WebSocket协议 核心特点: 全双工通信(双向同时传输) 基于TCP 低开销(头部仅2字节) 持久连接 协议升级: # 客户端发起升级请求 GET /chat HTTP/1.1 Host: example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Sec-WebSocket-Version: 13 # 服务器同意升级 HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= # 升级完成,使用WebSocket通信 Spring Boot WebSocket 服务端 // WebSocket配置 @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(chatHandler(), "/chat") .setAllowedOrigins("*"); // CORS } @Bean public ChatHandler chatHandler() { return new ChatHandler(); } } // WebSocket处理器 @Component public class ChatHandler extends TextWebSocketHandler { private static final Set<WebSocketSession> sessions = new CopyOnWriteArraySet<>(); @Override public void afterConnectionEstablished(WebSocketSession session) { sessions.add(session); System.out.println("新连接:" + session.getId()); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { String payload = message.getPayload(); System.out.println("收到消息:" + payload); // 广播给所有客户端 for (WebSocketSession s : sessions) { if (s.isOpen()) { s.sendMessage(new TextMessage("服务器转发:" + payload)); } } } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { sessions.remove(session); System.out.println("连接关闭:" + session.getId()); } } 客户端(JavaScript) // 建立WebSocket连接 const ws = new WebSocket('ws://localhost:8080/chat'); // 连接打开 ws.onopen = () => { console.log('WebSocket连接已建立'); ws.send('你好,服务器'); }; // 接收消息 ws.onmessage = (event) => { console.log('收到消息:', event.data); }; // 连接关闭 ws.onclose = () => { console.log('WebSocket连接已关闭'); }; // 连接错误 ws.onerror = (error) => { console.error('WebSocket错误:', error); }; STOMP:WebSocket消息协议 问题 原始WebSocket只传输文本/二进制: ...

2025-11-20 · maneng

DNS域名解析:从域名到IP的查询过程

DNS基础 为什么需要DNS? 人类习惯:www.baidu.com 计算机需要:180.97.33.108 DNS(Domain Name System):域名系统 作用:域名 → IP地址 DNS分层架构 根域名服务器(Root DNS) . ├── com(顶级域) │ ├── baidu.com(二级域) │ │ ├── www.baidu.com │ │ └── api.baidu.com │ └── google.com ├── cn(顶级域) │ └── taobao.com └── org(顶级域) └── wikipedia.org DNS查询过程 递归查询 vs 迭代查询 递归查询(客户端 → 本地DNS): 客户端:请帮我查 www.baidu.com 本地DNS:好的,我帮你查到底,然后告诉你结果 迭代查询(本地DNS → 各级DNS服务器): 本地DNS → 根DNS:www.baidu.com的IP是? 根DNS → 本地DNS:我不知道,你去问.com的DNS服务器 本地DNS → .com DNS:www.baidu.com的IP是? .com DNS → 本地DNS:我不知道,你去问baidu.com的DNS服务器 本地DNS → baidu.com DNS:www.baidu.com的IP是? baidu.com DNS → 本地DNS:是180.97.33.108 完整流程 1. 浏览器缓存(Chrome://net-internals/#dns) 2. 操作系统缓存(/etc/hosts,系统DNS缓存) 3. 本地DNS服务器(ISP提供,如8.8.8.8) 4. 根DNS服务器(全球13个根服务器集群) 5. 顶级域DNS服务器(.com、.cn等) 6. 权威DNS服务器(baidu.com的DNS服务器) 示例: ...

2025-11-20 · maneng

HTTP/3与QUIC:基于UDP的零RTT连接

HTTP/2的最后问题:TCP队头阻塞 应用层多路复用 vs TCP层阻塞 HTTP/2解决了应用层队头阻塞: Stream 1: [帧1] [帧2] [帧3] Stream 2: [帧1] [帧2] [帧3] 但TCP层仍然有问题: TCP传输:[S1-帧1][S2-帧1][S1-帧2]【丢失】[S2-帧2]... ↑ TCP丢包重传,阻塞所有Stream! 即使Stream 2的数据已到达,也要等待Stream 1的丢包重传完成 QUIC协议:UDP上的可靠传输 核心思想 QUIC = UDP + TCP的可靠性 + TLS + HTTP/2多路复用 传统协议栈: 应用层 HTTP/2 安全层 TLS/SSL 传输层 TCP 网络层 IP QUIC协议栈: 应用层 HTTP/3 传输层 QUIC(在用户空间实现) 网络层 UDP 为什么选择UDP? TCP的限制: 内核实现,难以更新(Windows XP仍在用,无法升级TCP) 握手固定,无法优化 队头阻塞无法解决 UDP的优势: 没有队头阻塞 用户空间实现(QUIC协议栈在应用层) 灵活升级 QUIC核心特性 1. 0-RTT连接建立 TCP + TLS 1.2:3-RTT 客户端 → 服务器: SYN(TCP握手1) 客户端 ← 服务器: SYN-ACK(TCP握手2) 客户端 → 服务器: ACK(TCP握手3) 客户端 → 服务器: Client Hello(TLS握手1) 客户端 ← 服务器: Server Hello + Certificate(TLS握手2) 客户端 → 服务器: Finished(TLS握手3) 客户端 → 服务器: HTTP请求 总延迟:3-RTT QUIC(首次连接):1-RTT ...

2025-11-20 · maneng

HTTP/2协议:多路复用解决队头阻塞问题

HTTP/1.1的问题 队头阻塞(Head-of-Line Blocking) HTTP/1.1管道化(Pipelining): 客户端:GET /1.js → GET /2.js → GET /3.js ↓ ↓ ↓ 服务器:200 OK 等待... 等待... (1.js) 问题:第一个响应慢,阻塞后续响应 解决方案的局限 方案1:并发多个TCP连接 浏览器同时开6-8个TCP连接: 连接1: GET /1.js 连接2: GET /2.js 连接3: GET /3.js ... 问题: - TCP连接数有限 - 每个连接都要握手(延迟) - 拥塞控制独立(带宽利用率低) 方案2:域名分片 static1.example.com static2.example.com static3.example.com 每个域名6个连接 × 3个域名 = 18个连接 问题: - DNS解析开销 - TLS握手开销 - 服务器资源浪费 HTTP/2核心特性 1. 二进制分帧 HTTP/1.x:文本协议 GET /api/users HTTP/1.1\r\n Host: api.example.com\r\n \r\n HTTP/2:二进制协议 [帧头部(9字节)] +-----------------------------------------------+ | Length (24) | Type (8) | Flags (8) | Stream ID (31) | +-----------------------------------------------+ [帧负载] 帧类型: ...

2025-11-20 · maneng

HTTPS与TLS/SSL:加密通信原理与证书验证

为什么需要HTTPS? HTTP的三大安全问题 1. 明文传输(窃听) 中间人可以看到所有内容: 客户端 → 路由器: POST /login {"username":"admin", "password":"123456"} ↑ 窃听者看到密码! 2. 篡改(中间人攻击) 中间人可以修改请求/响应: 客户端 → 中间人 → 服务器 ↑ 修改转账金额 1000 → 10000 3. 冒充(钓鱼网站) 客户端访问 http://bank.com 中间人伪造假网站,窃取用户密码 HTTPS = HTTP + TLS/SSL HTTP over TLS/SSL 应用层 HTTP ---- 安全层 TLS/SSL(加密) ---- 传输层 TCP 加密基础 对称加密 相同的密钥加密和解密 加密:明文 + 密钥 → 密文 解密:密文 + 密钥 → 明文 示例: 明文:"Hello" 密钥:"secret123" 密文:"Xf9kL2p" 常见算法:AES、DES、3DES 优点:速度快 缺点:密钥如何安全传递? 非对称加密 公钥加密,私钥解密 公钥:公开,任何人都可以获取 私钥:保密,只有服务器持有 加密:明文 + 公钥 → 密文 解密:密文 + 私钥 → 明文 示例: 服务器生成密钥对:公钥A、私钥B 客户端用公钥A加密:密文C 服务器用私钥B解密:得到明文 常见算法:RSA、ECC ...

2025-11-20 · maneng

HTTP协议进阶:Keep-Alive、缓存机制与内容协商

HTTP/1.0 vs HTTP/1.1 HTTP/1.0的问题 短连接(每次请求都要三次握手): 客户端 → 服务器: SYN(握手) 客户端 ← 服务器: SYN-ACK 客户端 → 服务器: ACK 客户端 → 服务器: GET /index.html 客户端 ← 服务器: HTTP/1.0 200 OK 客户端 → 服务器: FIN(挥手) 下一个请求又要重新握手! 问题: 每个请求都要建立TCP连接(+1.5 RTT延迟) 频繁握手挥手,浪费资源 服务器TIME_WAIT状态过多 HTTP/1.1的改进 1. 持久连接(Keep-Alive):默认开启 # HTTP/1.1请求 GET /api/users HTTP/1.1 Host: api.example.com Connection: keep-alive # HTTP/1.1响应 HTTP/1.1 200 OK Connection: keep-alive Keep-Alive: timeout=5, max=100 一个TCP连接,多个HTTP请求: 客户端 → 服务器: 三次握手 客户端 → 服务器: GET /api/users 客户端 ← 服务器: 200 OK 客户端 → 服务器: GET /api/orders # 复用连接 客户端 ← 服务器: 200 OK 客户端 → 服务器: GET /api/products # 复用连接 客户端 ← 服务器: 200 OK ...(5秒后无请求,关闭连接) 2. 管道化(Pipelining):不等响应,连续发送请求 ...

2025-11-20 · maneng

HTTP协议基础:请求方法、状态码与头部详解

HTTP协议概述 HTTP(HyperText Transfer Protocol):超文本传输协议 核心特点: 基于TCP(可靠传输) 无状态协议(每次请求独立) 请求-响应模式 文本协议(HTTP/1.x) HTTP请求方法 常用方法 方法 用途 是否幂等 是否安全 GET 获取资源 ✅ 是 ✅ 是 POST 创建资源/提交数据 ❌ 否 ❌ 否 PUT 更新资源(完整替换) ✅ 是 ❌ 否 PATCH 更新资源(部分修改) ❌ 否 ❌ 否 DELETE 删除资源 ✅ 是 ❌ 否 HEAD 获取响应头(不返回body) ✅ 是 ✅ 是 OPTIONS 查询支持的方法 ✅ 是 ✅ 是 幂等性:多次调用效果相同 安全性:不修改服务器状态 RESTful API设计 // 用户管理API @RestController @RequestMapping("/api/users") public class UserController { // GET /api/users - 查询所有用户 @GetMapping public List<User> listUsers() { return userService.findAll(); } // GET /api/users/123 - 查询单个用户 @GetMapping("/{id}") public User getUser(@PathVariable Long id) { return userService.findById(id); } // POST /api/users - 创建用户 @PostMapping public User createUser(@RequestBody User user) { return userService.create(user); } // PUT /api/users/123 - 完整更新用户 @PutMapping("/{id}") public User updateUser(@PathVariable Long id, @RequestBody User user) { return userService.update(id, user); } // PATCH /api/users/123 - 部分更新用户 @PatchMapping("/{id}") public User patchUser(@PathVariable Long id, @RequestBody Map<String, Object> updates) { return userService.patch(id, updates); } // DELETE /api/users/123 - 删除用户 @DeleteMapping("/{id}") public void deleteUser(@PathVariable Long id) { userService.delete(id); } } HTTP状态码 分类 类别 含义 常见状态码 1xx 信息响应 100 Continue, 101 Switching Protocols 2xx 成功 200 OK, 201 Created, 204 No Content 3xx 重定向 301 Moved Permanently, 302 Found, 304 Not Modified 4xx 客户端错误 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found 5xx 服务器错误 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable 重要状态码详解 200 OK HTTP/1.1 200 OK Content-Type: application/json {"id": 123, "name": "张三"} 201 Created HTTP/1.1 201 Created Location: /api/users/123 {"id": 123, "name": "张三"} 204 No Content HTTP/1.1 204 No Content # 删除成功,无返回内容 301 vs 302 301 Moved Permanently:永久重定向(浏览器会缓存) 302 Found:临时重定向(不缓存) 示例: 旧网站 http://old.com → 301 → http://new.com 304 Not Modified # 客户端请求 GET /api/users/123 If-None-Match: "abc123" # 服务器响应(资源未改变) HTTP/1.1 304 Not Modified ETag: "abc123" # 客户端使用缓存 401 vs 403 401 Unauthorized:未认证(需要登录) 403 Forbidden:已认证但无权限(权限不足) 示例: 未登录访问:401 普通用户访问管理员接口:403 502 vs 503 vs 504 502 Bad Gateway:网关从上游服务器收到无效响应 503 Service Unavailable:服务暂时不可用(维护/过载) 504 Gateway Timeout:网关超时 微服务场景: API网关 → 用户服务(宕机)→ 502 API网关 → 用户服务(超时)→ 504 用户服务主动拒绝(限流)→ 503 HTTP请求头 常用请求头 请求头 作用 示例 Host 指定服务器域名 Host: api.example.com User-Agent 客户端信息 User-Agent: Mozilla/5.0... Accept 可接受的响应类型 Accept: application/json Content-Type 请求体类型 Content-Type: application/json Authorization 认证信息 Authorization: Bearer <token> Cookie 会话Cookie Cookie: sessionId=abc123 If-None-Match 条件请求(ETag) If-None-Match: "abc123" Range 请求部分内容 Range: bytes=0-1023 微服务场景实战 // Feign客户端添加请求头 @FeignClient(name = "user-service") public interface UserClient { @GetMapping("/api/users/{id}") User getUser(@PathVariable Long id, @RequestHeader("Authorization") String token, @RequestHeader("X-Request-Id") String requestId); } // 全局请求拦截器 @Component public class FeignRequestInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { // 添加链路追踪ID template.header("X-Trace-Id", MDC.get("traceId")); // 添加租户ID(多租户场景) template.header("X-Tenant-Id", TenantContext.getTenantId()); // 添加认证Token template.header("Authorization", "Bearer " + getToken()); } } HTTP响应头 常用响应头 响应头 作用 示例 Content-Type 响应体类型 Content-Type: application/json; charset=utf-8 Content-Length 响应体长度 Content-Length: 1234 Cache-Control 缓存策略 Cache-Control: max-age=3600 ETag 资源版本标识 ETag: "abc123" Location 重定向地址 Location: /api/users/123 Set-Cookie 设置Cookie Set-Cookie: sessionId=abc; HttpOnly Access-Control-Allow-Origin CORS跨域 Access-Control-Allow-Origin: * Spring Boot响应头配置 // 方式1:在Controller中设置 @GetMapping("/api/users/{id}") public ResponseEntity<User> getUser(@PathVariable Long id) { User user = userService.findById(id); return ResponseEntity.ok() .header("X-Custom-Header", "custom-value") .eTag("\"" + user.getVersion() + "\"") // ETag .cacheControl(CacheControl.maxAge(1, TimeUnit.HOURS)) // 缓存1小时 .body(user); } // 方式2:全局响应拦截器 @Component public class ResponseHeaderInterceptor implements HandlerInterceptor { @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { // 添加安全响应头 response.setHeader("X-Content-Type-Options", "nosniff"); response.setHeader("X-Frame-Options", "DENY"); response.setHeader("X-XSS-Protection", "1; mode=block"); } } Cookie与Session Cookie工作原理 [首次请求] 客户端 → 服务器: GET /login 服务器 → 客户端: Set-Cookie: sessionId=abc123; Path=/; HttpOnly [后续请求] 客户端 → 服务器: GET /api/users Cookie: sessionId=abc123 服务器根据sessionId识别用户 Cookie属性 属性 作用 示例 Expires 过期时间(绝对时间) Expires=Wed, 21 Oct 2025 07:28:00 GMT Max-Age 有效期(秒) Max-Age=3600 Domain 作用域 Domain=.example.com Path 路径 Path=/api Secure 仅HTTPS Secure HttpOnly 禁止JavaScript访问 HttpOnly SameSite 防止CSRF SameSite=Strict Session管理(微服务) 问题:微服务场景Session共享 ...

2025-11-20 · maneng

TCP与UDP选择策略:微服务场景的协议决策指南

引言 在前面的文章中,我们深入学习了TCP和UDP两种传输层协议的工作原理、优缺点和适用场景。今天是传输层协议篇的最后一篇,我们来系统总结:如何在实际项目中选择TCP还是UDP? 核心问题: ✅ 什么时候必须用TCP?什么时候必须用UDP? ✅ 微服务场景如何选择协议? ✅ 主流RPC框架(Dubbo、gRPC)为什么选择TCP? ✅ 如何权衡可靠性与性能? 今天我们来理解: ✅ TCP vs UDP的决策树 ✅ 微服务通信的协议选择 ✅ RPC框架的协议策略 ✅ 性能调优的权衡之道 TCP vs UDP决策树 决策流程图 开始选择协议 | ↓ 需要可靠传输? | ├─ 是 ────────────────────────┐ | | ↓ ↓ 需要顺序保证? 需要建立连接? | | ├─ 是 ─────┐ ├─ 是 ─────┐ | | | | ↓ ↓ ↓ ↓ 需要流量控制? → TCP 需要拥塞控制? → TCP | | ├─ 否 ─────┘ ├─ 否 ─────┘ | | ↓ ↓ 实时性优先? 支持广播/多播? | | ├─ 是 ─────┐ ├─ 是 ─────┐ | | | | ↓ ↓ ↓ ↓ 能容忍丢包? → UDP 简单请求响应? → UDP | | ├─ 否 ─────┘ └─ 否 ─────┘ | | └──────────────────────────────┘ | ↓ 使用TCP 快速决策表 需求 协议 典型应用 数据完整性最重要 TCP 文件传输、数据库同步、支付交易 实时性最重要 UDP 视频直播、在线游戏、VoIP 需要顺序保证 TCP HTTP/HTTPS、邮件传输 广播/多播 UDP 设备发现、IPTV组播 简单请求-响应 UDP DNS查询、SNMP监控 长连接 TCP WebSocket、数据库连接池 低延迟 UDP 高频交易、实时监控 微服务场景的协议选择 场景1:RESTful API(HTTP/HTTPS) 协议:TCP ...

2025-11-20 · maneng

UDP协议:极简设计,高效但不可靠的传输

引言 在前面的文章中,我们深入学习了TCP协议(三次握手、四次挥手、流量控制、拥塞控制、重传机制)。今天我们来学习另一个重要的传输层协议:UDP(User Datagram Protocol)。 UDP的核心特点: ✅ 极简设计:没有连接建立、流量控制、拥塞控制、重传 ✅ 高效率:开销小,延迟低 ❌ 不可靠:不保证数据到达、不保证顺序、不保证不重复 今天我们来理解: ✅ UDP的头部结构(仅8字节) ✅ 为什么UDP是不可靠的? ✅ UDP的优势场景 ✅ UDP vs TCP性能对比 UDP头部结构:极简的8字节 UDP报文段格式 0 16 31 +-------------------+-------------------+ | Source Port | Destination Port | 2字节 + 2字节 = 4字节 +-------------------+-------------------+ | Length | Checksum | 2字节 + 2字节 = 4字节 +-------------------+-------------------+ | | | Data(应用数据) | | ... | +---------------------------------------+ 总共:8字节头部 + 数据 字段说明: 字段 长度 说明 Source Port 16位(2字节) 源端口号(可选,不需要时可以设为0) Destination Port 16位(2字节) 目标端口号(必须) Length 16位(2字节) UDP报文段总长度(头部8字节 + 数据长度) Checksum 16位(2字节) 校验和(检测数据是否损坏,可选) 示例: ...

2025-11-20 · maneng

TCP重传机制:超时重传与SACK选择性确认

引言 在前面的文章中,我们学习了TCP的拥塞控制机制(慢启动、拥塞避免等)。今天我们来学习TCP如何应对数据丢失:重传机制(Retransmission)。 为什么需要重传? 网络是不可靠的:数据包可能丢失、损坏、重复、乱序 TCP要提供可靠传输:保证数据正确、完整、有序地到达 重传是可靠性的核心保障 今天我们来理解: ✅ 超时重传(RTO)的计算方法 ✅ 快速重传的触发条件 ✅ SACK选择性确认机制 ✅ 如何用Wireshark分析重传问题 第一性原理:如何检测数据丢失? 两种丢失检测方式 1. 超时检测(Timeout-based) 原理:发送方设置一个定时器,如果超时还没收到ACK,认为数据丢失 发送方 接收方 | seq=100(发送) | |---------------------------------->| | 启动定时器(RTO=1秒) | | | | 等待ACK... | | | (数据包丢失) | 1秒后,超时! | | seq=100(重传) | |---------------------------------->| | ACK=101 | |<----------------------------------| 优点:可以检测到所有丢包 缺点:超时时间较长,影响性能 2. 重复ACK检测(Duplicate ACK) 原理:接收方收到乱序数据时,发送重复ACK,发送方收到3个重复ACK后立即重传 发送方 接收方 | seq=100(到达) | |---------------------------------->| | ACK=101 | |<----------------------------------| | | | seq=101(丢失!) | |----X | | | | seq=102(到达) | |---------------------------------->| | ACK=101(重复ACK #1) | |<----------------------------------| | | | seq=103(到达) | |---------------------------------->| | ACK=101(重复ACK #2) | |<----------------------------------| | | | seq=104(到达) | |---------------------------------->| | ACK=101(重复ACK #3) | |<----------------------------------| | | | 收到3个重复ACK,立即重传! | | seq=101(快速重传) | |---------------------------------->| | ACK=105(确认101-104都收到了) | |<----------------------------------| 优点:快速检测,不需要等待超时 缺点:需要后续数据包到达才能触发 ...

2025-11-20 · maneng

如约数科科技工作室

浙ICP备2025203501号

👀 本站总访问量 ...| 👤 访客数 ...| 📅 今日访问 ...