Eclipse Ditto 节流机制

一、主要内容

本文主要描述Eclipse Ditto 的节流机制。

二、日志解析

(1)数据流展示如下:

复制代码
tian@hang:~/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 [type=pre-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 [message='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"'.', errorCode=things:precondition.notmodified, httpStatus=HttpStatus [code=304, category=REDIRECTION], description='The 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.', href=null, dittoHeaders=ImmutableDittoHeaders [{correlation-id=85fe8a2a-7316-449c-a014-fea56f9389cc, sec-fetch-mode=cors, referer=http://localhost:5173/, if-none-match="rev:17", x-ditto-pre-authenticated=nginx:ditto, sec-fetch-site=same-origin, accept-language=zh-CN, zh;q=0.9, zh-TW;q=0.8, zh-HK;q=0.7, en-US;q=0.6, en;q=0.5, x-forwarded-for=172.18.0.1, priority=u=4, accept=*/*, authorization=***, x-real-ip=172.18.0.1, x-forwarded-user=ditto, host=localhost:8080, sec-fetch-dest=empty, user-agent=Mozilla/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-originator=nginx:ditto, ditto-read-subjects=["nginx:ditto"], ditto-entity-id=thing:my-demo:device001, etag="rev: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.0"

(2)数据流解析

复制代码
数据流转: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-1:Ditto 的反向代理器,

转发外部向 ditto 传递过来的信息。nginx是结果输出类型,在通讯完成后打印日志。

MongoDB:数据库

  • 关键日志mongodb-1 服务输出的 Connection acceptedclient metadata

  • 这意味着什么 :Ditto 内部真正存东西的地方是 MongoDB。刚才的 things.commands:retrieveThing 命令执行成功后,Ditto 的 things-1 服务必须去连接 MongoDB 查取设备最新数据

  • 注意细节 :日志里写了 Connection not authenticating(未进行身份认证)。这说明你的 MongoDB 容器目前是免密连接的,这在本地 Docker 开发环境中是非常正常和标准的配置。

gateway-1:Ditto 原生 API 网关

  • 它的角色 :它是 Ditto 微服务架构中的一个核心组件(Ditto Gateway 服务),通常运行在 Akka 集群的最前端。

  • 它的核心工作 :它接收到 Nginx 转发过来的 HTTP 请求后,不直接读数据库,而是做两件事:

    1. 执行鉴权 :比如你日志里看到的 PreAuthenticatedAuthenticationProvider,它读取 Authorization 头,把访问者认证为 nginx:ditto 身份。

    2. 命令翻译与路由 :它把 HTTP 的 GET /api/2/things/...,翻译成 Ditto 集群内部能听懂的内部消息指令------也就是你看到的 things.commands:retrieveThing,然后把指令路由给负责存储这个设备的 things-1 节点去真正干活。

Thing:物模型

Policy 权限缺失 :创建 Thing 前必须先建 Policy,否则 403 无权限

相关推荐
折哥的程序人生 · 物流技术专研1 小时前
《Java 100 天进阶之路》第50篇:阻塞队列与并发容器(2026版)
java·面试题·java进阶·blockingqueue·并发容器·集合源码·java100天进阶
诚信定制8392 小时前
Typora插件开发指南:打造专属IDE式写作环境
ide
ai_coder_ai2 小时前
编写自动化脚本,在自己后端服务中使用Open Api进行设备相关操作
java·运维·自动化
硕风和炜2 小时前
【LeetCode: 2492. 两个城市间路径的最小分数 + DFS】
java·算法·leetcode·深度优先·dfs·bfs·并查集
格子软件2 小时前
2026年GEO贴牌代理:分布式多级分账状态机源码深度解构
java·vue.js·分布式·vue·geo
我是一颗柠檬3 小时前
【Java项目技术亮点】加权轮询负载均衡算法
java·算法·负载均衡
灯厂码农3 小时前
C语言动态内存分配完全指南(malloc、calloc、realloc、free)
java·c语言·算法
梦梦代码精4 小时前
电商系统不是技术堆叠:LikeShop如何用分层Hold住复杂业务?
java·docker·代码规范
负责的蛋挞4 小时前
异步HttpModule的实现方式
java·服务器·前端
AC赳赳老秦4 小时前
防火墙规则批量配置实战:OpenClaw 自动生成模板、批量下发与合规性校验全解析
java·开发语言·人工智能·python·github·php·openclaw