Browse Source

ft:添加选项说明

master
SisMaker 3 months ago
parent
commit
e34fa7229f
2 changed files with 210 additions and 24 deletions
  1. +31
    -23
      include/eNet.hrl
  2. +179
    -1
      src/misc/erlang套接字编程和性能优化.md

+ 31
- 23
include/eNet.hrl View File

@ -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.

+ 179
- 1
src/misc/erlang套接字编程和性能优化.md View File

@ -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.
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、内存及网络指标变化。

Loading…
Cancel
Save