十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

DBX Agent Protocol v2 详解:多会话运行时的会话生命周期、并发模型与结构化错误恢复机制

DBX Agent Protocol v2 详解:多会话运行时的会话生命周期、并发模型与结构化错误恢复机制 DBX Agent Protocol v2 详解多会话运行时的会话生命周期、并发模型与结构化错误恢复机制【免费下载链接】dbx15MB轻量级跨平台数据库客户端、数据库管理工具。支持 MySQL、PostgreSQL、SQLite、Redis、MongoDB、DuckDB、ClickHouse、SQL Server 等。15MB, lightweight, cross-platform database client. Supports MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, ClickHouse, SQL Server and more.项目地址: https://gitcode.com/t8y2/dbxDBX 的 Agent 协议 v2 让单个 Agent 进程从一个池一个进程升级为一个进程服务多个相互隔离的数据库会话并配套引入了multi_session、structured_error_v1两项能力以及统一的会话级错误恢复语义。本文基于 agents/docs/agent-protocol-v2.md 展开结合 agents/common 下MultiSessionJsonRpcServer、AgentProtocol、AgentRpcError等源码实现完整梳理 v2 协议的会话生命周期、手动事务、并发规则、运行时复用、资源限制与结构化错误契约并给出 Java 与原生 Agent 的落地指南。读完你将能够理解 v2 协议下 Agent 进程内部如何工作以及如何把现有 Agent 迁移到多会话运行时。从 v1 到 v2一个进程为何要服务多个会话协议 v1 的生命周期是一个进程对应一个连接池one-process-per-pool每个数据库连接都独占一个 Agent 进程DBX 需要按池启动、回收进程。协议 v2 允许一个 Agent 进程同时承载多个相互隔离的逻辑数据库会话logical database session从而显著降低进程数量与启动开销。判断一个 Agent 是否运行在 v2 多会话路径上取决于握手handshake时声明的能力使用共享 JDBC 基础common 结构化错误生产者的池化 JDBC Agent 会在握手时同时广告protocolVersion: 2、multi_session、structured_error_v1三个字段通用/自定义 v2 handler如非 JDBC 的原生 Agent可以只广告multi_session当multi_session能力缺失时DBX 自动回退到 v1 的 one-process-per-pool 生命周期保证旧 Agent 二进制与 JAR 的兼容。在源码侧AgentProtocol.java 中定义了两组能力集合MULTI_SESSION_CAPABILITIES在 v1 基础能力connect、test_connection、metadata、query、paged_query、transaction、ddl之上追加multi_sessionMULTI_SESSION_JDBC_CAPABILITIES再追加structured_error_v1并通过multiSessionJdbcHandshakeResult()返回给 DBX。协议方法的完整清单含handshake、会话管理方法与查询/元数据方法同时维护在 agent-protocol-v2.json 中其中明确规定sessionField为agentSessionId逻辑连接标识cursorSessionField为sessionId游标分页标识二者不可混用。会话生命周期从 open 到 shutdown 的五步闭环v2 协议通过五个核心 RPC 方法管理会话完整定义在 agent-protocol-v2.json 的commonMethods中方法作用open_session创建一个逻辑数据库会话。参数包含常规连接字段外加agentSessionId与可选的sessionRolevalidate_session校验该会话并在支持的情况下仅对该会话重连cancel_session仅取消该会话正在执行的活动语句与游标抓取同一运行时内的其他会话不受影响close_session关闭该会话的会话资源、查询游标与表读取游标不影响其他会话shutdown关闭全部会话并终止整个运行时进程此外每一个连接作用域connection-scoped的 RPC 请求都必须携带agentSessionId服务端用它把请求路由到对应的逻辑会话。在 MultiSessionJsonRpcServer.java 的实现中会话被存放在ConcurrentHashMapString, Session中openSession(sessionId, params)会先检查sessions.size() MAX_SESSIONS常量MAX_SESSIONS 256超限时抛出资源类错误随后为会话创建DatabaseAgent实例JDBC 模式下还会在满足条件时把连接池注册表挂到 agent 上连接失败时立即从 map 中移除并隔离关闭防止失败会话残留closeSession(sessionId)从 map 中移除会话并调度清理若清理触发隔离配额上限会返回resource类错误并请求替换整个运行时run()启动时先向 stdout 输出{ready:true}随后逐行读取 stdin 上的 JSON-RPC 请求遇到shutdown方法即停止循环并close()关闭全部会话、请求线程池与清理线程池。agentSessionId 与 sessionId 的职责分离v2 协议明确区分两类标识符agentSessionId标识一条逻辑数据库连接是会话路由的唯一依据既有sessionId字段仍然是分页游标标识符pagination cursor identifier禁止被当作逻辑连接标识使用。这一点在 agent-protocol-v2.json 中通过sessionField/cursorSessionField两个字段固化下来避免新实现把游标 ID 误用于会话路由。sessionRoleworkload 与 metadata 的分工sessionRole用于标识会话的用途默认值为workload对应编辑器执行等真实负载DBX 在发起对象树、补全等只读元数据任务时发送metadata新运行时应该用该角色为元数据会话保留检出容量metadata checkout capacity旧运行时可以忽略该字段因此它是向后兼容的可选字段。由于 DBX 会为独立的元数据任务使用短生命周期的唯一逻辑会话避免它们排在编辑器执行之后排队sessionRole帮助运行时识别这类短暂会话并合理调度。手动交互式事务一条会话内的三阶段 RPC当运行时广告了transaction能力并且支持会话粘性sticky sessions时DBX 可能为手动事务打开一个专用工作负载会话然后依次调用begin_manual_transaction{ schema? }— 固定一条物理连接并开启一个打开状态的事务execute_query及相关查询方法— 在打开的事务上执行直到 commit/rollbackcommit_manual_transaction/rollback_manual_transaction— 结束交互式事务。这与一次性execute_transaction是两种不同的语义execute_transaction在单个 RPC 内部完成 begin、执行语句列表、commit/rollback 的完整闭环而手动事务跨多个 RPC 且依赖会话粘性。协议还有一个重要的运行时约束如果运行时通过validate_session重连了会话必须清除该会话上任何打开的手动事务否则会出现重连后仍残留未提交事务的状态泄漏。并发模型跨会话并行、同会话串行v2 的并发规则非常明确不同会话的请求可以并发执行同一会话的请求被串行化——因为连接状态、事务、schema 变更与驱动连接通常不能安全地并发使用JSON-RPC 响应允许乱序返回通过请求id关联客户端不能依赖响应顺序。在 MultiSessionJsonRpcServer.java 中可以找到对应的实现证据每个Session内部持有一个ReentrantLockhandle()与connect()都在lock.lock()保护下执行从而保证同一会话内的请求串行请求执行使用ThreadPoolExecutorMAX_REQUEST_THREADS 64SynchronousQueueAbortPolicy作为有界执行器请求被提交到该线程池异步处理因此不同会话可以并行、响应可以乱序当线程池拒绝请求容量耗尽时通过AgentRpcError.backpressure(request, ...)返回背压错误categoryresource、retryabletrue、sessionDispositionkeep让 DBX 稍后重试而不是丢弃会话清理操作使用独立的MAX_CLEANUP_THREADS 16有界执行器保证返回/驱逐/物理关闭连接不会与检出/创建连接互相死锁。共享连接池基础不可变连接身份与有状态连接驱逐所有 Java JDBC 运行时通过AbstractJdbcAgent以**不可变连接身份immutable connection identity**共享 HikariCP 连接池。核心机制见 AbstractJdbcAgent.java无状态请求借出连接、使用完归还borrow/return不占用会话状态有状态请求分页游标、显式会话状态 SQL 会把物理连接**固定pin**到逻辑会话上有状态连接驱逐当该会话关闭时被固定的连接会被驱逐保证连接状态不会跨会话泄漏自定义 URL 构造、传输回退、连接初始化与原生驱动访问仍然保留在共享生命周期钩子如beforePooledConnectionReturn之后Agent 作者只需关注数据库差异无需重写池化逻辑。此外池化连接还带有一套毒化poison机制一旦某次借用/请求的超时边界无法确认物理连接状态该连接身份即被标记为 poisoned后续借用必须驱逐并关闭它而不是把它发布给新的请求。运行时兼容与复用什么属于会话、什么属于运行时运行时runtime复用键reuse key由以下要素构成Agent 驱动键driver key可执行文件或 JAR 路径启动参数launch arguments工作目录JRE 选择、JVM 选项、影响 classpath 的选项原生可执行文件的版本边界native executable version boundary。而host、account、schema、credentials 属于会话数据不参与运行时键——这正是一个运行时服务多个不同数据库会话的前提。v2 协议在兼容性上采取渐进策略ZooKeeper Agent不广告multi_session能力因此保留传统单会话路径etcd Agentsv3 与 v2与 SQL Agents 一样运行在共享多会话路径上旧版 Agent 二进制与 JAR继续走传统v1生命周期路径。资源限制与恢复256 会话、30 秒宽限期与绝对截止时间会话数量与进程退出单个运行时最多接受256个逻辑会话源码常量MAX_SESSIONS 256超限抛IllegalStateException(Agent session limit reached: 256)转换为背压错误关闭最后一个会话后进程进入30 秒宽限期grace period再退出避免用户快速开关标签页时反复冷启动运行时进程 EOF 会失败所有挂起请求该运行时从复用池中移除并在下次需要时重新创建连接校验与重连validate_session只作用于单个逻辑会话。结构化错误契约structured_error_v1v2 的 JSON-RPC 失败响应可以携带结构化恢复数据完整字段如下{ contractVersion: 1, category: timeout|canceled|connection|protocol|resource|sql, retryable: false, sessionDisposition: keep|quarantine|replace_runtime, agentSessionId: optional-session-id, stage: request|checkout|connect|validate|execute|fetch|cancel|close, operationOutcome: not_started|unknown, sqlState: optional-jdbc-sql-state, vendorCode: 0, exceptionClass: optional-java-exception-class }字段语义与约束contractVersion: 1只在握手广告structured_error_v1时才被保证允许出现未知的附加字段但以下情况属于契约违规未知的枚举值、缺少必填字段、类型非法或agentSessionId与当前请求不匹配operationOutcome描述用户操作是否可能已经到达数据库not_startedrequest/checkout/connect/validate 阶段或unknownexecute/fetch/cancel/close 阶段retryable仅是内部提示绝不授权自动重放 SQL——防止对可能已执行的写操作做无脑重试。在 AgentRpcError.java 中可以看到该契约的生成逻辑classify()根据异常类型与 RPC 方法推导出category、stage、disposition、retryable其中SQLTimeoutException归类为timeoutSQLRecoverableException/SQLTransientConnectionException/ SQLState 以08开头等连接类错误归类为connection其余 SQLException 归类为sqlCancellationException/InterruptedException归类为canceled兜底为protocol。sessionDisposition 三态语义取值含义keep保留逻辑会话可继续服务请求quarantine仅将该会话从路由中移除隔离replace_runtimeDBX 需在终止该运行时之前原子地移除所有共享该运行时的连接池关键约束Agent 代码只负责报告 disposition绝不能自行终止共享运行时——因为它并不拥有 DBX 的路由状态。典型的临时工作负载检出背压使用categoryresource、retryabletrue、sessionDispositionkeep只有不可恢复的运行时级或清理饱和问题才请求replace_runtime。有界执行器与绝对截止时间完整的 JDBC 连接池检出checkout过程运行在有界运行时执行器内覆盖 HikariCP 空闲连接校验、物理连接创建与驱动初始化工作负载准入、运行时级物理连接预算、物理创建、检出共享同一个绝对截止时间absolute deadline而不是在每个阶段各自重启超时——避免每个阶段都允许几分钟导致总体超时失控连接归还、驱逐与物理关闭使用独立的有界执行器从而不会反过来阻塞检出或创建流程如果驱动调用超出了它的时间边界或清理流程无法确认物理连接状态该连接身份即被毒化并在当前或下一次检出时返回categoryresourcesessionDispositionreplace_runtime迟到的连接必须被驱逐并关闭而不是发布给请求方DBX 也不得自动重放超时的用户操作。驱动作者指南Java Agent 与原生 Agent 的落地方式Java SQL Agent一行代码接入对于 Java SQL Agent推荐直接使用MultiSessionJsonRpcServer(YourAgent::new)每个逻辑会话都会获得一个全新的DatabaseAgent实例与相互隔离的连接状态物理 JDBC 连接池归共享运行时所有。模板工程 TemplateAgent.java 展示了最简形态public final class TemplateAgent extends ConfiguredJdbcAgent { public static final JdbcAgentProfile TEMPLATE_PROFILE new JdbcAgentProfile( com.example.jdbc.TemplateDriver, jdbc:template://{host}:{port}/{database}, 1234 ); public TemplateAgent() { super(TEMPLATE_PROFILE); } Override public String setSchemaSQL(String schema) { return SET SCHEMA JdbcIdentifiers.INSTANCE.doubleQuote(schema); } public static void main(String[] args) { new MultiSessionJsonRpcServer(TemplateAgent::new).run(); } }仓库中 30 余个 JDBC 驱动模块如 Db2Agent.java、H2Agent.java、FirebirdAgent.java 等均以该方式启动可直接作为参考实现。作者必须遵守的硬性约束不要在静态可变字段中保存连接、语句、游标、事务或 schema 状态——这会让隔离在多会话下失效分页查询资源必须挂在会话执行上下文session execution context上随会话生命周期释放非 JDBC 的通用 v2 服务端可通过MultiSessionJsonRpcServer.forSessionHandlers(...)接入SessionRpcHandler见 MultiSessionJsonRpcServer.java 的forSessionHandlers工厂方法并在run()主循环中处理handshake/open_session等会话级方法。原生 Agent等效的逐会话状态与同步输出非 Java 的原生 Agent 必须提供等效能力逐会话状态隔离per-session state每个逻辑会话拥有独立状态不能共享可变全局量同步的 stdout 写入synchronized stdout writes多会话并发响应输出时必须保证每条 JSON-RPC 响应的写入是原子的防止交错破坏 JSON 帧。案例Xugu 原生 Agent 的取消策略Xugu 原生 Agent 采用每逻辑会话一条数据库连接 每个数据库端点一条共享控制连接的结构。由于go-xugu-driver无法通过context.Context中断网络读取取消操作的实现方式是记录服务端会话 ID通过共享控制连接调用DBMS_DBA.KILL_SESSION_TRANS杀掉目标事务。这个模式说明当驱动缺少可中断 I/O 能力时Agent 需要借助服务端原语如 kill session配合共享控制连接来兑现cancel_session的语义。测试与验证v2 多会话行为在仓库中有对应的测试覆盖JdbcConnectionPoolingTest.java 验证 JDBC 连接池化与共享连接身份下的借还、固定与驱逐行为CommonJavaCompatibilityTest.java 校验能力集合与协议常量的一致性各驱动模块如 H2AgentProcessTest.java、MongoAgentTest.java以及 Go 驱动的main_test.go覆盖握手与执行路径的进程级回归。驱动作者在交付前应执行python3 scripts/validate_agents.py与./gradlew test shadowJar --continue具体检查清单参见 agent-authoring.md 与 release-checklist.md。小结Agent Protocol v2 用一个进程、多逻辑会话、共享连接池、结构化错误恢复重构了 DBX 的 Agent 运行时模型agentSessionId承担逻辑连接路由、sessionId仅作游标标识sessionRole区分 workload 与 metadata 负载同会话串行、跨会话并行的并发规则配合有界线程池与绝对截止时间保证了进程内资源可控structured_error_v1的sessionDisposition三态keep/quarantine/replace_runtime把会话该留、该隔离、还是整个运行时该换的决策权明确划归 DBXAgent 只负责如实上报。对驱动作者而言Java Agent 用MultiSessionJsonRpcServer(YourAgent::new)即可获得全部隔离与池化能力原生 Agent 则需自行保证逐会话状态与同步输出——理解这套契约是让新数据库平滑接入 DBX 多会话架构的第一步。【免费下载链接】dbx15MB轻量级跨平台数据库客户端、数据库管理工具。支持 MySQL、PostgreSQL、SQLite、Redis、MongoDB、DuckDB、ClickHouse、SQL Server 等。15MB, lightweight, cross-platform database client. Supports MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, ClickHouse, SQL Server and more.项目地址: https://gitcode.com/t8y2/dbx创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表