T3 Code 非官方中文翻译不是官方产品,也不是 T3 Tools 官方文档。源提交 d2c25950b4d9英文原文GitHub 源文件
Skip to content

连接运行时 ​

Web、桌面渲染进程和移动端,在 packages/client-runtime 里按环境(environment)共用一个连接所有者。平台代码提供存储、凭据、网络信号和应用生命周期事件。React 视图使用这个运行时。把重试和会话生存期放在这里,多个视图需要同一个环境时,就不会各自跑一套互相抢的重连循环。

唯一的传输重试所有者 ​

监督器掌握传输重试策略;解析端点、打开 RPC 会话,都只尝试一次。瞬时失败会按带抖动的指数退避重试,上限五分钟,并且只有连接保持可用之后才重置。没有抖动的话,一台刚重启的服务器,它的每个客户端(client)都会在同一秒里重连;上限要是太短,一个永远连不上的客户端就会一整天不停地重试。离线状态和认证失败会等一次唤醒,而不是在情况并没有变化时把重试次数耗掉。

进入前台、网络变化、显式重试和离线报告,都会探测已经建立的会话,只有探测失败才重连。离线报告经常是错的,回环服务器就是例子。操作系统可能在没有关闭事件的情况下终止一条已挂起的套接字,因此探测若在 500 毫秒后仍无应答,就会用一次新请求向主机索取它的公开描述符,之后每秒再要一次,直到主机应答。如果主机应答了,而套接字没有送达任何帧,这条套接字就是死的,需要换掉,即便网络报告为离线也一样:回环和局域网上立刻换;在其他地方,从检查开始起 2.5 秒后再换,因为那里丢掉一个数据包,就可能让一条仍然活着的套接字停住。没有这份证据时,由探测的截止时间来定:回环和局域网路由上,以及移动端长时间挂起之后(这时屏幕上的数据已经过时),是 3 秒;其他地方是 15 秒,因为经 DERP 的 tailnet 或 VPN,在一条活着的套接字上也可能比 3 秒更慢。长时间挂起之后的探测一旦超时,无论网络如何报告,都会换掉套接字。Web 和桌面在进入前台时的探测会跳过这次新请求,因为繁忙的主机可能先应答它,而排在队列里的探测还没被处理。进入前台还会立刻唤醒一次待处理的重试。长时间挂起会重新开始正在进行的尝试;短时间挂起则不去动它。

注册表按环境给连接划分范围。非主动断开会保留注册信息和缓存数据。显式移除会关闭该范围,并清除凭据、投影(projection),以及平台持有的状态,例如草稿。云账号变更作用于中继(relay)注册;它们不得丢弃直接配对的环境。

HTTP 授权 ​

RPC 会话在套接字升级时完成认证,而 HTTP 请求要从授权服务取当前凭据。为了给 HTTP 续期就换掉一条仍然健康的套接字,会打断对话,并在传输并没有失败时改掉传输代次。刷新失败归这次 HTTP 操作自己管。

服务器在会话被撤销或过期时关闭套接字。T3 Connect 的访问令牌有效期是一小时,因此驱动(driver)会在一条活着的套接字的令牌进入最后一分钟时续期。服务器随后把该套接字迁到同一把 DPoP 密钥、同一个主体和同一组作用域下的较新会话(SessionStore.awaitInvalidation)。续期失败的话,套接字在过期时关闭,由监督器重新连接。

会话列表在凭据过期之后,仍须保留尚未撤销的已连接会话,这样一条还开着的连接就不会从连接管理里消失。这并不会延长该凭据的有效期。新的 HTTP 请求和套接字升级仍然需要有效凭据。

传输健康与数据新鲜度是分开的 ​

套接字已经打开,并不足以说明环境(environment)可用。RPC 会话要等到初始服务器配置到达,才变为就绪。此后,外壳(shell)数据和对话线程(thread)数据各自有自己的同步状态。外壳订阅失败可以和一条健康的连接同时存在;把这种状态标成「正在重连」,等于承诺一次永远不会发生的传输重试。

缓存下来的投影(projection)在离线时仍可阅读。它们既不能让人以为连接还活着,也不能在重连期间覆盖更新的实时数据。加载和恢复快照归共享状态服务负责,这样每个视图对哪一份数据才是当前数据都一致。

外壳快照可以按客户端(client)的要求略去一些行。宣告 shellThreadFamilies 的服务器会略去空闲的子智能体子项:子项只要还有正在进行或已排队的运行、待处理的请求或后台工作,或者自己还有子智能体子项,就会留下,这样徽章和「工作中」状态的遍历不会遇到缺口。同时宣告 shellOlderSettled 的服务器,只保留最新的 25 条已落定(Settled)顶层对话线程(以及与第 25 条并列的、已固定的、已暂缓的、仍在活动的,或位于某个被保留子项之上的那些);快照里的 olderSettled 标出这条分界,并按项目(project)统计其余数量。被略去的行按需加载,合并到外壳行的后面(对话线程族):对话线程视图或一条排队消息所挂载的族,会持有与该对话线程共享同一根的每一个活动对话线程,因此也会把通过链接打开的、较旧的已落定对话线程带回来;已落定搁架读取分界以下的页;搜索返回更早匹配项的行。所以,缺少某一外壳行并不能证明这条对话线程已经消失:一发现缺失就删除数据或工作树(worktree)的代码,必须先问过该族或服务器。

已落定计数等于本地的已落定行,减去分界以下的本地行,再加上服务器的计数(更早的已落定对话线程)。一次移除,或一条旧行的到达或变更,有可能挪动这些计数时,会重新读取,因此归档任意一条对话线程只多一次很小的读取。搁架列出更早的行时,只列到仍能继续加载的那些环境里最高的页前沿之上,这样某一页还在加载时,合并后的列表仍然是正确的前缀。本次会话还没有快照的环境,或某一页失败的环境,不会挡住别人。按需读取会等到该会话的第一份权威快照(generation > 0),并且每一次都以代次为键。跳过那份快照的恢复,在核实之后仍必须把 generation 加一,并且只有缓存快照的省略项与该会话所要求的一致时,才可以复用它。

对话线程详情把订阅的生命周期和缓存的生命周期分开。已挂载的消费者共用一条实时流,最后一个消费者卸载时这条流才停止;隐藏但仍然挂载的路由也算在内。注册表本地的缓存会把状态和它的重放游标保留五分钟空闲时间,这样返回时可以接着用,不必再下载一次快照。

桌面应用再加一个消费者:保活会在每个已启用的环境里,挂载提供方会话(session)正在启动或正在运行的每一个对话线程。之后打开一个正在运行的对话线程就不需要重放。外壳流和详情流彼此独立,因此外壳可能在详情加载或追上之前就报告停止。已停止的对话线程会继续挂载,直到它自己的流变为实时并显示出这次停止,然后流关闭,并保存已落定状态。 Web 和移动端不会让对话线程保持存活。

状态和游标只在一次更新完成之后才一起保留。取消不得把缓存游标推进到已经应用的数据之外,旧范围也不得覆盖继任者的缓存。复用时保留分页数据,但清除已取消的加载状态。

RPC 边界在执行时按当前会话解析请求。持久订阅会跟着替换后的会话走。传输失败之后,它们等待监督器;预期之中的领域失败,可以在同一条健康会话上重新订阅。重连不会自动重放变更,这些变更的重试和幂等规则属于操作本身。

非官方译文 · 源提交 d2c25950b4d9 · 不代表 T3 Tools