跳转到内容

限速与连接限制

katana 按用户限速。节点上的每个用户都有一个令牌桶,该用户传输的每个字节,无论方向、无论走 TCP 还是 UDP,都要从这个桶里扣除。速率来自面板,katana 配置中的一个配置项可以为整个节点覆盖它。

本页说明用户的速率从何而来、令牌桶如何执行限速、修改何时生效,以及哪些固定的连接限制在保护节点。在配置带限速的套餐、用户反映速度与套餐不符,或为繁忙节点估算容量时,请阅读本页。

大多数情况下由面板设置限速,katana 无需改动:在面板中给用户或其套餐设置限速,katana 会在下一次轮询时应用。如果想让一个节点的所有用户使用同一个速度上限,请在 [node.api] 中设置 speed_limit:

/etc/katana/config.toml
[log]
level = "info"
[[node]]
panel_type = "NewV2board"
[node.api]
host = "https://panel.example.com"
node_id = 1
key = "replace-with-the-panel-key"
node_type = "V2ray"
speed_limit = 50 # 单位 Mbps;此节点上每个用户都以 50 Mbps 运行
[node.controller]
listen_ip = "0.0.0.0"
update_periodic = 60
[node.controller.cert]
mode = "file"
cert_file = "/etc/katana/cert/fullchain.pem"
key_file = "/etc/katana/cert/privkey.pem"

使用这个文件时,无论面板为用户设置了什么,节点 1 上的每个用户都会得到一个 50 Mbps 的令牌桶,即每秒 6,250,000 字节。在用它启动 katana,或用它覆盖运行中的 katana 所监视的文件之前,先用 katana --test -c /etc/katana/config.toml 检查文件。

[node.api] 中有两个配置项与限制有关。[node.api] 的其余配置项见配置文件。

配置项 类型 默认值 说明
speed_limit float 0 此节点每个用户的限速,单位 Mbps。大于 0 的值会替换每个用户的面板限速和面板的节点限速,即使面板限速更低也是如此。0 或负值表示沿用面板的限速。允许小数,例如 12.5。
device_limit integer 0 接受但忽略。katana 没有设备数限制。

speed_limit 必须是数字。带引号的值会导致解析错误:katana 会拒绝以此启动,包含该值的热重载会被拒绝,katana 继续使用当前配置。katana --test 对此的输出如下:

configuration error: config parse error: TOML parse error at line 12, column 15
|
12 | speed_limit = "50"
| ^^^^
invalid type: string "50", expected f64

拼错的配置项也会以同样方式被拒绝,因为 [node.api] 拒绝未知字段:

configuration error: config parse error: TOML parse error at line 12, column 1
|
12 | speedlimit = 50
| ^^^^^^^^^^
unknown field `speedlimit`, expected one of `host`, `node_id`, `key`, `node_type`, `enable_vless`, `vless_flow`, `timeout`, `speed_limit`, `device_limit`, `rule_list_path`, `disable_custom_config`

在运行中的节点上修改 speed_limit 后,无需重启 katana。保存文件后,热重载会立即应用新值,且不会断开任何连接。哪些流会使用新速率,见修改何时生效。在面板中修改的限速同样无需重启,katana 会在下一次轮询时读取。

katana 根据最多三个来源为每个用户算出一个速率:

来源 设置位置 传输单位 面板
用户限速 面板中的用户(或其套餐) Mbps Xboard / V2board、SSPanel
节点限速 面板中的节点 Mbps 仅 SSPanel
本地覆盖 [node.api] 中的 speed_limit Mbps 两者均支持

katana 从 UniProxy 用户列表中每个用户的 speed_limit 字段读取用户限速,其值为 Mbps 整数。katana 不从 UniProxy 节点配置中读取节点级限速,因此在这类面板上节点限速始终为 0。

合并规则如下:

  1. 换算为字节每秒。 katana 把 1 兆比特视为 1,000,000 比特,因此 1 Mbps 等于每秒 125,000 字节。结果向下取整到整字节。0 或更小的值变为 0,表示不限速。
  2. 应用本地覆盖。 如果 speed_limit 大于 0,它会同时替换用户限速和节点限速。
  3. 取最小的非零限速。 用户的速率取节点限速和用户限速中较小的一个,为 0 的一方忽略。如果两者都是 0,该用户不限速。
用户限速 节点限速(SSPanel) speed_limit 实际速率
20 Mbps 0 0 20 Mbps = 2,500,000 B/s
20 Mbps 50 Mbps 0 20 Mbps
0 50 Mbps 0 50 Mbps = 6,250,000 B/s
0 0 0 不限速
-1 0 0 不限速
20 Mbps 50 Mbps 100 100 Mbps = 12,500,000 B/s
0 0 12.5 12.5 Mbps = 1,562,500 B/s

第六行最容易让人意外:覆盖值不是上限,而是替换面板的数值,因此套餐为 20 Mbps 的用户在该节点上会以 100 Mbps 运行。

节点上的每个用户恰好有一个令牌桶,桶中存放的是字节:

  • 它按用户的速率补充。
  • 它最多存放一秒的速率量。50 Mbps 的用户最多能积攒 6,250,000 字节,无论空闲多久都不会更多,因此一段空闲之后,用户最多多得一秒的数据量,随后即按速率限制。
  • katana 首次见到该用户时,或用户的速率变化时,令牌桶以满桶开始。
  • 该用户的所有流共用它:所有连接、两个方向、TCP 流和 UDP 数据包都一样。限制针对的是总量,因此 50 Mbps 的用户同时上传和下载时,两者合计 50 Mbps,而不是每个方向各 50 Mbps。

令牌桶属于节点,因此同一个 katana 进程中出现在两个 [[node]] 条目上的用户有两个独立的令牌桶,其面板限速在每个节点上分别生效。

只要令牌桶没有欠额,就可以开始一次传输。传输完成后,katana 按其完整大小扣费,即使超过桶的容量也是如此。此时余额变为负数,该用户之后的每次传输,无论在哪个连接上,都要等到补充把欠额还清。

flowchart TB
  A["一次传输准备就绪"] --> B{"用户的令牌桶有欠额吗?"}
  B -- "有" --> W["等待补充还清欠额"]
  W --> B
  B -- "没有" --> M["传输这些字节"]
  M --> C["按完整大小扣费;余额可以为负"]
  C --> R["按用户速率补充,最多一秒的速率量"]
  R --> A

例如,速率为每秒 10,000 字节且令牌桶已满。一次 30,000 字节的写入会立即通过:10,000 字节从桶中扣除,20,000 字节成为欠额。下一次传输,无论上传还是下载,都要等待两秒。长期来看,该用户每秒传输 10,000 字节,恰好等于限速。

这就是限速不依赖分块大小的原因。如果每个分块都要先等待“足够的令牌”,那么比整个桶还大的分块只能在一个补充周期后放行,大块读取就会跑得比限速更快。采用欠额后,无论字节如何分块,长期平均速率都遵守限速。代价是单次传输可能短暂超出限速,由其后的传输来偿还。

katana 在中继的出站侧计量。它统计并扣费的是纯载荷字节:在入站协议去掉其帧封装和加密之后、出站协议加上自己的封装之前。因此这些数字不随入站协议或传输层变化,TLS、WebSocket 或 VMess 的开销不计入用户。

  • 用户发送的字节计为上传,接收的字节计为下载。二者走同一个令牌桶。
  • 被路由到 block 出站或被审计规则禁止的 TCP 流会在建立前被拒绝,因此不产生任何费用。
  • UDP 关联按数据包逐个路由。被阻止或禁止的数据包会被丢弃且不计费;每个发出的数据包和每个回复都会像流字节一样计费和限速。

流量上报使用的是同一组计数器。它们如何上报给面板,见流量上报。

katana 每隔 update_periodic 秒(默认 60 秒)轮询一次面板。如果某个用户的实际速率与上次轮询不同,无论是因为用户限速还是 SSPanel 节点限速发生了变化,katana 都会给该用户一个新的计数器和一个新的满桶。它不会断开该用户的任何连接。

  • 新的流使用新的计数器和新的速率。
  • 已有的流保留打开时的计数器,并按旧速率完成。一个 UDP 关联算作一个流,因此在结束前一直使用旧速率。
  • 旧计数器上的字节会继续上报,直到其最后一个流结束,因此速率变化不会丢失流量。

修改后的短时间内,用户的旧流和新流从不同的令牌桶取用,因此用户可能短暂超过任一速率。速率没有变化的用户在多次轮询之间保留其计数器和令牌桶。

在配置文件中修改 speed_limit 也走同样的路径,而且无需等待下一次轮询。当热重载改变了 speed_limit 时,katana 会为该节点新建一个面板客户端,并立即从面板完整读取节点及其用户。实际速率发生变化的每个用户都会按上文所述得到新的计数器和令牌桶:用户表在原地刷新,已打开的流保留旧速率,新的流使用新速率。在本地描述的 Hysteria 2 节点也是如此,其 speed_limit 同时也是节点限速。如果此时无法连接面板,新速率会在第一次得到面板响应的轮询时作用于每个用户。如果同一次修改还改变了构建监听器所用的设置,例如 listen_ip、证书或路由,监听器会被重建,节点上的所有连接都会断开;各配置项的效果见热重载。

用户从面板的用户列表中移除时处理方式不同:katana 立即拒绝其新的流,并结束其已有的流。

除按用户的限速外,每个节点还有一组固定的连接护栏。它们是代码中的常量,而不是配置项。其取值远高于正常流量,用于防止扫描、大量空闲客户端或卡住的对端耗尽内存或文件描述符。

下表中的限制适用于基于 TCP 的节点类型:VMess、VLESS、Trojan 和 Shadowsocks。“每个监听器”即每个节点,因为每个节点只有一个监听器。

护栏 取值 作用范围 达到时
活动连接 65,536 每个监听器 新 socket 被接受后立即关闭。名额在整个连接期间都被占用,包括 gRPC 连接承载的每个流。
并发传输层握手 2,048 每个监听器 katana 暂停接受连接,直到有名额空出;后续客户端在内核的 listen backlog 中等待。涵盖 TLS、WebSocket 升级和 HTTP/2 preface。
传输层握手名额 10 s 每个 socket socket 在传输层产出第一个流时或 10 秒后释放握手名额,以先到者为准。
认证前的流 512 每个监听器 如果仍有 512 个流处于协议握手阶段,新到达的流会被立即丢弃。
协议握手时限 10 s 每个流 10 秒后仍未完成协议握手的客户端会被断开。
中继空闲超时 300 s 每个连接 中继在两个方向上 300 秒都没有传输任何数据的连接会被关闭。
进度看门狗 360 s 每个连接 360 秒内完全没有传输字节的连接会被丢弃。它能捕获空闲超时无法关闭的连接,例如停止读取的客户端。
每个 UDP 关联的出站链路 64 每个 UDP 关联 一个关联为其使用的每个出站打开一条链路,而不是为每个目标打开一条。打开第 65 条时,会关闭最久未发送数据的那一条。

Hysteria 2 节点使用 QUIC,有自己的一组限制:

护栏 取值 作用范围 达到时
QUIC 连接 4,096 每个监听器 新连接在到达时即被拒绝,客户端能立即得知,而不是对着无响应的服务端反复重试。
活动 circuit 65,536 每个监听器 代理流和 UDP 会话合并计数,覆盖其整个生命周期。新的代理流会被拒绝,新 UDP 会话的数据包会被丢弃;客户端的其他流不受影响。
打开的流 1,024 每个 QUIC 连接 QUIC 不再发放流额度,客户端需等待后才能打开新的流。
UDP 会话 256 每个 QUIC 连接 会开启第 257 个会话的数据包会被丢弃。
QUIC 空闲超时 30 s 每个 QUIC 连接 30 秒内没有任何数据包的连接会被关闭。

这些限制都不是按用户的。没有配置项可以为某个用户调低它们,也无法为某个节点调高它们。

护栏造成的拒绝以 debug 级别记录,每次一行,例如:

dropping connection; live connection limit reached
node V2ray_0.0.0.0_443: dropping a stream; pre-auth limit reached
inbound handshake failed: timed out after 10s
connection ended: nothing moved for 360s
hysteria2: refusing a connection; the listener is full
hysteria2: refusing a stream; the listener is at its circuit limit

在默认的 info 级别下,你看到的是汇总。在基于 TCP 的节点上,katana 按节点统计失败的入站握手:错误的传输层帧、认证失败、格式错误的请求、握手超时,以及被活动连接限制和认证前限制拒绝的连接。当一秒内的计数超过 10 时,katana 会记录一条警告:

node V2ray_0.0.0.0_443: 37 inbound handshake failures in the last 1s (possible handshake scan/DoS or misconfigured clients)

这条警告仅用于提醒,katana 不会因此阻止任何连接。持续出现此类警告通常意味着有扫描器,或客户端的 UUID、密码或传输层设置有误。

现象 可能原因 处理方法
节点上所有用户速度相同,与套餐无关 [node.api] 中设置了 speed_limit,它会替换所有面板限速 设置 speed_limit = 0 并保存文件。热重载会应用该值,无需重启。
修改 speed_limit 后没有效果 重载被拒绝,katana 保留了当前配置。值无法解析(例如带了引号)时,日志中会出现 config reload failed, keeping current: …;同一文件中有节点无法构建(例如 node_type 未知)时,日志中会出现 reload: node …: …; keeping current config 运行 katana --test -c /etc/katana/config.toml,修正它输出的错误,然后再次保存文件。
SSPanel 节点上用户速度低于其套餐 节点的 node_speedlimit 低于用户的限速,取较小者 在面板中调高或清除节点限速。
同时大量上传和下载时,用户每个方向的速度只有套餐的一半 限速同时覆盖两个方向 这是预期行为;请按上下行合计流量设置套餐限速。
新的面板限速只作用于用户的部分连接 修改前打开的流保留旧速率 等待旧连接关闭,或让客户端重新连接。
用户可以打开无限多的连接或设备 katana 没有按用户的连接数或设备数限制 在其他地方实施设备数限制。