对话机器人 Rasa(二十六):生产环境支持多并发的配置

更新日期: 2024-04-06 阅读次数: 1106 字数: 842 分类: AI

默认,Rasa 服务器只使用 1 个 worker。

对于生产环境,这肯定是不能接受的。

rasa 多并发

单机高并发设置

  • 环境变量 SANIC_WORKERS 设置为 1 以上
  • 默认 Rasa custom action server 也只使用 1 个worker。将环境变量 ACTION_SERVER_SANIC_WORKERS 的值设置为 1 以上
  • 注意:locker store 的设置不是 InMemoryLockStore 时, 才可以启用多个 worker。

为何直接增加 worker 数量行不通

因为每一次请求的回复都需要基于对话的历史,而同一个用户的多个请求分散到多个 worker 上时, 就需要一个中心化的存储。

这里的 Rasa 名词为:

  • tracker store: 对话历史存储 https://rasa.com/docs/rasa/tracker-stores
  • lock store: 分布式锁,保证对话上下文处理顺序 https://rasa.com/docs/rasa/lock-stores

tracker store 及 lock store 的选型

Rasa 内置了多种 tracker store 方案:

  • InMemoryTrackerStore (默认)。顾名思义,数据存储在内存中,重启 Rasa 服务即清空
  • SQLTrackerStore。即 SQL 数据库存储,支持 PostgreSQL, Oracle, SQLite, 唯独不支持 MySQL ...
  • RedisTrackerStore。我准备使用 Redis 方案。其 docker 部署方法可以参见:https://rasa.com/docs/rasa/tracker-stores#redistrackerstore
  • MongoTrackerStore
  • DynamoTrackerStore
  • Custom Tracker Store

内置的 Lock Store 方案

  • InMemoryLockStore (默认)
  • ConcurrentRedisLockStore: 仅支持 Rasa Pro 付费版本
  • RedisLockStore
  • Custom Lock Store

所以,看起来 Redis 是最简洁的配置方案。

redis docker

redis 最新的版本,2023-09-30 星期六, 从官网看最新版本是 7.2.1.

但是,我看到版本说明中说 7 版本包含了不向后兼容的变更。所以,谨慎起见,我选择了 6.2.13 版本。

docker-compose.yml 中增加

redis:
  image: redis:6.2.13-alpine
  restart: on-failure
  ports:
    - 6379
  command: redis-server --maxmemory 200M --maxmemory-policy allkeys-lru
  volumes:
    - /some/path/redis_data:/data

endpoints.yml

注意,tracker store 与 lock store 使用不同的 redis database。例如一个使用 0,一个使用 1。

启动命令增加环境变量

worker 数量设置为多少合适?等同于 CPU 的核数。例如双核就设置为 2.

例如 action_server ACTION_SERVER_SANIC_WORKERS 的配置:

action_server:
  image: rasa/rasa:3.4.6-full
  ports:
    - 5055:5055
  environment:
    - ACTION_SERVER_SANIC_WORKERS=2
  volumes:
    - ./:/app
  command:
    - run
    - actions

SANIC_WORKERS 的配置相同。

Redis 限制最大内存使用量

防止内存不足

command: redis-server --maxmemory 200M --maxmemory-policy allkeys-lru

重启 docker compose 以生效

docker-compose up -d

这个命令除了会执行 redis 容器的创建,还会同时重启其他已存在的服务。

无法访问 docker 中的 redis

  File "/opt/venv/lib/python3.10/site-packages/rasa/core/agent.py", line 419, in handle_message
    async with self.lock_store.lock(message.sender_id):
  File "/usr/lib/python3.10/contextlib.py", line 199, in __aenter__
    return await anext(self.gen)
  File "/opt/venv/lib/python3.10/site-packages/rasa/core/lock_store.py", line 101, in lock
    ticket = self.issue_ticket(conversation_id, lock_lifetime)
  File "/opt/venv/lib/python3.10/site-packages/rasa/core/lock_store.py", line 87, in issue_ticket
    raise LockError(f"Error while acquiring lock. Error:\n{e}")
rasa.core.lock_store.LockError: Error while acquiring lock. Error:
Error 99 connecting to localhost:6379. Cannot assign requested address.

将 endpoints.yml 中的配置 localhost 改成对应的 docker-compose.yml 中的 redis 服务名即可。

最好是备份一个线上的 endpoints.yml 文件。

custom action aiohttp

https://forum.rasa.com/t/need-details-on-multi-thread-architecture-of-rasa-server/41385/7

A common root cause of poor performance is when the action server is using a synchronous method to call external services, for example with the requests package.

These type of calls block the asyncio logic and all other calls will wait.

Make sure to use async logic everwhere in your custom actions, for example, replace the requests package with the aiohttp package.

lock store 为空的原因

https://forum.rasa.com/t/redis-lock-store-do-not-save-conversations/59410

通过 redis monitor 能看到实时的 set get del 操作。确实有用到,用完就删除了。

例如:

1696837537.683873 [1 172.18.0.4:43904] "GET" "lock:test_user"
1696837537.684096 [1 172.18.0.4:43904] "SET" "lock:test_user" "{\"conversation_id\": \"test_user\", \"tickets\": []}"
1696837537.684218 [1 172.18.0.4:43904] "GET" "lock:test_user"
1696837537.684326 [1 172.18.0.4:43904] "DEL" "lock:test_user"

查看合集

📖 对话机器人 Rasa 中文系列教程

tags: rasa

关于作者 🌱

我是来自山东烟台的一名开发者,有敢兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式