
一、主要内容本文主要描述Eclipse Ditto 的节流机制。二、日志解析1数据流展示如下tianhang:~/ditto/deployment/docker$ docker compose logs -f gateway-1 | 2026-07-04 05:29:21,304 INFO [85fe8a2a-7316-449c-a014-fea56f9389cc][] o.e.d.g.s.s.a.p.PreAuthenticatedAuthenticationProvider - Pre-authentication has been applied resulting in AuthorizationContext ImmutableAuthorizationContext [typepre-authenticated-http, authorizationSubjects[nginx:ditto]]. gateway-1 | 2026-07-04 05:29:21,309 INFO [85fe8a2a-7316-449c-a014-fea56f9389cc][] o.e.d.e.s.d.EdgeCommandForwarderActor pekko://ditto-cluster/user/gatewayRoot/edgeCommandForwarder - Forwarding thing signal with ID my-demo:device001 and type things.commands:retrieveThing to things shard region things-1 | 2026-07-04 05:29:21,317 INFO [85fe8a2a-7316-449c-a014-fea56f9389cc][] o.e.d.t.s.e.ThingEnforcerActor pekko://ditto-cluster/system/sharding/thing/9/my-demo%3Adevice001/en - Completed enforcement of message type things.commands:retrieveThing with outcome success things-1 | 2026-07-04 05:29:21,319 INFO [85fe8a2a-7316-449c-a014-fea56f9389cc][] o.e.d.t.s.p.a.ThingSupervisorActor pekko://ditto-cluster/system/sharding/thing/9/my-demo%3Adevice001 - Received DittoRuntimeException during enforcement or forwarding to target actor, telling sender: ThingPreconditionNotModifiedException [messageThe comparison of precondition header if-none-match for the requested Thing resource evaluated to false. Expected: rev:17 not to match actual: rev:17., errorCodethings:precondition.notmodified, httpStatusHttpStatus [code304, categoryREDIRECTION], descriptionThe comparison of the provided precondition header if-none-match with the current ETag value of the requested Thing resource evaluated to false. Check the value of your conditional header value., hrefnull, dittoHeadersImmutableDittoHeaders [{correlation-id85fe8a2a-7316-449c-a014-fea56f9389cc, sec-fetch-modecors, refererhttp://localhost:5173/, if-none-matchrev:17, x-ditto-pre-authenticatednginx:ditto, sec-fetch-sitesame-origin, accept-languagezh-CN, zh;q0.9, zh-TW;q0.8, zh-HK;q0.7, en-US;q0.6, en;q0.5, x-forwarded-for172.18.0.1, priorityu4, accept*/*, authorization***, x-real-ip172.18.0.1, x-forwarded-userditto, hostlocalhost:8080, sec-fetch-destempty, user-agentMozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:152.0) Gecko/20100101 Firefox/152.0, ditto-auth-context{type:pre-authenticated-http,subjects:[nginx:ditto]}, ditto-originatornginx:ditto, ditto-read-subjects[nginx:ditto], ditto-entity-idthing:my-demo:device001, etagrev:17}]] gateway-1 | 2026-07-04 05:29:21,323 INFO [85fe8a2a-7316-449c-a014-fea56f9389cc][] o.e.d.i.u.c.AskWithRetryCommandForwarder - ThingPreconditionNotModifiedException: The comparison of precondition header if-none-match for the requested Thing resource evaluated to false. Expected: rev:17 not to match actual: rev:17. gateway-1 | 2026-07-04 05:29:21,328 INFO [85fe8a2a-7316-449c-a014-fea56f9389cc][] o.e.d.g.s.e.a.HttpRequestActor pekko://ditto-cluster/user/$K - DittoRuntimeException things:precondition.notmodified: The comparison of precondition header if-none-match for the requested Thing resource evaluated to false. Expected: rev:17 not to match actual: rev:17.. gateway-1 | 2026-07-04 05:29:21,328 DEBUG [][] o.a.p.a.CoordinatedShutdown CoordinatedShutdown(pekko://ditto-cluster) - Successfully cancelled CoordinatedShutdown task [service-requests-done-http-request-actor] from phase [service-requests-done]. gateway-1 | 2026-07-04 05:29:21,328 INFO [85fe8a2a-7316-449c-a014-fea56f9389cc][] o.e.d.g.s.e.d.RequestResultLoggingDirective - StatusCode of request GET /api/2/things/my-demo:device001 was: 304 nginx-1 | 172.18.0.1 - ditto [04/Jul/2026:03:29:21 0000] GET /api/2/things/my-demo:device001 HTTP/1.1 304 0 http://localhost:5173/ Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:152.0) Gecko/20100101 Firefox/152.02数据流解析数据流转gateway-1 ➔ gateway-1 ➔ things-1 ➔ things-1 ➔ gateway-1 ➔ gateway-1 ➔ gateway-1 ➔ gateway-1 ➔ nginx-1 第 1 步gateway-1鉴权拦截 时间05:29:21,304 动作Ditto 网关收到请求执行预认证PreAuthenticatedAuthenticationProvider确认 Vite 代理传过来的 Authorization 头有效赋予了 nginx:ditto 身份。 第 2 步gateway-1转发命令 时间05:29:21,309 动作网关将 HTTP 请求转换成内部的 retrieveThing 指令通过 EdgeCommandForwarderActor 转发给负责存储这个设备数据的“Things 分片节点shard”。 第 3 步things-1执行查询 时间05:29:21,317 动作负责本分片的 ThingEnforcerActor 收到命令根据权限校验成功读取到了该设备的当前数据内部执行状态为 success。 第 4 步things-1触发 304 异常拦截 时间05:29:21,319 动作ThingSupervisorActor 准备返回数据时对比了前端发来的 If-None-Match: rev:17 和服务端当前实际的版本 rev:17。因为完全一致它直接抛出了一个 ThingPreconditionNotModifiedException 异常拦截了数据的下发。 第 5 步gateway-1捕获异常 时间05:29:21,323 动作AskWithRetryCommandForwarder 负责把这一步的异常结果带回给网关层并打印出了这条提示日志。 第 6 步gateway-1翻译异常 时间05:29:21,328 动作HttpRequestActor 接收到了这个 304 异常将它翻译成符合 HTTP 规范的标准响应状态码为 304且不携带任何 JSON 响应体。 第 7 步gateway-1记录结果状态 时间05:29:21,328 动作RequestResultLoggingDirective 记录下了本次请求的最终处理结果状态码为 304。 第 8 步gateway-1旁路无关日志 时间05:29:21,328 (顺便说一下你的日志里紧接着有一个 DEBUG [][] o.a.p.a.CoordinatedShutdown。这个是 Ditto 底层 Akka 集群的“协调关闭”任务被取消的日志跟你的这个请求毫无关系是系统后台的杂音可以直接忽略不影响你的交互过程。) 第 9 步nginx-1访问日志落地 时间05:29:21,328 动作Nginx 作为反向代理在最后的链路端输出一行访问日志GET ... 304 0。这里的 0 代表响应体大小为 0 字节完美印证了上面第 6 步没有返回 JSON 数据的结论。三、内容总结(1)认证与鉴权Vite 代理配置里写了headers: { Authorization: Basic ZGl0dG86ZGl0dG8 }。Nginx作为反向代理把这个请求头传给 Ditto 网关。Ditto 的PreAuthenticatedAuthenticationProvider预认证提供者率先接管这个请求读取了这个 Header 并验证通过。(2)版本控制在第二步拿到rev:17后Ditto 并没有马上把 JSON 数据发返回而是先看了一眼你发来的请求头。你的浏览器或 fetch 请求在内部携带了一个If-None-Match: rev:17。Ditto 拿这个值和设备的实际版本rev:17做比较发现一模一样。根据 HTTP 标准Ditto 认为“既然前端浏览器手里已经有这个rev:17的旧数据了而我的数据也没变那我就不用把完整的 JSON 再发一遍了告诉你‘没变’即可。”于是它扔出一个ThingPreconditionNotModifiedException异常网关截获这个异常直接将其翻译成 HTTP 状态码304返回给前端不附带任何 JSON 响应体。四、流程梳理 你的浏览器 (地址栏:localhost:5173)⬇️请求/api/2/things/... Vite 开发服务器 (Node.js 进程, 监听 5173)(读取vite.config.ts中的proxy配置)⬇️转发请求到http://localhost:8080️ Nginx (反向代理, 监听 8080)(向外界暴露 Ditto 的接口)⬇️转发到 Ditto Gateway... (此后进入你刚才看到的那一长串 Ditto 内部处理流程)⬆️最终结果原路返回给浏览器五、名词解释vite:Vite 是打包工具但在开发阶段它同时也是一个“开发服务器Dev Server”。当你运行npm run dev或pnpm dev时Vite 并没有真正打包你的代码而是启动了一个基于 Node.js 的本地 HTTP 服务器。这个服务器不仅负责实时编译 Vue 组件并提供热更新HMR它最核心的另一个功能就是充当“反向代理”。nginx-1Ditto 的反向代理器转发外部向 ditto 传递过来的信息。nginx是结果输出类型在通讯完成后打印日志。MongoDB数据库关键日志mongodb-1服务输出的Connection accepted和client metadata。这意味着什么Ditto 内部真正存东西的地方是 MongoDB。刚才的things.commands:retrieveThing命令执行成功后Ditto 的things-1服务必须去连接 MongoDB 查取设备最新数据。注意细节日志里写了Connection not authenticating未进行身份认证。这说明你的 MongoDB 容器目前是免密连接的这在本地 Docker 开发环境中是非常正常和标准的配置。gateway-1Ditto 原生 API 网关它的角色它是 Ditto 微服务架构中的一个核心组件Ditto Gateway 服务通常运行在 Akka 集群的最前端。它的核心工作它接收到 Nginx 转发过来的 HTTP 请求后不直接读数据库而是做两件事执行鉴权比如你日志里看到的PreAuthenticatedAuthenticationProvider它读取Authorization头把访问者认证为nginx:ditto身份。命令翻译与路由它把 HTTP 的GET /api/2/things/...翻译成 Ditto 集群内部能听懂的内部消息指令——也就是你看到的things.commands:retrieveThing然后把指令路由给负责存储这个设备的things-1节点去真正干活。Thing物模型Policy 权限缺失创建 Thing 前必须先建 Policy否则 403 无权限