跳到主要内容

控制与限制

网关存在的意义不是「开放」,而是 「开放但不受伤害」。本页将说明什么被阻止、什么被限制、什么被记录,并附上具体数值。

修改模式的语句会被阻止​

如果第一个令牌是以下六种之一,则返回 400 QUERY_SYNTAX。

DROP · TRUNCATE · ALTER · CREATE · GRANT · REVOKE
{"result":"ERROR","error_code":"QUERY_SYNTAX",
"message":"DDL statements are not allowed: DROP"}

无法通过注释绕过。 在移除块注释和行注释后,我们才会查看第一个令牌。

/* 안녕 */ DROP TABLE pp.mm_tag -- 막힙니다
-- 한 줄
DROP TABLE pp.mm_tag -- 막힙니다

如果移除注释后无任何内容留存,这也返回 400。

存在分号即拒绝​

{"result":"ERROR","error_code":"QUERY_SYNTAX",
"message":"Multiple statements are not allowed"}
只接受「单条」语句是 DDL 拦截的前提

仅检查第一个令牌的方法,只有当一条请求中包含一条语句时才成立。如果允许 SELECT 1; DROP TABLE x,那么第一个令牌就是 SELECT,检查便会直接通过。因此,一旦看到分号,在查看内容之前就会拒绝。

结果行数上限​

设置默认值
data.gateway.query.max.rows100,000(0 = 无限制)

达到上限后,网关会在此处停止读取——而不是读取完整个结果后再截断。这样,即便结果巨大,也不会产生无限流。

  • POST /api/v1/query → 响应中的 truncated 字段
  • POST /api/v1/query/stream → end 行中的 truncated 字段

服务器日志中也会记录。

Query result truncated to max_rows=[100000], lang=[PQL]
被截断的结果会以「成功」状态返回

HTTP 返回 200,行数也正常。如果不查看 truncated,就会误以为结果是完整的并进行汇总。 这就是始终携带此标志的原因。

速率限制​

设置默认值说明
data.gateway.rate.limit1000每个客户端每秒请求数
data.gateway.rate.limit.global0(无限制)所有客户端合计的上限
data.gateway.trusted.proxies空信任的直连对等节点列表,用于信任 X-Forwarded-For

桶键是可信的客户端 IP。

X-Forwarded-For 默认情况下被忽略

仅当直连对等节点存在于 trusted.proxies 中时,才信任 XFF 的第一跳作为客户端 IP。如果留空,则始终使用实际连接地址——否则,伪造一个请求头即可绕过限额。

如果部署在反向代理之后,请务必将该代理的地址加入此列表。否则,所有请求都会集中到该代理的一个桶中。

全局上限是对「桶激增」的防御——即使创建大量不同 IP,总吞吐量也不会超过此值。

/admin/* 也「共享」相同的限额

/api/* 和 /admin/* 使用同一个计数器。如果分开设置,全局上限会实际翻倍,并且可以分摊到两个入口点来绕过限制。

请求大小​

设置默认值
data.gateway.max.body.bytes1,048,576(1 MB)
data.gateway.max.params.bytes65,536(64 KB)

超出后以 413 提前拒绝——在读取之前就断开,而不是读取之后。

超时与慢日志​

设置默认值说明
data.gateway.async.timeout.ms60,000异步请求超时
data.gateway.slow.query.ms10,000比此值更慢的读取记录为警告
data.gateway.slow.update.ms2,000比此值更慢的写入记录为警告

请求使用虚拟线程处理。并发执行上限是 data.gateway.async.concurrency.limit,默认值为 256(若为 0 则无限制)。达到上限时,提交方会等待有空间可用——不会丢弃,而是施加背压。启动日志会输出实际值。

Async executor: virtual threads, concurrency limit=[256]
改用虚拟线程的原因

以前的固定线程池,在队列达到 500 个请求之前不会将线程数增加到超过 20,因此实际并发查询为 20,而队列中等待的请求会因 60 秒超时而返回 503。实际瓶颈在于数据库连接池,因此现在只设置一个并发上限,而不是调整池的大小。

查询日志 — 所有请求都会记录​

项目值
表tm_data_gateway_log(Aster TSDB,timeuuid 主键)
保留内容语言 · 查询语句 · 行数 · 执行时间 · 是否失败 · 错误信息
写入放入队列并批量 flush — 不会阻塞查询路径

队列的状态由 GET /admin/metrics 中的 gateway.querylog.* 指标输出。

/api/health 是就绪状态,而非存活状态

仅凭上下文存在就报告 UP 没有意义——只有准备好接收查询时才是 UP。检查集(database · inmemory · query_log)必须全部为真,否则为 STARTING 或 DEGRADED。查询日志未初始化的网关不是 UP — 如果缺少审计记录却显示为「正常」,以后就无法回答「谁查询了什么」。

认证 · 控制台账户​

  • 认证由一条 Spring Security 链决定。不在列表中的路径会返回 401,而不是匿名访问。
  • 不创建会话,也不使用 cookie(STATELESS)。凭证在每个请求中通过请求头传递。
  • 控制台登录账户(data.gateway.web.user · data.gateway.web.password)必须都填写才能访问。如果缺少任何一个,界面会提示「未配置」,并全部以 503 LOGIN_NOT_CONFIGURED 拒绝。
不内置默认账户 — 这是有意为之的默认值

如果内置默认账户,所有客户安装都会携带相同的凭证,而且没有人会更改它们。 请将值放入渲染的配置(plantpulse-datalake-cli 的模板)中。

用户名和密码始终都要进行比对(即使一个错误,也不会跳过另一个),失败响应不会指明是哪一侧错误。

更改数值​

权威配置是 plantpulse-datalake-cli 中的 config/templates/。在容器内修改的内容会在重启后丢失。

docker exec plantpulse-datalake pd config diff --templates
docker exec plantpulse-datalake pd restart data-gateway

流程参见更改配置,密钥轮换参见更改密码 · API 密钥。

相关文档​