【问题标题】:Guidelines for creating a Microsoft Bot Framework State Storage Adapter for a custom database solution为自定义数据库解决方案创建 Microsoft Bot Framework 状态存储适配器的指南
【发布时间】:2020-01-04 04:46:53
【问题描述】:

在为将与 Microsoft Bot 框架正常工作的所述数据库创建自定义适配器时,我计划创建类似于 bot 框架中的 cosmosDBPartitionedStorage 类的东西。

据我所知,有 3 个读取、写入和删除操作是从 botbuilder 存储继承/实现的。

从数据库的角度来看,在创建此适配器时是否需要考虑任何从阅读几层源代码中看不到的东西。比如initialization()。那是宇宙特有的,我应该如何解释我需要的解决方案?

我计划使用 2 个数据库,其中一个是 redis。我可以在 Azure redis 实例中测试它以进行本地开发,我认为这是一个很好的起点。简而言之,这最初是针对 redis 适配器的。


更新:我选择了仅 Redis 的集群解决方案,它很可靠。我无法实现并发检查,因为那必须是服务器端脚本,我将它们用于我的 CRUD 操作,这将更多地参与 v2 更新。

@mrichardson 在下面的答案中提供的帮助对于创建您自己的数据存储非常宝贵。我也能够在我的 TypeScript 实现的单元测试中获得大部分重要的基础测试!除了并发测试。

在使用 Redis 时,我能够通过 RedisJson module 创建与 JSON 兼容的适配器。这是一个 Redis 模块,您必须通过 cmd 或 conf 文件配置安装。

我使用的库是IORedis from Luin,学习曲线很陡峭,不一定是他的库,但是将 Redis 与他的库一起做的集成以及作为集群和使用 RedisJson 模块集成是一个很好的挑战!

由于使用 RedisJson 模块,如果脚本尚未加载或因任何原因丢失,我必须为每个回退到 EVAL 的 CRUD 操作使用 LUA 脚本 load EVALSHA。它还会在失败时重新建立脚本。

我不确定将 EVALSHA LUA 脚本用于读写操作是否有很大的性能提升,但 Redis 文档似乎表明确实如此。

脚本的一大优势是它能够以最小的延迟读取和写入数据,从而使读取、计算、写入等操作非常快速(在这种情况下,流水线无法提供帮助,因为客户端需要在它可以调用写命令之前读命令)。

但更重要的是,我最初使用脚本的原因更多是与 IORedis 客户端有关。它确实流水线,但由于没有对 RedisJson 命令的本机支持,我不得不做一个自定义脚本(在 IORedis 中,它不允许流水线但为你做 evalhas 后备)或创建我自己的 EVALSHAEVAL后备方案。

似乎工作得非常棒!

代码库用于 RedisCluster,一旦我完成了一些调整,我将通过 github 和 npm 将其作为 typescript npm 包发布。

输入还采用 TTL 设置,这对于消息传递应用程序(例如 Microsoft bot 框架)来说是一个很好的安全性和性能抽象。

【问题讨论】:

  • 你能用我的回答来解决这个问题吗?如果是这样,请“接受”并投票,以便其他人可以快速找到答案,我可以从我的支持跟踪器中清除它。如果没有,请告诉我我还能提供哪些帮助!

标签: typescript redis botframework azure-cosmosdb


【解决方案1】:

据我所知,有 3 个读取、写入和删除操作是从 botbuilder 存储继承/实现的。

正确。这就是你真正需要的。只要你能成功地做到这些,它就可以正常工作。

从数据库的角度来看,在创建此适配器时是否需要考虑任何从阅读几层源代码中看不到的东西。比如initialization()。那是宇宙特有的,我应该如何解释我需要的解决方案?

也正确。那是 Cosmos 特有的。基本上,它:

  1. 如果数据库不存在则创建数据库
  2. 将现有/创建的数据库存储为类的属性,以便以后检查数据库是否存在时,它只查找类属性而不需要发出 HTTP 请求
  3. 在执行上述操作时锁定类以防止并发问题

如果您想在尝试任何类型的读/写之前首先检查数据库是否存在,您可能需要类似initialization() 函数。拥有这样的东西来让你的机器人适应未来可能是一个很好的做法(如果你更改/添加数据库或其他东西),但这不是必需的。

这最初是用于 redis 适配器的。

很遗憾,我们没有任何 Redis 存储适配器,但您可以在构建自己的存储适配器时查看以下一些额外的存储适配器:

在编写您的测试时,如果您想确保它正常工作,我们有一组存储基础测试可供您使用。您的适配器应该通过所有这些。

【讨论】:

  • 太棒了。就一个问题。这与这个问题有关。当循环通过对话框并将状态写入存储时,它是否会在每次完成后覆盖用户节点。如果我转向另一个对话并最终完成对话路径会怎样,重写仍然会发生以不淹没节点
  • 您应该在每回合后保存DialogState/ConversationState,否则用户将无法完成对话步骤。但是,在 BotState 中完成了一些状态缓存,以确保它仅在发生更改时写入。 UserState 有点不同,您可以随时手动保存。您可以让分支对话框向根对话框返回一个值,然后然后在根对话框中获得分支对话框的返回值后调用saveChanges()
  • 所以两个豁免都可以在回合结束时发生。保存对话框状态时是否会覆盖先前的状态?这就是我在宇宙中看到的。但是,我确实遇到过一种情况,我认为我打破了这条路,并且保存是累加的,导致宇宙破裂
  • @ChristianMatthew 是的,它应该覆盖。它可能看起来是附加的,因为对话框堆栈会增长,所以随着更多对话框的启动,它基本上会不断添加到对话框堆栈中。我不能说我已经看到了你破碎的 Cosmos 场景,但如果它再次发生,请提出一个新问题,我可以提供帮助。可能有助于阅读Managing State
  • @ChristianMatthew 正确。祝你好运!
猜你喜欢
  • 2022-08-19
  • 1970-01-01
  • 1970-01-01
  • 2011-03-21
  • 2019-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-05
相关资源
最近更新 更多