news 2026/9/14 23:00:51

Effect 平台 Deno 套接字服务端升级:统一 node-shared 实现、新增 reader 级 TLS 升级支持与迁移指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Effect 平台 Deno 套接字服务端升级:统一 node-shared 实现、新增 reader 级 TLS 升级支持与迁移指南

Effect 平台 Deno 套接字服务端升级:统一 node-shared 实现、新增 reader 级 TLS 升级支持与迁移指南

【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect

导读

本篇基于 Effect 生态仓库中的变更说明(changeset)文件,讲解@effect/platform-deno在最新迭代中对DenoSocketServer的底层实现调整:它不再使用 Deno 原生实现,而是复用@effect/platform-node-shared的 Node 套接字服务端,从而让被接受的 TCP 连接支持以 reader 作用域(reader-scoped)为粒度的服务端 TLS 升级。你将掌握新的make/layer参数形态(host替代hostname{ path }替代{ transport: "unix", path })、Deno 版本要求(2.8.3 及以上),以及本次变更在源码层面的实现原理与实测用例验证。

一、变更概览:一次针对 Deno 套接字服务端的底层重构

该变更说明(.changeset/pre/eff-972-deno-socket-server.md)同时涉及两个包,均以patch级别发布:

包名变更级别说明
@effect/platform-denopatch套接字服务端实现切换到 node-shared 版本
@effect/platform-node-sharedpatch底层能力被 Deno 平台复用,并承载 reader 作用域的 TLS 升级逻辑

核心结论一句话:Deno 平台现在使用 node-shared 的套接字服务端,因此被接受的 TCP 连接能够支持以 reader 作用域为单位的服务端 TLS 升级(即先以明文 TCP 接受连接、在读取阶段按需升级为 TLS 的 STARTTLS 式能力)。

从源码结构可以直接看到这一结论:Deno 平台的套接字服务端模块 packages/platform/deno/src/DenoSocketServer.ts 全文只有一行导出语句:

export * from "@effect/platform-node-shared/NodeSocketServer"

也就是说DenoSocketServer现在等价于 node-shared 的NodeSocketServer命名空间,其makelayermakeTlslayerTlsmakeWebSocketlayerWebSocket等构造器与 Layer 全部来自后者。包级入口 packages/platform/deno/src/index.ts 中DenoSocketServer子模块的导出保持不变(export * as DenoSocketServer from "./DenoSocketServer.ts"),因此对外 API 命名不受影响。

二、为什么这样做:reader 作用域的 TLS 升级

2.1 变更动机

变更说明原文明确指出动机:

Use the node-shared socket server on Deno so accepted TCP connections support reader-scoped server TLS upgrades.

即:让 Deno 上被接受(accepted)的 TCP 连接具备 reader 作用域的服务端 TLS 升级能力。

从源码实现看,这一能力由Socket.Reader上的upgrade操作承载。在 packages/platform/node-shared/src/NodeSocket.ts 的fromDuplex中,reader返回{ pull, upgrade }两个成员,其中upgrade允许在读取阶段将底层连接升级为 TLS:

  • 服务端场景(tlsServer: true)下,fromDuplex使用secure事件作为升级完成标志(客户端场景使用secureConnect),参见 NodeSocket.ts 的const secureEvent = isServer ? "secure" : "secureConnect"
  • 服务端升级要求同时提供keycert,否则以SocketUpgradeError失败;升级完成后会通过Tls.TLSSocket包装原始连接并继续透传读写,参见 NodeSocket.ts 的upgrade实现。

这正是“reader 作用域”的含义:升级动作不是发生在接受连接时,而是在 handler 获取 reader 之后、按需触发,每次连接的升级状态(upgradeAvailable)也由 reader 的 scope 生命周期管理,scope 结束即失效。

2.2 升级语义细节(Node 实现)

在 NodeSocket.ts 中,升级前会做参数校验:

  • 服务端场景必须同时提供keycert,否则报错 "server TLS upgrade requires both key and cert";
  • 客户端场景同样要求keycert成对出现;
  • 可选参数包括capassphrasealpnProtocolsrequestCertrejectUnauthorized,其中keypassphrase使用Redacted类型承载,避免密钥以明文形式直接暴露。

值得注意的是,Deno 平台的客户端升级走的是Deno.startTls(见 DenoSocket.ts 的fromConn),它不支持客户端证书,因此keycertpassphraserequestCert在 Deno 客户端升级中不生效,且rejectUnauthorized: false仅关闭主机名校验、证书链校验仍然生效。本次变更解决的是服务端一侧:让 Deno 服务端通过 node-shared 实现获得完整的、与 Node 一致的服务端 TLS 升级能力。

三、Breaking Changes 与迁移指南

变更说明列出的破坏性变更集中于DenoSocketServer.makeDenoSocketServer.layer的参数形态,以及运行时版本要求。下面逐一给出新旧对比与迁移方法。

3.1 TCP 监听:hostnamehost

变更前(Deno 原生参数):

DenoSocketServer.make({ hostname: "127.0.0.1", port: 8080 })

变更后(Node listen 选项,即node:netNet.ServerOpts & Net.ListenOptions):

DenoSocketServer.make({ host: "127.0.0.1", port: 8080 })

layer的用法同步变化:

DenoSocketServer.layer({ host: "127.0.0.1", port: 8080 })

从源码看,makelayer的类型签名已切换为options: Net.ServerOpts & Net.ListenOptions(NodeSocketServer.ts),这正是 Nodenet.Server.listen的选项集合,因此支持hostportbacklogexclusiveipv6OnlyreusePort等 Node 标准监听选项。

3.2 Unix 套接字:{ transport: "unix", path }{ path }

变更前:

DenoSocketServer.make({ transport: "unix", path: "/tmp/echo.sock" })

变更后:

DenoSocketServer.make({ path: "/tmp/echo.sock" })

layer同理:

DenoSocketServer.layer({ path: "/tmp/echo.sock" })

在 node-shared 实现中,服务端监听后会把绑定的地址归一化为NetAddress.SocketAddress:字符串形式的server.address()映射为UnixPathAddressAddressInfo对象映射为InetAddressV4/InetAddressV6(见 NodeSocketServer.ts 的socketAddressFromNode)。因此你可以通过server.address拿到标准化的NetAddress结果,测试中即以此断言地址类型与端口。

3.3 运行时要求:Deno 2.8.3 及以上

变更说明明确:Deno 2.8.3 或更新版本为必需条件。原因是 node-shared 套接字服务端依赖 Deno 的node:net/node:tls兼容层。源码中对此有专门适配:

  • NodeSocketServer.ts 通过const isDeno = "Deno" in globalThis检测运行环境;
  • 在 Deno 下不显式调用conn.pause(),因为 Deno 的node:net兼容层在显式 pause 之后会破坏readable事件分发(见 NodeSocketServer.ts 与 NodeSocket.ts 的注释说明)。

这意味着升级到该版本前,请确认部署环境的 Deno 版本不低于 2.8.3,否则可能出现兼容层行为异常。

四、源码级解析:node-shared 服务端如何工作

理解本次变更,建议通读 packages/platform/node-shared/src/NodeSocketServer.ts。该模块把node:net的 TCP/Unix 服务端、node:tls的 TLS 服务端以及ws的 WebSocket 服务端统一封装为 scoped 的SocketServer.SocketServer服务,核心是内部函数makeNetServer(NodeSocketServer.ts):

  1. 创建并监听createServer()创建底层 server,随后server.listen(listenOptions),并通过Deferred+raceFirst将监听错误(如端口占用)转化为SocketServerError
  2. 挂起连接:在run被调用之前,所有到达的连接被记录在pendingMap 中并保持暂停,避免数据丢失;
  3. 按需接管run(handler)被调用时,把之前的 pending 连接逐一交给新的onConnection回调,用Fiber.runIn(scope)将每个连接的 handler 织入独立 Fiber,并在 handler 结束时关闭 scope;
  4. 作用域清理:服务端作为Effect.acquireRelease资源,scope 结束时销毁所有 pending 连接并关闭底层 server(NodeSocketServer.ts)。

每个被接受的连接会通过NodeSocket.fromDuplex(..., { tlsServer: true })包装为Socket.Socket,同时以Context提供NodeSocket.NetSocket服务标签,让 handler 能访问底层 Nodenet.Socket(NodeSocketServer.ts)。

TLS 服务端则使用makeTls/layerTls:监听secureConnection事件,握手失败的连接被直接销毁、服务端继续监听(NodeSocketServer.ts)。

五、迁移后的正确用法(附测试用例佐证)

仓库测试 packages/platform/deno/test/DenoSocketServer.test.ts 完整演示了迁移后的 API 形态,可直接作为迁移参考。

5.1 TCP echo 服务端

import * as DenoSocketServer from "@effect/platform-deno/DenoSocketServer" const server = yield* DenoSocketServer.make({ host: "127.0.0.1", port: 0 }) const address = server.address // address._tag === "InetAddressV4" yield* server.run(echo).pipe(Effect.forkScoped)

注意port: 0表示由系统分配空闲端口,实际端口可通过server.address获取(测试断言address.port不为 0)。客户端连接使用DenoSocket.makeTcp({ hostname, port })(注意:客户端连接参数仍是hostname,只有服务端监听参数改为host,两者不要混淆)。

5.2 Unix 套接字 echo 服务端

const server = yield* DenoSocketServer.make({ path: "/tmp/echo.sock" }) // server.address._tag === "UnixPathAddress" yield* server.run(echo).pipe(Effect.forkScoped) // 客户端连接保持原有形态 const socket = yield* DenoSocket.makeTcp({ transport: "unix", path })

5.3 服务端 TLS 升级(本次变更的核心能力)

测试用例 "upgrades a plain server connection to TLS"(DenoSocketServer.test.ts)演示了如何在 handler 中对普通 TCP 连接按需升级为 TLS:

const server = yield* DenoSocketServer.make({ host: "127.0.0.1", port: 0 }) yield* server.run((socket) => echo(socket, { cert, key: Redacted.make(key) // 密钥以 Redacted 承载 }) ).pipe(Effect.forkScoped) // 客户端同样在 reader 阶段升级,并传入 CA 信任锚 const socket = yield* DenoSocket.makeTcp({ hostname, port }) const output = yield* sendHelloDeno(socket, { ca: [ca] })

其中服务端 handler 的核心逻辑为:

const { pull, upgrade } = yield* socket.reader if (upgradeOptions !== undefined) { yield* upgrade(upgradeOptions) // 读取阶段按需升级 TLS }

这正是“reader 作用域的 TLS 升级”:服务端以明文 TCP 接受连接,handler 在 reader 阶段调用upgrade完成STARTTLS式升级,之后读写都走 TLS 通道。测试使用仓库内置的测试证书(packages/platform/deno/test/fixtures/tls/ 下的ca.pemcert.pemkey.pem),断言升级后仍能正确完成 echo 往返。

5.4 资源关闭语义

测试 "closes with a pending pre-run connection"(DenoSocketServer.test.ts)验证了:在run尚未被调用时关闭服务端 scope,挂起的连接也会被正确清理,且关闭过程在 1 秒内完成。这印证了makeNetServer中 finalizer 对 pending 连接的销毁逻辑。

六、真实使用场景:Effect Cluster 在 Deno 上的套接字服务端

该变更并非孤立存在——DenoSocketServer正是 Effect Cluster 在 Deno 平台上的套接字传输底座。packages/platform/deno/src/DenoClusterSocket.ts 中的layerSocketServer直接使用迁移后的DenoSocketServer.layer

return DenoSocketServer.layer({ host: listenAddress.value.host, port: listenAddress.value.port })

其监听地址取自ShardingConfig.runnerListenAddress(回退到runnerAddress)。这意味着本次参数迁移会同步影响Deno 平台上 Cluster runner 的套接字监听配置——在 Deno 2.8.3+ 下,Cluster 套接字服务端同样获得 reader 作用域 TLS 升级能力,为跨节点加密通信(例如在不可信网络上运行的 runner 间传输)提供了按连接粒度开启 TLS 的手段。

七、迁移清单与注意事项

升级前请对照以下清单完成迁移:

  1. 升级 Deno 运行时到 2.8.3 或更新版本;
  2. 搜索代码中DenoSocketServer.make/DenoSocketServer.layer的所有调用点,将 TCP 监听的hostname参数改为host
  3. 将 Unix 监听的{ transport: "unix", path }对象改为{ path }
  4. 确认客户端连接 API(DenoSocket.makeTcp)参数不变,仍为{ hostname, port }{ transport: "unix", path }
  5. 如需服务端 TLS 升级,在 handler 的 reader 阶段调用upgrade({ key, cert }),其中key使用Redacted.make包装,keycert必须成对提供;
  6. 若代码中依赖server.address,注意其类型已归一化为NetAddress.SocketAddress(TCP 为InetAddressV4/InetAddressV6,Unix 为UnixPathAddress),读取 IP 使用NetAddress.formatIp,读取 Unix 路径使用.path
  7. 回归验证 Cluster 场景(如适用):layerSocketServer的监听参数已随迁移改变,确认ShardingConfig中的监听地址配置与新的host参数语义一致。

结语

本次@effect/platform-deno的 patch 变更,通过将DenoSocketServer收敛到@effect/platform-node-shared的 Node 实现,为 Deno 平台补齐了 reader 作用域的服务端 TLS 升级能力,同时带来两处明确的破坏性参数变更与一个版本门槛。源码与测试均已同步更新(DenoSocketServer.ts、NodeSocketServer.ts、DenoSocketServer.test.ts),按上文清单迁移即可平滑升级,并在 Deno 上获得与 Node 一致、按连接粒度可控的 TLS 套接字服务能力。

【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 22:59:10

Java进阶学习路线:从JVM到企业级开发实战

1. Java进阶学习路线全景解析作为从业15年的Java老司机,我见证了无数开发者从入门到精通的成长历程。Java作为企业级开发的常青树,其技术栈的深度和广度常常让学习者感到迷茫。本文将基于我指导团队新人的实际经验,拆解一条可落地的Java进阶路…

作者头像 李华
网站建设 2026/9/14 22:53:35

宠物行业数据中台架构设计与业务应用实践

1. 宠物行业数据中台的市场价值与现状宠物行业正经历前所未有的数字化变革。根据2023年中国宠物行业白皮书显示,国内宠物市场规模已突破3000亿元,年复合增长率保持在18%以上。在这个快速扩张的市场中,数据中台正成为企业实现精细化运营的核心…

作者头像 李华
网站建设 2026/9/14 22:52:40

Jetpack Compose性能优化与自定义布局实战指南

## 1. 项目概述最近在重构一个大型Compose项目时,我深刻体会到性能优化和自定义布局的重要性。当界面元素超过200个时,哪怕1ms的布局计算差异都会导致明显的卡顿。这份指南将分享我在处理复杂列表、嵌套滚动和自定义测量时的实战经验。Compose的声明式特…

作者头像 李华
网站建设 2026/9/14 22:52:34

SpringBoot物流管理系统开发实战与技术解析

1. 项目概述"基于SpringBoot企业物流管理系统"是一个典型的Java EE企业级应用开发案例,它采用当前主流的SpringBoot框架作为技术底座,结合MySQL等数据库技术,实现了一套完整的物流业务管理解决方案。这类系统在实际企业环境中有着广…

作者头像 李华
网站建设 2026/9/14 22:51:59

Flutter与OpenHarmony融合开发:Dio网络请求实战

1. 项目背景与目标解析在当今移动应用开发领域,跨平台技术已经成为提升开发效率的关键解决方案。本次训练营聚焦于开源鸿蒙(OpenHarmony)与Flutter的融合开发,特别针对网络请求这一核心功能模块进行深度实践。Dio作为Flutter生态中…

作者头像 李华