From e34fa7229fe0e5aca7d7f9210c4c374eefa8f149 Mon Sep 17 00:00:00 2001 From: SisMaker <1713699517@qq.com> Date: Mon, 24 Feb 2025 16:08:06 +0800 Subject: [PATCH] =?UTF-8?q?ft=EF=BC=9A=E6=B7=BB=E5=8A=A0=E9=80=89=E9=A1=B9?= =?UTF-8?q?=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/eNet.hrl | 54 +++--- .../erlang套接字编程和性能优化.md | 180 +++++++++++++++++- 2 files changed, 210 insertions(+), 24 deletions(-) diff --git a/include/eNet.hrl b/include/eNet.hrl index 874b765..d3169b8 100644 --- a/include/eNet.hrl +++ b/include/eNet.hrl @@ -7,16 +7,24 @@ -define(mSockReady, mSockReady). -define(DefTpOpts, [ - binary - , {packet, 4} - , {active, false} - , {reuseaddr, true} - , {nodelay, false} - , {delay_send, true} - , {send_timeout, 15000} - , {keepalive, true} - , {exit_on_close, true} - , {backlog, 1024} + binary + , {packet, 4} + , {active, false} + , {reuseaddr, true} + , {nodelay, true} % 禁用Nagle 可以减少延迟(对于需要低延迟的游戏) + , {delay_send, true} % 提升吞吐量 尤其在高并发场景下,减少调度器争用 增加约10-50μs的延迟(取决于队列深度和调度频率) + , {send_timeout, 15000} % 发送超时时间 + , {send_timeout_close, true} % 发送超时自动关闭连接(防止半开连接) + , {keepalive, true} % 检测死连接(默认间隔2小时,需系统级调整) + , {exit_on_close, true} % 当socket被关闭时,与其关联的控制进程(controlling process) 会收到{'EXIT', Port, Reason}信号 强制清理异常连接 + , {backlog, 1024} % 等待连接队列长度 + , {buffer, 128 * 1024} % 接收缓冲区1MB + , {recbuf, 128 * 1024} % 内核接收缓冲区 通常接受的数据比较小 + , {sndbuf, 512 * 1024} % 内核发送缓冲区 + , {high_watermark, 32 * 1024} % 32KB Erlang内部Socket实现的数据队列 当队列数据量达到此阈值时,Socket标记为**繁忙(busy)**,发送进程会被挂起。 + , {low_watermark, 16 * 1024} % 16KB 当队列数据量降低到此阈值时,Socket恢复**非繁忙状态**,允许继续发送。 + , {high_msgq_watermark, 64 * 1024} % 64KB Erlang进程消息队列**的繁忙状态 消息队列数据量达到此值时,队列标记为繁忙,阻止新消息进入 + , {low_msgq_watermark, 32 * 1024} % 32KB 消息队列数据量低于此值时,恢复非繁忙状态 ]). @@ -26,22 +34,22 @@ -export_type([listenOpt/0]). -type listenOpt() :: - {aptCnt, non_neg_integer()} | - {conMod, atom()} | - {conArgs, atom()} | - {tcpOpts, [gen_tcp:listen_option()]} | - {sslOpts, [ssl:ssl_option()]} | - {sslHSTet, timeout()} | - {udpOpts, [gen_udp:option()]} | - {proxyPt, boolean()} | - {proxyPtTet, timeout()}. +{aptCnt, non_neg_integer()} | +{conMod, atom()} | +{conArgs, atom()} | +{tcpOpts, [gen_tcp:listen_option()]} | +{sslOpts, [ssl:ssl_option()]} | +{sslHSTet, timeout()} | +{udpOpts, [gen_udp:option()]} | +{proxyPt, boolean()} | +{proxyPtTet, timeout()}. %% 令牌桶相关定义 -record(tBucket, { - rate :: pos_integer() %% 速率 - , tokens :: non_neg_integer() %% 剩余tokens数量 - , lastTime :: pos_integer() %% 最后一次更新访问时间单位毫秒 - , bucketSize :: pos_integer() %% 桶大小 可以容纳的令牌数量 + rate :: pos_integer() %% 速率 + , tokens :: non_neg_integer() %% 剩余tokens数量 + , lastTime :: pos_integer() %% 最后一次更新访问时间单位毫秒 + , bucketSize :: pos_integer() %% 桶大小 可以容纳的令牌数量 }). -endif. \ No newline at end of file diff --git a/src/misc/erlang套接字编程和性能优化.md b/src/misc/erlang套接字编程和性能优化.md index ae16430..44c9314 100644 --- a/src/misc/erlang套接字编程和性能优化.md +++ b/src/misc/erlang套接字编程和性能优化.md @@ -654,4 +654,182 @@ listen_option() = {ip, inet:socket_address()} | {fd, Fd :: integer() >= 0} | {if address_family() | {port, inet:port_number()} | {backlog, B :: integer() >= 0} | {tcp_module, module()} | {netns, file: filename_all()} | {bind_to_device, binary()} | option() socket() -As returned by accept/1,2 and connect/3,4. \ No newline at end of file +As returned by accept/1,2 and connect/3,4. + +在Erlang的`gen_tcp`模块中,`low_watermark`、`high_watermark`、`high_msgq_watermark`和`low_msgq_watermark`是控制网络数据流和内存管理的核心参数。它们的协同作用直接影响吞吐量、延迟和系统稳定性。以下是详细解析: + +--- + +### **1. 参数定义与作用** +#### **(1) `high_watermark`与`low_watermark`** +- **功能**:控制**Erlang内部Socket实现的数据队列**。 + - `high_watermark`:当队列数据量达到此阈值时,Socket标记为**繁忙(busy)**,发送进程会被挂起。 + - `low_watermark`:当队列数据量降低到此阈值时,Socket恢复**非繁忙状态**,允许继续发送。 +- **默认值**:均为8KB(不同版本可能略有差异)。 +- **适用场景**:防止底层Socket缓冲区溢出,适用于高吞吐量场景(如文件传输、广播消息)。 + +#### **(2) `high_msgq_watermark`与`low_msgq_watermark`** +- **功能**:控制**Erlang进程消息队列**的繁忙状态。 + - `high_msgq_watermark`:消息队列数据量达到此值时,队列标记为繁忙,阻止新消息进入。 + - `low_msgq_watermark`:消息队列数据量低于此值时,恢复非繁忙状态。 +- **默认值**:分别为8KB和4KB。 +- **适用场景**:防止进程消息队列爆炸(如恶意客户端高频发送小包)。 + +--- + +### **2. 参数间的协同关系** +#### **(1) 触发条件优先级** +- **Socket繁忙状态**:当Socket队列(`high_watermark`)**或**消息队列(`high_msgq_watermark`)任一达到阈值时,发送进程会被挂起。 +- **恢复条件**:需**同时满足**Socket队列低于`low_watermark`且消息队列低于`low_msgq_watermark`,才会恢复发送。 + +#### **(2) 典型数据流路径** +```plaintext +发送进程 → 消息队列 → Erlang内部Socket队列 → 操作系统Socket缓冲区 → 网络 +``` +- **消息队列**:进程间通信的中间缓存(受`*_msgq_watermark`控制); +- **Socket队列**:Erlang虚拟机的发送缓冲区(受`*_watermark`控制)。 + +--- + +### **3. 配置建议与调优** +#### **(1) 默认值适用场景** +- **常规交互式服务**(如HTTP短连接):默认值(8KB/4KB)可平衡吞吐与内存占用。 +- **防御性设计**:通过`high_msgq_watermark`限制单个进程的负载,避免级联崩溃。 + +#### **(2) 高吞吐场景优化** +- **增大水位线**:例如设置`{high_watermark, 64*1024}`和`{low_watermark, 32*1024}`,提升批量数据传输能力。 +- **结合`delay_send`**:启用`{delay_send, true}`合并小包,减少调度器争用。 + +#### **(3) 低延迟场景优化** +- **降低水位线**:例如`{high_watermark, 4*1024}`,减少队列积压导致的延迟。 +- **禁用合并机制**:配合`{nodelay, true}`确保小包立即发送。 + +--- + +### **4. 监控与调试** +- **工具推荐**: + - `recon`:监控进程消息队列长度(`recon:proc_count(message_queue_len, 5)`); + - `inet:getopts/2`:查看Socket队列状态(如`inet:getopts(Socket, [high_watermark])`)。 +- **异常处理**: + - 若频繁触发繁忙状态,需检查网络拥塞或协议设计问题; + - 若消息队列持续满载,考虑优化消息处理逻辑或拆分进程。 + +--- + +### **总结** +- **核心目标**:通过水位线机制平衡吞吐量、延迟和内存安全; +- **调优原则**:根据场景选择水位线阈值,结合`delay_send`、`nodelay`等选项协同优化; +- **风险规避**:避免盲目增大水位线导致内存溢出,或过度降低引发频繁阻塞。 + +具体参数需通过压测工具(如`Tsung`)结合实际流量验证。 + +针对SLG卡牌和MMO游戏的后端项目,`low_watermark`、`high_watermark`、`high_msgq_watermark`和`low_msgq_watermark`的设置需结合游戏特性和网络负载需求。以下是具体建议及优化逻辑: + +--- + +### **一、SLG卡牌类游戏** +#### **1. 核心需求** +- **低频交互**:回合制战斗、抽卡、养成等操作,数据包间隔较长(0.5~2秒/次); +- **突发流量**:活动开启时(如限时抽卡)可能产生瞬时高并发; +- **数据一致性**:需保证抽卡结果、装备强化等操作的原子性。 + +#### **2. 推荐配置** +```erlang +% gen_tcp 监听配置 +gen_tcp:listen(Port, [ + {high_watermark, 32 * 1024}, % 32KB + {low_watermark, 16 * 1024}, % 16KB + {high_msgq_watermark, 64 * 1024}, % 64KB + {low_msgq_watermark, 32 * 1024} % 32KB +]). +``` + +#### **3. 配置解析** +- **Socket队列(high/low_watermark)** + - 设置中等缓冲区(32KB/16KB):应对活动期的瞬时流量,避免频繁阻塞; + - 抽卡接口的协议包通常较小(1~4KB),32KB可缓存约8~32个请求。 + +- **消息队列(high/low_msgq_watermark)** + - 较大阈值(64KB/32KB):防止活动期间消息队列溢出(如万人同时十连抽); + - 配合`{active, 100}`模式,允许短时堆积后自动恢复。 + +#### **4. 调优建议** +- **监控重点**: + - 使用`recon:proc_count(message_queue_len, 10)`检查消息队列堆积; + - 若活动期间`high_watermark`频繁触发,可提升至64KB。 + +--- + +### **二、MMO类游戏** +#### **1. 核心需求** +- **高频实时交互**:玩家移动、技能释放需50~200ms内同步; +- **高吞吐量**:万人同屏时每秒处理10万+数据包; +- **低延迟优先**:避免队列堆积增加额外延迟。 + +#### **2. 推荐配置** +```erlang +% gen_tcp 监听配置 +gen_tcp:listen(Port, [ + {high_watermark, 64 * 1024}, % 64KB + {low_watermark, 8 * 1024}, % 8KB + {high_msgq_watermark, 128 * 1024}, % 128KB + {low_msgq_watermark, 64 * 1024} % 64KB +]). +``` + +#### **3. 配置解析** +- **Socket队列(high/low_watermark)** + - 大缓冲区(64KB)应对广播风暴(如国战技能特效); + - 低恢复阈值(8KB):快速解除阻塞,确保实时性。 + +- **消息队列(high/low_msgq_watermark)** + - 超大阈值(128KB/64KB):容忍突发流量(如玩家瞬移引发的周围实体状态同步); + - 需配合`{active, once}`模式防御DDoS攻击。 + +#### **4. 调优建议** +- **动态调整**: + - 战斗密集期临时提升`high_watermark`至128KB; + - 使用`inet:setopts(Socket, [{high_watermark, NewVal}])`动态修改。 +- **硬件协同**: + - 10Gbps网卡需设置`{recbuf, 256KB}`和`{sndbuf, 256KB}`,匹配内核参数。 + +--- + +### **三、通用优化策略** +#### **1. 防御性设计** +- **消息队列监控**: + ```erlang + %% 检查进程消息队列并告警 + check_msgq(Pid) -> + case process_info(Pid, message_queue_len) of + {message_queue_len, Len} when Len > 1000 -> + alert_system_operator(Pid, Len); + _ -> ok + end. + ``` +- **自动熔断**:若队列持续超过`high_msgq_watermark`的80%,触发限流。 + +#### **2. 压测验证** +- **工具选择**: + - SLG:用Locust模拟抽卡、装备强化峰值; + - MMO:用Tsung模拟万人移动同步。 +- **关键指标**: + - Socket队列平均深度(`inet:getopts/2`); + - 消息队列峰值长度(`recon`)。 + +--- + +### **四、参数设置总结** +| 游戏类型 | high_watermark | low_watermark | high_msgq_watermark | low_msgq_watermark | 核心逻辑 | +|------------|----------------|---------------|----------------------|---------------------|---------------------------------| +| **SLG卡牌**| 32KB | 16KB | 64KB | 32KB | 平衡突发流量与内存占用,避免活动期卡顿 | +| **MMO** | 64KB | 8KB | 128KB | 64KB | 容忍广播风暴,同时保障实时性 | + +--- + +### **五、注意事项** +1. **不要盲目增大水位线**:过高的`high_watermark`可能导致内存溢出(尤其在32位系统); +2. **结合`delay_send`使用**:MMO可启用`{delay_send, true}`提升吞吐,但需测试延迟影响; +3. **版本差异**:Erlang/OTP 25+优化了水位线算法,相同配置下性能可能提升20%~30%。 + +实际配置需通过**灰度发布**逐步验证,观察CPU、内存及网络指标变化。 \ No newline at end of file