ARTICLE DETAIL

资讯详情

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

ShareDB 服务端核心 Backend 类完全解析:构造选项、中间件钩子与数据读写 API

ShareDB 服务端核心 Backend 类完全解析:构造选项、中间件钩子与数据读写 API 后端数据库【免费下载链接】sharedbRealtime database backend based on Operational Transformation (OT)项目地址https://gitcode.com/gh_mirrors/sh/sharedb点击查看免费下载ShareDB 是一个基于操作转换Operational TransformationOT的实时数据库后端而Backend类正是其**服务端单实例server-side instance**的化身它负责与客户端建立连接、把读写请求派发给数据库适配器同时承担构造期配置、中间件注册以及投影projection定义等工作。本文以官方 API 文档 docs/api/backend.md 为主线逐项讲解Backend的构造选项、公开属性与全部常用方法并结合 lib/backend.js 的源码实现与 test/backend.js 测试用例说明每个 API 的真实调用链与注意事项。读完本文你将能够独立完成 ShareDB 服务端的初始化、适配器挂载、中间件接入、投影配置以及服务端直连模式下的文档读写与查询。Backend() 构造函数Backend通过sharedb包导出直接new即可var Backend require(sharedb) new Backend([options])在源码中lib/backend.js构造函数会依次完成默认值注入、内部状态初始化projections、middleware、agentsCount等以及错误处理器的绑定。构造函数的全部可选参数如下。构造选项总览db—— 数据库适配器可选默认值new MemoryDB()一个全新的内存数据库实例作用ShareDB 的数据存储层即数据库适配器实例负责持久化文档内容与操作op日志⚠️重要警告默认的内存适配器是非持久化的。内存数据库把所有文档与操作都存放在进程内存中见 lib/db/memory.js 的注释内存占用无上限增长、无法跨 Node 进程扩展、服务重启即丢数据绝不能用于生产环境。生产环境应显式传入如sharedb-mongo、sharedb-postgres等外部适配器。var backend new Backend({ db: new MemoryDB() })pubsub—— 发布/订阅适配器可选默认值new MemoryPubSub()内存 Pub/Sub 实例作用用于在多个 ShareDB 实例之间广播数据变更通知的通道见 Pub/Sub 适配器与数据库适配器不同文档明确说明内存 Pub/Sub 适配器在单机独立部署的生产环境中是允许使用的因为 Pub/Sub 状态无需跨进程持久化。只有当需要横向扩展为多个服务器进程时才需要换成外部的 Pub/Sub 适配器如 Redis 类实现。内存实现本身是完整可用的lib/pubsub/memory.js。milestoneDb—— 里程碑数据库适配器可选默认值null源码中实际注入为new NoOpMilestoneDB()见 lib/milestone-db/no-op.js作用里程碑快照milestone snapshots的数据存储里程碑快照是按指定版本间隔保存的文档历史快照如果省略该选项里程碑快照将不启用但文档历史仍可访问只是可能带来性能损耗。extraDbs—— 附加数据库集合可选默认值{}作用一个对象其值可以是额外的DB适配器实例用于查询场景。对象的键即为查询选项db字段中可传入的名字在源码的查询路径中lib/backend.jsqueryFetch/querySubscribe会读取options.db并到backend.extraDbs[options.db]中查找对应的适配器找不到时会抛出ERR_DATABASE_ADAPTER_NOT_FOUND错误。suppressPublish—— 是否抑制发布可选默认值false作用设为true时所有已提交的变更不会通过 Pub/Sub 对外发布。适用于希望写入数据库但不通知其他订阅实例的场景maxSubmitRetries—— 提交最大重试次数可选默认值null作用允许一次提交submit被重试的次数。省略时请求将被无限次重试这对应 OT 并发冲突处理的底层机制当两个客户端同时提交基于同一版本的 op 时后提交者需要基于新版本重新转换并重试提交详见SubmitRequest的提交逻辑。presence—— 启用在线状态可选默认值false作用设为true时启用 Presence在线光标、在线用户列表等实时状态功能。源码中对应this.presenceEnabled !!options.presencelib/backend.js且Agent._handleMessage中只有在presenceEnabled为真时才会处理 presence 相关的消息类型lib/agent.jsdoNotCommitNoOps—— 是否不提交空操作可选默认值false作用设为true时将避免把 no-op空操作或经转换后变成 no-op 的操作提交到数据库。客户端提交的 no-op 会像正常提交一样被确认ack但文档版本不会递增。该选项对需要严格控制版本号增长的场景非常有用errorHandler—— 非致命错误处理器可选默认值ShareDB 默认将错误写入日志源码默认实现即logger.error(error)见 lib/backend.js作用ShareDB 服务端运行中出现的非致命错误都会被传入该函数function(error, context) { logger.error(error) }在 test/backend.js 中可以看到new Backend({errorHandler: handler})的用法测试通过自定义 handler 捕获并断言服务端错误。当需要把服务端错误接入自己的监控/报警体系时自定义该选项是标准做法。属性MIDDLEWARE_ACTIONS—— 中间件动作映射Backend暴露一个MIDDLEWARE_ACTIONS对象其值为可用的中间件动作名映射。在源码中lib/backend.js它定义了 13 个动作完整列表如下动作触发时机afterWrite一个操作成功写入数据库之后apply一个操作即将被应用到快照上、尚未提交到数据库commit一个操作已应用到快照、即将写入数据库connect新客户端连接到服务器op一个操作从数据库加载出来query一个查询即将发送到数据库readSnapshots快照已从数据库读取、即将返回给客户端receive收到来自客户端的消息reply即将向客户端发送非错误回复receivePresence服务器收到 presence 信息sendPresence即将向客户端发送 presence 信息submit一个操作已提交到服务器此外源码还定义了SNAPSHOT_TYPEScurrent、byVersion、byTimestamp用于标识readSnapshots中间件中快照的读取类型具体可参考 docs/middleware/actions.md。中间件的注册与触发分别由use()与trigger()完成trigger会把request.action、request.agent、request.backend挂到上下文上并串行执行该动作下的所有中间件函数lib/backend.js。方法connect()建立与 ShareDB 的连接返回一个用于与 ShareDB 交互的Connection客户端实例。它是浏览器端new Connection(socket)的服务端等价物——也就是说你可以在 Node 服务端直接创建并操作文档对象而不必经过网络。backend.connect([connection [, request]])参数说明connection可选Connection实例要绑定到Backend的连接。默认会新建一个Connection实例。request可选Object默认{}连接上下文对象可携带 cookie、会话数据等信息供中间件中通过agent.custom读取。返回值一个Connection。从源码看lib/backend.jsconnect()内部会创建一个StreamSocket并调用listen()生成Agent随后把agent引用挂到connection.agent上——这为服务端代码在中间件中缓存和读取会话状态提供了便利。listen()将一个StreamNode.js 流注册到后端。当服务器收到来自客户端的新连接时应调用此方法。backend.listen(stream [, request])stream一个Stream或Stream-like 对象用于新Agent与Backend之间的通信。request可选Object默认{}连接上下文对象同样会在中间件中以agent.custom形式暴露。返回值一个Agent该Agent也会在中间件上下文中可用。这是 WebSocket 接入 ShareDB 的标准入口。结合 getting-started 中的示例先用teamwork/websocket-json-stream把 WebSocket 转成Stream再交给backend.listen(stream)webSocketServer.on(connection, (webSocket) { var stream new WebSocketJSONStream(webSocket) backend.listen(stream) })在源码中lib/backend.jslisten()会new Agent(this, stream)并触发connect中间件若中间件返回错误则直接关闭该Agent。close()断开 ShareDB 及其全部底层服务数据库、Pub/Sub 等。backend.close([callback])callback可选Functionfunction(error) { ... }在所有服务停止后调用若至少有一个服务无法停止则以 error 形式回调。源码实现lib/backend.js会依次关闭pubsub、db、milestoneDb并遍历关闭extraDbs中所有附加数据库全部完成后才触发回调。use()注册中间件。backend.use(action, middleware)actionstring | string[]——单个动作名或动作名数组决定中间件在何时执行。可用动作见上文MIDDLEWARE_ACTIONS。middlewarefunction(context, next) { next(error) }——一个中间件函数。源码支持传入动作数组内部会递归地对每个动作注册同一中间件lib/backend.js。test/backend.js中可以看到典型用法例如在afterWrite、submit等动作上挂载中间件test/backend.js。中间件上下文context上始终带有action、agent、backend三个属性以及各动作特有的附加属性详见 docs/middleware/actions.md。addProjection()定义一个投影projection。backend.addProjection(name, collection, fields)namestring投影的名字。collectionstring被投影的目标集合。fieldsObject需要投影的字段集合键为字段名值必须为trueshare.addProjection(names, users, {name: true})⚠️不支持子字段投影。也就是说不能定义像{profile: {name: true}}这样的嵌套字段筛选。从源码看lib/backend.jsaddProjection()会做严格校验投影名重复会抛出Projection already exists字段值不是true会抛出Invalid field错误。投影被保存在this.projections映射中后续submit、getOps、fetch等所有方法都会据此把投影名解析为真实集合名projection.target。submit()向Backend提交一个操作。backend.submit(agent, index, id, op [, options [, callback]])agentAgent实例会传给中间件。indexstring集合名或投影名。idstring文档 ID。opObject要提交的操作。options可选Object默认{}透传给数据库适配器commit方法的选项适配器commit支持的任何选项都可在此使用。callback可选function (error, ops) { ... }其中ops是从提交的 op 被提交到真正落库之间其他客户端提交的 op 列表——这正是 OT 变换后需要回放给客户端的那部分操作。在源码中lib/backend.jssubmit()会先做ot.checkOp(op)合法性检查然后依次触发submit中间件 →SubmitRequest.submit()内部完成 OT 变换与数据库commit→afterWrite中间件 → 对 ops 做投影清洗_sanitizeOps。在客户端提交路径中Agent._submit会在成功后将ops通过_sendOps发送给客户端让客户端补齐错过的操作lib/agent.js。getOps()获取某个文档在指定版本区间内的操作记录其中from包含、to不包含。backend.getOps(agent, index, id, from, to [, options [, callback]])agentAgent实例传给中间件。indexstring集合名或投影名。idstring文档 ID。fromnumber要获取的起始 op 版本设为null则从最早版本开始获取。tonumber结束版本不会被获取即to不包含设为null则一直获取到最新版本。options可选Object默认{}options.opsOptions可选默认{}直接透传给数据库驱动的getOps例如请求 op 元数据{ opsOptions: { metadata: true, }, }callbackfunction (error, ops) { ... }成功时返回请求到的 ops。从源码看lib/backend.jsgetOps()会把投影解析为目标集合把agent.custom注入opsOptions然后调用db.getOps()最后对每个 op 执行_sanitizeOps投影过滤 op中间件。因此返回给调用方的 ops 已经过投影与中间件处理。getOpsBulk()批量获取一个集合中多个文档在指定版本区间内的操作记录语义与getOps一致from包含、to不包含。backend.getOpsBulk(agent, index, fromMap, toMap [, options [, callback]])agentAgent实例。indexstring集合名或投影名。fromMapObject键为文档 ID值为该文档请求的起始版本包含。例如{abc: 3}表示获取文档abc从版本3起的 ops。toMapObject键为文档 ID值为该文档请求的结束版本不包含。例如{abc: 3}表示获取文档abc截至版本3不含的 ops。options可选Object默认{}options.opsOptions直接透传给数据库驱动的getOpsBulk用法同getOps。callbackfunction (error, opsMap) { ... }返回文档 ID 到 ops 的映射如{abc: []}。fetch()获取一个文档的当前快照。backend.fetch(agent, index, id, [, options [, callback]])agentAgent实例。indexstring集合名或投影名。idstring文档 ID。options可选Object默认{}options.opsOptions直接透传给数据库驱动的fetch用法同上。callbackfunction (error, snapshot) { ... }成功时返回请求的快照。在源码中lib/backend.jsfetch()调用db.getSnapshot()拿到快照后会经_sanitizeSnapshots处理若有投影则先做快照投影裁剪再触发readSnapshots中间件快照类型为SNAPSHOT_TYPES.current。如果中间件通过request.rejectSnapshotRead(snapshot, error)拒绝了该快照的读取fetch也会以错误回调见_sanitizeSnapshots对部分拒绝的处理lib/backend.js。fetchBulk()从集合中批量获取多个文档快照。backend.fetchBulk(agent, index, ids, [, options [, callback]])agentAgent实例。indexstring集合名或投影名。idsstring[]文档 ID 数组。options可选Object默认{}options.opsOptions直接透传给数据库驱动的fetchBulk。callbackfunction (error, snapshots) { ... }成功时返回文档 ID 到快照的映射。源码中lib/backend.jsfetchBulk通过db.getSnapshotBulk()批量读取并支持部分拒绝当readSnapshots中间件拒绝了其中部分文档的快照读取时被拒绝的文档在返回的snapshotMap中会以{error: ...}对象形式标记其余文档正常返回整体不会因单个文档被拒而失败。queryFetch()获取匹配查询条件的快照。在大多数情况下直接查询底层数据库更合适但queryFetch可以在避免Doc实例开销的前提下应用中间件链路。backend.queryFetch(agent, index, query, [, options [, callback]])agentAgent实例。indexstring集合名或投影名。queryObject查询对象其格式取决于所用的数据库适配器例如sharedb-mongo使用 MongoDB 查询语法而内置 MemoryDB 默认无查询支持直接返回集合全部文档见 lib/db/memory.js。options可选Object默认{}options.db可选string指定在哪个数据库上执行查询。这些附加数据库通过构造选项extraDbs挂载。callbackfunction (error, snapshot) { ... }成功时返回请求的快照。源码路径lib/backend.js 与 lib/backend.js显示queryFetch先触发query中间件中间件可以改写查询、频道甚至切换数据库随后从options.db或extraDbs解析出目标数据库调用其query()方法最后对结果快照执行投影裁剪与readSnapshots中间件。源码中的其他实用方法补充除文档列出的 API 外lib/backend.js 中还实现了一批供客户端协议路径使用的方法了解它们有助于理解 Backend 的全貌subscribe(agent, index, id, version, ...)lib/backend.js订阅文档变更流配合pubsub.subscribe(channel)使用。version为null时同时抓取当前快照否则只抓取指定版本之后的 ops。subscribeBulk(agent, index, versions, callback)lib/backend.js批量订阅版本映射。querySubscribe(agent, index, query, options, callback)lib/backend.js订阅查询结果并随数据变化推送 diff内部使用QueryEmitter轮询数据库。fetchSnapshot(agent, index, id, version, callback)与fetchSnapshotByTimestamp(agent, index, id, timestamp, callback)lib/backend.js按版本号或时间戳获取历史快照需要milestoneDb配合并会结合getOps从里程碑快照重建到目标版本的文档状态。getChannels(collection, id)lib/backend.js返回文档订阅涉及的频道集合频道collection与文档频道collection.id是 Pub/Sub 频道命名的核心逻辑。一个完整的服务端使用示例综合以上 API一个典型的生产级 ShareDB 服务端初始化如下var Backend require(sharedb) var backend new Backend({ db: dbAdapter, // 例如 sharedb-mongo 实例勿用默认 MemoryDB 上生产 pubsub: pubsubAdapter, // 多实例部署时可换成外部 Pub/Sub 适配器 milestoneDb: milestoneAdapter, // 可选启用里程碑快照 presence: true, // 启用在线状态功能 doNotCommitNoOps: true, // 可选跳过 no-op 提交 suppressPublish: false, maxSubmitRetries: 5, // 限制提交重试次数 errorHandler: function(error, context) { myMonitoring.report(error) } }) // 中间件与投影 backend.use(backend.MIDDLEWARE_ACTIONS.submit, function(context, next) { // 提交前校验或改写 next() }) backend.addProjection(names, users, {firstName: true, lastName: true}) // WebSocket 接入 webSocketServer.on(connection, (webSocket) { var stream new WebSocketJSONStream(webSocket) backend.listen(stream, {cookies: req.cookies}) // req 可在 connect 中间件中通过 agent.custom 读取 }) // 服务端直连读写等价于服务端版本的客户端 Connection var connection backend.connect() var doc connection.get(users, 123) doc.fetch(function(error) { // ... }) process.on(SIGTERM, function() { backend.close(function() { process.exit(0) }) })小结Backend是 ShareDB 服务端的枢纽对象它把四类职责收敛在一个类中构造期配置数据库、Pub/Sub、里程碑库、附加库及各类行为开关、连接管理listen/connect/close、扩展点use注册中间件、addProjection定义投影以及数据读写submit、getOps、getOpsBulk、fetch、fetchBulk、queryFetch。理解每个选项在 lib/backend.js 中的落点以及各方法如何串联中间件 → OT 变换 → 适配器调用 → 投影清洗这条链路是安全、高效地把 ShareDB 集成进生产系统的前提。更深入的中间件动作定义、数据库与 Pub/Sub 适配器接口、投影与文档历史机制可分别查阅 docs/middleware/actions.md、docs/adapters/database.md、docs/adapters/pub-sub.md、docs/projections.md 与 docs/document-history.md。赞分享后端数据库【免费下载链接】sharedbRealtime database backend based on Operational Transformation (OT)项目地址https://gitcode.com/gh_mirrors/sh/sharedb点击查看免费下载相关推荐揭秘支付宝签名底层原理alipay_sdk_cj中RSA2与SHA256实现深度解析揭秘支付宝签名底层原理alipay_sdk_cj中RSA2与SHA256实现深度解析 alipay_sdk_cj 是一款面向 仓颉语言 的 支付宝支付后端 S后端数据库Sails HTTP 核心钩子Core Hook深入解析HTTP 服务器启动、中间件栈绑定与配置Sails HTTP 核心钩子Core Hook深入解析HTTP 服务器启动、中间件栈绑定与配置 Sails 是一个面向 Node.js 的实时Real后端Qiskit QuantumCircuit 类完全指南量子电路的核心数据结构与 API 详解Qiskit QuantumCircuit 类完全指南量子电路的核心数据结构与 API 详解 QuantumCircuit 是 Qiskit 中表示量子电路的科学计算上一篇用 Turf.js 计算两点间地理方位角Bearingturf/bearing 完全指南下一篇external-speaker外接喇叭进阶仿照它的积木架构开发你自己的硬件扩展完整教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表