From 56ff0e3a3251653cfe78f80298697f9c41f62074 Mon Sep 17 00:00:00 2001 From: dsx137 <70027572+dsx137@users.noreply.github.com> Date: Tue, 10 Mar 2026 16:24:52 +0800 Subject: [PATCH 1/9] feat(docs): add fault recovery guide and enhance getting started docs - Add faultRecovery.mdx with backup, recovery, and testing procedures - Update all getting started guides with OpenIMSDK naming consistency - Enhance dockerCompose.mdx with better instructions and troubleshooting - Update cluster.mdx with proper OpenIMServer naming - Add comprehensive quickTestServer.mdx with validation steps # Conflicts: # docs/guides/gettingStarted/production.md --- docs/guides/gettingStarted/cluster.md | 9 +- docs/guides/gettingStarted/dockerCompose.md | 10 +- docs/guides/gettingStarted/env-comp.md | 43 ++-- docs/guides/gettingStarted/faq.md | 6 +- docs/guides/gettingStarted/faultRecovery.mdx | 203 ++++++++++++++++++ .../gettingStarted/imSourceCodeDeployment.md | 188 +++++++++++----- .../gettingStarted/internalDeployment.md | 13 +- .../gettingStarted/nginxDomainConfig.md | 112 ++++++---- docs/guides/gettingStarted/ports.md | 64 +++--- docs/guides/gettingStarted/production.md | 61 ------ docs/guides/gettingStarted/quickTestServer.md | 46 +++- 11 files changed, 511 insertions(+), 244 deletions(-) create mode 100644 docs/guides/gettingStarted/faultRecovery.mdx diff --git a/docs/guides/gettingStarted/cluster.md b/docs/guides/gettingStarted/cluster.md index dafad29060..4707cdf798 100644 --- a/docs/guides/gettingStarted/cluster.md +++ b/docs/guides/gettingStarted/cluster.md @@ -5,9 +5,9 @@ sidebar_position: 5 -# 同一内网下IMServer源码集群部署指南 +# 同一内网下 OpenIMServer 源码集群部署指南 -本文以A、B两台机器(内网 IP 分别为 `IP_A` 和 `IP_B`)为例,它们位于同一内网环境中,用于部署集群版 IM Server 与 Nginx。 +本文以 A、B 两台机器(内网 IP 分别为 `IP_A` 和 `IP_B`)为例,它们位于同一内网环境中,用于部署集群版 OpenIMServer 与 Nginx。 假设您已部署 Redis 集群、MongoDB 分片集群、Kafka 集群及 Etcd 集群,具体地址如下: - **Redis 集群地址**: `redisAddr1`, `redisAddr2`, `redisAddr3` - **MongoDB 集群地址**: `mongoAddr1`, `mongoAddr2`, `mongoAddr3` @@ -40,7 +40,7 @@ A 和 B 两台机器以及组件集群内网互通,且A、B两台机器都有 ### 1. 克隆仓库 -在两台机器(A 和 B)上分别执行以下命令以克隆 `open-im-server` 仓库: +在两台机器(A 和 B)上分别执行以下命令以克隆 OpenIMServer 仓库: ```bash git clone https://github.com/openimsdk/open-im-server @@ -113,7 +113,7 @@ http { } upstream im_api { - # IM API 服务器地址,可根据部署情况指定多个 + # OpenIMServer API 地址,可根据部署情况指定多个 server IP_A:10002; server IP_B:10002; } @@ -188,4 +188,3 @@ mage start 1. 部署`kafka`时,需要修改`kafka`广播的端口。如果使用`open-im-server`中的`docker-compose.yml`部署,修改`service.kafka.environment.KAFKA_CFG_ADVERTISED_LISTENERS`中的`EXTERNAL`为访问`kafka`组件的地址。其他部署方式请自行修改。 例如:`KAFKA_CFG_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,EXTERNAL://192.168.2.36:19094`。 2. 多台机器部署需要保证时钟一致,服务才可正常运行。如`token`的签发允许各个机器的时钟误差在`5s`以内。 - diff --git a/docs/guides/gettingStarted/dockerCompose.md b/docs/guides/gettingStarted/dockerCompose.md index a6fa650b05..e9681b92fd 100644 --- a/docs/guides/gettingStarted/dockerCompose.md +++ b/docs/guides/gettingStarted/dockerCompose.md @@ -6,7 +6,7 @@ sidebar_position: 2 ## 1.环境准备 🌍 对于服务器硬件、软件、操作系统、以及所依赖组件请参考[此文档](./env-comp) -## 2. 部署 IMServer +## 2. 部署 OpenIMServer ### 2.1 仓库克隆 🗂️ ```bash @@ -15,7 +15,7 @@ git clone https://github.com/openimsdk/openim-docker && cd openim-docker ### 2.2 配置修改 🔧 -- 修改 `.env` 文件,配置MinIO外网 IP,以支持发送图片视频文件,其中your-server-ip为服务端外网IP +- 修改 `.env` 文件,配置 MinIO 外网 IP,以支持发送图片和文件,其中 `your-server-ip` 为服务端外网 IP。 ```plaintext MINIO_EXTERNAL_ADDRESS="http://your-server-ip:10005" @@ -46,7 +46,9 @@ docker logs -f openim-server ## 3. 快速体验 ⚡ -快速体验 OpenIMSDK 核心能力,并测试部署是否正常,请参考[快速验证](./quickTestServer)。 +快速体验 OpenIMSDK 核心能力,并测试 OpenIMServer/ChatServer 部署是否正常,请参考[快速验证](./quickTestServer)。 + +> 补充(基于当前项目目录):如果你是按 `open-im-server` + `chat` 两个源码仓库部署,`open-im-server/docker-compose.yml` 主要用于依赖组件,ChatServer 仍需在 `chat` 目录执行 `mage start`。可参考[源码部署](./imSourceCodeDeployment)。 ## 4. 常见问题 @@ -57,4 +59,4 @@ docker logs -f openim-server ### 配置项修改 进入容器修改config目录下的修改配置文件无效! -必须采用环境变量的方式修改配置,参考[设置环境变量指南](https://github.com/openimsdk/openim-docker/issues/136)。 \ No newline at end of file +必须采用环境变量的方式修改配置,参考[设置环境变量指南](https://github.com/openimsdk/openim-docker/issues/136)。 diff --git a/docs/guides/gettingStarted/env-comp.md b/docs/guides/gettingStarted/env-comp.md index f6a6030a32..e096d7ec16 100644 --- a/docs/guides/gettingStarted/env-comp.md +++ b/docs/guides/gettingStarted/env-comp.md @@ -5,34 +5,29 @@ sidebar_position: 1 # 🧩 平台及组件要求 ---- - -## 🌐 操作系统及硬件 - -| 系统及硬件 | 详细说明 | -| --- | --- | -| **操作系统** | 支持 Linux、Windows、macOS 系统 | -| **硬件资源** | 4核CPU 8GB内存 10M带宽 100GB硬盘 以上| - +适用于 OpenIMSDK 服务端生产环境源码部署(单机)。 +--- +## 一、环境要求 -## 🌐 软件 -| 软件 | 详细说明 | -| --- | --- | -| **Golang** | v1.21 或更高版本,[安装参考](https://go.dev/learn/) | -| **Docker** | v24.0.5 或更高版本,[安装参考](https://www.docker.com/get-started/) | -| **Git** | v2.17.1 或更高版本,[安装参考](https://git-scm.com/downloads) | +| 注意事项 | 详细说明 | 补充说明 | +| --- | --- | --- | +| 操作系统 | Linux | 官方使用 `ubuntu 22.04` | +| 硬件资源 | 8核16G,10M带宽,1T磁盘 | 按 10 万注册用户、10% 日常在线、5 万大群、每秒 600 条消息估算;需有外网 IP | +| CPU 架构 | `x86_64` | arm 架构需自行测试 | +| Golang | `v1.22.7` 或更高版本 | [安装参考](https://go.dev/learn/) | +| Docker | `v24.0.5` 或更高版本 | 自带 `compose` 功能 | +| Git | `v2.17.1` 或更高版本 | [安装参考](https://git-scm.com/downloads) | -## 💾 组件要求 +## 二、外部组件要求 -| 存储组件 | 建议版本 | -| --- | --- | -| **MongoDB** | v7.0 | -| **Redis** | v7.0.0 | -| **Etcd** | v3.5.13 | -| **Kafka** | v3.5.1 | -| **MinIO** | RELEASE.2024-01-11T07-46-16Z | +| 存储组件 | 建议版本 | 支持模式 | 支持云服务 | +| --- | --- | --- | --- | +| MongoDB | `v7.0` | 单机、分片集群 | 支持 | +| Redis | `v7.0.0` | 单机、分片集群 | 支持 | +| Etcd | `v3.5.13` | 单机、分片集群 | 不支持 | +| Kafka | `v3.5.1` | 单机、分布式集群 | 支持 | +| MinIO | `RELEASE.2024-01-11T07-46-16Z` | 单机 | S3 兼容存储系统(`cos`、`oss`、`kodo`、`aws`) | --- - diff --git a/docs/guides/gettingStarted/faq.md b/docs/guides/gettingStarted/faq.md index bc22fc43fe..45c1dbb0c3 100644 --- a/docs/guides/gettingStarted/faq.md +++ b/docs/guides/gettingStarted/faq.md @@ -61,7 +61,7 @@ sidebar_position: 10 --- ## 二、 如何迁移数据 -在使用`docker compose up -d`命令启动`IMServer`依赖的各个组件之后,`IMServer`根目录下会生成一个`components`的文件夹,`IMServer`运行后产生的数据(如用户、群聊、消息等等)都保存在这个文件夹中。如果需要迁移数据,需要先关闭服务和组件: +在使用`docker compose up -d`命令启动 OpenIMServer 依赖的各个组件之后,OpenIMServer 根目录下会生成一个`components`文件夹,OpenIMServer 运行后产生的数据(如用户、群聊、消息等)都保存在这个文件夹中。如果需要迁移数据,需要先关闭服务和组件: `docker`部署: @@ -119,13 +119,13 @@ docker compose down # 关闭组件 一般发送图片失败是由于没有配置第三方存储的原因。默认使用的第三方存储为`minio`,需修改相关配置 ``` 源码部署: -修改 config/minio.yml 文件,配置MinIO外网 IP,以支持发送图片视频文件,其中your-server-ip为服务端外网IP +修改 config/minio.yml 文件,配置 MinIO 外网 IP,以支持发送图片和文件,其中 `your-server-ip` 为服务端外网 IP externalAddress="http://your-server-ip:10005" ``` ``` docker部署 -修改 .env 文件,配置MinIO外网 IP,以支持发送图片视频文件,其中your-server-ip为服务端外网IP +修改 .env 文件,配置 MinIO 外网 IP,以支持发送图片和文件,其中 `your-server-ip` 为服务端外网 IP MINIO_EXTERNAL_ADDRESS="http://your-server-ip:10005" ``` --- diff --git a/docs/guides/gettingStarted/faultRecovery.mdx b/docs/guides/gettingStarted/faultRecovery.mdx new file mode 100644 index 0000000000..fc1fd1f9b8 --- /dev/null +++ b/docs/guides/gettingStarted/faultRecovery.mdx @@ -0,0 +1,203 @@ +--- +title: '异常处理及数据恢复' +sidebar_position: 8 +slug: /gettingStarted/production +--- + +# 异常处理及数据恢复 + +在生产环境中,通常会采用集群部署来保证组件和服务的高可用性。然而,在资源有限的情况下,也可能采用单机部署(源码部署或 Docker 部署)。 + +## 一、mongo 定时数据备份 + +OpenIMServer 核心数据存储在 MongoDB 中,因此备份 MongoDB 数据可以恢复大部分业务数据。 + +### 1. 修改备份目录 + +- 在 `.env` 中修改 `MONGO_BACKUP_DIR`(默认 `components/backup/mongo/`)。 +- 建议将备份目录放在与 `components` 不同的磁盘,避免同盘故障导致源数据与备份同时损坏。 + +### 2. 配置定时备份 + +```bash +crontab -e +``` + +添加示例任务(每天 2 点备份,保留最近 2 份): + +```bash +0 2 * * * docker exec mongo mongodump --uri="mongodb://openIM:openIM123@localhost:27017/openim_v3" --out="/data/backup/$(expr $(date +\%s) / 86400 \% 2)" +``` + +检查是否生效: + +```bash +crontab -l +``` + +## 二、异常测试范围与执行方法 + +### 1. 测试范围(单机微服务) + +- 外部组件:`MongoDB` `Redis` `Kafka` `Etcd` `MinIO` +- OpenIMServer 服务:`openim-api` `openim-msggateway` `openim-rpc-*` `openim-msgtransfer` `openim-push` `openim-crontask` +- ChatServer 服务:`chat-api` `chat-rpc` `admin-api` `admin-rpc` `bot-api` `bot-rpc` + +### 2. 标准执行步骤(每个用例都按此流程) + +1. 记录基线:`mage check`、核心接口探针、关键日志状态。 +2. 故障注入:停组件或 kill 单个服务。 +3. 影响观察:记录“完全不可用 / 部分可用 / 可用但退化”。 +4. 执行恢复:重启组件或服务,必要时恢复数据。 +5. 验收复测:探针恢复、链路恢复、日志无持续报错。 + +### 3. 测试前基线命令 + +```bash +# OpenIMServer +cd /path/to/open-im-server +mage check + +# ChatServer +cd /path/to/chat +mage check +``` + +```bash +# 外部组件(docker compose 部署时) +docker ps --format '{{.Names}}\t{{.Status}}' | grep -E 'mongo|redis|kafka|etcd|minio' +``` + +```bash +# OpenIMServer 鉴权探针 +curl -sS -X POST "http://127.0.0.1:10002/auth/get_admin_token" \ + -H "Content-Type: application/json" \ + -H "operationID: fault-baseline" \ + -d '{"secret":"your_openim_secret","userID":"imAdmin"}' +``` + +```bash +# ChatServer 业务探针 +curl -sS -X POST "http://127.0.0.1:10008/application/latest_version" \ + -H "Content-Type: application/json" \ + -H "operationID: fault-baseline-chat" \ + -d '{}' +``` + +## 三、异常测试方案(影响与恢复矩阵) + +### 1. 外部组件故障 + +| 用例ID | 注入对象 | 注入方式(示例) | 主要影响 | 是否可部分服务 | 修复动作 | +| --- | --- | --- | --- | --- | --- | +| EXT-01 | MongoDB | `docker stop mongo` | 注册/登录、历史消息与业务数据读写异常 | 否(核心数据链路受阻) | `docker start mongo`,必要时执行备份恢复 | +| EXT-02 | Redis | `docker stop redis` | OpenIM 鉴权链路报 `auth-rpc-service down` | 是(部分接口仍可返回) | `docker start redis` 后通常还需执行 OpenIM `mage stop && mage start` | +| EXT-03 | Kafka | `docker stop kafka` | 消息转发与推送链路受阻,可能积压 | 是(非消息链路可部分可用) | `docker start kafka`,核查 topic 消费恢复 | +| EXT-04 | Etcd | `docker stop etcd` | 运行中实例短时可用;`mage start` 阶段会卡在组件检查/服务注册 | 是(短时) | 先 `docker start etcd`,再执行 OpenIM/Chat 全量重启与复检 | +| EXT-05 | MinIO | `docker stop minio` | 图片/文件上传下载失败 | 是(文本消息通常可用) | `docker start minio`,检查 `externalAddress` | + +### 2. OpenIMServer 单服务故障 + +| 用例ID | 注入对象 | 注入方式(示例) | 主要影响 | 是否可部分服务 | 修复动作 | +| --- | --- | --- | --- | --- | --- | +| IM-01 | `openim-api` | `pkill -f openim-api` | REST API 不可用,Chat 对 OpenIM 调用失败 | 否 | 实测单独 `mage start openim-api` 不足以恢复完整链路,建议 `mage stop && mage start` | +| IM-02 | `openim-msggateway` | `pkill -f openim-msggateway` | WS 连接断开,实时消息中断 | 否(实时链路) | 同上 | +| IM-03 | `openim-rpc-auth` | `pkill -f openim-rpc-auth` | token 相关能力异常 | 是(部分不依赖鉴权链路) | 同上 | +| IM-04 | `openim-rpc-msg` | `pkill -f openim-rpc-msg` | 消息发送/拉取异常 | 是(非消息接口可用) | 同上 | +| IM-05 | `openim-msgtransfer` | `pkill -f openim-msgtransfer` | 消息转发链路中断或高延迟 | 是(非消息接口可用) | 同上 | +| IM-06 | `openim-push` | `pkill -f openim-push` | 离线推送失败 | 是(在线消息通常可用) | 同上 | +| IM-07 | `openim-crontask` | `pkill -f openim-crontask` | 定时任务不执行 | 是(实时链路通常可用) | 同上 | + +### 3. ChatServer 单服务故障 + +| 用例ID | 注入对象 | 注入方式(示例) | 主要影响 | 是否可部分服务 | 修复动作 | +| --- | --- | --- | --- | --- | --- | +| CHAT-01 | `chat-api` | `pkill -f chat-api` | 业务系统接口失败(10008) | 否(Chat 业务入口受阻) | 实测单独 `mage start chat-api` 可能连带影响其他 Chat 服务,建议 `mage stop && mage start` | +| CHAT-02 | `chat-rpc` | `pkill -f chat-rpc` | Chat 核心业务接口异常 | 否(Chat 核心链路) | 同上 | +| CHAT-03 | `admin-api` | `pkill -f admin-api` | 管理后台接口失败(10009),10008 可继续提供服务 | 是(用户侧链路可继续) | 实测单独 `mage start admin-api` 可能出现 `admin-rpc` 不可用,建议 `mage stop && mage start` | +| CHAT-04 | `admin-rpc` | `pkill -f admin-rpc` | 管理后台业务失败 | 是 | 同上 | +| CHAT-05 | `bot-api`/`bot-rpc` | `pkill -f bot-api` / `pkill -f bot-rpc` | Bot 相关能力失败 | 是(主链路可继续) | 同上 | + +## 四、组件异常停止处理(恢复 Runbook) + +### 1. 通用恢复顺序 + +1. 先恢复外部组件。 +2. 再恢复 OpenIMServer。 +3. 最后恢复 ChatServer。 +4. 全量执行 `mage check` + 探针复测。 + +### 2. 外部组件恢复 + +```bash +cd /path/to/open-im-server +docker compose up -d mongodb redis kafka etcd minio +``` + +```bash +docker ps --format '{{.Names}}\t{{.Status}}' | grep -E 'mongo|redis|kafka|etcd|minio' +``` + +### 2.1 实测补充(2026-03-06,`/root/test`) + +1. Chat 相关 API 探针必须带 `operationID` 请求头,否则会返回参数错误。 +2. Redis 恢复后,OpenIM 不一定自动恢复,需要执行 OpenIM 全量重启。 +3. Etcd 宕机时,已运行实例可短时可用;但在服务重启阶段会失败,需先恢复 Etcd。 +4. Chat 单服务拉起(如 `mage start chat-api`、`mage start admin-api`)在异常场景下可能导致其他 Chat 进程缺失,故障恢复优先采用 Chat 全量重启。 + +### 3. OpenIMServer 恢复 + +```bash +cd /path/to/open-im-server +mage check +mage stop +mage start +mage check +``` + +### 4. ChatServer 恢复 + +```bash +cd /path/to/chat +mage check +mage stop +mage start +mage check +``` + +### 5. MongoDB 数据恢复(需要时) + +```bash +docker exec -it mongo mongorestore \ + --uri="mongodb://openIM:openIM123@localhost:27017/openim_v3" \ + /data/backup/your_backup_name/openim_v3 +``` + +## 五、潜在风险 + +1. 单机部署存在单点故障,无法等价替代高可用集群。 +2. 若源数据盘与备份盘同时损坏,无法直接恢复,只能依赖云厂商快照或其他离线备份。 +3. MongoDB 恢复到历史备份点后,备份时间点之后的数据会丢失。 +4. 删除 Redis 数据可能导致未读数、会话状态等短期不一致。 + +## 六、完整异常测试报告模板 + +每个用例必须记录以下字段。 + +| 字段 | 内容 | +| --- | --- | +| 用例ID | 例如 `EXT-01` | +| 测试日期 | YYYY-MM-DD HH:mm | +| 测试环境 | 服务器规格、版本、部署方式 | +| 故障注入对象 | 组件或服务名 | +| 注入命令 | 实际执行命令 | +| 注入开始时间 | 时间戳 | +| 业务影响现象 | 用户可见现象与接口报错 | +| 影响范围 | OpenIMClientSDK、APP 业务服务器、APP 管理员后台 | +| 恢复动作 | 实际执行步骤 | +| 恢复完成时间 | 时间戳 | +| MTTR | 恢复耗时 | +| 数据影响 | 是否丢消息/丢数据/状态不一致 | +| 验收结果 | 通过/不通过 | +| 根因分析 | 简述根因 | +| 改进项 | 监控、告警、配置、流程优化 | diff --git a/docs/guides/gettingStarted/imSourceCodeDeployment.md b/docs/guides/gettingStarted/imSourceCodeDeployment.md index 1c60704f57..ede7106bec 100644 --- a/docs/guides/gettingStarted/imSourceCodeDeployment.md +++ b/docs/guides/gettingStarted/imSourceCodeDeployment.md @@ -3,95 +3,183 @@ title: '源码部署' sidebar_position: 3 --- -# 🛠 源码部署 +# OpenIMSDK 服务端生产环境源码部署(单机) -## 1. 环境准备 +## 一、环境及组件要求 -对于服务器硬件、软件、操作系统、以及所依赖组件请参考[此文档](./env-comp) +OpenIMServer 与外部组件部署在同一台机器上,部分组件可按需使用云服务。 -## 2. 部署 IMServer +- 环境要求请先参考:[平台及组件要求](./env-comp) -### 2.1 clone仓库并切换到最新稳定版tag +## 二、获取 OpenIMServer 并部署依赖组件 -``` -git clone https://github.com/openimsdk/open-im-server && cd open-im-server -``` +从开源仓库获取 OpenIMServer 源码(或下载对应 tag 源码包),解压后进入项目根目录。 -### 2.2 部署组件 (mongodb/redis/kafka/MinIO/Etcd) -``` -docker compose up -d -``` +> 注意:后续所有命令都在 OpenIMServer 项目根目录执行。 +### 2.1 部署外部组件(Docker Compose) +确保 `docker` 和 `docker compose` 已可用。 -### 2.3 配置修改 🔧 +1. 如果本机已部署 `mongodb/redis/kafka/minio/etcd` 中一个或多个组件,或计划改用云服务(`etcd` 不支持云服务),可在 `docker-compose.yml` 注释对应组件。 +2. 强烈建议修改 `docker-compose.yml` 里的默认账号和密码。 -- 修改 `config/minio.yml` 文件,配置MinIO外网 IP,以支持发送图片视频文件,其中your-server-ip为服务端外网IP +| 组件 | 在 `docker-compose.yml` 中的位置 | +| --- | --- | +| MongoDB | `MONGO_INITDB_ROOT_USERNAME` `MONGO_INITDB_ROOT_PASSWORD` `MONGO_OPENIM_USERNAME` `MONGO_OPENIM_PASSWORD` | +| Redis | `redis-server --requirepass ...` | +| MinIO | `MINIO_ROOT_USER` `MINIO_ROOT_PASSWORD` | +| Etcd | `ETCD_ROOT_USER` `ETCD_ROOT_PASSWORD`(启用鉴权时) | +| Kafka | `KAFKA_USERNAME` `KAFKA_PASSWORD`(启用鉴权时) | -```plaintext -externalAddress="http://your-server-ip:10005" - ``` +3. 修改 `.env` 中的 `DATA_DIR`,指向大磁盘目录用于外部组件数据存储。 +4. 执行以下命令部署外部组件: +```bash +docker compose up -d +``` + +### 2.2 自行部署组件或使用云服务时的初始化要求 + +| 存储组件 | 初始化要求 | +| --- | --- | +| MongoDB | 预先创建数据库:`openim_v3` | +| Kafka | 预先创建 4 个 topic:`toRedis` `toMongo` `toPush` `toOfflinePush`,每个 topic 设置 8 个分区 | +## 三、部署 OpenIMServer -### 2.4 🛠️ 初始化下载mage +确保 Go 已正确安装。 -第一次编译前,linux/mac 下执行: +### 3.1 中国境内建议设置 Go 代理 +```bash +go env -w GO111MODULE=on +go env -w GOPROXY=https://goproxy.cn,direct ``` + +### 3.2 初始化(仅执行一次) + +```bash bash bootstrap.sh ``` -windows 执行 +### 3.3 编译 -``` -bootstrap.bat +```bash +mage ``` +首次编译耗时较长,请耐心等待。 -中国境内建议设置go代理 -``` -$ go env -w GO111MODULE=on -$ go env -w GOPROXY=https://goproxy.cn,direct -``` +### 3.4 基础配置修改 -### 2.5 🛠️ 编译(linux/windows/mac 平台均可用) +| 描述 | 所在文件 | +| --- | --- | +| Kafka 用户名、密码、地址 | `config/kafka.yml` | +| Redis 密码、地址 | `config/redis.yml` | +| MinIO 用户名、密码、地址;`externalAddress` 必须改为外网 IP 或域名路径 | `config/minio.yml` | +| S3 云存储密钥(使用 S3 时) | `config/openim-rpc-third.yml` | +| Etcd 用户名、密码、地址 | `config/discovery.yml` | +| MongoDB 用户名、密码、地址 | `config/mongodb.yml` | +| OpenIMServer `secret` | `config/share.yml` | +> `minio.yml` 的 `externalAddress` 必须改为外网 IP 或域名路径,否则 IM 无法正常发送图片和文件。 -``` +### 3.5 启动/停止/检测 + +| 任务 | 命令 | 说明 | +| --- | --- | --- | +| 后台启动 | `nohup mage start >> _output/logs/openim.log 2>&1 &` | 生产环境建议使用 | +| 停止 | `mage stop` | - | +| 检测 | `mage check` | - | + +## 四、获取 Chat + +> 如果已有自有账号系统,可不部署 ChatServer。 + +从开源仓库下载稳定版本源码: + +- `https://github.com/openimsdk/chat/tags` + +解压后进入 ChatServer 项目根目录。 + +## 五、部署 ChatServer + +> 注意:以下命令都在 ChatServer 项目根目录执行。 + +### 5.1 编译 + +```bash mage ``` -### 2.6 🚀 启动/停止/检测(linux/windows/mac 平台均可用) +### 5.2 基础配置修改 -``` -# 启动 -mage start -# 或 后台启动 收集日志 -nohup mage start >> _output/logs/openim.log 2>&1 & -# 停止 -mage stop -# 检测 -mage check -``` +| 描述 | 所在文件 | +| --- | --- | +| Redis 用户名、密码、地址 | `config/redis.yml` | +| Etcd 用户名、密码、地址 | `config/discovery.yml` | +| MongoDB 用户名、密码、地址 | `config/mongodb.yml` | +| OpenIMServer `secret` | `config/share.yml` | +| ChatServer `secret` | `config/chat-rpc-admin.yml` | +### 5.3 启动/停止/检测 +| 任务 | 命令 | 说明 | +| --- | --- | --- | +| 后台启动 | `nohup mage start >> _output/logs/chat.log 2>&1 &` | 生产环境建议使用 | +| 停止 | `mage stop` | - | +| 检测 | `mage check` | - | -## 3. 快速验证 +## 六、配置文件说明 -请参考[快速验证](./quickTestServer)文档 +- OpenIMServer 配置文件说明: + `https://github.com/openimsdk/open-im-server/blob/main/config/README_zh_CN.md` +- ChatServer 配置文件说明: + `https://github.com/openimsdk/chat/blob/main/config/README_zh_CN.md` ---- +## 七、关于离线推送 + +- 个推:开源版对个推支持不够精细,需按业务自行调试。 +- Firebase:修改 `config/openim-push.yml` 中 `fcm.filepath`。 + +## 八、服务实例个数修改(可选) + +在 `start-config.yml` 的 `serviceBinaries` 中,除 `openim-msggateway` 和 `openim-api` 外,其他服务可直接调整实例数。 + +对 `openim-msggateway` 与 `openim-api`: + +- 服务实例数要和对应配置文件里的端口数量保持一致。 +- 修改后重启服务生效。 + +## 九、监控告警(可选) + +可按需启用服务器资源与 OpenIMServer/ChatServer 指标监控、仪表盘展示与告警通知。 + +## 十、重要指引 + +### 10.1 `secret` 修改 + +强烈建议修改默认 `secret`: + +- 至少 8 位 +- 数字 + 字母组合 +- 妥善保密 + +### 10.2 端口开放与客户端地址 -## 4. 常见问题 +不使用域名/SSL 时,端口开放与 SDK 地址请参考:[端口和防火墙](./ports) -### 4.1 📜 日志查看 +- `apiAddr: http://your_server_ip:10002` +- `wsAddr: ws://your_server_ip:10001` -IMServer日志位置: `_output/logs/openim-service-log.*` +使用域名/SSL 时,配置请参考:[域名及 SSL 证书配置](./nginxDomainConfig) -### 4.2 🚀 启动顺序 -启动顺序如下: -- 依赖的组件:mongodb/redis/kafka/minio/etcd -- IMServer +- 按部署要求修改域名解析,绑定 IP 与域名 +- 域名模式一般仅对外开放 `443` +- `apiAddr: https://your_domain.com/api` +- `wsAddr: wss://your_domain.com/msg_gateway` +### 10.3 单机生产环境数据备份及恢复 +请参考:[单机生产环境数据备份及恢复](./faultRecovery) diff --git a/docs/guides/gettingStarted/internalDeployment.md b/docs/guides/gettingStarted/internalDeployment.md index 52d5e667b2..b94b0dc65d 100644 --- a/docs/guides/gettingStarted/internalDeployment.md +++ b/docs/guides/gettingStarted/internalDeployment.md @@ -4,7 +4,7 @@ sidebar_position: 4 --- ## 📌 内网部署指南 -本指南将指导您在一台纯内网的机器上部署OpenIM相关服务。 +本指南将指导您在一台纯内网机器上部署 OpenIMSDK 相关服务。 ### **Docker部署** @@ -80,20 +80,20 @@ sidebar_position: 4 ### **源码部署** -1. 使用一台连接到互联网的机器,克隆server仓库建议切换到 release-v3.8.2 分支: +1. 使用一台连接到互联网的机器,克隆 OpenIMServer 仓库,建议切换到 `release-v3.8.2` 分支: ```sh git clone https://github.com/openimsdk/open-im-server ``` -2. 克隆`chat`仓库建议切换到 release-v1.8.3 分支 +2. 克隆 ChatServer 仓库,建议切换到 `release-v1.8.3` 分支 ```bash git clone https://github.com/openimsdk/chat ``` 3. 参考[docker部署](#docker部署)步骤,保存镜像,区别为不需要`openim/openim-server:release-v3.8.2`和`openim/openim-chat:v1.8.2`。 -4. 通过内网或者物理介质将**镜像文件**、**server仓库文件**、**chat仓库文件**拷贝到部署机器上。 +4. 通过内网或者物理介质将**镜像文件**、**OpenIMServer 仓库文件**、**ChatServer 仓库文件**拷贝到部署机器上。 5. 导入镜像到`docker`中,命令为: @@ -107,18 +107,17 @@ sidebar_position: 4 docker load -i mongo.tar ``` -6. 在`server`目录下依次运行: +6. 在 OpenIMServer 目录下依次运行: ```bash docker compose up -d # 如需启用监控组件则为 docker compose --profile m up -d mage mage start ``` -7. 在`chat`目录下运行: +7. 在 ChatServer 目录下运行: ```bash mage mage start ``` - diff --git a/docs/guides/gettingStarted/nginxDomainConfig.md b/docs/guides/gettingStarted/nginxDomainConfig.md index 71eb8f54cf..8907ffa219 100644 --- a/docs/guides/gettingStarted/nginxDomainConfig.md +++ b/docs/guides/gettingStarted/nginxDomainConfig.md @@ -5,55 +5,49 @@ sidebar_position: 7 # 域名及 SSL 证书配置 -## 1. 前置条件 🛠️ +> 本页仅包含 OpenIMServer 与 ChatServer 相关域名配置。 -- **IMServer** 成功启动。 -- **Nginx** 已成功安装,包括 SSL 模块。 -- 申请域名(或子域名)及 SSL 证书,例如:`im.yourhost.com` ,用于 IMServer。 -- 开放 443端口。 +## 1. 前置条件 -## 2. 域名配置模板 📝 +- OpenIMServer、ChatServer 已成功启动。 +- Nginx 已安装并启用 SSL 模块。 +- 已申请域名和 SSL 证书(示例:`im.yourhost.com`)。 +- 服务器已放行 `443` 端口。 -> 🚀 **提示**: 确保替换成您的实际域名、SSL 证书路径和 SSL 密钥。 +## 2. Nginx 配置模板 -```nginx +> 请替换为你的实际域名、证书路径与服务地址。 -upstream msg_gateway{ - #IM Message server address Multiple can be specified according to the deployment +```nginx +upstream msg_gateway { + # OpenIMServer message gateway server 127.0.0.1:10001; } -upstream im_api{ - #IM Group user api server address Multiple can be specified according to the deployment + +upstream im_api { + # OpenIMServer API server 127.0.0.1:10002; } -upstream minio_s3_2{ - #Minio address can be assigned to multiple modules dependingon deployment - server 127.0.0.1:10005; +upstream im_chat_api { + # ChatServer API + server 127.0.0.1:10008; } +upstream minio_s3 { + # MinIO object storage + server 127.0.0.1:10005; +} server { - listen 443; #Listening on port 443 - server_name im.yourhost.com; #Your domain + listen 443; + server_name im.yourhost.com; + ssl on; - #Path of pem file for ssl certificate ssl_certificate /usr/local/nginx/conf/ssh/im.yourhost.com_bundle.pem; - #Key file path of ssl certificate ssl_certificate_key /usr/local/nginx/conf/ssh/im.yourhost.com.key; - gzip on; - gzip_min_length 1k; - gzip_buffers 4 16k; - gzip_comp_level 2; - gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/wasm; - gzip_vary off; - gzip_disable "MSIE [1-6]\."; - - default_type application/wasm; - - - location /msg_gateway{ + location /msg_gateway { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; @@ -62,7 +56,7 @@ server { proxy_pass http://msg_gateway/; } - location ^~/api/{ + location ^~/api/ { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; @@ -72,7 +66,14 @@ server { proxy_pass http://im_api/; } - + location ^~/chat/ { + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header X-real-ip $remote_addr; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_pass http://im_chat_api/; + } location ^~/im-minio-api/ { proxy_set_header Host $http_host; @@ -80,34 +81,51 @@ server { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 300; - proxy_http_version 1.1; proxy_set_header Connection ""; chunked_transfer_encoding off; - proxy_pass http://minio_s3_2/; + proxy_pass http://minio_s3/; } - - - } +``` +## 3. MinIO 配置 +- 源码部署:修改 `config/minio.yml` 中 `externalAddress` +- Docker 部署:修改 `.env` 中 `MINIO_EXTERNAL_ADDRESS` +示例值: + +```text +https://im.yourhost.com/im-minio-api ``` -## 3. Minio 配置 🗄️ +## 4. 重载 Nginx -- **源码部署**: 修改 `config/minio.yml` 文件中的 `externalAddress`值为 `"https://im.yourhost.com/im-minio-api"`. -- **Docker部署**: 修改 `.env` 文件中的 `MINIO_EXTERNAL_ADDRESS` 值为 `"https://im.yourhost.com/im-minio-api"`. +```bash +nginx -s reload +``` -## 4. 启动 Nginx 🚀 +## 5. OpenIMClientSDK 初始化参数 -执行命令 `nginx -s reload` 以重载 Nginx 配置。 +```text +apiAddr: https://im.yourhost.com/api +wsAddr: wss://im.yourhost.com/msg_gateway +``` +## 6. 网关路径对照 -## 5. 修改客户端 SDK 初始化参数 +| 路径 | 服务 | +| --- | --- | +| `/api/*` | OpenIMServer API(10002) | +| `/msg_gateway` | OpenIMServer WS(10001) | +| `/chat/*` | ChatServer API(10008) | +| `/im-minio-api/*` | MinIO 对象存储(10005) | -在客户端 SDK 中,配置初始化参数如下: +## 7. 命名统一 -- `apiAddr`: `https://im.yourhost.com/api` -- `wsAddr`: `wss://im.yourhost.com/msg_gateway` +- OpenIMSDK:项目整体名称。 +- OpenIMClientSDK:客户端 SDK。 +- OpenIMServer:IM 基础服务。 +- ChatServer:业务扩展服务。 +- 管理类 REST API 调用账号统一称为 APP 管理员。 diff --git a/docs/guides/gettingStarted/ports.md b/docs/guides/gettingStarted/ports.md index f996b5a841..483e90a596 100644 --- a/docs/guides/gettingStarted/ports.md +++ b/docs/guides/gettingStarted/ports.md @@ -5,54 +5,42 @@ sidebar_position: 6 # 🔐 端口及防火墙 ---- - -## 📡 IM Server 端口 +## 一、不使用域名及 SSL 证书 -以下端口需在防火墙中放行,以确保 IMServer 正常通信。 +以下端口需要在服务器放行,其他端口不要对外。 -| TCP 端口 | 说明 | 操作 ⚙️ | -| --- | --- | --- | -| **10001** | WebSocket 消息端口,用于客户端 SDK 通信 | ✅ 放行 | -| **10002** | API 端口,提供用户、好友、群组、消息等接口 | ✅ 放行 | -| **10005** | 当使用 MinIO 存储时需开启(IMServer 默认使用 MinIO) | ✅ 放行 | - ---- +| 模块 | 端口 | 说明 | 操作 | +| --- | --- | --- | --- | +| OpenIMServer | TCP:10001 | ws 协议,消息端口,用于 OpenIMClientSDK | 端口放行 | +| OpenIMServer | TCP:10002 | api 端口,如用户、好友、群组、消息等接口 | 端口放行 | +| OpenIMServer | TCP:10005 | MinIO 作为对象存储 | 端口放行 | +| ChatServer | TCP:10008 | 业务系统,如注册、登录等 | 端口放行 | +| ChatServer | TCP:10009 | 管理后台,如统计、封号等 | 端口放行 | -## 💻 Web 前端与管理后台端口 +在客户端 SDK 中,初始化参数如下: -| TCP 端口 | 说明 | 操作 ⚙️ | -| --- | --- | --- | -| **11001** | PC Web 端与管理后台前端资源端口 | ✅ 放行 | +```text +apiAddr: http://your_server_ip:10002 +wsAddr: ws://your_server_ip:10001 +``` ---- +## 二、使用域名及 SSL 证书 -## 📊 Grafana 监控端口 +以下端口需要在服务器放行,其他端口不要对外。 -| TCP 端口 | 说明 | 操作 ⚙️ | +| 端口 | 说明 | 操作 | | --- | --- | --- | -| **13000** | Grafana 监控可视化端口 | ✅ 放行 | - ---- - -> ⚠️ **提示:** -> 若服务器启用了防火墙(如 `ufw` 或 `firewalld`),请确保以上端口已放行。 -> 例如在 Linux 上使用命令: -> -> ```bash -> sudo ufw allow 10001:10005/tcp -> sudo ufw allow 11001/tcp -> sudo ufw allow 13000/tcp -> sudo ufw reload -> ``` -> +| TCP:443 | HTTPS 默认网络端口 | 端口放行 | -使用在线端口检测工具(适合公网服务器) +配置参考:[域名及 SSL 证书配置](./nginxDomainConfig) -你也可以使用网页工具检测,例如: +请先完成域名解析,设置 IP 与域名绑定关系。 -https://portchecker.co/ +在客户端 SDK 中,初始化参数如下: -https://www.yougetsignal.com/tools/open-ports/ +```text +apiAddr: https://your_domain.com/api +wsAddr: wss://your_domain.com/msg_gateway +``` -在输入框中填入你的服务器公网 IP 和端口号,即可查看是否能被访问。 +> 说明:监控、前端等其他服务端口建议仅内网开放,按实际部署需求控制暴露范围。 diff --git a/docs/guides/gettingStarted/production.md b/docs/guides/gettingStarted/production.md index 34f14503ab..e69de29bb2 100644 --- a/docs/guides/gettingStarted/production.md +++ b/docs/guides/gettingStarted/production.md @@ -1,61 +0,0 @@ ---- -title: '生产环境' -sidebar_position: 8 ---- - - - - -在生产环境中,通常会采用集群部署来保证组件和服务的高可用性。然而,在资源有限的情况下,一些开发者可能会选择在生产环境中进行单机部署(使用源码部署或`docker`容器)。本文将介绍在单机部署环境下如何进行数据备份、异常恢复,以及潜在的风险。 - -## 一、mongo定时数据备份 -IMServer核心数据存储在MongoDB中,因此备份MongoDB数据就能恢复大部分数据。在容器启动之前,设置mongo数据备份目录和定时任务。 -### 数据备份 - -IMServer服务的核心数据存储在MongoDB中,因此备份MongoDB数据就能恢复大部分数据。以下是备份的步骤: - -1. **修改备份目录** - - - `.env`文件中修改`MONGO_BACKUP_DIR`的路径,默认值为`components/backup/mongo/`。建议将备份目录设置为与`components`目录不同的磁盘路径,以避免同一磁盘故障导致原始数据和备份数据同时丢失。 -3. **定时备份配置** - - 配置Linux系统的定时备份任务,执行以下命令编辑crontab: - ```sh - crontab -e - ``` - - 添加如下定时任务,表示每天凌晨2点执行备份,并保存最新的2个备份文件。如果需要其他定时规则,请调整`cron`表达式: - ```sh - 0 2 * * * docker exec mongo mongodump --uri="mongodb://openIM:openIM123@localhost:27017/openim_v3" --out="/data/backup/$(expr $(date +\%s) / 86400 \% 2)" - ``` - - 使用`crontab -l`命令可以查看当前定时任务是否设置成功。 - - - -## 二、组件异常停止处理 - -1. 如果 `mongo`、`redis`、`kafka`、`etcd` 等组件异常停止,首先尝试重启所有组件和 IMServer 服务。 - -2. 如果由于数据问题(如磁盘故障、磁盘满等)导致服务启动失败,则先停止所有组件和 IMServer 服务。 - - 如果 `redis` 启动失败,删除 `components/redis/` 目录。 - - 如果 `kafka` 启动失败,删除 `components/kafka/` 目录。 - - 如果 `mongo` 启动失败 - - 1. 删除 `components/redis/` `components/mongodb/` `components/kafka/`目录 - - 2. 恢复备份数据 docker exec -it mongo mongorestore --uri="mongodb://openIM:openIM123@localhost:27017/openim_v3" /data/backup/your_backup_name/openim_v3 - - **your_backup_name 为0 或者1, 选择时间较新的那个目录** - - 如果 `etcd` 启动失败,删除 `components/etcd/` 目录。 - -3. 在进行上述操作后,重启所有组件和 IMServer 服务。 - -## 三、潜在风险 - -1. **单机部署风险** - 如果机器故障导致原始数据磁盘和备份磁盘都无法访问,则无法直接恢复数据。此时,可能需要通过运营商的快照服务来恢复数据。 - -2. **备份目录建议** - 为防止由于单一磁盘故障导致的数据丢失,建议将 `mongo` 的备份目录 `MONGO_BACKUP_DIR` 设置为与 `components` 目录分开的磁盘。 - -3. **数据恢复风险** - 恢复 MongoDB 数据时,备份时间之后的数据将会丢失。因此,备份频率过快可能会对 MongoDB 的性能造成较大的影响。 - -4. **Redis 数据删除的影响** - 如果删除 Redis 中的数据,可能会导致 **消息未读数不正确**。 - diff --git a/docs/guides/gettingStarted/quickTestServer.md b/docs/guides/gettingStarted/quickTestServer.md index 0f22d1546d..6b1f3b3bee 100644 --- a/docs/guides/gettingStarted/quickTestServer.md +++ b/docs/guides/gettingStarted/quickTestServer.md @@ -2,6 +2,9 @@ title: '快速验证' sidebar_position: 9 --- + +import Image3 from './assets/pc-web.png'; + ## 📌 一、部署服务端 请参考 [docker部署](./dockerCompose) 或 [源码部署](./imSourceCodeDeployment) 来进行部署。 @@ -12,20 +15,53 @@ sidebar_position: 9 参考 [端口和防火墙](./ports) -## 三、PC Web验证 - +## 📌 三、PC Web验证 :::tip -在浏览器中输入 `http://your_server_ip:11001` 来访问 PC Web。`your_server_ip` 为IMServer服务端`ip`地址。 +在浏览器中输入 `http://your_server_ip:11001` 来访问 PC Web。`your_server_ip` 为部署前端服务的服务器 IP 地址。 ::: +PC Web Interface +## 📌 四、服务进程验证 -import Image3 from './assets/pc-web.png'; +确认 OpenIMServer 与 ChatServer 进程状态正常。 -PC Web Interface +```bash +docker ps | grep -E 'openim-server|openim-chat' +``` + +如果是源码部署,可执行: + +```bash +mage check +``` + +## 📌 五、域名与网关验证 + +使用域名和 SSL 时,建议直接调用实际接口确认 OpenIMServer 与 ChatServer 网关路由可达。 + +```bash +curl -sS -X POST "https://your_domain/api/auth/get_admin_token" \ + -H "Content-Type: application/json" \ + -H "operationID: verify-openim" \ + -d '{"secret":"your_openim_secret","userID":"imAdmin"}' +``` +```bash +curl -sS -X POST "https://your_domain/chat/application/latest_version" \ + -H "Content-Type: application/json" \ + -d '{}' +``` +> 如果接口返回业务错误但已返回 JSON,通常也表示网关反向代理链路已通。 +如果要验证 WebSocket 网关,可使用任意 ws 客户端测试: +```text +wss://your_domain/msg_gateway +``` +> 建议在生产环境统一通过 `443` 端口访问,OpenIMClientSDK 初始化时使用: +> - `apiAddr`: `https://your_domain/api` +> - `wsAddr`: `wss://your_domain/msg_gateway` From de41d5f66b69b0707828d82bfa3368b9acce5093 Mon Sep 17 00:00:00 2001 From: dsx137 <70027572+dsx137@users.noreply.github.com> Date: Fri, 6 Mar 2026 16:04:17 +0800 Subject: [PATCH 2/9] fix(docs): Update faultRecovery.mdx with improved recovery steps This commit updates the faultRecovery.mdx documentation to clarify and improve the steps for OpenIMServer and ChatServer recovery, including specific commands and considerations for various failure scenarios. The changes address inconsistencies and provide more detailed instructions for restoring services after an outage. --- docs/guides/gettingStarted/faultRecovery.mdx | 43 ++++---------------- 1 file changed, 9 insertions(+), 34 deletions(-) diff --git a/docs/guides/gettingStarted/faultRecovery.mdx b/docs/guides/gettingStarted/faultRecovery.mdx index fc1fd1f9b8..d21bd81cbf 100644 --- a/docs/guides/gettingStarted/faultRecovery.mdx +++ b/docs/guides/gettingStarted/faultRecovery.mdx @@ -37,13 +37,13 @@ crontab -l ## 二、异常测试范围与执行方法 -### 1. 测试范围(单机微服务) +### 1. 测试范围 - 外部组件:`MongoDB` `Redis` `Kafka` `Etcd` `MinIO` - OpenIMServer 服务:`openim-api` `openim-msggateway` `openim-rpc-*` `openim-msgtransfer` `openim-push` `openim-crontask` - ChatServer 服务:`chat-api` `chat-rpc` `admin-api` `admin-rpc` `bot-api` `bot-rpc` -### 2. 标准执行步骤(每个用例都按此流程) +### 2. 标准执行步骤 1. 记录基线:`mage check`、核心接口探针、关键日志状态。 2. 故障注入:停组件或 kill 单个服务。 @@ -84,7 +84,7 @@ curl -sS -X POST "http://127.0.0.1:10008/application/latest_version" \ -d '{}' ``` -## 三、异常测试方案(影响与恢复矩阵) +## 三、异常测试方案 ### 1. 外部组件故障 @@ -118,7 +118,7 @@ curl -sS -X POST "http://127.0.0.1:10008/application/latest_version" \ | CHAT-04 | `admin-rpc` | `pkill -f admin-rpc` | 管理后台业务失败 | 是 | 同上 | | CHAT-05 | `bot-api`/`bot-rpc` | `pkill -f bot-api` / `pkill -f bot-rpc` | Bot 相关能力失败 | 是(主链路可继续) | 同上 | -## 四、组件异常停止处理(恢复 Runbook) +## 四、组件异常停止处理 ### 1. 通用恢复顺序 @@ -138,13 +138,6 @@ docker compose up -d mongodb redis kafka etcd minio docker ps --format '{{.Names}}\t{{.Status}}' | grep -E 'mongo|redis|kafka|etcd|minio' ``` -### 2.1 实测补充(2026-03-06,`/root/test`) - -1. Chat 相关 API 探针必须带 `operationID` 请求头,否则会返回参数错误。 -2. Redis 恢复后,OpenIM 不一定自动恢复,需要执行 OpenIM 全量重启。 -3. Etcd 宕机时,已运行实例可短时可用;但在服务重启阶段会失败,需先恢复 Etcd。 -4. Chat 单服务拉起(如 `mage start chat-api`、`mage start admin-api`)在异常场景下可能导致其他 Chat 进程缺失,故障恢复优先采用 Chat 全量重启。 - ### 3. OpenIMServer 恢复 ```bash @@ -165,7 +158,7 @@ mage start mage check ``` -### 5. MongoDB 数据恢复(需要时) +### 5. MongoDB 数据恢复 ```bash docker exec -it mongo mongorestore \ @@ -179,25 +172,7 @@ docker exec -it mongo mongorestore \ 2. 若源数据盘与备份盘同时损坏,无法直接恢复,只能依赖云厂商快照或其他离线备份。 3. MongoDB 恢复到历史备份点后,备份时间点之后的数据会丢失。 4. 删除 Redis 数据可能导致未读数、会话状态等短期不一致。 - -## 六、完整异常测试报告模板 - -每个用例必须记录以下字段。 - -| 字段 | 内容 | -| --- | --- | -| 用例ID | 例如 `EXT-01` | -| 测试日期 | YYYY-MM-DD HH:mm | -| 测试环境 | 服务器规格、版本、部署方式 | -| 故障注入对象 | 组件或服务名 | -| 注入命令 | 实际执行命令 | -| 注入开始时间 | 时间戳 | -| 业务影响现象 | 用户可见现象与接口报错 | -| 影响范围 | OpenIMClientSDK、APP 业务服务器、APP 管理员后台 | -| 恢复动作 | 实际执行步骤 | -| 恢复完成时间 | 时间戳 | -| MTTR | 恢复耗时 | -| 数据影响 | 是否丢消息/丢数据/状态不一致 | -| 验收结果 | 通过/不通过 | -| 根因分析 | 简述根因 | -| 改进项 | 监控、告警、配置、流程优化 | +5. Chat 相关 API 探针必须带 `operationID` 请求头,否则会返回参数错误。 +6. Redis 恢复后,OpenIM 不一定自动恢复,可能需要执行 OpenIM 全量重启(`mage stop && mage start`)。 +7. Etcd 宕机时,已运行实例可短时可用;但在服务重启阶段可能失败,需先恢复 Etcd。 +8. Chat 单服务拉起(如 `mage start chat-api`、`mage start admin-api`)在异常场景下可能导致其他 Chat 进程缺失,故障恢复优先采用 Chat 全量重启。 From 940957256139a11ed491925377cb6b21ed3b3d3e Mon Sep 17 00:00:00 2001 From: dsx137 <70027572+dsx137@users.noreply.github.com> Date: Fri, 6 Mar 2026 18:40:23 +0800 Subject: [PATCH 3/9] refactor(docs): refine deployment and fault recovery docs --- docs/guides/gettingStarted/cluster.md | 5 + docs/guides/gettingStarted/dockerCompose.md | 8 ++ docs/guides/gettingStarted/faq.md | 30 +++--- docs/guides/gettingStarted/faultRecovery.mdx | 58 ++++++------ .../gettingStarted/imSourceCodeDeployment.md | 30 ++++-- .../gettingStarted/internalDeployment.md | 92 ++++++++----------- 6 files changed, 125 insertions(+), 98 deletions(-) diff --git a/docs/guides/gettingStarted/cluster.md b/docs/guides/gettingStarted/cluster.md index 4707cdf798..f18a201484 100644 --- a/docs/guides/gettingStarted/cluster.md +++ b/docs/guides/gettingStarted/cluster.md @@ -45,8 +45,13 @@ A 和 B 两台机器以及组件集群内网互通,且A、B两台机器都有 ```bash git clone https://github.com/openimsdk/open-im-server cd open-im-server +git fetch --tags +LATEST_TAG=$(git tag --sort=-v:refname | head -n 1) +git checkout "$LATEST_TAG" ``` +> 建议两台机器使用同一个 tag(默认 latest tag)。如需固定版本(例如 `v3.8.3-patch.12`),请在两台机器都执行 `git checkout v3.8.3-patch.12`。 + ### 2. 配置修改 在机器 A 和 B 上,按照以下步骤修改配置文件,确保各组件正确连接。所有地址字段均采用单行列表格式 `address: [addr1, addr2, addr3]`。 diff --git a/docs/guides/gettingStarted/dockerCompose.md b/docs/guides/gettingStarted/dockerCompose.md index e9681b92fd..0aab36c53c 100644 --- a/docs/guides/gettingStarted/dockerCompose.md +++ b/docs/guides/gettingStarted/dockerCompose.md @@ -9,10 +9,18 @@ sidebar_position: 2 ## 2. 部署 OpenIMServer ### 2.1 仓库克隆 🗂️ +建议使用 **latest tag**,避免直接跟随未发布变更。 + ```bash git clone https://github.com/openimsdk/openim-docker && cd openim-docker +git fetch --tags +LATEST_TAG=$(git tag --sort=-v:refname | head -n 1) +git checkout "$LATEST_TAG" +echo "using openim-docker tag: $LATEST_TAG" ``` +> 如需复现指定版本(例如 `v3.8.3-patch.12`),将 `git checkout "$LATEST_TAG"` 替换为 `git checkout v3.8.3-patch.12`。 + ### 2.2 配置修改 🔧 - 修改 `.env` 文件,配置 MinIO 外网 IP,以支持发送图片和文件,其中 `your-server-ip` 为服务端外网 IP。 diff --git a/docs/guides/gettingStarted/faq.md b/docs/guides/gettingStarted/faq.md index 45c1dbb0c3..9670651854 100644 --- a/docs/guides/gettingStarted/faq.md +++ b/docs/guides/gettingStarted/faq.md @@ -5,7 +5,7 @@ sidebar_position: 10 ## 一、如何升级 -在同一大版本内,不同小版本之间的数据是兼容的。例如,版本 **3.8.0** 的数据可以在升级到 **3.8.2** 后正常运行。本文将重点介绍这种升级情况的具体操作步骤。 +在同一大版本内,不同小版本之间的数据通常兼容。建议优先升级到目标仓库的 **latest tag**;如需固定版本(例如 `v3.8.3-patch.12`),请显式 checkout 对应 tag。 ### Docker 部署 @@ -14,22 +14,24 @@ sidebar_position: 10 cd openim-docker ``` -2. **编辑 `.env` 文件,修改相应的镜像标签(tag)。例如,将:** - ```env - OPENIM_SERVER_IMAGE=openim/openim-server:release-v3.8.0 - ``` - **修改为:** - ```env - OPENIM_SERVER_IMAGE=openim/openim-server:release-v3.8.2 +2. **拉取最新 tag 并切换:** + ```bash + git fetch --tags + TARGET_TAG=$(git tag --sort=-v:refname | head -n 1) + git checkout "$TARGET_TAG" + echo "upgrade openim-docker to tag: $TARGET_TAG" ``` -3. **停止现有的 Docker 服务:** +3. **检查 `.env` 中镜像 tag 与当前仓库版本一致(必要时按发布说明手动调整)。** + +4. **停止现有的 Docker 服务:** ```bash docker compose down ``` -4. **启动更新后的 Docker 服务:** +5. **启动更新后的 Docker 服务:** ```bash + docker compose pull docker compose up -d ``` @@ -46,9 +48,11 @@ sidebar_position: 10 mage stop ``` -3. **切换分支并更新代码:** +3. **切换到最新 tag(或指定 tag)并更新代码:** ```bash - git pull + git fetch --tags + TARGET_TAG=$(git tag --sort=-v:refname | head -n 1) + git checkout "$TARGET_TAG" ``` 4. **编译并启动服务:** @@ -57,6 +61,8 @@ sidebar_position: 10 mage start ``` +5. **如果部署了 ChatServer,也建议同步升级到对应 tag 后再重启 Chat 服务。** + --- ## 二、 如何迁移数据 diff --git a/docs/guides/gettingStarted/faultRecovery.mdx b/docs/guides/gettingStarted/faultRecovery.mdx index d21bd81cbf..eb93d9241d 100644 --- a/docs/guides/gettingStarted/faultRecovery.mdx +++ b/docs/guides/gettingStarted/faultRecovery.mdx @@ -50,9 +50,12 @@ crontab -l 3. 影响观察:记录“完全不可用 / 部分可用 / 可用但退化”。 4. 执行恢复:重启组件或服务,必要时恢复数据。 5. 验收复测:探针恢复、链路恢复、日志无持续报错。 +6. 分时复测:恢复后建议在 `0s / 3s / 10s / 30s` 各复测一次;涉及 Etcd 的场景建议延长到 `60s+` 观察服务注册回稳。 ### 3. 测试前基线命令 +源码部署执行 `mage` 相关命令前,请先确保安装 `Go` 与 `mage`。 + ```bash # OpenIMServer cd /path/to/open-im-server @@ -88,35 +91,37 @@ curl -sS -X POST "http://127.0.0.1:10008/application/latest_version" \ ### 1. 外部组件故障 -| 用例ID | 注入对象 | 注入方式(示例) | 主要影响 | 是否可部分服务 | 修复动作 | -| --- | --- | --- | --- | --- | --- | -| EXT-01 | MongoDB | `docker stop mongo` | 注册/登录、历史消息与业务数据读写异常 | 否(核心数据链路受阻) | `docker start mongo`,必要时执行备份恢复 | -| EXT-02 | Redis | `docker stop redis` | OpenIM 鉴权链路报 `auth-rpc-service down` | 是(部分接口仍可返回) | `docker start redis` 后通常还需执行 OpenIM `mage stop && mage start` | -| EXT-03 | Kafka | `docker stop kafka` | 消息转发与推送链路受阻,可能积压 | 是(非消息链路可部分可用) | `docker start kafka`,核查 topic 消费恢复 | -| EXT-04 | Etcd | `docker stop etcd` | 运行中实例短时可用;`mage start` 阶段会卡在组件检查/服务注册 | 是(短时) | 先 `docker start etcd`,再执行 OpenIM/Chat 全量重启与复检 | -| EXT-05 | MinIO | `docker stop minio` | 图片/文件上传下载失败 | 是(文本消息通常可用) | `docker start minio`,检查 `externalAddress` | +| 注入对象 | 注入方式(示例) | 主要影响 | 是否可部分服务 | 修复动作 | +| --- | --- | --- | --- | --- | +| MongoDB | `docker stop mongo` | 注册/登录、历史消息与业务数据读写异常 | 是(鉴权可用,业务数据链路受阻) | `docker start mongo`,必要时执行备份恢复 | +| Redis | `docker stop redis` | OpenIM 鉴权链路报 `auth-rpc-service down` | 是(部分接口仍可返回) | `docker start redis` 后先观察 `30-60s`;若鉴权仍异常再执行 OpenIM `mage stop && mage start` | +| Kafka | `docker stop kafka` | 消息转发与推送链路受阻,可能积压 | 是(非消息链路可部分可用) | `docker start kafka`,核查 topic 消费恢复 | +| Etcd | `docker stop etcd` | 运行中实例短时可用;`mage start` 阶段会卡在组件检查/服务注册 | 是(短时) | 先 `docker start etcd` 并等待服务注册回稳(建议 `5-10s`),再执行 OpenIM/Chat 全量重启与复检 | +| MinIO | `docker stop minio` | 图片/文件上传下载失败 | 是(文本消息通常可用) | `docker start minio`,检查 `externalAddress` | ### 2. OpenIMServer 单服务故障 -| 用例ID | 注入对象 | 注入方式(示例) | 主要影响 | 是否可部分服务 | 修复动作 | -| --- | --- | --- | --- | --- | --- | -| IM-01 | `openim-api` | `pkill -f openim-api` | REST API 不可用,Chat 对 OpenIM 调用失败 | 否 | 实测单独 `mage start openim-api` 不足以恢复完整链路,建议 `mage stop && mage start` | -| IM-02 | `openim-msggateway` | `pkill -f openim-msggateway` | WS 连接断开,实时消息中断 | 否(实时链路) | 同上 | -| IM-03 | `openim-rpc-auth` | `pkill -f openim-rpc-auth` | token 相关能力异常 | 是(部分不依赖鉴权链路) | 同上 | -| IM-04 | `openim-rpc-msg` | `pkill -f openim-rpc-msg` | 消息发送/拉取异常 | 是(非消息接口可用) | 同上 | -| IM-05 | `openim-msgtransfer` | `pkill -f openim-msgtransfer` | 消息转发链路中断或高延迟 | 是(非消息接口可用) | 同上 | -| IM-06 | `openim-push` | `pkill -f openim-push` | 离线推送失败 | 是(在线消息通常可用) | 同上 | -| IM-07 | `openim-crontask` | `pkill -f openim-crontask` | 定时任务不执行 | 是(实时链路通常可用) | 同上 | +| 注入对象 | 注入方式(示例) | 主要影响 | 是否可部分服务 | 修复动作 | +| --- | --- | --- | --- | --- | +| `openim-api` | `pkill -f openim-api` | REST API 不可用,Chat 对 OpenIM 调用失败 | 否 | 实测单独 `mage start openim-api` 不足以恢复完整链路,建议 `mage stop && mage start` | +| `openim-msggateway` | `pkill -f openim-msggateway` | WS 连接断开,实时消息中断 | 否(实时链路) | 同上 | +| `openim-rpc-auth` | `pkill -f openim-rpc-auth` | token 相关能力异常 | 是(部分不依赖鉴权链路) | 同上 | +| `openim-rpc-msg` | `pkill -f openim-rpc-msg` | 消息发送/拉取异常 | 是(非消息接口可用) | 同上 | +| `openim-msgtransfer` | `pkill -f openim-msgtransfer` | 消息转发链路中断或高延迟 | 是(非消息接口可用) | 同上 | +| `openim-push` | `pkill -f openim-push` | 离线推送失败 | 是(在线消息通常可用) | 同上 | +| `openim-crontask` | `pkill -f openim-crontask` | 定时任务不执行 | 是(实时链路通常可用) | 同上 | ### 3. ChatServer 单服务故障 -| 用例ID | 注入对象 | 注入方式(示例) | 主要影响 | 是否可部分服务 | 修复动作 | -| --- | --- | --- | --- | --- | --- | -| CHAT-01 | `chat-api` | `pkill -f chat-api` | 业务系统接口失败(10008) | 否(Chat 业务入口受阻) | 实测单独 `mage start chat-api` 可能连带影响其他 Chat 服务,建议 `mage stop && mage start` | -| CHAT-02 | `chat-rpc` | `pkill -f chat-rpc` | Chat 核心业务接口异常 | 否(Chat 核心链路) | 同上 | -| CHAT-03 | `admin-api` | `pkill -f admin-api` | 管理后台接口失败(10009),10008 可继续提供服务 | 是(用户侧链路可继续) | 实测单独 `mage start admin-api` 可能出现 `admin-rpc` 不可用,建议 `mage stop && mage start` | -| CHAT-04 | `admin-rpc` | `pkill -f admin-rpc` | 管理后台业务失败 | 是 | 同上 | -| CHAT-05 | `bot-api`/`bot-rpc` | `pkill -f bot-api` / `pkill -f bot-rpc` | Bot 相关能力失败 | 是(主链路可继续) | 同上 | +| 注入对象 | 注入方式(示例) | 主要影响 | 是否可部分服务 | 修复动作 | +| --- | --- | --- | --- | --- | +| `chat-api` | `pkill -f chat-api` | 业务系统接口失败(10008) | 否(Chat 业务入口受阻) | 实测单独 `mage start chat-api` 可能连带影响其他 Chat 服务,建议 `mage stop && mage start` | +| `chat-rpc` | `pkill -f chat-rpc` | Chat 核心业务接口异常 | 否(Chat 核心链路) | 同上 | +| `admin-api` | `pkill -f admin-api` | 管理后台接口失败(10009),10008 可继续提供服务 | 是(用户侧链路可继续) | 实测单独 `mage start admin-api` 可能出现 `admin-rpc` 不可用,建议 `mage stop && mage start` | +| `admin-rpc` | `pkill -f admin-rpc` | 管理后台业务失败 | 是 | 同上 | +| `bot-api`/`bot-rpc` | `pkill -f bot-api` / `pkill -f bot-rpc` | Bot 相关能力失败 | 是(主链路可继续) | 同上 | + +> 说明:上表“单服务拉起风险”主要针对 `mage start ` 场景。`mage` 在单服务启动时会先停止现有服务并执行全量状态校验,异常恢复期可能出现联动影响;若直接拉起对应二进制且依赖健康,单服务通常可恢复。 ## 四、组件异常停止处理 @@ -172,7 +177,8 @@ docker exec -it mongo mongorestore \ 2. 若源数据盘与备份盘同时损坏,无法直接恢复,只能依赖云厂商快照或其他离线备份。 3. MongoDB 恢复到历史备份点后,备份时间点之后的数据会丢失。 4. 删除 Redis 数据可能导致未读数、会话状态等短期不一致。 -5. Chat 相关 API 探针必须带 `operationID` 请求头,否则会返回参数错误。 -6. Redis 恢复后,OpenIM 不一定自动恢复,可能需要执行 OpenIM 全量重启(`mage stop && mage start`)。 -7. Etcd 宕机时,已运行实例可短时可用;但在服务重启阶段可能失败,需先恢复 Etcd。 +5. OpenIM/Chat 相关 API 探针必须带 `operationID` 请求头,否则会返回参数错误。 +6. Redis 恢复后多数场景可自动恢复;若鉴权仍异常,再执行 OpenIM 全量重启(`mage stop && mage start`)。 +7. Etcd 宕机时,已运行实例可短时可用;但在服务重启阶段可能失败,且恢复后可能存在短暂注册延迟,需先恢复 Etcd 并观察。 8. Chat 单服务拉起(如 `mage start chat-api`、`mage start admin-api`)在异常场景下可能导致其他 Chat 进程缺失,故障恢复优先采用 Chat 全量重启。 +9. Kafka/MinIO 场景应补充消息链路、文件链路闭环探针,避免仅依据基础探针判断恢复完成。 diff --git a/docs/guides/gettingStarted/imSourceCodeDeployment.md b/docs/guides/gettingStarted/imSourceCodeDeployment.md index ede7106bec..a0ef350141 100644 --- a/docs/guides/gettingStarted/imSourceCodeDeployment.md +++ b/docs/guides/gettingStarted/imSourceCodeDeployment.md @@ -13,7 +13,17 @@ OpenIMServer 与外部组件部署在同一台机器上,部分组件可按需 ## 二、获取 OpenIMServer 并部署依赖组件 -从开源仓库获取 OpenIMServer 源码(或下载对应 tag 源码包),解压后进入项目根目录。 +建议通过仓库拉取并切到 **latest tag**(而不是直接跟随分支)。 + +```bash +git clone https://github.com/openimsdk/open-im-server && cd open-im-server +git fetch --tags +LATEST_TAG=$(git tag --sort=-v:refname | head -n 1) +git checkout "$LATEST_TAG" +echo "using open-im-server tag: $LATEST_TAG" +``` + +> 如需复现指定版本(例如 `v3.8.3-patch.12`),将 `git checkout "$LATEST_TAG"` 替换为 `git checkout v3.8.3-patch.12`。 > 注意:后续所有命令都在 OpenIMServer 项目根目录执行。 @@ -97,11 +107,17 @@ mage > 如果已有自有账号系统,可不部署 ChatServer。 -从开源仓库下载稳定版本源码: +同样建议拉取后切到 **latest tag**: -- `https://github.com/openimsdk/chat/tags` +```bash +git clone https://github.com/openimsdk/chat && cd chat +git fetch --tags +LATEST_TAG=$(git tag --sort=-v:refname | head -n 1) +git checkout "$LATEST_TAG" +echo "using chat tag: $LATEST_TAG" +``` -解压后进入 ChatServer 项目根目录。 +> 如需复现指定版本(例如 `v3.8.3-patch.12` 对应联调版本),按实际发布说明改为固定 tag。 ## 五、部署 ChatServer @@ -133,10 +149,8 @@ mage ## 六、配置文件说明 -- OpenIMServer 配置文件说明: - `https://github.com/openimsdk/open-im-server/blob/main/config/README_zh_CN.md` -- ChatServer 配置文件说明: - `https://github.com/openimsdk/chat/blob/main/config/README_zh_CN.md` +- OpenIMServer 配置说明请以当前检出代码的 `config/README_zh_CN.md` 为准。 +- ChatServer 配置说明请以当前检出代码的 `config/README_zh_CN.md` 为准。 ## 七、关于离线推送 diff --git a/docs/guides/gettingStarted/internalDeployment.md b/docs/guides/gettingStarted/internalDeployment.md index b94b0dc65d..dd3c234084 100644 --- a/docs/guides/gettingStarted/internalDeployment.md +++ b/docs/guides/gettingStarted/internalDeployment.md @@ -6,69 +6,50 @@ sidebar_position: 4 本指南将指导您在一台纯内网机器上部署 OpenIMSDK 相关服务。 -### **Docker部署** +## 版本策略(重要) -1. 使用一台连接到互联网的机器,克隆仓库: +- 默认建议:在联网机器上拉取仓库后,切换到 **latest tag** 再做离线打包。 +- 如需固定版本(例如 `v3.8.3-patch.12`):将 `git checkout "$LATEST_TAG"` 替换为 `git checkout v3.8.3-patch.12`。 +- 强烈建议:OpenIMServer、ChatServer、openim-docker 使用同一批次发布版本,避免跨版本组合。 - ```sh - git clone https://github.com/openimsdk/openim-docker - ``` - -2. 运行`docker compose up -d`以拉取镜像。 +### **Docker部署** -3. 保存相应的镜像。命令如下: +1. 使用一台连接到互联网的机器,克隆仓库并切换到 latest tag: ```sh - docker save -o image-name.tar image-name:tag + git clone https://github.com/openimsdk/openim-docker && cd openim-docker + git fetch --tags + LATEST_TAG=$(git tag --sort=-v:refname | head -n 1) + git checkout "$LATEST_TAG" + echo "using openim-docker tag: $LATEST_TAG" ``` - 例如,需要保存`openim-server`镜像,命令应为: +2. 运行 `docker compose up -d` 拉取镜像并生成本地镜像清单。 + +3. 导出当前 compose 实际使用镜像(避免手工维护版本号): ```sh - docker save -o openim-server.tar openim/openim-server:release-v3.8.1 + docker compose config --images | sort -u > images.txt ``` - 保存`mongo`镜像,命令应为: +4. 批量保存镜像: ```sh - docker save -o mongo.tar mongo:7.0 + while read -r image; do + docker save -o "$(echo "$image" | tr '/:' '_').tar" "$image" + done < images.txt ``` - 可以使用`docker images`查看拉取的镜像信息,或者在`.env`文件中确认镜像的版本信息。 - - 所有需要保存的镜像为: - - - `mongo:7.0` - - `redis:7.0.0` - - `bitnami/kafka:3.5.1` - - `minio/minio:RELEASE.2024-01-11T07-46-16Z` - - `quay.io/coreos/etcd:v3.5.13` - - `openim/openim-web-front:release-v3.8.1` - - `openim/openim-admin-front:release-v1.8.3` - - `openim/openim-server:release-v3.8.2` - - `openim/openim-chat:v1.8.2` - - 以下为监控告警组件镜像,可根据需求选择性部署: - - - `prom/prometheus:v2.51.2` - - `prom/alertmanager:v0.27.0` - - `grafana/grafana:11.0.1` - - `prom/node-exporter:v1.7.0` +5. 通过内网或物理介质将**镜像文件**和**openim-docker 仓库文件**拷贝到部署机器。 -4. 通过内网或者物理介质将**镜像文件**和**docker仓库文件**拷贝到部署机器上。 +6. 在部署机器导入镜像: -5. 导入镜像到`docker`中,命令为: ```bash docker load -i image-name.tar ``` - 例如`openim-server`镜像导入命令为: +7. 在仓库目录下运行: - ```sh - docker load -i openim-server.tar - ``` - -6. 在仓库目录下运行: ```sh docker compose up -d ``` @@ -80,18 +61,27 @@ sidebar_position: 4 ### **源码部署** -1. 使用一台连接到互联网的机器,克隆 OpenIMServer 仓库,建议切换到 `release-v3.8.2` 分支: +1. 使用一台连接到互联网的机器,克隆 OpenIMServer 并切换到 latest tag: ```sh - git clone https://github.com/openimsdk/open-im-server + git clone https://github.com/openimsdk/open-im-server && cd open-im-server + git fetch --tags + LATEST_TAG=$(git tag --sort=-v:refname | head -n 1) + git checkout "$LATEST_TAG" + echo "using open-im-server tag: $LATEST_TAG" ``` -2. 克隆 ChatServer 仓库,建议切换到 `release-v1.8.3` 分支 +2. 克隆 ChatServer 并切换到 latest tag: + ```bash - git clone https://github.com/openimsdk/chat + git clone https://github.com/openimsdk/chat && cd chat + git fetch --tags + LATEST_TAG=$(git tag --sort=-v:refname | head -n 1) + git checkout "$LATEST_TAG" + echo "using chat tag: $LATEST_TAG" ``` -3. 参考[docker部署](#docker部署)步骤,保存镜像,区别为不需要`openim/openim-server:release-v3.8.2`和`openim/openim-chat:v1.8.2`。 +3. 参考 [docker部署](#docker部署) 步骤保存依赖组件镜像(源码部署场景不需要服务端业务镜像)。 4. 通过内网或者物理介质将**镜像文件**、**OpenIMServer 仓库文件**、**ChatServer 仓库文件**拷贝到部署机器上。 @@ -115,9 +105,7 @@ sidebar_position: 4 ``` 7. 在 ChatServer 目录下运行: - ```bash - mage - mage start - ``` - - + ```bash + mage + mage start + ``` From a362d0658e3704f110f67fd1aadb5842dec3a9a9 Mon Sep 17 00:00:00 2001 From: dsx137 <70027572+dsx137@users.noreply.github.com> Date: Tue, 10 Mar 2026 09:48:34 +0800 Subject: [PATCH 4/9] refactor: replace latest tag with stable release tag Update documentation and config to enforce stable release usage instead of luminous 'latest' tags, clarifying terminology and reducing instability risks. --- docs/guides/gettingStarted/cluster.md | 8 ++--- docs/guides/gettingStarted/dockerCompose.md | 10 +++---- docs/guides/gettingStarted/faq.md | 14 +++++---- .../gettingStarted/imSourceCodeDeployment.md | 20 ++++++------- .../gettingStarted/internalDeployment.md | 29 ++++++++++--------- 5 files changed, 42 insertions(+), 39 deletions(-) diff --git a/docs/guides/gettingStarted/cluster.md b/docs/guides/gettingStarted/cluster.md index f18a201484..52c8843503 100644 --- a/docs/guides/gettingStarted/cluster.md +++ b/docs/guides/gettingStarted/cluster.md @@ -40,17 +40,17 @@ A 和 B 两台机器以及组件集群内网互通,且A、B两台机器都有 ### 1. 克隆仓库 -在两台机器(A 和 B)上分别执行以下命令以克隆 OpenIMServer 仓库: +在两台机器(A 和 B)上分别执行以下命令以克隆 OpenIMServer 仓库,并切到 GitHub Releases 页面绿色 **Latest** 对应的**最新正式发布 tag**: ```bash git clone https://github.com/openimsdk/open-im-server cd open-im-server git fetch --tags -LATEST_TAG=$(git tag --sort=-v:refname | head -n 1) -git checkout "$LATEST_TAG" +LATEST_STABLE_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/open-im-server/releases/latest)") +git checkout "$LATEST_STABLE_TAG" ``` -> 建议两台机器使用同一个 tag(默认 latest tag)。如需固定版本(例如 `v3.8.3-patch.12`),请在两台机器都执行 `git checkout v3.8.3-patch.12`。 +> 这里的 latest 指 GitHub Releases 页面绿色 Latest 的**正式发布版**,不包含 alpha/beta/rc 等预发布版本。建议两台机器使用同一个正式版 tag;如需固定版本(例如 `v3.8.3-patch.12`),请在两台机器都执行 `git checkout v3.8.3-patch.12`。 ### 2. 配置修改 diff --git a/docs/guides/gettingStarted/dockerCompose.md b/docs/guides/gettingStarted/dockerCompose.md index 0aab36c53c..9cd06a027f 100644 --- a/docs/guides/gettingStarted/dockerCompose.md +++ b/docs/guides/gettingStarted/dockerCompose.md @@ -9,17 +9,17 @@ sidebar_position: 2 ## 2. 部署 OpenIMServer ### 2.1 仓库克隆 🗂️ -建议使用 **latest tag**,避免直接跟随未发布变更。 +建议使用 GitHub Releases 页面绿色 **Latest** 对应的**最新正式发布 tag**,不要直接按 tag 名字排序,也不要使用 alpha/rc 等预发布版本。 ```bash git clone https://github.com/openimsdk/openim-docker && cd openim-docker git fetch --tags -LATEST_TAG=$(git tag --sort=-v:refname | head -n 1) -git checkout "$LATEST_TAG" -echo "using openim-docker tag: $LATEST_TAG" +LATEST_STABLE_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/openim-docker/releases/latest)") +git checkout "$LATEST_STABLE_TAG" +echo "using openim-docker stable release tag: $LATEST_STABLE_TAG" ``` -> 如需复现指定版本(例如 `v3.8.3-patch.12`),将 `git checkout "$LATEST_TAG"` 替换为 `git checkout v3.8.3-patch.12`。 +> 这里的 latest 指 GitHub Releases 页面绿色 Latest 的**正式发布版**,不包含 alpha/beta/rc 等预发布版本。 ### 2.2 配置修改 🔧 diff --git a/docs/guides/gettingStarted/faq.md b/docs/guides/gettingStarted/faq.md index 9670651854..f578323afb 100644 --- a/docs/guides/gettingStarted/faq.md +++ b/docs/guides/gettingStarted/faq.md @@ -5,7 +5,7 @@ sidebar_position: 10 ## 一、如何升级 -在同一大版本内,不同小版本之间的数据通常兼容。建议优先升级到目标仓库的 **latest tag**;如需固定版本(例如 `v3.8.3-patch.12`),请显式 checkout 对应 tag。 +在同一大版本内,不同小版本之间的数据通常兼容。建议优先升级到目标仓库 GitHub Releases 页面绿色 **Latest** 对应的**最新正式发布 tag**;如需固定版本(例如 `v3.8.3-patch.12`),请显式 checkout 对应 tag。 ### Docker 部署 @@ -14,12 +14,12 @@ sidebar_position: 10 cd openim-docker ``` -2. **拉取最新 tag 并切换:** +2. **拉取最新正式版 release tag 并切换:** ```bash git fetch --tags - TARGET_TAG=$(git tag --sort=-v:refname | head -n 1) + TARGET_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/openim-docker/releases/latest)") git checkout "$TARGET_TAG" - echo "upgrade openim-docker to tag: $TARGET_TAG" + echo "upgrade openim-docker to stable release tag: $TARGET_TAG" ``` 3. **检查 `.env` 中镜像 tag 与当前仓库版本一致(必要时按发布说明手动调整)。** @@ -48,10 +48,10 @@ sidebar_position: 10 mage stop ``` -3. **切换到最新 tag(或指定 tag)并更新代码:** +3. **切换到最新正式版 release tag(或指定 tag)并更新代码:** ```bash git fetch --tags - TARGET_TAG=$(git tag --sort=-v:refname | head -n 1) + TARGET_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/open-im-server/releases/latest)") git checkout "$TARGET_TAG" ``` @@ -63,6 +63,8 @@ sidebar_position: 10 5. **如果部署了 ChatServer,也建议同步升级到对应 tag 后再重启 Chat 服务。** +> 说明:这里的 latest 指 GitHub Releases 页面绿色 Latest 的**正式发布版**,不包含 alpha/beta/rc 等预发布版本。 + --- ## 二、 如何迁移数据 diff --git a/docs/guides/gettingStarted/imSourceCodeDeployment.md b/docs/guides/gettingStarted/imSourceCodeDeployment.md index a0ef350141..7c943226dc 100644 --- a/docs/guides/gettingStarted/imSourceCodeDeployment.md +++ b/docs/guides/gettingStarted/imSourceCodeDeployment.md @@ -13,17 +13,17 @@ OpenIMServer 与外部组件部署在同一台机器上,部分组件可按需 ## 二、获取 OpenIMServer 并部署依赖组件 -建议通过仓库拉取并切到 **latest tag**(而不是直接跟随分支)。 +建议通过仓库拉取并切到 GitHub Releases 页面绿色 **Latest** 对应的**最新正式发布 tag**。 ```bash git clone https://github.com/openimsdk/open-im-server && cd open-im-server git fetch --tags -LATEST_TAG=$(git tag --sort=-v:refname | head -n 1) -git checkout "$LATEST_TAG" -echo "using open-im-server tag: $LATEST_TAG" +LATEST_STABLE_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/open-im-server/releases/latest)") +git checkout "$LATEST_STABLE_TAG" +echo "using open-im-server stable release tag: $LATEST_STABLE_TAG" ``` -> 如需复现指定版本(例如 `v3.8.3-patch.12`),将 `git checkout "$LATEST_TAG"` 替换为 `git checkout v3.8.3-patch.12`。 +> 这里的 latest 指 GitHub Releases 页面绿色 Latest 的**正式发布版**,不包含 alpha/beta/rc 等预发布版本。当前该仓库会把 `v3.8.3-patch.12` 识别为最新正式版,而不会误选 `v3.8.4-alpha.1` 这类预发布版本。 > 注意:后续所有命令都在 OpenIMServer 项目根目录执行。 @@ -107,17 +107,17 @@ mage > 如果已有自有账号系统,可不部署 ChatServer。 -同样建议拉取后切到 **latest tag**: +同样建议拉取后切到 GitHub Releases 页面绿色 **Latest** 对应的**最新正式发布 tag**: ```bash git clone https://github.com/openimsdk/chat && cd chat git fetch --tags -LATEST_TAG=$(git tag --sort=-v:refname | head -n 1) -git checkout "$LATEST_TAG" -echo "using chat tag: $LATEST_TAG" +LATEST_STABLE_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/chat/releases/latest)") +git checkout "$LATEST_STABLE_TAG" +echo "using chat stable release tag: $LATEST_STABLE_TAG" ``` -> 如需复现指定版本(例如 `v3.8.3-patch.12` 对应联调版本),按实际发布说明改为固定 tag。 +> 如需复现指定版本,按实际发布说明改为固定 tag;若服务端联调需要固定到 `v3.8.3-patch.12` 对应版本,也请同时固定 ChatServer 的对应正式版 tag。 ## 五、部署 ChatServer diff --git a/docs/guides/gettingStarted/internalDeployment.md b/docs/guides/gettingStarted/internalDeployment.md index dd3c234084..c0cc198e0b 100644 --- a/docs/guides/gettingStarted/internalDeployment.md +++ b/docs/guides/gettingStarted/internalDeployment.md @@ -8,20 +8,21 @@ sidebar_position: 4 ## 版本策略(重要) -- 默认建议:在联网机器上拉取仓库后,切换到 **latest tag** 再做离线打包。 -- 如需固定版本(例如 `v3.8.3-patch.12`):将 `git checkout "$LATEST_TAG"` 替换为 `git checkout v3.8.3-patch.12`。 +- 默认建议:在联网机器上拉取仓库后,切换到 GitHub Releases 页面绿色 **Latest** 对应的**最新正式发布 tag** 再做离线打包。 +- 这里的 latest 指**正式发布版**,不包含 alpha/beta/rc 等预发布版本。 +- 如需固定版本(例如 `v3.8.3-patch.12`):直接执行 `git checkout v3.8.3-patch.12`。 - 强烈建议:OpenIMServer、ChatServer、openim-docker 使用同一批次发布版本,避免跨版本组合。 ### **Docker部署** -1. 使用一台连接到互联网的机器,克隆仓库并切换到 latest tag: +1. 使用一台连接到互联网的机器,克隆仓库并切换到最新正式发布 tag: ```sh git clone https://github.com/openimsdk/openim-docker && cd openim-docker git fetch --tags - LATEST_TAG=$(git tag --sort=-v:refname | head -n 1) - git checkout "$LATEST_TAG" - echo "using openim-docker tag: $LATEST_TAG" + LATEST_STABLE_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/openim-docker/releases/latest)") + git checkout "$LATEST_STABLE_TAG" + echo "using openim-docker stable release tag: $LATEST_STABLE_TAG" ``` 2. 运行 `docker compose up -d` 拉取镜像并生成本地镜像清单。 @@ -61,24 +62,24 @@ sidebar_position: 4 ### **源码部署** -1. 使用一台连接到互联网的机器,克隆 OpenIMServer 并切换到 latest tag: +1. 使用一台连接到互联网的机器,克隆 OpenIMServer 并切换到最新正式发布 tag: ```sh git clone https://github.com/openimsdk/open-im-server && cd open-im-server git fetch --tags - LATEST_TAG=$(git tag --sort=-v:refname | head -n 1) - git checkout "$LATEST_TAG" - echo "using open-im-server tag: $LATEST_TAG" + LATEST_STABLE_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/open-im-server/releases/latest)") + git checkout "$LATEST_STABLE_TAG" + echo "using open-im-server stable release tag: $LATEST_STABLE_TAG" ``` -2. 克隆 ChatServer 并切换到 latest tag: +2. 克隆 ChatServer 并切换到最新正式发布 tag: ```bash git clone https://github.com/openimsdk/chat && cd chat git fetch --tags - LATEST_TAG=$(git tag --sort=-v:refname | head -n 1) - git checkout "$LATEST_TAG" - echo "using chat tag: $LATEST_TAG" + LATEST_STABLE_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/chat/releases/latest)") + git checkout "$LATEST_STABLE_TAG" + echo "using chat stable release tag: $LATEST_STABLE_TAG" ``` 3. 参考 [docker部署](#docker部署) 步骤保存依赖组件镜像(源码部署场景不需要服务端业务镜像)。 From 11e03e5f2161f9ee356400682ee0d09614c52ce4 Mon Sep 17 00:00:00 2001 From: dsx137 <70027572+dsx137@users.noreply.github.com> Date: Tue, 10 Mar 2026 13:34:00 +0800 Subject: [PATCH 5/9] docs(getting-started): refactor and improve getting started guide - Add health check timing and operational warnings across deployment docs - Clarify naming differences between openim-docker and open-im-server - Restructure validation steps and deployment procedures for clarity - Include missing scenarios: non-domain validation, internal deployment - Update compatibility notes (Debian 13) and port configurations - Enhance fault recovery with more detailed scenarios and steps --- docs/guides/gettingStarted/cluster.md | 13 ++++++++- docs/guides/gettingStarted/dockerCompose.md | 7 +++++ docs/guides/gettingStarted/env-comp.md | 2 +- docs/guides/gettingStarted/faq.md | 14 +++++---- docs/guides/gettingStarted/faultRecovery.mdx | 6 ++-- .../gettingStarted/imSourceCodeDeployment.md | 10 ++++++- .../gettingStarted/internalDeployment.md | 8 +++++ .../gettingStarted/nginxDomainConfig.md | 16 +++++----- docs/guides/gettingStarted/ports.md | 4 +++ docs/guides/gettingStarted/quickTestServer.md | 29 +++++++++++++++++++ 10 files changed, 91 insertions(+), 18 deletions(-) diff --git a/docs/guides/gettingStarted/cluster.md b/docs/guides/gettingStarted/cluster.md index 52c8843503..01c22c8a2d 100644 --- a/docs/guides/gettingStarted/cluster.md +++ b/docs/guides/gettingStarted/cluster.md @@ -38,6 +38,8 @@ A 和 B 两台机器以及组件集群内网互通,且A、B两台机器都有 - **Etcd 集群** - **MinIO 服务** +> 本文仅覆盖两台 OpenIMServer 业务节点与 Nginx 的部署,不包含 Redis/MongoDB/Kafka/Etcd 集群本身的搭建过程。若当前只有两台空机器,请先完成这些外部组件集群的部署后再继续本文步骤。 + ### 1. 克隆仓库 在两台机器(A 和 B)上分别执行以下命令以克隆 OpenIMServer 仓库,并切到 GitHub Releases 页面绿色 **Latest** 对应的**最新正式发布 tag**: @@ -130,7 +132,7 @@ http { ssl_certificate /usr/local/nginx/conf/ssl/your_host_bundle.pem; # 替换为您的证书路径 ssl_certificate_key /usr/local/nginx/conf/ssl/your_host.key; # 替换为您的证书密钥路径 - location ^~/api/ { + location ^~ /api/ { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; @@ -177,6 +179,15 @@ $ go env -w GOPROXY=https://goproxy.cn,direct ``` #### 5.1 编译 + +首次在每台机器执行前,建议先运行: + +```bash +bash bootstrap.sh +``` + +该步骤会安装 `mage`。如果你的机器已预装 `mage`,可跳过。 + ```bash mage ``` diff --git a/docs/guides/gettingStarted/dockerCompose.md b/docs/guides/gettingStarted/dockerCompose.md index 9cd06a027f..3ab4aa142e 100644 --- a/docs/guides/gettingStarted/dockerCompose.md +++ b/docs/guides/gettingStarted/dockerCompose.md @@ -39,6 +39,12 @@ echo "using openim-docker stable release tag: $LATEST_STABLE_TAG" docker compose up -d ``` +> 首次执行会拉取较大的镜像,耗时可能较长。启动完成后建议等待 `30-60s`,再执行健康检查或接口验证。 + +> 本文档默认在**干净环境**下启动。如果机器上已经存在同名容器(如 `mongo`、`redis`、`kafka`、`etcd`、`minio`、`openim-server`、`openim-chat`),`docker compose up -d` 会因为 `container_name` 冲突而失败。此时应先停掉并删除同名容器,或改用已存在组件并调整配置。 + +> 如果启动时看到 `ETCD_USERNAME`、`ETCD_PASSWORD`、`KAFKA_USERNAME`、`KAFKA_PASSWORD` 未设置的 warning,而你并未启用这些组件的鉴权,这类提示通常可以忽略。 + - 停止服务: @@ -64,6 +70,7 @@ docker logs -f openim-server ### unhealthy定位 1. 执行 `docker exec -it openim-server mage check` 确认是否超过一分钟; 2. 执行 docker logs -f openim-server 查看日志; +3. 如果 `openim-chat` 在启动初期短暂报 `connect: connection refused`,先等待 `30-60s` 后再复查健康状态;这通常是依赖 `openim-server` 尚未完全就绪导致的启动时序现象。 ### 配置项修改 进入容器修改config目录下的修改配置文件无效! diff --git a/docs/guides/gettingStarted/env-comp.md b/docs/guides/gettingStarted/env-comp.md index e096d7ec16..6000c94ec1 100644 --- a/docs/guides/gettingStarted/env-comp.md +++ b/docs/guides/gettingStarted/env-comp.md @@ -13,7 +13,7 @@ sidebar_position: 1 | 注意事项 | 详细说明 | 补充说明 | | --- | --- | --- | -| 操作系统 | Linux | 官方使用 `ubuntu 22.04` | +| 操作系统 | Linux | 官方使用 `ubuntu 22.04`,实测 `Debian 13` 也可运行 | | 硬件资源 | 8核16G,10M带宽,1T磁盘 | 按 10 万注册用户、10% 日常在线、5 万大群、每秒 600 条消息估算;需有外网 IP | | CPU 架构 | `x86_64` | arm 架构需自行测试 | | Golang | `v1.22.7` 或更高版本 | [安装参考](https://go.dev/learn/) | diff --git a/docs/guides/gettingStarted/faq.md b/docs/guides/gettingStarted/faq.md index f578323afb..fd81ee7585 100644 --- a/docs/guides/gettingStarted/faq.md +++ b/docs/guides/gettingStarted/faq.md @@ -69,7 +69,7 @@ sidebar_position: 10 --- ## 二、 如何迁移数据 -在使用`docker compose up -d`命令启动 OpenIMServer 依赖的各个组件之后,OpenIMServer 根目录下会生成一个`components`文件夹,OpenIMServer 运行后产生的数据(如用户、群聊、消息等)都保存在这个文件夹中。如果需要迁移数据,需要先关闭服务和组件: +在使用 `docker compose up -d` 启动组件后,当前部署仓库根目录下会生成一个 `components` 文件夹(例如 `openim-docker/components` 或 `open-im-server/components`)。运行后产生的数据(如用户、群聊、消息等)都保存在这个目录中。如果需要迁移数据,需要先关闭服务和组件: `docker`部署: @@ -89,7 +89,7 @@ docker compose down # 关闭组件 `docker`部署: ```sh -docker compose up -d # 启动组 +docker compose up -d # 启动组件 ``` 源码部署: @@ -117,7 +117,7 @@ mage stop # 关闭服务 docker compose down # 关闭组件 ``` -然后删除`open-im-server`下的`components`文件夹。 +然后删除当前部署仓库下的 `components` 文件夹。 客户端方面需要重新卸载重装`app`。 @@ -128,7 +128,7 @@ docker compose down # 关闭组件 ``` 源码部署: 修改 config/minio.yml 文件,配置 MinIO 外网 IP,以支持发送图片和文件,其中 `your-server-ip` 为服务端外网 IP -externalAddress="http://your-server-ip:10005" +externalAddress: http://your-server-ip:10005 ``` ``` @@ -142,10 +142,12 @@ MINIO_EXTERNAL_ADDRESS="http://your-server-ip:10005" 如果是是使用`docker`部署的各个组件,可以通过在`docker-compose.yml`文件中限制`mongo`和`kafka`的内存的方式来减小内存的占用。 -`mongo`: +`mongo`(`openim-docker` 中的服务名)或 `mongodb`(`open-im-server` 中的服务名): + +> 如果你使用源码部署,请把下面示例中的 `mongo` 替换为 `mongodb`。 ```yml - mongodb: + mongo: environment: - wiredTigerCacheSizeGB=0.5 # 修改为适当的值,单位GB ``` diff --git a/docs/guides/gettingStarted/faultRecovery.mdx b/docs/guides/gettingStarted/faultRecovery.mdx index eb93d9241d..95bb924dca 100644 --- a/docs/guides/gettingStarted/faultRecovery.mdx +++ b/docs/guides/gettingStarted/faultRecovery.mdx @@ -94,10 +94,10 @@ curl -sS -X POST "http://127.0.0.1:10008/application/latest_version" \ | 注入对象 | 注入方式(示例) | 主要影响 | 是否可部分服务 | 修复动作 | | --- | --- | --- | --- | --- | | MongoDB | `docker stop mongo` | 注册/登录、历史消息与业务数据读写异常 | 是(鉴权可用,业务数据链路受阻) | `docker start mongo`,必要时执行备份恢复 | -| Redis | `docker stop redis` | OpenIM 鉴权链路报 `auth-rpc-service down` | 是(部分接口仍可返回) | `docker start redis` 后先观察 `30-60s`;若鉴权仍异常再执行 OpenIM `mage stop && mage start` | +| Redis | `docker stop redis` | OpenIM 鉴权链路异常;源码部署常见 `auth-rpc-service down`,Docker 一体化部署常见 Redis 连接/解析错误 | 是(部分接口仍可返回) | `docker start redis` 后先观察 `30-60s`;若鉴权仍异常再执行 OpenIM `mage stop && mage start` | | Kafka | `docker stop kafka` | 消息转发与推送链路受阻,可能积压 | 是(非消息链路可部分可用) | `docker start kafka`,核查 topic 消费恢复 | | Etcd | `docker stop etcd` | 运行中实例短时可用;`mage start` 阶段会卡在组件检查/服务注册 | 是(短时) | 先 `docker start etcd` 并等待服务注册回稳(建议 `5-10s`),再执行 OpenIM/Chat 全量重启与复检 | -| MinIO | `docker stop minio` | 图片/文件上传下载失败 | 是(文本消息通常可用) | `docker start minio`,检查 `externalAddress` | +| MinIO | `docker stop minio` | 图片/文件上传下载失败;源码部署下基础探针通常仍可用,Docker 一体化部署下可能连带 `10002/10008/10009` 基础探针异常 | 视部署方式而定 | `docker start minio`,检查 `externalAddress`;若 `30-60s` 后基础探针仍未恢复,再重启 OpenIM/Chat 服务栈 | ### 2. OpenIMServer 单服务故障 @@ -139,6 +139,8 @@ cd /path/to/open-im-server docker compose up -d mongodb redis kafka etcd minio ``` +> 如果你使用的是 `openim-docker` 仓库,对应服务名为 `mongo redis kafka etcd minio`;如果你使用的是 `open-im-server` 仓库,对应服务名为 `mongodb redis kafka etcd minio`。 + ```bash docker ps --format '{{.Names}}\t{{.Status}}' | grep -E 'mongo|redis|kafka|etcd|minio' ``` diff --git a/docs/guides/gettingStarted/imSourceCodeDeployment.md b/docs/guides/gettingStarted/imSourceCodeDeployment.md index 7c943226dc..6a4613a015 100644 --- a/docs/guides/gettingStarted/imSourceCodeDeployment.md +++ b/docs/guides/gettingStarted/imSourceCodeDeployment.md @@ -49,6 +49,8 @@ echo "using open-im-server stable release tag: $LATEST_STABLE_TAG" docker compose up -d ``` +> 当前 `open-im-server/docker-compose.yml` 除外部组件外,还会一并拉起 `openim-web-front`、`openim-admin-front`。如果你当前只想部署依赖组件,可以按需调整 compose 文件后再启动。 + ### 2.2 自行部署组件或使用云服务时的初始化要求 | 存储组件 | 初始化要求 | @@ -60,6 +62,8 @@ docker compose up -d 确保 Go 已正确安装。 +> `bootstrap.sh` 会尝试自动安装 `mage`,但前提是系统里已经有可用的 `go` 命令。因此如果 `go version` 不通过,后续 `bash bootstrap.sh`、`mage` 都无法执行。 + ### 3.1 中国境内建议设置 Go 代理 ```bash @@ -103,6 +107,8 @@ mage | 停止 | `mage stop` | - | | 检测 | `mage check` | - | +> 首次启动后建议等待 `20-30s` 再执行 `mage check` 或接口验证,避免把启动过程中的短暂连接失败误判为最终异常。 + ## 四、获取 Chat > 如果已有自有账号系统,可不部署 ChatServer。 @@ -147,6 +153,8 @@ mage | 停止 | `mage stop` | - | | 检测 | `mage check` | - | +> ChatServer 启动时依赖 OpenIMServer 先可用,建议在 OpenIMServer `mage check` 正常后,再启动 ChatServer,并等待 `20-30s` 再验证 `10008/10009` 接口。 + ## 六、配置文件说明 - OpenIMServer 配置说明请以当前检出代码的 `config/README_zh_CN.md` 为准。 @@ -196,4 +204,4 @@ mage ### 10.3 单机生产环境数据备份及恢复 -请参考:[单机生产环境数据备份及恢复](./faultRecovery) +请参考:[单机生产环境数据备份及恢复](./faultRecovery.mdx) diff --git a/docs/guides/gettingStarted/internalDeployment.md b/docs/guides/gettingStarted/internalDeployment.md index c0cc198e0b..f257c1279a 100644 --- a/docs/guides/gettingStarted/internalDeployment.md +++ b/docs/guides/gettingStarted/internalDeployment.md @@ -84,8 +84,14 @@ sidebar_position: 4 3. 参考 [docker部署](#docker部署) 步骤保存依赖组件镜像(源码部署场景不需要服务端业务镜像)。 + > 注意:`open-im-server/docker-compose.yml` 当前默认还会拉起 `openim-web-front`、`openim-admin-front`。如果部署机上的 compose 文件不做裁剪,则还需要一并准备这两个前端镜像;如果你只想部署依赖组件,请先在联网机器调整 compose 文件后再离线拷贝。 + 4. 通过内网或者物理介质将**镜像文件**、**OpenIMServer 仓库文件**、**ChatServer 仓库文件**拷贝到部署机器上。 + > 纯内网机器如果无法访问 Go 模块源,直接执行 `mage` 可能失败,因为源码仓库默认**不包含 vendor 依赖**。这种场景建议二选一: + > 1. 在联网机器提前编译好 `_output/bin` 后再拷贝到内网机器; + > 2. 在联网机器预先准备 `go` 依赖缓存和 `mage` 可执行文件,再一起拷贝到内网机器。 + 5. 导入镜像到`docker`中,命令为: ```bash @@ -101,12 +107,14 @@ sidebar_position: 4 6. 在 OpenIMServer 目录下依次运行: ```bash docker compose up -d # 如需启用监控组件则为 docker compose --profile m up -d + bash bootstrap.sh # 已预装 mage 时可跳过 mage mage start ``` 7. 在 ChatServer 目录下运行: ```bash + bash bootstrap.sh # 已预装 mage 时可跳过 mage mage start ``` diff --git a/docs/guides/gettingStarted/nginxDomainConfig.md b/docs/guides/gettingStarted/nginxDomainConfig.md index 8907ffa219..ffe3ab10db 100644 --- a/docs/guides/gettingStarted/nginxDomainConfig.md +++ b/docs/guides/gettingStarted/nginxDomainConfig.md @@ -14,6 +14,8 @@ sidebar_position: 7 - 已申请域名和 SSL 证书(示例:`im.yourhost.com`)。 - 服务器已放行 `443` 端口。 +如果系统尚未安装 Nginx,可先通过发行版包管理器安装(例如 Debian/Ubuntu:`apt-get install -y nginx`)。 + ## 2. Nginx 配置模板 > 请替换为你的实际域名、证书路径与服务地址。 @@ -40,12 +42,11 @@ upstream minio_s3 { } server { - listen 443; + listen 443 ssl; server_name im.yourhost.com; - ssl on; - ssl_certificate /usr/local/nginx/conf/ssh/im.yourhost.com_bundle.pem; - ssl_certificate_key /usr/local/nginx/conf/ssh/im.yourhost.com.key; + ssl_certificate /usr/local/nginx/conf/ssl/im.yourhost.com_bundle.pem; + ssl_certificate_key /usr/local/nginx/conf/ssl/im.yourhost.com.key; location /msg_gateway { proxy_http_version 1.1; @@ -56,7 +57,7 @@ server { proxy_pass http://msg_gateway/; } - location ^~/api/ { + location ^~ /api/ { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; @@ -66,7 +67,7 @@ server { proxy_pass http://im_api/; } - location ^~/chat/ { + location ^~ /chat/ { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; @@ -75,7 +76,7 @@ server { proxy_pass http://im_chat_api/; } - location ^~/im-minio-api/ { + location ^~ /im-minio-api/ { proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -103,6 +104,7 @@ https://im.yourhost.com/im-minio-api ## 4. 重载 Nginx ```bash +nginx -t nginx -s reload ``` diff --git a/docs/guides/gettingStarted/ports.md b/docs/guides/gettingStarted/ports.md index 483e90a596..1e91e961d3 100644 --- a/docs/guides/gettingStarted/ports.md +++ b/docs/guides/gettingStarted/ports.md @@ -16,6 +16,8 @@ sidebar_position: 6 | OpenIMServer | TCP:10005 | MinIO 作为对象存储 | 端口放行 | | ChatServer | TCP:10008 | 业务系统,如注册、登录等 | 端口放行 | | ChatServer | TCP:10009 | 管理后台,如统计、封号等 | 端口放行 | +| Web 前端(可选) | TCP:11001 | PC Web 前端,快速验证页面访问时需要放行 | 按需放行 | +| Admin 前端(可选) | TCP:11002 | 管理后台前端页面 | 按需放行 | 在客户端 SDK 中,初始化参数如下: @@ -44,3 +46,5 @@ wsAddr: wss://your_domain.com/msg_gateway ``` > 说明:监控、前端等其他服务端口建议仅内网开放,按实际部署需求控制暴露范围。 + +> 如果你要按 [快速验证](./quickTestServer) 中的浏览器步骤直接访问 `11001`,则对应前端端口必须可达;若不需要前端页面验证,可不对外开放 `11001/11002`。 diff --git a/docs/guides/gettingStarted/quickTestServer.md b/docs/guides/gettingStarted/quickTestServer.md index 6b1f3b3bee..97bbe30ea3 100644 --- a/docs/guides/gettingStarted/quickTestServer.md +++ b/docs/guides/gettingStarted/quickTestServer.md @@ -31,9 +31,15 @@ import Image3 from './assets/pc-web.png'; docker ps | grep -E 'openim-server|openim-chat' ``` +> `docker部署` 场景下,`openim-server` 与 `openim-chat` 在启动初期可能先显示 `health: starting`;建议等待 `20-30s`,确认状态进入 `healthy` 后再继续接口验证。 + 如果是源码部署,可执行: ```bash +# 在 open-im-server 目录执行 +mage check + +# 在 chat 目录执行 mage check ``` @@ -51,11 +57,14 @@ curl -sS -X POST "https://your_domain/api/auth/get_admin_token" \ ```bash curl -sS -X POST "https://your_domain/chat/application/latest_version" \ -H "Content-Type: application/json" \ + -H "operationID: verify-chat" \ -d '{}' ``` > 如果接口返回业务错误但已返回 JSON,通常也表示网关反向代理链路已通。 +> 如果当前仅部署了服务端而未部署前端页面,则 `11001` 的 PC Web 验证不适用;该步骤主要用于 `docker部署` 默认前端镜像已启动,或你已自行部署 Web 前端的场景。 + 如果要验证 WebSocket 网关,可使用任意 ws 客户端测试: ```text @@ -65,3 +74,23 @@ wss://your_domain/msg_gateway > 建议在生产环境统一通过 `443` 端口访问,OpenIMClientSDK 初始化时使用: > - `apiAddr`: `https://your_domain/api` > - `wsAddr`: `wss://your_domain/msg_gateway` + +## 📌 六、非域名场景接口验证 + +如果当前未配置域名和 SSL,可直接通过服务端 IP + 默认端口验证: + +```bash +curl -sS -X POST "http://your_server_ip:10002/auth/get_admin_token" \ + -H "Content-Type: application/json" \ + -H "operationID: verify-openim-local" \ + -d '{"secret":"your_openim_secret","userID":"imAdmin"}' +``` + +```bash +curl -sS -X POST "http://your_server_ip:10008/application/latest_version" \ + -H "Content-Type: application/json" \ + -H "operationID: verify-chat-local" \ + -d '{}' +``` + +> ChatServer 的 POST 接口同样要求 `operationID` 请求头;缺少该请求头会直接返回参数错误。 From 58b48ee83c7168bacd0e8dd63cb3097a236dd954 Mon Sep 17 00:00:00 2001 From: dsx137 <70027572+dsx137@users.noreply.github.com> Date: Tue, 10 Mar 2026 16:08:45 +0800 Subject: [PATCH 6/9] feat(docs): add production deployment guide and reorganize gettingStarted docs Add production.md with single-machine production deployment guide, internalDeployment.md with internal network deployment guide, and env-comp.md with platform requirements. Reorganize gettingStarted docs with clearer structure and improved content. --- docs/guides/gettingStarted/cluster.md | 12 +- docs/guides/gettingStarted/dockerCompose.md | 17 +- docs/guides/gettingStarted/env-comp.md | 36 ++- docs/guides/gettingStarted/faq.md | 33 +-- docs/guides/gettingStarted/faultRecovery.mdx | 186 -------------- .../gettingStarted/imSourceCodeDeployment.md | 47 +++- .../gettingStarted/internalDeployment.md | 232 +++++++++++------- .../gettingStarted/nginxDomainConfig.md | 18 +- docs/guides/gettingStarted/ports.md | 6 +- .../0.png | Bin docs/guides/gettingStarted/production.md | 93 +++++++ docs/guides/gettingStarted/quickTestServer.md | 8 +- 12 files changed, 367 insertions(+), 321 deletions(-) delete mode 100644 docs/guides/gettingStarted/faultRecovery.mdx rename docs/guides/gettingStarted/{faultRecovery.assets => production.assets}/0.png (100%) diff --git a/docs/guides/gettingStarted/cluster.md b/docs/guides/gettingStarted/cluster.md index 01c22c8a2d..7d81716e32 100644 --- a/docs/guides/gettingStarted/cluster.md +++ b/docs/guides/gettingStarted/cluster.md @@ -120,17 +120,17 @@ http { } upstream im_api { - # OpenIMServer API 地址,可根据部署情况指定多个 + # OpenIMServer API addresses; add more upstreams if needed server IP_A:10002; server IP_B:10002; } server { listen 443 ssl; - server_name yourhost.com; # 替换为您的域名 + server_name yourhost.com; # Replace with your domain - ssl_certificate /usr/local/nginx/conf/ssl/your_host_bundle.pem; # 替换为您的证书路径 - ssl_certificate_key /usr/local/nginx/conf/ssl/your_host.key; # 替换为您的证书密钥路径 + ssl_certificate /usr/local/nginx/conf/ssl/your_host_bundle.pem; # Replace with your certificate path + ssl_certificate_key /usr/local/nginx/conf/ssl/your_host.key; # Replace with your private key path location ^~ /api/ { proxy_http_version 1.1; @@ -152,10 +152,10 @@ http { } } - # 可选: HTTP 重定向到 HTTPS + # Optional: redirect HTTP to HTTPS server { listen 80; - server_name yourhost.com; # 替换为您的域名 + server_name yourhost.com; # Replace with your domain return 301 https://$host$request_uri; } diff --git a/docs/guides/gettingStarted/dockerCompose.md b/docs/guides/gettingStarted/dockerCompose.md index 3ab4aa142e..5885aa775a 100644 --- a/docs/guides/gettingStarted/dockerCompose.md +++ b/docs/guides/gettingStarted/dockerCompose.md @@ -19,7 +19,7 @@ git checkout "$LATEST_STABLE_TAG" echo "using openim-docker stable release tag: $LATEST_STABLE_TAG" ``` -> 这里的 latest 指 GitHub Releases 页面绿色 Latest 的**正式发布版**,不包含 alpha/beta/rc 等预发布版本。 +> 这里的 latest 指 GitHub Releases 页面绿色 Latest 的**正式发布版**,不包含 alpha/beta/rc 等预发布版本。`main` 为开发版分支,生产环境不要直接使用 `main`。 ### 2.2 配置修改 🔧 @@ -58,6 +58,21 @@ docker compose down docker logs -f openim-server ``` +### 2.4 监控告警(可选) + +如需同时启动 `Prometheus`、`Alertmanager`、`Grafana`、`node-exporter`,可执行: + +```bash +docker compose --profile m up -d +``` + +默认端口以当前 `.env` 为准,常用值如下: + +- `19090`:Prometheus +- `19093`:Alertmanager +- `13000`:Grafana +- `19100`:node-exporter + ## 3. 快速体验 ⚡ 快速体验 OpenIMSDK 核心能力,并测试 OpenIMServer/ChatServer 部署是否正常,请参考[快速验证](./quickTestServer)。 diff --git a/docs/guides/gettingStarted/env-comp.md b/docs/guides/gettingStarted/env-comp.md index 6000c94ec1..3f2e151a4d 100644 --- a/docs/guides/gettingStarted/env-comp.md +++ b/docs/guides/gettingStarted/env-comp.md @@ -5,11 +5,27 @@ sidebar_position: 1 # 🧩 平台及组件要求 -适用于 OpenIMSDK 服务端生产环境源码部署(单机)。 +适用于 `docs/guides/gettingStarted` 下的 OpenIMServer、ChatServer 部署文档。 --- -## 一、环境要求 +## 一、名词约定 + +- **OpenIMSDK**:项目统称,包含 OpenIMClientSDK 与 OpenIMServer。 +- **OpenIMClientSDK**:客户端 SDK。 +- **OpenIMServer**:IM 基础服务端。 +- **ChatServer**:业务扩展服务端,文档中不再使用 `Chat` 作为独立产品名称。 +- **APP 管理员**:调用管理类接口(如 `10009`)的后台管理角色。 +- **APP 业务服务器**:调用业务扩展接口(如 `10008`)的应用服务端。 + +## 二、版本与分支策略 + +- `main`:开发版分支,用于持续集成未发布变更,不建议直接用于生产环境。 +- `vX.Y.Z...`:稳定版发布版本命名。 +- 生产环境建议优先使用 GitHub Releases 页面绿色 **Latest** 对应的**最新正式发布版**。 +- 如需问题复现、灰度回滚或多环境统一,请固定到明确的稳定版本 tag。 + +## 三、环境要求 | 注意事项 | 详细说明 | 补充说明 | | --- | --- | --- | @@ -20,14 +36,14 @@ sidebar_position: 1 | Docker | `v24.0.5` 或更高版本 | 自带 `compose` 功能 | | Git | `v2.17.1` 或更高版本 | [安装参考](https://git-scm.com/downloads) | -## 二、外部组件要求 +## 四、外部组件要求 -| 存储组件 | 建议版本 | 支持模式 | 支持云服务 | -| --- | --- | --- | --- | -| MongoDB | `v7.0` | 单机、分片集群 | 支持 | -| Redis | `v7.0.0` | 单机、分片集群 | 支持 | -| Etcd | `v3.5.13` | 单机、分片集群 | 不支持 | -| Kafka | `v3.5.1` | 单机、分布式集群 | 支持 | -| MinIO | `RELEASE.2024-01-11T07-46-16Z` | 单机 | S3 兼容存储系统(`cos`、`oss`、`kodo`、`aws`) | +| 组件 | 建议版本 | OpenIMServer 支持模式 | ChatServer 接入方式 | 支持云服务 / 备注 | +| --- | --- | --- | --- | --- | +| MongoDB | `v7.0` | `standalone`、`replicaSet` | `address` 或 `uri` | 支持;如接副本集,建议优先使用 `uri` | +| Redis | `v7.0.0` | `standalone`、`cluster`、`sentinel` | `standalone`、`clusterMode` | 支持;`sentinel` 仅在 OpenIMServer 配置层有显式支持 | +| Etcd | `v3.5.13` | 单机、多节点集群 | 多地址接入 | 不支持云服务 | +| Kafka | `v3.5.1` | 单机、分布式集群 | 不直接依赖 | 支持;需按文档预建 topic | +| MinIO | `RELEASE.2024-01-11T07-46-16Z` | 单机 | 不直接依赖 | 可替换为 S3 兼容存储(`COS`、`OSS`、`Kodo`、`AWS S3`) | --- diff --git a/docs/guides/gettingStarted/faq.md b/docs/guides/gettingStarted/faq.md index fd81ee7585..584acadf4c 100644 --- a/docs/guides/gettingStarted/faq.md +++ b/docs/guides/gettingStarted/faq.md @@ -61,7 +61,7 @@ sidebar_position: 10 mage start ``` -5. **如果部署了 ChatServer,也建议同步升级到对应 tag 后再重启 Chat 服务。** +5. **如果部署了 ChatServer,也建议同步升级到对应 tag 后再重启 ChatServer。** > 说明:这里的 latest 指 GitHub Releases 页面绿色 Latest 的**正式发布版**,不包含 alpha/beta/rc 等预发布版本。 @@ -80,8 +80,8 @@ docker compose down 源码部署: ```sh -mage stop # 关闭服务 -docker compose down # 关闭组件 +mage stop # Stop services +docker compose down # Stop components ``` 然后移动整个文件夹到数据目录,修改.env文件中DATA_DIR的值为新数据目录,再启动服务和组件: @@ -89,14 +89,14 @@ docker compose down # 关闭组件 `docker`部署: ```sh -docker compose up -d # 启动组件 +docker compose up -d # Start components ``` 源码部署: ```sh -docker compose up -d # 启动组件 -mage start # 启动服务 +docker compose up -d # Start components +mage start # Start services ``` --- @@ -113,8 +113,8 @@ docker compose down 源码部署: ```sh -mage stop # 关闭服务 -docker compose down # 关闭组件 +mage stop # Stop services +docker compose down # Stop components ``` 然后删除当前部署仓库下的 `components` 文件夹。 @@ -125,15 +125,16 @@ docker compose down # 关闭组件 ## 四、 发送文本消息正常,但发送图片失败 一般发送图片失败是由于没有配置第三方存储的原因。默认使用的第三方存储为`minio`,需修改相关配置 -``` -源码部署: -修改 config/minio.yml 文件,配置 MinIO 外网 IP,以支持发送图片和文件,其中 `your-server-ip` 为服务端外网 IP + +源码部署:修改 `config/minio.yml`,将 `externalAddress` 改为外网 IP 或域名路径。 + +```yaml externalAddress: http://your-server-ip:10005 ``` -``` -docker部署 -修改 .env 文件,配置 MinIO 外网 IP,以支持发送图片和文件,其中 `your-server-ip` 为服务端外网 IP +Docker 部署:修改 `.env`,将 `MINIO_EXTERNAL_ADDRESS` 改为外网 IP 或域名路径。 + +```dotenv MINIO_EXTERNAL_ADDRESS="http://your-server-ip:10005" ``` --- @@ -149,7 +150,7 @@ MINIO_EXTERNAL_ADDRESS="http://your-server-ip:10005" ```yml mongo: environment: - - wiredTigerCacheSizeGB=0.5 # 修改为适当的值,单位GB + - wiredTigerCacheSizeGB=0.5 # Adjust to an appropriate value, unit: GB ``` `kafka`: @@ -157,7 +158,7 @@ MINIO_EXTERNAL_ADDRESS="http://your-server-ip:10005" ```yml kafka: environment: - KAFKA_HEAP_OPTS: "-Xms256m -Xmx256m" # 添加该限制 + KAFKA_HEAP_OPTS: "-Xms256m -Xmx256m" # Add this memory limit ``` --- diff --git a/docs/guides/gettingStarted/faultRecovery.mdx b/docs/guides/gettingStarted/faultRecovery.mdx deleted file mode 100644 index 95bb924dca..0000000000 --- a/docs/guides/gettingStarted/faultRecovery.mdx +++ /dev/null @@ -1,186 +0,0 @@ ---- -title: '异常处理及数据恢复' -sidebar_position: 8 -slug: /gettingStarted/production ---- - -# 异常处理及数据恢复 - -在生产环境中,通常会采用集群部署来保证组件和服务的高可用性。然而,在资源有限的情况下,也可能采用单机部署(源码部署或 Docker 部署)。 - -## 一、mongo 定时数据备份 - -OpenIMServer 核心数据存储在 MongoDB 中,因此备份 MongoDB 数据可以恢复大部分业务数据。 - -### 1. 修改备份目录 - -- 在 `.env` 中修改 `MONGO_BACKUP_DIR`(默认 `components/backup/mongo/`)。 -- 建议将备份目录放在与 `components` 不同的磁盘,避免同盘故障导致源数据与备份同时损坏。 - -### 2. 配置定时备份 - -```bash -crontab -e -``` - -添加示例任务(每天 2 点备份,保留最近 2 份): - -```bash -0 2 * * * docker exec mongo mongodump --uri="mongodb://openIM:openIM123@localhost:27017/openim_v3" --out="/data/backup/$(expr $(date +\%s) / 86400 \% 2)" -``` - -检查是否生效: - -```bash -crontab -l -``` - -## 二、异常测试范围与执行方法 - -### 1. 测试范围 - -- 外部组件:`MongoDB` `Redis` `Kafka` `Etcd` `MinIO` -- OpenIMServer 服务:`openim-api` `openim-msggateway` `openim-rpc-*` `openim-msgtransfer` `openim-push` `openim-crontask` -- ChatServer 服务:`chat-api` `chat-rpc` `admin-api` `admin-rpc` `bot-api` `bot-rpc` - -### 2. 标准执行步骤 - -1. 记录基线:`mage check`、核心接口探针、关键日志状态。 -2. 故障注入:停组件或 kill 单个服务。 -3. 影响观察:记录“完全不可用 / 部分可用 / 可用但退化”。 -4. 执行恢复:重启组件或服务,必要时恢复数据。 -5. 验收复测:探针恢复、链路恢复、日志无持续报错。 -6. 分时复测:恢复后建议在 `0s / 3s / 10s / 30s` 各复测一次;涉及 Etcd 的场景建议延长到 `60s+` 观察服务注册回稳。 - -### 3. 测试前基线命令 - -源码部署执行 `mage` 相关命令前,请先确保安装 `Go` 与 `mage`。 - -```bash -# OpenIMServer -cd /path/to/open-im-server -mage check - -# ChatServer -cd /path/to/chat -mage check -``` - -```bash -# 外部组件(docker compose 部署时) -docker ps --format '{{.Names}}\t{{.Status}}' | grep -E 'mongo|redis|kafka|etcd|minio' -``` - -```bash -# OpenIMServer 鉴权探针 -curl -sS -X POST "http://127.0.0.1:10002/auth/get_admin_token" \ - -H "Content-Type: application/json" \ - -H "operationID: fault-baseline" \ - -d '{"secret":"your_openim_secret","userID":"imAdmin"}' -``` - -```bash -# ChatServer 业务探针 -curl -sS -X POST "http://127.0.0.1:10008/application/latest_version" \ - -H "Content-Type: application/json" \ - -H "operationID: fault-baseline-chat" \ - -d '{}' -``` - -## 三、异常测试方案 - -### 1. 外部组件故障 - -| 注入对象 | 注入方式(示例) | 主要影响 | 是否可部分服务 | 修复动作 | -| --- | --- | --- | --- | --- | -| MongoDB | `docker stop mongo` | 注册/登录、历史消息与业务数据读写异常 | 是(鉴权可用,业务数据链路受阻) | `docker start mongo`,必要时执行备份恢复 | -| Redis | `docker stop redis` | OpenIM 鉴权链路异常;源码部署常见 `auth-rpc-service down`,Docker 一体化部署常见 Redis 连接/解析错误 | 是(部分接口仍可返回) | `docker start redis` 后先观察 `30-60s`;若鉴权仍异常再执行 OpenIM `mage stop && mage start` | -| Kafka | `docker stop kafka` | 消息转发与推送链路受阻,可能积压 | 是(非消息链路可部分可用) | `docker start kafka`,核查 topic 消费恢复 | -| Etcd | `docker stop etcd` | 运行中实例短时可用;`mage start` 阶段会卡在组件检查/服务注册 | 是(短时) | 先 `docker start etcd` 并等待服务注册回稳(建议 `5-10s`),再执行 OpenIM/Chat 全量重启与复检 | -| MinIO | `docker stop minio` | 图片/文件上传下载失败;源码部署下基础探针通常仍可用,Docker 一体化部署下可能连带 `10002/10008/10009` 基础探针异常 | 视部署方式而定 | `docker start minio`,检查 `externalAddress`;若 `30-60s` 后基础探针仍未恢复,再重启 OpenIM/Chat 服务栈 | - -### 2. OpenIMServer 单服务故障 - -| 注入对象 | 注入方式(示例) | 主要影响 | 是否可部分服务 | 修复动作 | -| --- | --- | --- | --- | --- | -| `openim-api` | `pkill -f openim-api` | REST API 不可用,Chat 对 OpenIM 调用失败 | 否 | 实测单独 `mage start openim-api` 不足以恢复完整链路,建议 `mage stop && mage start` | -| `openim-msggateway` | `pkill -f openim-msggateway` | WS 连接断开,实时消息中断 | 否(实时链路) | 同上 | -| `openim-rpc-auth` | `pkill -f openim-rpc-auth` | token 相关能力异常 | 是(部分不依赖鉴权链路) | 同上 | -| `openim-rpc-msg` | `pkill -f openim-rpc-msg` | 消息发送/拉取异常 | 是(非消息接口可用) | 同上 | -| `openim-msgtransfer` | `pkill -f openim-msgtransfer` | 消息转发链路中断或高延迟 | 是(非消息接口可用) | 同上 | -| `openim-push` | `pkill -f openim-push` | 离线推送失败 | 是(在线消息通常可用) | 同上 | -| `openim-crontask` | `pkill -f openim-crontask` | 定时任务不执行 | 是(实时链路通常可用) | 同上 | - -### 3. ChatServer 单服务故障 - -| 注入对象 | 注入方式(示例) | 主要影响 | 是否可部分服务 | 修复动作 | -| --- | --- | --- | --- | --- | -| `chat-api` | `pkill -f chat-api` | 业务系统接口失败(10008) | 否(Chat 业务入口受阻) | 实测单独 `mage start chat-api` 可能连带影响其他 Chat 服务,建议 `mage stop && mage start` | -| `chat-rpc` | `pkill -f chat-rpc` | Chat 核心业务接口异常 | 否(Chat 核心链路) | 同上 | -| `admin-api` | `pkill -f admin-api` | 管理后台接口失败(10009),10008 可继续提供服务 | 是(用户侧链路可继续) | 实测单独 `mage start admin-api` 可能出现 `admin-rpc` 不可用,建议 `mage stop && mage start` | -| `admin-rpc` | `pkill -f admin-rpc` | 管理后台业务失败 | 是 | 同上 | -| `bot-api`/`bot-rpc` | `pkill -f bot-api` / `pkill -f bot-rpc` | Bot 相关能力失败 | 是(主链路可继续) | 同上 | - -> 说明:上表“单服务拉起风险”主要针对 `mage start ` 场景。`mage` 在单服务启动时会先停止现有服务并执行全量状态校验,异常恢复期可能出现联动影响;若直接拉起对应二进制且依赖健康,单服务通常可恢复。 - -## 四、组件异常停止处理 - -### 1. 通用恢复顺序 - -1. 先恢复外部组件。 -2. 再恢复 OpenIMServer。 -3. 最后恢复 ChatServer。 -4. 全量执行 `mage check` + 探针复测。 - -### 2. 外部组件恢复 - -```bash -cd /path/to/open-im-server -docker compose up -d mongodb redis kafka etcd minio -``` - -> 如果你使用的是 `openim-docker` 仓库,对应服务名为 `mongo redis kafka etcd minio`;如果你使用的是 `open-im-server` 仓库,对应服务名为 `mongodb redis kafka etcd minio`。 - -```bash -docker ps --format '{{.Names}}\t{{.Status}}' | grep -E 'mongo|redis|kafka|etcd|minio' -``` - -### 3. OpenIMServer 恢复 - -```bash -cd /path/to/open-im-server -mage check -mage stop -mage start -mage check -``` - -### 4. ChatServer 恢复 - -```bash -cd /path/to/chat -mage check -mage stop -mage start -mage check -``` - -### 5. MongoDB 数据恢复 - -```bash -docker exec -it mongo mongorestore \ - --uri="mongodb://openIM:openIM123@localhost:27017/openim_v3" \ - /data/backup/your_backup_name/openim_v3 -``` - -## 五、潜在风险 - -1. 单机部署存在单点故障,无法等价替代高可用集群。 -2. 若源数据盘与备份盘同时损坏,无法直接恢复,只能依赖云厂商快照或其他离线备份。 -3. MongoDB 恢复到历史备份点后,备份时间点之后的数据会丢失。 -4. 删除 Redis 数据可能导致未读数、会话状态等短期不一致。 -5. OpenIM/Chat 相关 API 探针必须带 `operationID` 请求头,否则会返回参数错误。 -6. Redis 恢复后多数场景可自动恢复;若鉴权仍异常,再执行 OpenIM 全量重启(`mage stop && mage start`)。 -7. Etcd 宕机时,已运行实例可短时可用;但在服务重启阶段可能失败,且恢复后可能存在短暂注册延迟,需先恢复 Etcd 并观察。 -8. Chat 单服务拉起(如 `mage start chat-api`、`mage start admin-api`)在异常场景下可能导致其他 Chat 进程缺失,故障恢复优先采用 Chat 全量重启。 -9. Kafka/MinIO 场景应补充消息链路、文件链路闭环探针,避免仅依据基础探针判断恢复完成。 diff --git a/docs/guides/gettingStarted/imSourceCodeDeployment.md b/docs/guides/gettingStarted/imSourceCodeDeployment.md index 6a4613a015..25da913f76 100644 --- a/docs/guides/gettingStarted/imSourceCodeDeployment.md +++ b/docs/guides/gettingStarted/imSourceCodeDeployment.md @@ -3,7 +3,7 @@ title: '源码部署' sidebar_position: 3 --- -# OpenIMSDK 服务端生产环境源码部署(单机) +# OpenIMServer 与 ChatServer 生产环境源码部署(单机) ## 一、环境及组件要求 @@ -23,7 +23,7 @@ git checkout "$LATEST_STABLE_TAG" echo "using open-im-server stable release tag: $LATEST_STABLE_TAG" ``` -> 这里的 latest 指 GitHub Releases 页面绿色 Latest 的**正式发布版**,不包含 alpha/beta/rc 等预发布版本。当前该仓库会把 `v3.8.3-patch.12` 识别为最新正式版,而不会误选 `v3.8.4-alpha.1` 这类预发布版本。 +> 这里的 latest 指 GitHub Releases 页面绿色 Latest 的**正式发布版**,不包含 alpha/beta/rc 等预发布版本。`main` 为开发版分支,生产环境不要直接使用 `main`。 > 注意:后续所有命令都在 OpenIMServer 项目根目录执行。 @@ -109,9 +109,9 @@ mage > 首次启动后建议等待 `20-30s` 再执行 `mage check` 或接口验证,避免把启动过程中的短暂连接失败误判为最终异常。 -## 四、获取 Chat +## 四、获取 ChatServer -> 如果已有自有账号系统,可不部署 ChatServer。 +> 如果已有自有账号体系,可不部署 ChatServer。 同样建议拉取后切到 GitHub Releases 页面绿色 **Latest** 对应的**最新正式发布 tag**: @@ -162,7 +162,7 @@ mage ## 七、关于离线推送 -- 个推:开源版对个推支持不够精细,需按业务自行调试。 +- 个推:支持个推离线推送,按个推官方申请 `AppID`、`AppKey`、`MasterSecret` 后接入即可。 - Firebase:修改 `config/openim-push.yml` 中 `fcm.filepath`。 ## 八、服务实例个数修改(可选) @@ -176,7 +176,40 @@ mage ## 九、监控告警(可选) -可按需启用服务器资源与 OpenIMServer/ChatServer 指标监控、仪表盘展示与告警通知。 +### 9.1 组件组成 + +- `Prometheus`:采集 OpenIMServer 暴露的 Prometheus 指标。 +- `Alertmanager`:处理规则命中后的告警路由与通知。 +- `Grafana`:展示仪表盘。 +- `node-exporter`:采集主机 CPU、内存、磁盘、网络等资源指标。 + +### 9.2 启动方式 + +在 `open-im-server` 目录执行: + +```bash +docker compose --profile m up -d +``` + +### 9.3 关键配置文件 + +- `config/prometheus.yml`:Prometheus 抓取配置 +- `config/instance-down-rules.yml`:实例存活告警规则 +- `config/alertmanager.yml`:Alertmanager 路由配置 +- `config/email.tmpl`:邮件告警模板 + +### 9.4 默认端口 + +- `19091`:Prometheus +- `19093`:Alertmanager +- `13000`:Grafana +- `19100`:node-exporter + +### 9.5 使用建议 + +- 先确保 OpenIMServer 已正常启动并能访问 `10002`,再启动监控栈。 +- 如需邮件告警,请先补齐 `config/alertmanager.yml` 中的收件人与 SMTP 信息。 +- ChatServer 需要统一纳入监控时,建议沿用现有 Prometheus 体系继续扩展抓取配置。 ## 十、重要指引 @@ -204,4 +237,4 @@ mage ### 10.3 单机生产环境数据备份及恢复 -请参考:[单机生产环境数据备份及恢复](./faultRecovery.mdx) +请参考:[单机生产环境数据备份及恢复](production.md) diff --git a/docs/guides/gettingStarted/internalDeployment.md b/docs/guides/gettingStarted/internalDeployment.md index f257c1279a..7c799923f8 100644 --- a/docs/guides/gettingStarted/internalDeployment.md +++ b/docs/guides/gettingStarted/internalDeployment.md @@ -2,119 +2,185 @@ title: '内网部署' sidebar_position: 4 --- + ## 📌 内网部署指南 -本指南将指导您在一台纯内网机器上部署 OpenIMSDK 相关服务。 +内网部署主流程:**在联网构建机执行 `mage export` 导出部署包,再拷贝到内网目标机运行**。 + + +## 一、版本与分支策略 + +- `main`:开发版分支,仅用于跟进未发布变更。 +- `vX.Y.Z...`:稳定版发布版本。 +- 内网生产环境建议统一使用 GitHub Releases 页面绿色 **Latest** 对应的**最新正式发布版**。 + +## 二、总体流程 + +1. 在**联网构建机**拉取 OpenIMServer / ChatServer 稳定版代码。 +2. 在联网构建机执行 `mage export`,生成可直接带走的部署归档。 +3. 将部署归档、配置文件、外部组件安装包拷贝到**内网目标机**。 +4. 在内网目标机解压部署归档,直接使用归档内自带的 `./mage` 启动和检查服务。 + +## 三、外部组件准备方式 + +OpenIMServer / ChatServer 的内网部署包只负责业务服务本身。外部组件(MongoDB、Redis、Kafka、Etcd、MinIO)有两种准备方式。 + +### 方案 A:目标机使用 Docker + +如果目标机尚未安装 Docker,建议在联网机器提前下载与目标机发行版、架构匹配的 Docker 离线安装包,再拷贝到内网目标机安装。 + +- Debian / Ubuntu:准备 `docker-ce`、`docker-ce-cli`、`containerd.io`、`docker-buildx-plugin`、`docker-compose-plugin` 对应的 `.deb` 包 +- RHEL / CentOS:准备上述组件对应的 `.rpm` 包 + +安装完成后,再通过 `docker load` 导入你提前保存好的外部组件镜像。 + +### 方案 B:目标机不使用 Docker + +可以直接将 MongoDB、Redis、Kafka、Etcd、MinIO 的官方二进制包或内部制品包拷贝到内网目标机,按各组件自己的 systemd / supervisor / 脚本方式启动。 + +这种模式下,OpenIMServer / ChatServer 只需要正确指向这些组件的地址与账号信息,不要求目标机安装 Docker。 + +## 四、联网构建机导出 OpenIMServer + +```bash +git clone https://github.com/openimsdk/open-im-server && cd open-im-server +git fetch --tags +LATEST_STABLE_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/open-im-server/releases/latest)") +git checkout "$LATEST_STABLE_TAG" + +bash bootstrap.sh +PLATFORMS="linux_amd64" mage export +``` + +执行成功后,部署归档默认生成在: + +```text +_output/export/ +``` + +典型文件名示例: + +```text +exported_open-im-server_v3.8.3-patch.12_linux_amd64.tar.gz +``` + +## 五、联网构建机导出 ChatServer + +```bash +git clone https://github.com/openimsdk/chat && cd chat +git fetch --tags +LATEST_STABLE_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/chat/releases/latest)") +git checkout "$LATEST_STABLE_TAG" + +bash bootstrap.sh +PLATFORMS="linux_amd64" mage export +``` + +典型文件名示例: + +```text +exported_chat_v1.8.4-patch.3_linux_amd64.tar.gz +``` -## 版本策略(重要) +## 六、部署包内容说明 -- 默认建议:在联网机器上拉取仓库后,切换到 GitHub Releases 页面绿色 **Latest** 对应的**最新正式发布 tag** 再做离线打包。 -- 这里的 latest 指**正式发布版**,不包含 alpha/beta/rc 等预发布版本。 -- 如需固定版本(例如 `v3.8.3-patch.12`):直接执行 `git checkout v3.8.3-patch.12`。 -- 强烈建议:OpenIMServer、ChatServer、openim-docker 使用同一批次发布版本,避免跨版本组合。 +`mage export` 生成的归档会包含: -### **Docker部署** +- 已编译好的业务二进制 +- `start-config.yml` +- 运行所需配置文件 +- 可直接在目标机执行的 `mage` 启动器 -1. 使用一台连接到互联网的机器,克隆仓库并切换到最新正式发布 tag: +因此,**内网目标机不需要再安装 Go,也不需要重新编译源码**。 - ```sh - git clone https://github.com/openimsdk/openim-docker && cd openim-docker - git fetch --tags - LATEST_STABLE_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/openim-docker/releases/latest)") - git checkout "$LATEST_STABLE_TAG" - echo "using openim-docker stable release tag: $LATEST_STABLE_TAG" - ``` +## 七、拷贝到内网目标机 -2. 运行 `docker compose up -d` 拉取镜像并生成本地镜像清单。 +将以下内容拷贝到内网目标机: -3. 导出当前 compose 实际使用镜像(避免手工维护版本号): +- OpenIMServer 导出归档 +- ChatServer 导出归档 +- 外部组件镜像包或二进制安装包 +- 你的实际配置文件(如域名、外部组件地址、`secret`、MinIO `externalAddress`) - ```sh - docker compose config --images | sort -u > images.txt - ``` +## 八、在内网目标机部署外部组件 -4. 批量保存镜像: +### 1. Docker 方式 - ```sh - while read -r image; do - docker save -o "$(echo "$image" | tr '/:' '_').tar" "$image" - done < images.txt - ``` +如果目标机使用 Docker: -5. 通过内网或物理介质将**镜像文件**和**openim-docker 仓库文件**拷贝到部署机器。 +```bash +docker load -i image-name.tar +``` -6. 在部署机器导入镜像: +导入完所有外部组件镜像后,再按你的组件编排文件启动。 - ```bash - docker load -i image-name.tar - ``` +### 2. 非 Docker 方式 -7. 在仓库目录下运行: +如果目标机不用 Docker: - ```sh - docker compose up -d - ``` +- 启动 MongoDB +- 启动 Redis +- 启动 Kafka +- 启动 Etcd +- 启动 MinIO - 需要启动监控组件则运行: - ```sh - docker compose --profile m up -d - ``` +然后把这些组件的地址、账号、密码写入 OpenIMServer / ChatServer 的配置文件。 -### **源码部署** +## 九、在内网目标机解压并启动 OpenIMServer -1. 使用一台连接到互联网的机器,克隆 OpenIMServer 并切换到最新正式发布 tag: +```bash +mkdir -p /opt/openim/open-im-server +tar -xzf exported_open-im-server_v*.tar.gz -C /opt/openim/open-im-server +cd /opt/openim/open-im-server +``` - ```sh - git clone https://github.com/openimsdk/open-im-server && cd open-im-server - git fetch --tags - LATEST_STABLE_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/open-im-server/releases/latest)") - git checkout "$LATEST_STABLE_TAG" - echo "using open-im-server stable release tag: $LATEST_STABLE_TAG" - ``` +修改配置文件中的外部组件地址、`secret`、MinIO `externalAddress` 后,执行: -2. 克隆 ChatServer 并切换到最新正式发布 tag: +```bash +./mage check +./mage start +./mage check +``` - ```bash - git clone https://github.com/openimsdk/chat && cd chat - git fetch --tags - LATEST_STABLE_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/chat/releases/latest)") - git checkout "$LATEST_STABLE_TAG" - echo "using chat stable release tag: $LATEST_STABLE_TAG" - ``` +## 十、在内网目标机解压并启动 ChatServer -3. 参考 [docker部署](#docker部署) 步骤保存依赖组件镜像(源码部署场景不需要服务端业务镜像)。 +```bash +mkdir -p /opt/openim/chat +tar -xzf exported_chat_v*.tar.gz -C /opt/openim/chat +cd /opt/openim/chat +``` - > 注意:`open-im-server/docker-compose.yml` 当前默认还会拉起 `openim-web-front`、`openim-admin-front`。如果部署机上的 compose 文件不做裁剪,则还需要一并准备这两个前端镜像;如果你只想部署依赖组件,请先在联网机器调整 compose 文件后再离线拷贝。 +修改 ChatServer 配置文件中的 Redis、MongoDB、Etcd、OpenIMServer `secret` 后,执行: -4. 通过内网或者物理介质将**镜像文件**、**OpenIMServer 仓库文件**、**ChatServer 仓库文件**拷贝到部署机器上。 +```bash +./mage check +./mage start +./mage check +``` - > 纯内网机器如果无法访问 Go 模块源,直接执行 `mage` 可能失败,因为源码仓库默认**不包含 vendor 依赖**。这种场景建议二选一: - > 1. 在联网机器提前编译好 `_output/bin` 后再拷贝到内网机器; - > 2. 在联网机器预先准备 `go` 依赖缓存和 `mage` 可执行文件,再一起拷贝到内网机器。 +## 十一、运行时常用命令 -5. 导入镜像到`docker`中,命令为: +OpenIMServer: - ```bash - docker load -i image-name.tar - ``` +```bash +cd /opt/openim/open-im-server +./mage check +./mage stop +./mage start +``` - 例如`mongo`镜像导入命令为: +ChatServer: - ```sh - docker load -i mongo.tar - ``` +```bash +cd /opt/openim/chat +./mage check +./mage stop +./mage start +``` -6. 在 OpenIMServer 目录下依次运行: - ```bash - docker compose up -d # 如需启用监控组件则为 docker compose --profile m up -d - bash bootstrap.sh # 已预装 mage 时可跳过 - mage - mage start - ``` +## 十二、内网部署注意事项 -7. 在 ChatServer 目录下运行: - ```bash - bash bootstrap.sh # 已预装 mage 时可跳过 - mage - mage start - ``` +1. `main` 是开发版,不要拿 `main` 做内网生产包。 +2. `mage export` 的目标机侧运行依赖已经随归档带出,因此不要再在目标机重新执行源码编译流程。 +3. 如果目标机与构建机架构不同,请在联网构建机通过 `PLATFORMS` 指定目标平台,例如 `PLATFORMS="linux_amd64" mage export` 或 `PLATFORMS="linux_arm64" mage export`。 +4. 外部组件无论使用 Docker 还是二进制拷贝方式,都需要先保证地址和鉴权信息与 OpenIMServer / ChatServer 配置一致。 diff --git a/docs/guides/gettingStarted/nginxDomainConfig.md b/docs/guides/gettingStarted/nginxDomainConfig.md index ffe3ab10db..a53d6ae94c 100644 --- a/docs/guides/gettingStarted/nginxDomainConfig.md +++ b/docs/guides/gettingStarted/nginxDomainConfig.md @@ -18,21 +18,21 @@ sidebar_position: 7 ## 2. Nginx 配置模板 -> 请替换为你的实际域名、证书路径与服务地址。 +> 请替换为你的实际域名、证书路径与服务地址。下面的注释说明了每一段配置的作用,建议直接按注释逐项替换。 ```nginx upstream msg_gateway { - # OpenIMServer message gateway + # OpenIMServer WebSocket gateway server 127.0.0.1:10001; } upstream im_api { - # OpenIMServer API + # OpenIMServer REST API server 127.0.0.1:10002; } upstream im_chat_api { - # ChatServer API + # ChatServer (APP business server) API server 127.0.0.1:10008; } @@ -42,13 +42,16 @@ upstream minio_s3 { } server { + # Unified external HTTPS entry listen 443 ssl; server_name im.yourhost.com; + # SSL certificate and private key paths ssl_certificate /usr/local/nginx/conf/ssl/im.yourhost.com_bundle.pem; ssl_certificate_key /usr/local/nginx/conf/ssl/im.yourhost.com.key; location /msg_gateway { + # OpenIMServer WebSocket reverse proxy; keep the Upgrade headers proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; @@ -58,6 +61,7 @@ server { } location ^~ /api/ { + # OpenIMServer REST API reverse proxy proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; @@ -68,6 +72,7 @@ server { } location ^~ /chat/ { + # ChatServer (APP business server) API reverse proxy proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; @@ -77,6 +82,7 @@ server { } location ^~ /im-minio-api/ { + # MinIO file access reverse proxy; keep it consistent with externalAddress proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -129,5 +135,7 @@ wsAddr: wss://im.yourhost.com/msg_gateway - OpenIMSDK:项目整体名称。 - OpenIMClientSDK:客户端 SDK。 - OpenIMServer:IM 基础服务。 -- ChatServer:业务扩展服务。 +- ChatServer:业务扩展服务,即 APP 业务服务器。 - 管理类 REST API 调用账号统一称为 APP 管理员。 + +> 当前模板仅覆盖 OpenIMServer 与 ChatServer(APP 业务服务器)的常用对外入口;如果你需要将 APP 管理员接口统一走域名入口,请额外为 `10009` 配置对应的 `upstream` 和 `location`。 diff --git a/docs/guides/gettingStarted/ports.md b/docs/guides/gettingStarted/ports.md index 1e91e961d3..49e8e698a5 100644 --- a/docs/guides/gettingStarted/ports.md +++ b/docs/guides/gettingStarted/ports.md @@ -14,10 +14,10 @@ sidebar_position: 6 | OpenIMServer | TCP:10001 | ws 协议,消息端口,用于 OpenIMClientSDK | 端口放行 | | OpenIMServer | TCP:10002 | api 端口,如用户、好友、群组、消息等接口 | 端口放行 | | OpenIMServer | TCP:10005 | MinIO 作为对象存储 | 端口放行 | -| ChatServer | TCP:10008 | 业务系统,如注册、登录等 | 端口放行 | -| ChatServer | TCP:10009 | 管理后台,如统计、封号等 | 端口放行 | +| ChatServer | TCP:10008 | APP 业务服务器接口,如注册、登录等 | 端口放行 | +| ChatServer | TCP:10009 | APP 管理员接口,如统计、封号等 | 端口放行 | | Web 前端(可选) | TCP:11001 | PC Web 前端,快速验证页面访问时需要放行 | 按需放行 | -| Admin 前端(可选) | TCP:11002 | 管理后台前端页面 | 按需放行 | +| Admin 前端(可选) | TCP:11002 | APP 管理员前端页面 | 按需放行 | 在客户端 SDK 中,初始化参数如下: diff --git a/docs/guides/gettingStarted/faultRecovery.assets/0.png b/docs/guides/gettingStarted/production.assets/0.png similarity index 100% rename from docs/guides/gettingStarted/faultRecovery.assets/0.png rename to docs/guides/gettingStarted/production.assets/0.png diff --git a/docs/guides/gettingStarted/production.md b/docs/guides/gettingStarted/production.md index e69de29bb2..ea70d827e2 100644 --- a/docs/guides/gettingStarted/production.md +++ b/docs/guides/gettingStarted/production.md @@ -0,0 +1,93 @@ +--- +title: '生产环境' +sidebar_position: 8 +slug: /gettingStarted/production +--- + +# 生产环境 + +本文只描述生产环境**运行时故障**会造成的影响,以及对应的恢复方式。 + +## 一、通用恢复顺序 + +1. 先恢复外部组件。 +2. 再恢复 OpenIMServer。 +3. 最后恢复 ChatServer。 + +## 二、外部组件运行时故障 + +| 组件故障 | 运行时影响 | 恢复方式 | +| --- | --- | --- | +| MongoDB 不可用 | OpenIMServer `10002` 可能仍可返回,但 ChatServer 与 APP 管理员接口常失败 | 先恢复 MongoDB;恢复后立即复测 `10002/10008/10009`,若仍异常,再重启 OpenIMServer / ChatServer | +| Redis 不可用 | OpenIMServer 鉴权链路异常;源码部署常见 `auth-rpc-service down`,Docker 一体化部署常见 Redis 连接或解析错误 | 先恢复 Redis;观察 `30-60s`,若 OpenIMServer 鉴权仍异常,再重启 OpenIMServer | +| Kafka 不可用 | 基础探针可能仍正常,但消息转发、推送链路会异常 | 先恢复 Kafka;恢复后补做消息发送、消费、推送闭环验证 | +| Etcd 不可用 | 已运行实例通常短时可继续服务,但服务重启阶段可能失败 | 先恢复 Etcd;如果服务注册未恢复,再重启 OpenIMServer / ChatServer | +| MinIO 不可用 | 文件上传下载失败;源码部署下基础探针通常仍可用,Docker 一体化部署下可能连带 `10002/10008/10009` 异常 | 先恢复 MinIO,并检查 `externalAddress`;若 Docker 一体化部署在 `30-60s` 后基础探针仍未恢复,再重启 OpenIMServer / ChatServer 服务栈 | + +### 外部组件恢复命令 + +`openim-docker` 部署: + +```bash +cd /path/to/openim-docker +docker compose up -d mongo redis kafka etcd minio +``` + +`open-im-server` 源码部署: + +```bash +cd /path/to/open-im-server +docker compose up -d mongodb redis kafka etcd minio +``` + +> `openim-docker` 默认服务名为 `mongo`,`open-im-server` 默认服务名为 `mongodb`。 + +## 三、OpenIMServer 运行时故障 + +| 服务故障 | 运行时影响 | 恢复方式 | +| --- | --- | --- | +| `openim-api` | `10002` 通常不可用 | 在 `open-im-server` 目录执行 `mage stop && mage start` | +| `openim-rpc-auth` | OpenIMServer 鉴权探针失败,ChatServer 探针可能仍可用 | 在 `open-im-server` 目录执行 `mage stop && mage start` | +| `openim-msggateway` | WebSocket 实时链路中断 | 在 `open-im-server` 目录执行 `mage stop && mage start` | +| `openim-msgtransfer` / `openim-push` | 消息链路、推送链路退化,基础探针不一定失败 | 在 `open-im-server` 目录执行 `mage stop && mage start`,恢复后补做消息闭环验证 | +| `openim-crontask` | 定时任务停止执行 | 在 `open-im-server` 目录执行 `mage stop && mage start` | + +OpenIMServer 恢复命令: + +```bash +cd /path/to/open-im-server +mage check +mage stop +mage start +mage check +``` + +## 四、ChatServer 运行时故障 + +| 服务故障 | 运行时影响 | 恢复方式 | +| --- | --- | --- | +| `chat-api` | APP 业务服务器接口 `10008` 通常不可用 | 在 `chat` 目录执行 `mage stop && mage start` | +| `chat-rpc` | ChatServer 核心业务调用失败,基础 HTTP 端口可能仍在 | 在 `chat` 目录执行 `mage stop && mage start` | +| `admin-api` | APP 管理员接口 `10009` 通常不可用,但 `10008` 可能仍可用 | 在 `chat` 目录执行 `mage stop && mage start` | +| `admin-rpc` | APP 管理员业务调用失败 | 在 `chat` 目录执行 `mage stop && mage start` | +| `bot-api` / `bot-rpc` | Bot 相关能力异常 | 在 `chat` 目录执行 `mage stop && mage start` | + +ChatServer 恢复命令: + +```bash +cd /path/to/chat +mage check +mage stop +mage start +mage check +``` + +## 五、恢复后确认 + +恢复完成后,至少确认以下三项: + +1. `mage check` 或 `docker ps` 正常。 +2. `10002`、`10008`、`10009` 三个基础探针恢复。 +3. Kafka、MinIO 场景补做消息与文件链路验证。 + +> OpenIMServer、ChatServer 的基础探针都必须带 `operationID` 请求头。 diff --git a/docs/guides/gettingStarted/quickTestServer.md b/docs/guides/gettingStarted/quickTestServer.md index 97bbe30ea3..b6e437a80d 100644 --- a/docs/guides/gettingStarted/quickTestServer.md +++ b/docs/guides/gettingStarted/quickTestServer.md @@ -36,16 +36,16 @@ docker ps | grep -E 'openim-server|openim-chat' 如果是源码部署,可执行: ```bash -# 在 open-im-server 目录执行 +# Run in the open-im-server directory mage check -# 在 chat 目录执行 +# Run in the chat directory mage check ``` ## 📌 五、域名与网关验证 -使用域名和 SSL 时,建议直接调用实际接口确认 OpenIMServer 与 ChatServer 网关路由可达。 +使用域名和 SSL 时,建议直接调用实际接口确认 OpenIMServer 与 ChatServer(APP 业务服务器)网关路由可达。 ```bash curl -sS -X POST "https://your_domain/api/auth/get_admin_token" \ @@ -93,4 +93,4 @@ curl -sS -X POST "http://your_server_ip:10008/application/latest_version" \ -d '{}' ``` -> ChatServer 的 POST 接口同样要求 `operationID` 请求头;缺少该请求头会直接返回参数错误。 +> ChatServer(APP 业务服务器)的 POST 接口同样要求 `operationID` 请求头;缺少该请求头会直接返回参数错误。 From 65da60f8100368617a0c9e2efa513bf018fb8378 Mon Sep 17 00:00:00 2001 From: dsx137 <70027572+dsx137@users.noreply.github.com> Date: Tue, 10 Mar 2026 17:22:46 +0800 Subject: [PATCH 7/9] feat: translate to English --- .../current.json | 4 +- .../current/gettingStarted/_category_.json | 2 +- .../cluster.assets/prometheus0.png | Bin 0 -> 103795 bytes .../gettingStarted/cluster.assets/rpc0.png | Bin 0 -> 66956 bytes .../current/gettingStarted/cluster.md | 96 ++++--- .../current/gettingStarted/dockerCompose.md | 58 +++-- .../current/gettingStarted/env-comp.md | 64 +++-- .../current/gettingStarted/faq.md | 79 +++--- .../gettingStarted/imSourceCodeDeployment.md | 235 ++++++++++++++---- .../gettingStarted/internalDeployment.md | 228 +++++++++++------ .../gettingStarted/nginxDomainConfig.md | 134 ++++++---- .../current/gettingStarted/ports.md | 68 +++-- .../gettingStarted/production.assets/0.png | Bin 0 -> 128928 bytes .../current/gettingStarted/production.md | 107 +++++--- .../current/gettingStarted/quickTestServer.md | 84 ++++++- 15 files changed, 778 insertions(+), 381 deletions(-) create mode 100644 i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/cluster.assets/prometheus0.png create mode 100644 i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/cluster.assets/rpc0.png create mode 100644 i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/production.assets/0.png diff --git a/i18n/en/docusaurus-plugin-content-docs-guides/current.json b/i18n/en/docusaurus-plugin-content-docs-guides/current.json index 7ed0a6e8bb..d1c4d10295 100644 --- a/i18n/en/docusaurus-plugin-content-docs-guides/current.json +++ b/i18n/en/docusaurus-plugin-content-docs-guides/current.json @@ -8,8 +8,8 @@ "description": "The label for category 'Quick Start' in sidebar 'tutorialSidebar'" }, "sidebar.tutorialSidebar.category.快速部署": { - "message": "Quick Deploy", - "description": "The label for category 'Quick Deploy' in sidebar 'tutorialSidebar'" + "message": "Quick Deployment", + "description": "The label for category 'Quick Deployment' in sidebar 'tutorialSidebar'" }, "sidebar.tutorialSidebar.category.解决方案": { "message": "Solutions", diff --git a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/_category_.json b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/_category_.json index 114965e1bb..3a8c0c3daf 100644 --- a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/_category_.json +++ b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/_category_.json @@ -1,6 +1,6 @@ { "position": 2, - "label": "Quick Deploy", + "label": "Quick Deployment", "collapsible": true, "collapsed": true } diff --git a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/cluster.assets/prometheus0.png b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/cluster.assets/prometheus0.png new file mode 100644 index 0000000000000000000000000000000000000000..ee4b154327677576ffa2a80c3abd9fb3a1d8f827 GIT binary patch literal 103795 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 z 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 z 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 Rbqe6;rrKTAlIvz6{|EK=alrrp literal 0 HcmV?d00001 diff --git a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/cluster.assets/rpc0.png b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/cluster.assets/rpc0.png new file mode 100644 index 0000000000000000000000000000000000000000..2f901229f38a3ba59036c89f2f8560f21acd74ad GIT binary patch literal 66956 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¨uPIf@o6hgFoYo6@R0YU&4h(*zlh8pMm-B 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 Q_5pu-+J;(Xnh(PNA2$n{k^lez literal 0 HcmV?d00001 diff --git a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/cluster.md b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/cluster.md index 32a8cf1725..05490cd25e 100644 --- a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/cluster.md +++ b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/cluster.md @@ -5,55 +5,62 @@ sidebar_position: 5 -# IMServer Source Code Cluster Deployment Guide (Same LAN) +# OpenIMServer Source Code Cluster Deployment Guide on the Same Internal Network -This guide uses machines A and B (with internal IPs `IP_A` and `IP_B`) as an example. They are located in the same LAN environment, used for deploying the clustered IM Server and Nginx. -Assuming you have already deployed Redis cluster, MongoDB sharded cluster, Kafka cluster, and Etcd cluster at the following addresses: -- **Redis Cluster**: `redisAddr1`, `redisAddr2`, `redisAddr3` -- **MongoDB Cluster**: `mongoAddr1`, `mongoAddr2`, `mongoAddr3` -- **Kafka Cluster**: `kafkaAddr1`, `kafkaAddr2`, `kafkaAddr3` -- **Etcd Cluster**: `etcdAddr1`, `etcdAddr2`, `etcdAddr3` +This guide uses machines A and B (with internal IPs `IP_A` and `IP_B`) as examples. They are located in the same internal network and are used to deploy clustered OpenIMServer and Nginx. +Assume that you have already deployed a Redis cluster, MongoDB sharded cluster, Kafka cluster, and Etcd cluster with the following addresses: +- **Redis cluster addresses**: `redisAddr1`, `redisAddr2`, `redisAddr3` +- **MongoDB cluster addresses**: `mongoAddr1`, `mongoAddr2`, `mongoAddr3` +- **Kafka cluster addresses**: `kafkaAddr1`, `kafkaAddr2`, `kafkaAddr3` +- **Etcd cluster addresses**: `etcdAddr1`, `etcdAddr2`, `etcdAddr3` -These components should be deployed on three or more nodes to ensure high availability and load balancing. +It is recommended to deploy these components on three or more nodes to ensure high availability and load balancing. -Additionally, MinIO's internal access address is configured as `your_minio_internal_address`, and the external access address is `your_minio_external_address`. -Machines A and B, as well as the component clusters, have internal network connectivity. Both machines A and B also have public IPs. +In addition, MinIO is configured with `your_minio_internal_address` for internal access and `your_minio_external_address` for external access. +Machines A and B, as well as the component clusters, must be reachable over the internal network, and both A and B must have public IPs. ### Table of Contents 1. [Prerequisites](#prerequisites) -2. [Clone Repository](#1-clone-repository) +2. [Clone the Repository](#1-clone-the-repository) 3. [Modify Configuration](#2-modify-configuration) 4. [Configure Nginx](#3-configure-nginx) -5. [Set Up DNS](#4-set-up-dns) +5. [Configure DNS](#4-configure-dns) 6. [Start Services](#5-start-services) ### Prerequisites -Ensure the following components are properly deployed and running: +Ensure that the following components are already deployed and running correctly: -- **Redis Cluster** -- **MongoDB Sharded Cluster** -- **Kafka Cluster** -- **Etcd Cluster** -- **MinIO Service** +- **Redis cluster** +- **MongoDB sharded cluster** +- **Kafka cluster** +- **Etcd cluster** +- **MinIO service** -### 1. Clone Repository +> This document only covers deploying two OpenIMServer business nodes and Nginx. It does not include the deployment of the Redis / MongoDB / Kafka / Etcd clusters themselves. If you currently only have two empty machines, complete those external component clusters first before continuing. -On both machines (A and B), run the following commands to clone the `open-im-server` repository: +### 1. Clone the Repository + +Run the following commands on both machines (A and B) to clone the OpenIMServer repository and switch to the latest official release tag marked with the green **Latest** badge on GitHub Releases: ```bash git clone https://github.com/openimsdk/open-im-server cd open-im-server +git fetch --tags +LATEST_STABLE_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/open-im-server/releases/latest)") +git checkout "$LATEST_STABLE_TAG" ``` +> Here, `latest` means the latest official release marked with the green **Latest** badge on GitHub Releases. It does not include alpha, beta, rc, or other pre-releases. It is recommended that both machines use the same stable tag. If you need a fixed version such as `v3.8.3-patch.12`, run `git checkout v3.8.3-patch.12` on both machines. + ### 2. Modify Configuration -On both machines A and B, modify the configuration files as follows to ensure all components are correctly connected. All address fields use the inline list format `address: [addr1, addr2, addr3]`. +On machines A and B, modify the configuration files as follows so that all components connect correctly. All address fields use the single-line list format `address: [addr1, addr2, addr3]`. #### 2.1 Kafka Configuration -Edit the `open-im-server/config/kafka.yml` file, setting the `address` field to the Kafka cluster address list: +Edit `open-im-server/config/kafka.yml` and set the `address` field to the Kafka cluster address list: ```yaml address: [kafkaAddr1, kafkaAddr2, kafkaAddr3] @@ -61,7 +68,7 @@ address: [kafkaAddr1, kafkaAddr2, kafkaAddr3] #### 2.2 MinIO Configuration -Edit the `open-im-server/config/minio.yml` file, setting `internalAddress` and `externalAddress`: +Edit `open-im-server/config/minio.yml` and set `internalAddress` and `externalAddress`: ```yaml internalAddress: your_minio_internal_address @@ -70,7 +77,7 @@ externalAddress: your_minio_external_address #### 2.3 MongoDB Configuration -Edit the `open-im-server/config/mongodb.yml` file, setting the `address` field to the MongoDB cluster address list: +Edit `open-im-server/config/mongodb.yml` and set the `address` field to the MongoDB cluster address list: ```yaml address: [mongoAddr1, mongoAddr2, mongoAddr3] @@ -78,7 +85,7 @@ address: [mongoAddr1, mongoAddr2, mongoAddr3] #### 2.4 Etcd Configuration -Edit the `open-im-server/config/discovery.yml` file, setting the `etcd.address` field to the Etcd cluster address list: +Edit `open-im-server/config/discovery.yml` and set `etcd.address` to the Etcd cluster address list: ```yaml etcd: @@ -87,7 +94,7 @@ etcd: #### 2.5 Redis Configuration -Edit the `open-im-server/config/redis.yml` file, setting the `address` field to the Redis cluster address list and enabling cluster mode: +Edit `open-im-server/config/redis.yml`, set `address` to the Redis cluster address list, and enable cluster mode: ```yaml address: [redisAddr1, redisAddr2, redisAddr3] @@ -96,9 +103,9 @@ clusterMode: true ### 3. Configure Nginx -Deploy `nginx` on both machines A and B using the following configuration. Make sure to replace with your actual domain name, SSL certificate path, and SSL key path. +Deploy `nginx` on machines A and B using the following configuration. Make sure to replace it with your actual domain, SSL certificate path, and SSL private key path. -> 🚀 **Tip**: Make sure to replace with your actual domain name, SSL certificate path, and SSL key. +> 🚀 **Tip**: Be sure to replace the example domain, SSL certificate path, and SSL key path with your actual values. ```nginx events { @@ -113,7 +120,7 @@ http { } upstream im_api { - # IM API server addresses — specify multiple based on your deployment + # OpenIMServer API addresses; add more upstreams if needed server IP_A:10002; server IP_B:10002; } @@ -123,9 +130,9 @@ http { server_name yourhost.com; # Replace with your domain ssl_certificate /usr/local/nginx/conf/ssl/your_host_bundle.pem; # Replace with your certificate path - ssl_certificate_key /usr/local/nginx/conf/ssl/your_host.key; # Replace with your certificate key path + ssl_certificate_key /usr/local/nginx/conf/ssl/your_host.key; # Replace with your private key path - location ^~/api/ { + location ^~ /api/ { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; @@ -145,7 +152,7 @@ http { } } - # Optional: HTTP to HTTPS redirect + # Optional: redirect HTTP to HTTPS server { listen 80; server_name yourhost.com; # Replace with your domain @@ -155,23 +162,32 @@ http { } ``` -Add this configuration to your `nginx` config file and reload to apply: +Add this configuration to the Nginx configuration file and reload it to apply the changes: -### 4. Set Up DNS +### 4. Configure DNS Point your domain `yourhost.com` to the public IP addresses of machines A and B. ### 5. Start Services -On both machines (A and B), run the following commands in the `open-im-server` directory to build and start the services: +Run the following commands in the `open-im-server` directory on both machines (A and B) to build and start the services: -For users in China, it is recommended to set a Go proxy: +For users in Mainland China, setting a Go proxy is recommended: ``` $ go env -w GO111MODULE=on $ go env -w GOPROXY=https://goproxy.cn,direct ``` #### 5.1 Build + +Before the first execution on each machine, it is recommended to run: + +```bash +bash bootstrap.sh +``` + +This step installs `mage`. If `mage` is already installed on your machine, you can skip it. + ```bash mage ``` @@ -183,8 +199,8 @@ mage start ``` -## **FAQ / Important Notes** +## **FAQ / Notes** -1. When deploying `Kafka`, you need to modify the Kafka advertised port. If using the `docker-compose.yml` from `open-im-server`, modify `service.kafka.environment.KAFKA_CFG_ADVERTISED_LISTENERS` where `EXTERNAL` should be set to the address for accessing the Kafka component. For other deployment methods, modify accordingly. - Example: `KAFKA_CFG_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,EXTERNAL://192.168.2.36:19094`. -2. Multi-machine deployments require clock synchronization across machines for services to run correctly. For example, the `token` signing process allows a maximum clock skew of `5 seconds` between machines. +1. When deploying `kafka`, you need to modify the Kafka advertised port. If you use `docker-compose.yml` from `open-im-server`, change the `EXTERNAL` listener in `service.kafka.environment.KAFKA_CFG_ADVERTISED_LISTENERS` to the address used to access the `kafka` component. If you use another deployment method, adjust it accordingly. + For example: `KAFKA_CFG_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,EXTERNAL://192.168.2.36:19094`. +2. In multi-machine deployment, the clocks of all machines must stay synchronized, or services may fail. For example, token issuing only tolerates clock drift within `5s`. diff --git a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/dockerCompose.md b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/dockerCompose.md index 49dc09a1ba..50f35e0ffe 100644 --- a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/dockerCompose.md +++ b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/dockerCompose.md @@ -3,26 +3,32 @@ title: 'Docker Deployment' sidebar_position: 2 --- -## 1. Prerequisites 🌍 -For server hardware, software, operating system, and component requirements, please refer to [this document](./env-comp). +## 1. Environment Preparation 🌍 +For server hardware, software, operating system, and dependent components, please refer to [this document](./env-comp). -## 2. Deploy IMServer +## 2. Deploy OpenIMServer ### 2.1 Clone the Repository 🗂️ +Use the latest official release tag marked with the green **Latest** badge on the GitHub Releases page. Do not sort tags manually, and do not use pre-release versions such as alpha or rc. + ```bash git clone https://github.com/openimsdk/openim-docker && cd openim-docker +git fetch --tags +LATEST_STABLE_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/openim-docker/releases/latest)") +git checkout "$LATEST_STABLE_TAG" +echo "using openim-docker stable release tag: $LATEST_STABLE_TAG" ``` -### 2.2 Modify Configuration 🔧 +> Here, `latest` means the latest official release marked with the green **Latest** badge on GitHub Releases. It does not include alpha, beta, rc, or other pre-releases. `main` is the development branch and should not be used directly in production. + +### 2.2 Configuration Changes 🔧 -- Edit the `.env` file to configure the MinIO external IP for sending images, videos, and files. Replace `your-server-ip` with your server's public IP address: +- Edit `.env` and configure the MinIO external IP to support image and file sending. Replace `your-server-ip` with your server's public IP. ```plaintext MINIO_EXTERNAL_ADDRESS="http://your-server-ip:10005" ``` - - ### 2.3 Start Services 🚀 - Start services: @@ -31,6 +37,11 @@ git clone https://github.com/openimsdk/openim-docker && cd openim-docker docker compose up -d ``` +> The first run pulls large images and may take some time. After startup, wait `30-60s` before running health checks or API verification. + +> This document assumes a **clean environment**. If the machine already has containers with the same names such as `mongo`, `redis`, `kafka`, `etcd`, `minio`, `openim-server`, or `openim-chat`, `docker compose up -d` will fail because of `container_name` conflicts. In that case, stop and remove those containers first, or reuse the existing components after adjusting configuration. + +> If startup shows warnings such as missing `ETCD_USERNAME`, `ETCD_PASSWORD`, `KAFKA_USERNAME`, or `KAFKA_PASSWORD`, and you have not enabled authentication for those components, these warnings can usually be ignored. - Stop services: @@ -44,17 +55,34 @@ docker compose down docker logs -f openim-server ``` -## 3. Quick Verification ⚡ +### 2.4 Monitoring & Alerting (Optional) + +If you also want to start `Prometheus`, `Alertmanager`, `Grafana`, and `node-exporter`, run: + +```bash +docker compose --profile m up -d +``` + +Default ports follow the current `.env`. Common values are: + +- `19090`: Prometheus +- `19093`: Alertmanager +- `13000`: Grafana +- `19100`: node-exporter + +## 3. Quick Experience ⚡ -To quickly test the core capabilities of OpenIMSDK and verify your deployment, refer to [Quick Verification](./quickTestServer). +To quickly experience core OpenIMSDK capabilities and verify whether OpenIMServer / ChatServer deployment is working, refer to [Quick Verification](./quickTestServer). +> Additional note for the current project layout: if you deploy from the two source repositories `open-im-server` and `chat`, `open-im-server/docker-compose.yml` is mainly used for dependency components, and ChatServer still needs to be started with `mage start` in the `chat` directory. See [Source Code Deployment](./imSourceCodeDeployment). ## 4. FAQ -### Troubleshooting Unhealthy Status -1. Run `docker exec -it openim-server mage check` and verify if it has been running for more than one minute. -2. Run `docker logs -f openim-server` to view logs. +### Troubleshooting `unhealthy` +1. Run `docker exec -it openim-server mage check` and confirm whether the state lasts longer than one minute. +2. Run `docker logs -f openim-server` to inspect logs. +3. If `openim-chat` briefly reports `connect: connection refused` during startup, wait `30-60s` and check again. This is usually a startup ordering issue while `openim-server` is still becoming ready. -### Modifying Configuration -Editing configuration files inside the `config` directory within the container has no effect! -You must use environment variables to modify configuration. Refer to the [Environment Variable Configuration Guide](https://github.com/openimsdk/openim-docker/issues/136). +### Configuration Changes +Editing files under the container `config` directory does not work. +Configuration changes must be made through environment variables. See the [environment variable guide](https://github.com/openimsdk/openim-docker/issues/136). diff --git a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/env-comp.md b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/env-comp.md index 414e3b358f..dc8bb8ebc7 100644 --- a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/env-comp.md +++ b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/env-comp.md @@ -5,33 +5,45 @@ sidebar_position: 1 # 🧩 Platform & Component Requirements ---- - -## 🌐 Operating System & Hardware - -| Requirement | Details | -| --- | --- | -| **Operating System** | Linux, Windows, macOS | -| **Hardware Resources** | 4-core CPU, 8 GB RAM, 10 Mbps bandwidth, 100 GB disk minimum | - +Applies to the OpenIMServer and ChatServer deployment documents under `docs/guides/gettingStarted`. +--- - -## 🌐 Software -| Software | Details | -| --- | --- | -| **Golang** | v1.21 or higher, [Installation Guide](https://go.dev/learn/) | -| **Docker** | v24.0.5 or higher, [Installation Guide](https://www.docker.com/get-started/) | -| **Git** | v2.17.1 or higher, [Installation Guide](https://git-scm.com/downloads) | - -## 💾 Component Requirements - -| Component | Recommended Version | -| --- | --- | -| **MongoDB** | v7.0 | -| **Redis** | v7.0.0 | -| **Etcd** | v3.5.13 | -| **Kafka** | v3.5.1 | -| **MinIO** | RELEASE.2024-01-11T07-46-16Z | +## 1. Terminology + +- **OpenIMSDK**: The overall project name, including OpenIMClientSDK and OpenIMServer. +- **OpenIMClientSDK**: The client SDK. +- **OpenIMServer**: The IM core server. +- **ChatServer**: The business extension server. This documentation no longer uses `Chat` as a standalone product name. +- **APP Administrator**: The backend management role that calls management APIs such as `10009`. +- **APP Business Server**: The application-side server that calls business extension APIs such as `10008`. + +## 2. Version and Branch Strategy + +- `main`: The development branch for unreleased changes in continuous integration. It is not recommended for production. +- `vX.Y.Z...`: Stable release version naming. +- For production, prefer the latest official release marked with the green **Latest** badge on the GitHub Releases page. +- If you need reproducible troubleshooting, rollback, or multi-environment consistency, pin an explicit stable release tag. + +## 3. Environment Requirements + +| Item | Details | Notes | +| --- | --- | --- | +| Operating system | Linux | Officially uses `ubuntu 22.04`; `Debian 13` has also been verified to work | +| Hardware resources | 8 CPU cores, 16 GB RAM, 10 Mbps bandwidth, 1 TB disk | Estimated for 100k registered users, 10% daily online ratio, 50k-member large groups, and 600 messages per second; requires a public IP | +| CPU architecture | `x86_64` | ARM requires separate verification | +| Golang | `v1.22.7` or higher | [Installation reference](https://go.dev/learn/) | +| Docker | `v24.0.5` or higher | Must include `compose` support | +| Git | `v2.17.1` or higher | [Installation reference](https://git-scm.com/downloads) | + +## 4. External Component Requirements + +| Component | Recommended Version | Supported Modes in OpenIMServer | ChatServer Access Mode | Cloud Support / Notes | +| --- | --- | --- | --- | --- | +| MongoDB | `v7.0` | `standalone`, `replicaSet` | `address` or `uri` | Supported; for replica sets, `uri` is preferred | +| Redis | `v7.0.0` | `standalone`, `cluster`, `sentinel` | `standalone`, `clusterMode` | Supported; `sentinel` is explicitly supported only in OpenIMServer config | +| Etcd | `v3.5.13` | Single node, multi-node cluster | Multi-address access | No managed cloud support | +| Kafka | `v3.5.1` | Single node, distributed cluster | Not directly used by ChatServer | Supported; required topics must be created in advance | +| MinIO | `RELEASE.2024-01-11T07-46-16Z` | Single node | Not directly used by ChatServer | Can be replaced with S3-compatible storage such as `COS`, `OSS`, `Kodo`, or `AWS S3` | --- diff --git a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/faq.md b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/faq.md index e6184c6b0b..5851092f4b 100644 --- a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/faq.md +++ b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/faq.md @@ -5,37 +5,39 @@ sidebar_position: 10 ## 1. How to Upgrade -Within the same major version, data is compatible across different minor versions. For example, data from version **3.8.0** will work correctly after upgrading to **3.8.2**. This section covers the specific upgrade steps for this scenario. +Within the same major version, data is usually compatible across minor versions. It is recommended to upgrade to the latest official release tag marked with the green **Latest** badge on GitHub Releases. If you need a fixed version (for example `v3.8.3-patch.12`), explicitly checkout that tag. ### Docker Deployment -1. **Navigate to the existing `openim-docker` directory:** +1. **Enter the existing `openim-docker` directory:** ```bash cd openim-docker ``` -2. **Edit the `.env` file and modify the image tag. For example, change:** - ```env - OPENIM_SERVER_IMAGE=openim/openim-server:release-v3.8.0 - ``` - **to:** - ```env - OPENIM_SERVER_IMAGE=openim/openim-server:release-v3.8.2 +2. **Fetch and switch to the latest official release tag:** + ```bash + git fetch --tags + TARGET_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/openim-docker/releases/latest)") + git checkout "$TARGET_TAG" + echo "upgrade openim-docker to stable release tag: $TARGET_TAG" ``` -3. **Stop the existing Docker services:** +3. **Check whether the image tags in `.env` match the current repository version, and adjust them manually according to the release notes if necessary.** + +4. **Stop the current Docker services:** ```bash docker compose down ``` -4. **Start the updated Docker services:** +5. **Start the upgraded Docker services:** ```bash + docker compose pull docker compose up -d ``` ### Source Code Deployment -1. **Navigate to the existing `open-im-server` directory:** +1. **Enter the existing `open-im-server` directory:** ```bash cd open-im-server @@ -46,9 +48,11 @@ Within the same major version, data is compatible across different minor version mage stop ``` -3. **Switch branch and update code:** +3. **Switch to the latest official release tag (or a specified tag) and update the code:** ```bash - git pull + git fetch --tags + TARGET_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/open-im-server/releases/latest)") + git checkout "$TARGET_TAG" ``` 4. **Build and start the service:** @@ -57,11 +61,14 @@ Within the same major version, data is compatible across different minor version mage start ``` +5. **If ChatServer is also deployed, it is recommended to upgrade ChatServer to the corresponding tag and then restart it as well.** + +> Here, `latest` means the latest official release marked with the green **Latest** badge on GitHub Releases. It does not include alpha, beta, rc, or other pre-releases. --- ## 2. How to Migrate Data -After starting each component that IMServer depends on using `docker compose up -d`, a `components` folder is generated in the IMServer root directory. All data produced by IMServer (such as users, groups, messages, etc.) is stored in this folder. To migrate data, first stop all services and components: +After starting components with `docker compose up -d`, a `components` directory is created in the current deployment repository root (for example `openim-docker/components` or `open-im-server/components`). Runtime data such as users, groups, and messages is stored there. If you need to migrate data, stop services and components first: `Docker` deployment: @@ -76,7 +83,7 @@ mage stop # Stop services docker compose down # Stop components ``` -Then move the entire folder to the new data directory, update the `DATA_DIR` value in the `.env` file to the new data directory path, and restart services and components: +Then move the entire folder to the new data directory, update the `DATA_DIR` value in `.env`, and start services and components again: `Docker` deployment: @@ -94,7 +101,7 @@ mage start # Start services --- ## 3. How to Clear Data -To clear all data, first stop all services and components: +If you need to clear all data, stop services and components first: `Docker` deployment: @@ -109,42 +116,44 @@ mage stop # Stop services docker compose down # Stop components ``` -Then delete the `components` folder under `open-im-server`. +Then delete the `components` directory under the current deployment repository. -On the client side, uninstall and reinstall the `app`. +On the client side, you need to uninstall and reinstall the app. --- ## 4. Text Messages Work but Image Sending Fails -Image sending failures are typically caused by unconfigured third-party object storage. The default object storage is `MinIO` — you need to modify the relevant configuration: -``` -Source code deployment: -Edit the config/minio.yml file and configure the MinIO external IP to support sending images, videos, and files. -Replace your-server-ip with your server's public IP. -externalAddress="http://your-server-ip:10005" -``` +If text messages work but images fail, the usual reason is that third-party object storage is not configured correctly. The default object storage is `minio`, so you need to modify the related configuration. + +For source deployment, edit `config/minio.yml` and change `externalAddress` to the public IP or domain path: +```yaml +externalAddress: http://your-server-ip:10005 ``` -Docker deployment: -Edit the .env file and configure the MinIO external IP to support sending images, videos, and files. -Replace your-server-ip with your server's public IP. + +For Docker deployment, edit `.env` and change `MINIO_EXTERNAL_ADDRESS` to the public IP or domain path: + +```dotenv MINIO_EXTERNAL_ADDRESS="http://your-server-ip:10005" ``` + --- -## 5. Reducing MongoDB and Kafka Memory Usage +## 5. Reduce MongoDB and Kafka Memory Usage + +If the components were deployed with `docker`, you can reduce memory usage by limiting `mongo` and `kafka` in `docker-compose.yml`. -If you deployed components using `Docker`, you can limit the memory usage of `mongo` and `kafka` in the `docker-compose.yml` file. +`mongo` (service name in `openim-docker`) or `mongodb` (service name in `open-im-server`): -`MongoDB`: +> If you use source deployment, replace `mongo` in the following example with `mongodb`. ```yml - mongodb: + mongo: environment: - - wiredTigerCacheSizeGB=0.5 # Set to an appropriate value in GB + - wiredTigerCacheSizeGB=0.5 # Adjust to an appropriate value, unit: GB ``` -`Kafka`: +`kafka`: ```yml kafka: diff --git a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/imSourceCodeDeployment.md b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/imSourceCodeDeployment.md index 4d25578b7d..8bcbde1725 100644 --- a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/imSourceCodeDeployment.md +++ b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/imSourceCodeDeployment.md @@ -3,93 +3,238 @@ title: 'Source Code Deployment' sidebar_position: 3 --- -# 🛠 Source Code Deployment +# Production Source Code Deployment for OpenIMServer and ChatServer (Single Node) -## 1. Prerequisites +## 1. Environment and Component Requirements -For server hardware, software, operating system, and component requirements, please refer to [this document](./env-comp). +OpenIMServer and external components are deployed on the same machine in this guide. Some components can be replaced with cloud services if needed. -## 2. Deploy IMServer +- For environment requirements, refer to [Platform & Components](./env-comp) -### 2.1 Clone the Repository and Switch to the Latest Stable Tag +## 2. Get OpenIMServer and Deploy Dependency Components -``` +It is recommended to pull the repository and switch to the latest official release tag marked with the green **Latest** badge on GitHub Releases. + +```bash git clone https://github.com/openimsdk/open-im-server && cd open-im-server +git fetch --tags +LATEST_STABLE_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/open-im-server/releases/latest)") +git checkout "$LATEST_STABLE_TAG" +echo "using open-im-server stable release tag: $LATEST_STABLE_TAG" ``` -### 2.2 Deploy Components (MongoDB/Redis/Kafka/MinIO/Etcd) -``` +> Here, `latest` means the latest official release marked with the green **Latest** badge on GitHub Releases. It does not include alpha, beta, rc, or other pre-releases. `main` is the development branch and should not be used directly in production. + +> Note: all commands below are executed in the OpenIMServer project root directory. + +### 2.1 Deploy External Components (Docker Compose) + +Make sure `docker` and `docker compose` are available. + +1. If one or more components among `mongodb/redis/kafka/minio/etcd` are already deployed on this machine, or if you plan to use cloud services instead (`etcd` does not support cloud services), comment out the corresponding components in `docker-compose.yml`. +2. It is strongly recommended to change the default usernames and passwords in `docker-compose.yml`. + +| Component | Location in `docker-compose.yml` | +| --- | --- | +| MongoDB | `MONGO_INITDB_ROOT_USERNAME` `MONGO_INITDB_ROOT_PASSWORD` `MONGO_OPENIM_USERNAME` `MONGO_OPENIM_PASSWORD` | +| Redis | `redis-server --requirepass ...` | +| MinIO | `MINIO_ROOT_USER` `MINIO_ROOT_PASSWORD` | +| Etcd | `ETCD_ROOT_USER` `ETCD_ROOT_PASSWORD` (when auth is enabled) | +| Kafka | `KAFKA_USERNAME` `KAFKA_PASSWORD` (when auth is enabled) | + +3. Modify `DATA_DIR` in `.env` so that it points to a large disk for storing external component data. +4. Run the following command to deploy the external components: + +```bash docker compose up -d ``` +> The current `open-im-server/docker-compose.yml` starts not only external components but also `openim-web-front` and `openim-admin-front`. If you only want dependency components, adjust the compose file before starting. +### 2.2 Initialization Requirements for Self-Hosted Components or Cloud Services -### 2.3 Modify Configuration 🔧 +| Component | Initialization Requirement | +| --- | --- | +| MongoDB | Create the database `openim_v3` in advance | +| Kafka | Create 4 topics in advance: `toRedis`, `toMongo`, `toPush`, `toOfflinePush`, and set each topic to 8 partitions | -- Edit the `config/minio.yml` file to configure the MinIO external IP for sending images, videos, and files. Replace `your-server-ip` with your server's public IP address: +## 3. Deploy OpenIMServer -```plaintext -externalAddress="http://your-server-ip:10005" - ``` +Make sure Go is installed correctly. +> `bootstrap.sh` attempts to install `mage`, but it requires a working `go` command in the system first. If `go version` fails, then `bash bootstrap.sh` and `mage` will also fail. +### 3.1 Go Proxy Recommendation for Mainland China -### 2.4 🛠️ Initialize and Download Mage +```bash +go env -w GO111MODULE=on +go env -w GOPROXY=https://goproxy.cn,direct +``` -Before the first build, run on Linux/macOS: +### 3.2 Initialization (Run Once Only) -``` +```bash bash bootstrap.sh ``` -On Windows: +### 3.3 Build -``` -bootstrap.bat +```bash +mage ``` +The first build may take a long time. Please be patient. -For users in China, it is recommended to set a Go proxy: -``` -$ go env -w GO111MODULE=on -$ go env -w GOPROXY=https://goproxy.cn,direct -``` +### 3.4 Basic Configuration Changes + +| Description | File | +| --- | --- | +| Kafka username, password, and address | `config/kafka.yml` | +| Redis password and address | `config/redis.yml` | +| MinIO username, password, and address; `externalAddress` must be changed to a public IP or domain path | `config/minio.yml` | +| S3 cloud storage credentials (when using S3) | `config/openim-rpc-third.yml` | +| Etcd username, password, and address | `config/discovery.yml` | +| MongoDB username, password, and address | `config/mongodb.yml` | +| OpenIMServer `secret` | `config/share.yml` | + +> The `externalAddress` in `minio.yml` must be changed to a public IP or domain path, otherwise IM will not be able to send images and files correctly. + +### 3.5 Start / Stop / Check + +| Task | Command | Notes | +| --- | --- | --- | +| Start in background | `nohup mage start >> _output/logs/openim.log 2>&1 &` | Recommended for production | +| Stop | `mage stop` | - | +| Check | `mage check` | - | + +> After the first startup, wait `20-30s` before running `mage check` or API verification, to avoid mistaking short startup connection failures for final errors. -### 2.5 🛠️ Build (Linux/Windows/macOS) +## 4. Get ChatServer +> If you already have your own account system, ChatServer may be optional. +It is also recommended to pull the repository and switch to the latest official release tag marked with the green **Latest** badge on GitHub Releases: + +```bash +git clone https://github.com/openimsdk/chat && cd chat +git fetch --tags +LATEST_STABLE_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/chat/releases/latest)") +git checkout "$LATEST_STABLE_TAG" +echo "using chat stable release tag: $LATEST_STABLE_TAG" ``` + +> If you need to reproduce a specific version, switch to the required fixed tag according to the release notes. If server-side integration must stay aligned with the version corresponding to `v3.8.3-patch.12`, also pin ChatServer to the matching official release tag. + +## 5. Deploy ChatServer + +> Note: all commands below are executed in the ChatServer project root directory. + +### 5.1 Build + +```bash mage ``` -### 2.6 🚀 Start/Stop/Check (Linux/Windows/macOS) +### 5.2 Basic Configuration Changes -``` -# Start -mage start -# Or start in background and collect logs -nohup mage start >> _output/logs/openim.log 2>&1 & -# Stop -mage stop -# Health check -mage check +| Description | File | +| --- | --- | +| Redis username, password, and address | `config/redis.yml` | +| Etcd username, password, and address | `config/discovery.yml` | +| MongoDB username, password, and address | `config/mongodb.yml` | +| OpenIMServer `secret` | `config/share.yml` | +| ChatServer `secret` | `config/chat-rpc-admin.yml` | + +### 5.3 Start / Stop / Check + +| Task | Command | Notes | +| --- | --- | --- | +| Start in background | `nohup mage start >> _output/logs/chat.log 2>&1 &` | Recommended for production | +| Stop | `mage stop` | - | +| Check | `mage check` | - | + +> ChatServer depends on OpenIMServer being available first. It is recommended to start ChatServer only after `mage check` succeeds on OpenIMServer, then wait `20-30s` before verifying the `10008/10009` APIs. + +## 6. Configuration File Reference + +- For OpenIMServer configuration details, use `config/README_zh_CN.md` in the currently checked-out code. +- For ChatServer configuration details, use `config/README_zh_CN.md` in the currently checked-out code. + +## 7. Offline Push + +- GeTui: supported. Apply for `AppID`, `AppKey`, and `MasterSecret` from GeTui, then integrate them according to the official process. +- Firebase: modify `fcm.filepath` in `config/openim-push.yml`. + +## 8. Change the Number of Service Instances (Optional) + +In `start-config.yml`, all services except `openim-msggateway` and `openim-api` can have their instance counts adjusted directly in `serviceBinaries`. + +For `openim-msggateway` and `openim-api`: + +- the number of service instances must match the number of ports configured in the corresponding config files +- restart the service after modification for the change to take effect + +## 9. Monitoring & Alerting (Optional) + +### 9.1 Components + +- `Prometheus`: collects Prometheus metrics exposed by OpenIMServer +- `Alertmanager`: routes and notifies alerts triggered by rules +- `Grafana`: displays dashboards +- `node-exporter`: collects host CPU, memory, disk, and network metrics + +### 9.2 How to Start + +Run the following in the `open-im-server` directory: + +```bash +docker compose --profile m up -d ``` +### 9.3 Key Configuration Files +- `config/prometheus.yml`: Prometheus scrape configuration +- `config/instance-down-rules.yml`: instance-down alert rules +- `config/alertmanager.yml`: Alertmanager routing configuration +- `config/email.tmpl`: email alert template -## 3. Quick Verification +### 9.4 Default Ports -Please refer to the [Quick Verification](./quickTestServer) document. +- `19091`: Prometheus +- `19093`: Alertmanager +- `13000`: Grafana +- `19100`: node-exporter ---- +### 9.5 Recommendations + +- Make sure OpenIMServer is already running and `10002` is reachable before starting the monitoring stack. +- If email alerts are required, complete the recipient and SMTP settings in `config/alertmanager.yml` first. +- If ChatServer also needs to be included in monitoring, extend the existing Prometheus setup accordingly. + +## 10. Important Guidance + +### 10.1 Change the `secret` + +It is strongly recommended to change the default `secret`: + +- at least 8 characters +- use a combination of letters and digits +- keep it secret and secure + +### 10.2 Open Ports and Client Addresses + +Without a domain or SSL, refer to [Ports and Firewall](./ports) + +- `apiAddr: http://your_server_ip:10002` +- `wsAddr: ws://your_server_ip:10001` -## 4. FAQ +With a domain and SSL, refer to [Domain and SSL Certificate Configuration](./nginxDomainConfig) -### 4.1 📜 Viewing Logs +- modify DNS records and bind the IP to the domain +- in domain mode, usually only `443` is exposed externally +- `apiAddr: https://your_domain.com/api` +- `wsAddr: wss://your_domain.com/msg_gateway` -IMServer log location: `_output/logs/openim-service-log.*` +### 10.3 Backup and Recovery for Single-Node Production -### 4.2 🚀 Startup Order -The startup order is as follows: -- Dependencies: MongoDB/Redis/Kafka/MinIO/Etcd -- IMServer +Please refer to [Backup and Recovery for Single-Node Production](production.md) diff --git a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/internalDeployment.md b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/internalDeployment.md index 68e44358bc..a6eae8d0c2 100644 --- a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/internalDeployment.md +++ b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/internalDeployment.md @@ -2,120 +2,184 @@ title: 'Air-Gapped Deployment' sidebar_position: 4 --- + ## 📌 Air-Gapped Deployment Guide -This guide walks you through deploying OpenIM services on a machine without internet access (air-gapped/internal network). +The main air-gapped deployment workflow is: **run `mage export` on an internet-connected build machine, then transfer the deployment package to the internal target machine and run it there**. + +## 1. Version and Branch Strategy + +- `main`: development branch, used only for unreleased changes. +- `vX.Y.Z...`: stable release versions. +- For air-gapped production, use the latest official release marked with the green **Latest** badge on GitHub Releases. + +## 2. Overall Workflow + +1. On an **internet-connected build machine**, pull the stable OpenIMServer / ChatServer source code. +2. Run `mage export` on the build machine to generate deployable archives. +3. Transfer the archives, configuration files, and external component installation packages to the **internal target machine**. +4. Extract the archives on the internal target machine and directly use the bundled `./mage` launcher to start and check services. + +## 3. Ways to Prepare External Components + +The OpenIMServer / ChatServer air-gapped deployment package only contains the business services themselves. External components (MongoDB, Redis, Kafka, Etcd, MinIO) can be prepared in two ways. + +### Option A: The Target Machine Uses Docker + +If Docker is not installed on the target machine, it is recommended to download Docker offline installation packages matching the target OS and architecture on an internet-connected machine first, and then transfer them to the internal target machine. + +- Debian / Ubuntu: prepare `.deb` packages for `docker-ce`, `docker-ce-cli`, `containerd.io`, `docker-buildx-plugin`, and `docker-compose-plugin` +- RHEL / CentOS: prepare the corresponding `.rpm` packages for the same components + +After installation, use `docker load` to import the external component images you exported in advance. + +### Option B: The Target Machine Does Not Use Docker + +You can directly copy the official binaries or internal artifact packages of MongoDB, Redis, Kafka, Etcd, and MinIO to the internal target machine, and run them with their own systemd / supervisor / script-based startup methods. + +In this mode, OpenIMServer / ChatServer only need correct addresses and credentials for these components. Docker is not required on the target machine. + +## 4. Export OpenIMServer on the Internet-Connected Build Machine + +```bash +git clone https://github.com/openimsdk/open-im-server && cd open-im-server +git fetch --tags +LATEST_STABLE_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/open-im-server/releases/latest)") +git checkout "$LATEST_STABLE_TAG" + +bash bootstrap.sh +PLATFORMS="linux_amd64" mage export +``` + +After a successful run, the deployment archive is generated by default in: + +```text +_output/export/ +``` + +A typical filename looks like: + +```text +exported_open-im-server_v3.8.3-patch.12_linux_amd64.tar.gz +``` + +## 5. Export ChatServer on the Internet-Connected Build Machine + +```bash +git clone https://github.com/openimsdk/chat && cd chat +git fetch --tags +LATEST_STABLE_TAG=$(basename "$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/openimsdk/chat/releases/latest)") +git checkout "$LATEST_STABLE_TAG" + +bash bootstrap.sh +PLATFORMS="linux_amd64" mage export +``` -### **Docker Deployment** +A typical filename looks like: -1. On an internet-connected machine, clone the repository: +```text +exported_chat_v1.8.4-patch.3_linux_amd64.tar.gz +``` - ```sh - git clone https://github.com/openimsdk/openim-docker - ``` +## 6. Deployment Package Contents -2. Run `docker compose up -d` to pull all required images. +The archive generated by `mage export` includes: -3. Save the required images. The command is: +- prebuilt service binaries +- `start-config.yml` +- required runtime configuration files +- a `mage` launcher that can be executed directly on the target machine - ```sh - docker save -o image-name.tar image-name:tag - ``` +Therefore, **the internal target machine does not need to install Go and does not need to rebuild source code**. - For example, to save the `openim-server` image: +## 7. Transfer to the Internal Target Machine - ```sh - docker save -o openim-server.tar openim/openim-server:release-v3.8.1 - ``` +Transfer the following to the internal target machine: - To save the `mongo` image: +- the OpenIMServer export archive +- the ChatServer export archive +- external component images or binary installation packages +- your real configuration files, such as domain settings, external component addresses, `secret`, and MinIO `externalAddress` - ```sh - docker save -o mongo.tar mongo:7.0 - ``` +## 8. Deploy External Components on the Internal Target Machine - You can use `docker images` to view pulled image information, or check the `.env` file for image version details. +### 1. Docker Method - All images that need to be saved: +If the target machine uses Docker: - - `mongo:7.0` - - `redis:7.0.0` - - `bitnami/kafka:3.5.1` - - `minio/minio:RELEASE.2024-01-11T07-46-16Z` - - `quay.io/coreos/etcd:v3.5.13` - - `openim/openim-web-front:release-v3.8.1` - - `openim/openim-admin-front:release-v1.8.3` - - `openim/openim-server:release-v3.8.2` - - `openim/openim-chat:v1.8.2` +```bash +docker load -i image-name.tar +``` - The following are monitoring and alerting component images (optional): +After importing all external component images, start them with your component orchestration files. - - `prom/prometheus:v2.51.2` - - `prom/alertmanager:v0.27.0` - - `grafana/grafana:11.0.1` - - `prom/node-exporter:v1.7.0` +### 2. Non-Docker Method -4. Transfer the **image files** and **docker repository files** to the deployment machine via internal network or physical media. +If the target machine does not use Docker: -5. Import images into Docker: - ```bash - docker load -i image-name.tar - ``` +- start MongoDB +- start Redis +- start Kafka +- start Etcd +- start MinIO - For example, to import the `openim-server` image: +Then write the addresses, usernames, and passwords of these components into the OpenIMServer / ChatServer configuration files. - ```sh - docker load -i openim-server.tar - ``` +## 9. Extract and Start OpenIMServer on the Internal Target Machine -6. In the repository directory, run: - ```sh - docker compose up -d - ``` +```bash +mkdir -p /opt/openim/open-im-server +tar -xzf exported_open-im-server_v*.tar.gz -C /opt/openim/open-im-server +cd /opt/openim/open-im-server +``` - To start with monitoring components: - ```sh - docker compose --profile m up -d - ``` +After updating external component addresses, `secret`, and MinIO `externalAddress`, run: -### **Source Code Deployment** +```bash +./mage check +./mage start +./mage check +``` -1. On an internet-connected machine, clone the server repository (recommended: switch to the release-v3.8.2 branch): +## 10. Extract and Start ChatServer on the Internal Target Machine - ```sh - git clone https://github.com/openimsdk/open-im-server - ``` +```bash +mkdir -p /opt/openim/chat +tar -xzf exported_chat_v*.tar.gz -C /opt/openim/chat +cd /opt/openim/chat +``` -2. Clone the `chat` repository (recommended: switch to the release-v1.8.3 branch): - ```bash - git clone https://github.com/openimsdk/chat - ``` +After updating Redis, MongoDB, Etcd, and the OpenIMServer `secret` in the ChatServer configuration, run: -3. Follow the [Docker deployment](#docker-deployment) steps to save images, except you do not need `openim/openim-server:release-v3.8.2` and `openim/openim-chat:v1.8.2`. +```bash +./mage check +./mage start +./mage check +``` -4. Transfer the **image files**, **server repository files**, and **chat repository files** to the deployment machine via internal network or physical media. +## 11. Common Runtime Commands -5. Import images into Docker: +OpenIMServer: - ```bash - docker load -i image-name.tar - ``` +```bash +cd /opt/openim/open-im-server +./mage check +./mage stop +./mage start +``` - For example, to import the `mongo` image: +ChatServer: - ```sh - docker load -i mongo.tar - ``` +```bash +cd /opt/openim/chat +./mage check +./mage stop +./mage start +``` -6. In the `server` directory, run the following commands in order: - ```bash - docker compose up -d # To enable monitoring: docker compose --profile m up -d - mage - mage start - ``` +## 12. Notes for Air-Gapped Deployment -7. In the `chat` directory, run: - ```bash - mage - mage start - ``` +1. `main` is the development branch and should not be used for air-gapped production packages. +2. The runtime dependencies for the target machine are already bundled in the `mage export` archive, so do not rerun the source build workflow on the target machine. +3. If the target machine architecture differs from the build machine, specify the target platform with `PLATFORMS`, for example `PLATFORMS="linux_amd64" mage export` or `PLATFORMS="linux_arm64" mage export`. +4. Whether you use Docker or direct binary deployment for external components, make sure the addresses and credentials match the OpenIMServer / ChatServer configuration. diff --git a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/nginxDomainConfig.md b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/nginxDomainConfig.md index c043530cb3..17bffb414f 100644 --- a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/nginxDomainConfig.md +++ b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/nginxDomainConfig.md @@ -1,59 +1,57 @@ --- -title: 'Domain & SSL Configuration' +title: 'Domain Configuration' sidebar_position: 7 --- -# Domain & SSL Certificate Configuration +# Domain and SSL Certificate Configuration -## 1. Prerequisites 🛠️ +> This page only covers domain configuration related to OpenIMServer and ChatServer. -- **IMServer** is successfully running. -- **Nginx** is installed with the SSL module enabled. -- A domain (or subdomain) and SSL certificate have been obtained, e.g., `im.yourhost.com` for IMServer. -- Port 443 is open. +## 1. Prerequisites -## 2. Domain Configuration Template 📝 +- OpenIMServer and ChatServer are already running successfully. +- Nginx is installed with SSL support enabled. +- You have applied for a domain and SSL certificate (for example `im.yourhost.com`). +- Port `443` is open on the server. -> 🚀 **Tip**: Make sure to replace with your actual domain name, SSL certificate path, and SSL key. +If Nginx is not installed yet, install it first using the package manager of your distribution (for example on Debian/Ubuntu: `apt-get install -y nginx`). -```nginx +## 2. Nginx Configuration Template + +> Replace the domain, certificate paths, and service addresses with your real values. The comments below explain the purpose of each block; it is recommended to replace them line by line according to the comments. -upstream msg_gateway{ - #IM Message server address Multiple can be specified according to the deployment +```nginx +upstream msg_gateway { + # OpenIMServer WebSocket gateway server 127.0.0.1:10001; } -upstream im_api{ - #IM Group user api server address Multiple can be specified according to the deployment + +upstream im_api { + # OpenIMServer REST API server 127.0.0.1:10002; } -upstream minio_s3_2{ - #Minio address can be assigned to multiple modules depending on deployment - server 127.0.0.1:10005; +upstream im_chat_api { + # ChatServer (APP business server) API + server 127.0.0.1:10008; } +upstream minio_s3 { + # MinIO object storage + server 127.0.0.1:10005; +} server { - listen 443; #Listening on port 443 - server_name im.yourhost.com; #Your domain - ssl on; - #Path of pem file for ssl certificate - ssl_certificate /usr/local/nginx/conf/ssh/im.yourhost.com_bundle.pem; - #Key file path of ssl certificate - ssl_certificate_key /usr/local/nginx/conf/ssh/im.yourhost.com.key; - - gzip on; - gzip_min_length 1k; - gzip_buffers 4 16k; - gzip_comp_level 2; - gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/wasm; - gzip_vary off; - gzip_disable "MSIE [1-6]\."; - - default_type application/wasm; - - - location /msg_gateway{ + # Unified external HTTPS entry + listen 443 ssl; + server_name im.yourhost.com; + + # SSL certificate and private key paths + ssl_certificate /usr/local/nginx/conf/ssl/im.yourhost.com_bundle.pem; + ssl_certificate_key /usr/local/nginx/conf/ssl/im.yourhost.com.key; + + location /msg_gateway { + # OpenIMServer WebSocket reverse proxy; keep the Upgrade headers proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; @@ -62,7 +60,8 @@ server { proxy_pass http://msg_gateway/; } - location ^~/api/{ + location ^~ /api/ { + # OpenIMServer REST API reverse proxy proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; @@ -72,42 +71,71 @@ server { proxy_pass http://im_api/; } + location ^~ /chat/ { + # ChatServer (APP business server) API reverse proxy + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header X-real-ip $remote_addr; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_pass http://im_chat_api/; + } - - location ^~/im-minio-api/ { + location ^~ /im-minio-api/ { + # MinIO file access reverse proxy; keep it consistent with externalAddress proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 300; - proxy_http_version 1.1; proxy_set_header Connection ""; chunked_transfer_encoding off; - proxy_pass http://minio_s3_2/; + proxy_pass http://minio_s3/; } +} +``` +## 3. MinIO Configuration +- Source deployment: modify `externalAddress` in `config/minio.yml` +- Docker deployment: modify `MINIO_EXTERNAL_ADDRESS` in `.env` -} +Example value: +```text +https://im.yourhost.com/im-minio-api +``` +## 4. Reload Nginx +```bash +nginx -t +nginx -s reload ``` -## 3. MinIO Configuration 🗄️ +## 5. OpenIMClientSDK Initialization Parameters -- **Source code deployment**: Edit the `externalAddress` value in `config/minio.yml` to `"https://im.yourhost.com/im-minio-api"`. -- **Docker deployment**: Edit the `MINIO_EXTERNAL_ADDRESS` value in the `.env` file to `"https://im.yourhost.com/im-minio-api"`. - -## 4. Start Nginx 🚀 +```text +apiAddr: https://im.yourhost.com/api +wsAddr: wss://im.yourhost.com/msg_gateway +``` -Run `nginx -s reload` to reload the Nginx configuration. +## 6. Gateway Path Mapping +| Path | Service | +| --- | --- | +| `/api/*` | OpenIMServer API (`10002`) | +| `/msg_gateway` | OpenIMServer WebSocket (`10001`) | +| `/chat/*` | ChatServer API (`10008`) | +| `/im-minio-api/*` | MinIO object storage (`10005`) | -## 5. Update Client SDK Initialization Parameters +## 7. Naming Consistency -In the client SDK, configure the initialization parameters as follows: +- OpenIMSDK: the overall project name. +- OpenIMClientSDK: the client SDK. +- OpenIMServer: the IM core service. +- ChatServer: the business extension service, i.e. the APP Business Server. +- Accounts calling management REST APIs are uniformly referred to as APP Administrators. -- `apiAddr`: `https://im.yourhost.com/api` -- `wsAddr`: `wss://im.yourhost.com/msg_gateway` +> The current template only covers the common external entry points for OpenIMServer and ChatServer (APP Business Server). If you also need to expose APP Administrator APIs through the domain entry, add an additional `upstream` and `location` for `10009`. diff --git a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/ports.md b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/ports.md index 798c83c1c6..5aabe306c4 100644 --- a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/ports.md +++ b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/ports.md @@ -1,56 +1,50 @@ --- -title: 'Ports & Firewall' +title: 'Ports' sidebar_position: 6 --- -# 🔐 Ports & Firewall +# 🔐 Ports and Firewall ---- - -## 📡 IM Server Ports +## 1. Without a Domain Name and SSL Certificate -The following ports must be allowed through the firewall for IMServer to communicate properly. - -| TCP Port | Description | Action ⚙️ | -| --- | --- | --- | -| **10001** | WebSocket message port for client SDK communication | ✅ Allow | -| **10002** | API port providing user, contact, group, and message interfaces | ✅ Allow | -| **10005** | Required when using MinIO storage (default in IMServer) | ✅ Allow | +The following ports must be opened on the server. Other ports should not be exposed publicly. ---- +| Module | Port | Description | Action | +| --- | --- | --- | --- | +| OpenIMServer | TCP:10001 | WebSocket message port used by OpenIMClientSDK | Open the port | +| OpenIMServer | TCP:10002 | API port for users, friends, groups, messages, and related APIs | Open the port | +| OpenIMServer | TCP:10005 | MinIO object storage | Open the port | +| ChatServer | TCP:10008 | APP Business Server APIs such as registration and login | Open the port | +| ChatServer | TCP:10009 | APP Administrator APIs such as statistics and user bans | Open the port | +| Web frontend (optional) | TCP:11001 | PC Web frontend; needed for browser-based quick verification | Open if needed | +| Admin frontend (optional) | TCP:11002 | APP Administrator frontend page | Open if needed | -## 💻 Web Frontend & Admin Dashboard Ports +In the client SDK, initialize with: -| TCP Port | Description | Action ⚙️ | -| --- | --- | --- | -| **11001** | PC Web client and admin dashboard frontend resources | ✅ Allow | +```text +apiAddr: http://your_server_ip:10002 +wsAddr: ws://your_server_ip:10001 +``` ---- +## 2. With a Domain Name and SSL Certificate -## 📊 Grafana Monitoring Port +The following ports must be opened on the server. Other ports should not be exposed publicly. -| TCP Port | Description | Action ⚙️ | +| Port | Description | Action | | --- | --- | --- | -| **13000** | Grafana monitoring dashboard | ✅ Allow | +| TCP:443 | Default HTTPS port | Open the port | ---- +Configuration reference: [Domain and SSL Certificate Configuration](./nginxDomainConfig) -> ⚠️ **Note:** -> If your server has a firewall enabled (such as `ufw` or `firewalld`), make sure the above ports are allowed. -> For example, on Linux: -> -> ```bash -> sudo ufw allow 10001:10005/tcp -> sudo ufw allow 11001/tcp -> sudo ufw allow 13000/tcp -> sudo ufw reload -> ``` -> +Complete DNS resolution first so that the IP is bound to the domain. -You can also use online port checking tools (for public-facing servers): +In the client SDK, initialize with: -https://portchecker.co/ +```text +apiAddr: https://your_domain.com/api +wsAddr: wss://your_domain.com/msg_gateway +``` -https://www.yougetsignal.com/tools/open-ports/ +> Monitoring, frontend, and other service ports are recommended to stay internal-only and be exposed only when needed. -Enter your server's public IP and port number to check if it is accessible. +> If you want to access `11001` directly from the browser as described in [Quick Verification](./quickTestServer), the frontend port must be reachable. If you do not need frontend page verification, you do not need to expose `11001/11002` publicly. diff --git a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/production.assets/0.png b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/production.assets/0.png new file mode 100644 index 0000000000000000000000000000000000000000..4703f00f9af6246ec5464c503225d6a67e6cd8a8 GIT binary patch literal 128928 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

r|ZG%dKgbf@_`?WFhEuY&s4=t0>4t|bTHTCbup zorSwl$? z4td|FMFm3K9x5bZ2~=vBx{RsHLf+bt7GcPAv;Cb94a3)vS0$W}6$`y9eoip_&jtn* zG&eAp)fH68VJ)$U_qP?jH6zyE4CAlPG7oG-J#E~eY*8LP`|cH&MpCd5h_TzwOa4Y9 zImwb71@HN8guZ@kFNs5R6)jZZq=*%heY^$Bi3GI){v_;)ru&YN7)wKg%hS{YW)qR- zBW*R@jvVc4M~*g`GTSIY8d@$YS*04Kr{dAx2r($QIV8X;$8!3SLQ%!T9TX?ACFeaC z#i0@pHTWV@-w6oG zjeqte%#H;vi)LPYiZ`_?DH>R%(w`5zmJC7c``evsvIRwWmqDE-z_>7{-|}oJ3Z|hZ zd6nJGMH;%Rp7#ZQUUXuDGK#eTXHAm(+kW)q0}>@`wo$7e-|__#-`@MFP0Vw-ghjy> zQKo1xenmufTWhzO3k`lR(djHnnj%___ySAIO2_Gz50#=K0U9ayD~*InnZc~2q!Gig#Azw`Av`{crCmsN3i9*PekZz&i$awLi(j?Bjaqr3OH zEfn)tG%mRUz>qLfFl?|~tPI#e6I(*Wghr}+CONW2X^)Vqat^d&P_L@yowXsi#m&sZ zwZ6VTXWZY!u$EoMQ`N@0->p1~7g)V5l<1#!G2m`NO%hNhReqC|PI4Y)_{6Z8??&DC zFq+A#kJv%j+Oyi_&g4CvD6827_-9^VZ6^hJh49!7z_1p#61D3f&8|I7W4wHM+>RV} zs{+6imwRpj;Y4*(%9g9+*EK0eUc5av3d^ug2H>j#`&&h++Wcm&X6wzGw>yR5bD#jV zR8Wvo6(4zwHHGnlnXkmF?W+*kL$o@Vc>=h_@#(VjL>aKv#l48KB>>FwCqH74z**wF z0p4n+IIi~WL$|tN5iHz!#PB|v@z7DDH(RcUJcI!{7hxh)zUIzw$3a50IU8%e;+?%2 zNTxkqk)8;&&8dz0Pqub`neeYND$vA_q)Qp3OnW6z&x^$9^pes-8LUDOxVPq}8eAx8-)pOyfY5Y3qAFM|TnIrkkn3j)k^DtN^F)27em z!dX2=v)I@Kn)VC`o4o^Pd24HZg|iyEd01@MUjxaCJ_q$#XtKFEu%EeKm5vxP69;Wej7-7)h6J9)Znk=pk4M^zp94@O;L$J#`Q+_$w-$F2a+{wVE_u9M zmQQ?VL0_-uemRsBYb=5;(hL0hC`(F7L0v_%fVsBzG%7+R5ZJ+az#L<`o3_PgVg4dF z9jDE&SWYj84e-F zm+Zl=M;X^%_o&c*ORXR4316Lq_drJsE!ik}FXAg-6@53R$yNby890(3XWoipwJ9f@ zt8ixa_1NoBGZ(Rr1)edosp5)(0fvT(WJSl92{Q4|Od3oB7r)@l$7#{997{JF&rH*X z*p2i`S4h0$rQ7%$Jd#!2W_R6xPb6l@xfmo9HV;#+H10X$f_x%$$i?+w)OY4StzMhXy;?m=c~KLoQFD~BPp|Jv~SfUb~4Wt za}r_gq(7Vc7owC@@dO8kLU9BnfgUtQLu8I1Yz8+YOhWQ_9W>T~>ydJ`?W&fQc!7<4 zG-tG-6&<)1Due7TPmNxm<1B3~fM$EETWby0*C`t#p~~*Mo`XO}z2*+{S#5#V1MtsS zl>BOBvb3P;R$X9I|CVB)fV-Z6xDRgwmJf8i0;{vJou#Cc*Fqx3@V;$b9~z%u-)@@1 zZo(hfSx**xH+MRMFH2i#-d1PdMD2UO7NMgAUhO2eAcE9!EC@IfKzu6J*8`?V}T@Qc4hts~|H}|*c7F0i{Xi*YJ!NZ+fM1x|ux3-JLE~+HA z%C78ZrFjBhAehdtNi-h?;*|=purWahqr zyRCNz-Zwx(rz85sVwPYhsO~pv8#!2F-=^89!$|2!N3PUH+e{I`rQVf$s4~?U6zQRI zFlNM(uf8HeA0FZL)mJn)l8Ml=(ja^$+?VYd8L*HuuXF4EGnO#p+Ogndy<}n+wqebj zXecJwgiESIJX4tL3!UbSv>Js8RUF_eB2nrPo=G6gintO(d4dKQo1?#D!CHo|y#~&c zYb=`i*#k_s7V1zQfmfurCF`L=wl0Hr)VlX`;HLzd$Xh6o(*^=GK|UQ@*uo@v7L}^-XIxarR!?4MTM^ zH^Fh>WPIVN&1f!Pa5$cON(nfSvYijH&)7lj?3-4@0*K#4MJ@uz>Gw@z9u-bl`T6X# zv+>)bj7}$B&j-1roJT;xdvPd&!(^+@i>`zGFFnO)y=*( zPHMOVs8N*}bF$qDNMF3U0s9hwngpsPj zGh!#QRAuL~Y@`ZgR|nkDwS1wN&x4??o-8N3C-W33&?4I%Fco3Nc+7=XJIG>y=P``( zHnrmrky_Z1ziCt9>_WR~kL2!dha{TX8@toNm@UP{!%Ade=e!W4)Xc%wl)zn(^L>D+ z$j!q>>*CpIiT<_l8!pDFCP?$8Gp`m+08V%N67o7+vhy6zU_)^~iwnoEwv=ghmdEa#z4 ziqzKou=mr9a>&^7J+KMP7R0B!wz0=D6YTjqbTsq;G*7bN64ZJ&HZY$svNwcg4xco1 zE8n$7`t)v9F8i?)(oGeguyeS(mDkS(GEfvfkJH+$*tRNX%g&irIv+ zl1=z+lO+RHZTV?a6a4PC`hju9K9hpa-%%A&ac!h0^w#cQ<<04i`mKi%u&eB4x`+!_ zvIac#faK6R>>@6t0}Gs81bPK$#0O_9+p@J~;QgWP^S>z%^yB7bVzAl25AgQq+CQk+%V`FfB)|YB zw>Ac|*<9u}C>y$S7Yf+1h%vEwbFa@y{BFDY+YGnMu*Ml9MR)iNvgkJ_+Yqs%;9 zG^{-L`!4&jXo%=l7lr~YHEQIc7Y?!_2%pz?T$v%|hCVQuws=4TMisil?E);{bE$-PdI@-nT`^ccvI(D6=Q;{6kA#*5bElCQsg#ReR(VwaT!OQy=gY$ zaPlj5gt%o``qYZ;O@0nZbhUdn$yxDoG*4EK#ysKt5g*V|+(T8cDm1tYlVtEnt(tL< zIBn2(=c&kDI#VQOe#7jmfCShgFM_b z5Z6;QK6MBQaoJ3gbS&a}ibS;(zj}(W$&0*-Tu)I4*Hbh=@pJ?xOAJpx-tiO-md-Ub z9+lc6Q7lVJ zQLS>wW)JJ7^!9z?k(!Ey6`hW_+ILd>v^Ur{>Wqf+mdz`4MNud###0Cl~jp| z&66-k!Ne;EpGqh{nL}^bAEQ3C8-ZqrN?1oSg-8G3Je?~87$#s*Yi7@^1Rq=V!cCl|r8iz`Gn_sqKp87H_%~a9lx8xJ%8g<4}afv;U>}^^4&%5u7ANLM#x%~NH zUZ-JoDLlJapjq}}3O>0WE2(@MhDPaPKKHb^MK(DSew}rTnH8Cg7z$Q-u*$OU+}{YmTrGaN-QgVmXnB@XfK*W? zMl;+zo^HJjeA?t4~Y?eeVlxDZS$*|PseHT?5x+q30( zL|Ut7OU$$t{$yvmk9Q=Pf7csH0KkJd?BHJ86=f;RtX3_0y!6+=UoUP|NQyk}9q6MA z3QDF4qmH#{t3Mv_xhAB1O@`7q$R7rruQooPHHmU1{vpq)VbG3kLLKtxcWFAiS5jmb zrLx*N%|prC5s&Cn!vEC$P(0=NqI6r>d}pOcvBwj55XulbhFyM+kstYGeGo}A)aWTO z5wc<@w77%i+PXS(;Q(BeJ0d-CF_MB0Jm+2*6Uj=#v&9jvb&eCsf!|CQ24=6 zANE78fIDLGMIxH!w?8^dRc9dhBS_$J;-?A$`d7}#hxotdjMi`J=9vI3H?fZPQdAez z2z#Ne-rqn%*~BqFx&m=Pd{e*m4`89}|C!nBrH^LPT#I&HQp25{u#=-8IqUVsot4~6G(akQp=4Co}Q+bv~ErKM(! z;=ecVHI-LMaa;d6quAf6H${EP3m)D9%rNwX0n#GBt+|dhzl)$H7Y!W5?R^%UUxaUd z!a8aQZ$}f%J_n zZ%31Y^Cb2!T*ak+ziI7r+Zt_utTJD4`p52rVCfNnoH;CFCnT9>f7WOzJhh|Q%3e-1 z#_{X&-M5(6;*CByjgTP zUJXny6UpDxJ{Yy#@EfvQ4^r;+xfieu1`Uo#_-O5Nbs<=3U+5QEI-V`gd<6_b98W4D z>QLw4E4UjSd{6x|-l=7}=YiUdv#?#RX(&kakgdUzl>T>f#d&a7EV9z|rp2^_5y3g? z06zIE70h8)x20>>{+8}Q`}~jYGa4n)A=k7&36%Z8%uC^O37@h zjdGK2G{~1QkiKIIe9Z`xL4@BQ2%|?3>^zbotVL83Fh&nhJ`SAP73atzf@=_b&>ZJR zF64&!m+Bd~v-+f^$h!4+eqk|_KSX$Dca;w*dx&}L%dc5H%D8i=zsn;_Hl2QnO&vc( zew;t~jD2j$_nIwAwt*2*YidMC$wzJwvByXP^6!YL3jM_NTx%i@19!1cP|QWafzXJd z$;h^Xa|LTPqSQtlVeUS%rI97PHv-|c%9L3e7n)r3Xtc?S*Im9+iqLA;t5k;h(W;;U z;rNd(^*%A*FbgveJ|o)LzS06-it4b#!nI$>_nn%_T7b!<4&aZ(Ge4QTRr!k}D~Ina z$JoPGJ~@Ndr?Qsg`m9H+Wu3*LDC@+$xkD5Ucl(44jz%_#&rX&Z9>Gx%PcIjVEH%A9 zwuxcW$o$Fysguh5X2Uh>AW5dj%~>#IxG!0!?4DMRlxMQ+0PjpkOZUq^4jlk~&_iye zgnr@94Dlt);o(Pw56AWe)>YO-M{50*P^Xs1%Sxy8J~045OKh?y3!F4R`bBg}^wCtY zKj(^SAmC+IJGWx@Z70%{1rKE(++Kqf|G5~^hsdSig%azustpad7WpkkPF4B_pJha_ zBARko6A4lafdSZn;2`YEIIppObdmEqvLPt=bhQ{tG2PK=b7h^j=(^za{Ux;(*y2Em zQR0tlEBy6a$ZeTX%U(h`z9=HV`u9d!_OR!GBlxbNkUDn)SB{@_7@J$)G!Bj(+YGuJ zg!M)5uZj4AmJ?A3Uj2*me`%zBvCfMQp$rG{Ldg%eLNZuen^o5Cvc@6swXd8deo|t; znT~@F!rU}T8B}lE8Xi?3GaCI^W`s0KQBS$m6Srdr?}rOt8(%j>UfO1AJX`F+3bj`f zEFhf-#73;SbPa*nD2?*=pg?Ry*!bRSL2tIrFN~YtGTDs|jx88SA?a+X29Nl`cNIFp z#aMzO*sM2JWSQCavO2+>xi3H?W3Qp;QaNjJS3av7S!I5uX3-@b7jwlc@gTRzXQtFp z5BH!OS~@bz&=VUyo6_`OfgNpTg+l6e$D4%}!RZC>B|0koQ?VoBW6Sb5UAaBTArGS@ zSn6w~i5CVrJKp+l^5|?qGc94enPv`}X@vV6A2sA(kY=c0=kEt!A45J+PrMvy=M)s* z%If~@is(^|;&=&$`YX?tI}Pa%*tn&U*|y4^+rtvtYSbu_%Czom7GGD=8sbsMvYHfB2{KG(y-dgN z@w9b25~L_r|9Ut1lC_^6+#pE)?UCj1z66MEiuBxK@?uO-4f|Y8LfN5#%Pp}3jyk^h zt$<19HEdPJhDq@Kfr#Z}My+!~!FTb^^;a~%-q+w;rquNp*lxJ58uIOQpx*;k87P;+ znAmLN3>WCzjPDGYmrLqUJ+0wQsu+)XGs&$Kwec<+w(4CF#m<3x+L;tkPdhq2kuzBN zQhZ+FTza#eHY8*xY?dYnr-3g~F-Ka`e2~DLRrPc(FQL#5sb9L-Jy9Bk5Ro0y9GM#4 zQ^?rbms=8JCePP*lP}ZHZiAqQa6PwABwX*OLqPR#$F+DX3gsH^RKkEz4az81#Dopomc63Lq@hc*J;o-+7&%0a^whOwsUYYt5g-8}ecuA@H&C z(^48Cmi>Afi~cy|?6+Q10oELRiUfls3hHTbbA_bq5ycr9L33qHP2X?Uru{Y@yL`RD zwF75qe01Id5fWG9oab!0CjZhWvbi$}1!(Vr{GtRTdIx^p zdRA;a!f@WjXr^9n9=fJ6m_Oz&F2UF7J&sQA@9MPb?Y8Ni>OGG}+A~lW#AD)P5zpS`$0f$AYYi8w^~Qiq`#`VWzW};z;@T< z)ZD9%GBzlrO0s>lhS-*VS2s=?agTw3ng-a8EP5SZyQ)6>s*0A0F+*EAdBTmf4VOw3 z^N6iv4Nbks?sD6()y}p3r{;Qs51}`9PWl0t>BXpFk6OHXS_pp_n+927=rV(jTfO$M zkBH16lami-4&>Lb{zcb2n`|~{qNz6YUW1gM6F0i=-GI3vOcNFPwGkVo5I-ND*diVp z!yL+tC4}V*k>$>%!L_wqvm7QEG+|D=$p&YI;dmtkVTCP(01`D#sMPtQjZ#l)I8!OrDZ1Lb zbd&?)Vm7?Yx$c+KCJ7~}AsavKpvRVic3LE8r>WPArVxrcN&GMKDckL|IC31mT!tnbV zYgUrx&&;}0Q=Uk)&?IfQ(~c8BJ8hPBzXOnIuU9##dEQ71mcPBmAy1!d@%y? z%<*Yip2P&OJ~8DoFSEqgp~)`UT7|b5Y-vW;Q>jV&*4PRa^Z+#2S*!3hZPiL~=xx3L z7YZ!s46DZX6m(vkTTK@!z16IN8r7?jgc1*T$C%Fj_8rF&dXqz$Vp?&p+_osa@oCpo z<)@^#9qD>1;gaDYo^^RIUfHVSxo zHEy{~_!SyG6q&;moq=8^3W4d^9UisaV5^sHrFEZGt_hN^I~wO%V6saWx_DndmtX}M zA000xD!7e!wdsjzs?b{m^J@Ra5E3-Nec@=c=$SPp@F3MoPnuXOCe9A`Vp>VgC*ZDt z;i>49S_U)P51(%Su-XYpQ_56;!?rz05625;KAFK+{T5Hoge#6cBl`RGy#*W%XS}P? z%3-P3TuniVe10L=IJOZ+c(Xc!DgZ`ugRdgx22jFN*KoNb}sa4br$F&JFfl za`p>2NX4t4%*)ytGUTCxQNf|m!TB}qi{1GoE95T2)?9%GspnE_OfcOV71H^Y3WwB# zuwTkC@`HOV6|tsaGoP?jXELd`)HS5OQ#NzU`CXbTGj!wZA(o98)vxTaWAw~8c}%QwaqRBHMlAsm)Y7&HnmLlz z>~l`sYlA&>a_6@`c#g|uuo{$GLey&hv7E&AKLeacZ2NQOp1lILA?-3PK{I#8_Mlb5 zcorBYuxS3hxqT}k))(xZ>VtZ(=AYz_-A%?t;wmOd36A;*XY3TLh}d#M^H3ep(A@e@ zV6D6dPy(jqnv^A_^9>@ix+f!8GHOrdI5S(y`&Uw#N~vD2-K2e~&qm@z{OW}M*_0HP z!4V?|sbA^bTnV(GkT#Gnx{+z@T1=s)M~msL>75x@kZ$P)cc!=dzg_JKD%y4-Vg3yl zQZ+D)nv9|~Zvz*S(mNMYVQ6^#sKmgypPrjR!G|s+-FGgeuiUhGV0)N>2KY<|m{O^GvolvC+@FTpsZAQK(cdyW7F65o?CtDMnsvU{8@ug78jhcBF_CWriLnb) zb3|XXZ)tE~R7y7Nq>q~ePS-7Me9v0e9%-B!n_nx7cTr|*&OS6{5vU<(Zv_V5&(sSu7+l*DgH`9@@kcd zg9WtrSc+^frWv8T6LCkY_^MOY>4-xoK`jkewp~lZJMw~T#>tYT99rX@;Jq8Ep1`M;(`kBWnG>N#$nA^gqAPw14yg=)ze)~cgn0! z*u0h;+`AT|Wn_x3l`iIG#^jUkP@el^kk7_&hW3iZw<=%g0Dx+V$w^lmU6yW~xupcA z)|589>~kt(2HQCvs)N>E1l~HL&&rK$2|y}*>+9i%>u$b!`7s7M5p)rZ410Z4l4W2o zFhK$QL|}rV%&u0HqHz7pIrtk=jbCwFZOMd){FrChs-6M2D36F-%XyZLlNPb0$l z%)n$IVplb|=$3S0p6>!_Gt;W3gFiYW$Nci_^A3#Oq7fZ7V4Wb4hHJ@Hn65`1;5;P9 z$lQTEyr#)5iLN(^Q>)e*bdpJ#o3WT0Q0D7+#l~+Yg{OOBj1s#;jkv`>ithbcR79ou zR+qN75=I7t^ba=R*|vCM21I;l(~%msH3IH6{4>a9xXm_fE z^$09a4Z8r{2n2BAG%*cmz;1gPY%r)~15;FBkA2WY4q&xR%XOo1GS2m>?nc-{1)T-U zNdeFP3caxz?kb!G;0;E7hX?V}cPfR1w5RVlm0emx9xz|MYja;pv0os$Q`~RBOZB7F zA}l^(J$$VW9c^w-X_qP5)#P7x#i112Xk)B5y+b_}l9(sl^a3eg#D)GCKYh+ndg`*@e;IHreBHI(lTW699&7E22jo`4b7C0>d zJl0mfxaeiZC~0itDGSaRhTN6;j=b9VtwsI=z6?M{m=+K^`$q_)9P59CF!H9%svo>_ zjC4SbCHNTd_1j!%G5TV;eKlm{4CEZSe1oN?sywQ73uuuZ-K02yhL%%eWTnQOkgIs$ zgy+S}_2m&3y}o)L26Da+?`DIkV8z^QwF4NMC~ocZ9{_b)VE7Z*vI=$jyieLJc>nax ze`SrTgTOm_-K%O`u4RQDWns;vE99F#1rmw5`z<>+MoNzj4y;=NFH-3ySx1maGr+iJ z%g~NWz$v1o~4wyWPfjmdJGgW8%Wnitw8xcY8A6;c z40ZaZTl!=N3_kraWMtOo9b?{a@*b%gNk0aAJ4182@A?En#gB#)G@Bc8TAB?8Wo5x< z1&n2fvsPu3UquaAy)UBa1d2Hz>2!PK8UoL2#c{r)aVlP{9mrm{6>V^XMGq(D^*(Mn z9mDYeyx>4;$k6y&v&bgPbYpRJ!j!UJ6IbmyaJdP47mEoFh~>=;O$=vmU|oY3jZ(@o zNj|`(>2iFw!KF)~LMEPBFSXuCKOia>a)UDFy>*!Kpe*Y_ba~h)QyB7SfT_5-buC$u zIS%V>5^A5oE-bBVY~5UDZt7I10iN`emHiFjj*36rEmDMXT?7vR={-V57ZHKPmf%VC zcmh#jc2+nV)0V?GiDZIJ((zcyD1(FEPpdsTl3!5yoqq&r0BWk?#dYoLsTO?pSOXf( z#z)iHOE&{HD@HX2liam7d84U$LJLC^H$3iRkS9BTbu|vb;g@Yp{R^Yneiy`dl*#m# zJR!XZrc=pQN~6qW0=GSePwzQUqp2{}eLS@{{y`iU<_QcV^u}g1Ek_VY>%WC;d&JlY zE|`4(7wV;HTpPTHpZbnb0tAbsz&S&{W==7}x%D)24yM!}QHkt;7SrlV>}Na^#LBX(cdV9^8L=p2grGGJD+uJYG_GggMlUV-FH$x-VMpXO^%=iT0 zqv|IyyKIEVpDmVO)p(&p;0XoA>Avg7oqTZKNa`S~$CLt)|DR(R$tHZJ|Gg>~RykYn zX{$sb%FnfFZECJ^Y!RsNeKeFY>ReF4?3b&vVabPDxaQ*9 zaiTrYQI@(2o*c-fpHCA!8r0V-d!5n#O18&hD`BqYd1CAuadpyI7mE_Bs= zr$#DxP(3IcEgX|n|7=w^Nd8k@DEja%Tbifi+~@lHp8Gh&%tq3OTLC{Zf9g&BYvUUJ zQbVB0Z0Y|2a{$L{)KDn;QN@<8K6w+fes_9v!4UZG0{+c$bXXLX-oUM@$zScJQ8t!d!?MpUMA!#ffcQ2}j60E)EZ;kCtm59bQ zuODbBk)tdGqH%mP*4@D;h{QDQUFryXydCo7XE8R!gY0&H?UO#$wVpTJ_i;1uCMw!d zSNys$4tc@nv~`aYKpG7JvxBHo&Jo*VOH|lm(BN~Ll@;zGp5C;S7pcg;Htq=(bZy|o zsIM^N@|8i5yli}BU(jBm_7iE*DBOVO2BEiI+(-9&N)JJv9AZILYVj`k1w@~cudvL| z!IxBW1BjrBrs;+I2S&oE*tf;cOAb1S=R`s0+0(7{3(#CQ8z*-^KKO2uE47e{-Q{k4 zV$#R|ezj_8F#Qsy{J8?~(&W0TEEo;I$iwn;WF3Oa-GZrRb~$t zG&de%_ZWKt)F@x%6u(b8O#0!JhXje*w@zY@1xbk*&kp^gL|m+ zn{{AZ_WK}yOE^a=V3h$_>*ea%R2XoUYq>cgyNIZMFj`Ge;-)r z8y&EFmKD%O>!NukyAzanu-~bpmF{;2qNtaOH-Y-lT6GLGK>+eG@D;EY04!EXuQ<#mz5*^}-Jq0l|X&pX!PJ8Yv3*{Lqge z_!}-F@6<{ktSJID!Gi7P26b!i7Vp6#9;_72re&nU$f}!bBFvtUUoC*=XlZIysIRiP zLuyE2kOSNBF<49031=h^SMXbzCL73$r}PZ5MRp&7Al{8y3a_`RBCT5=JBubHPWEl= zu}>#^r>W&esH#+RTfe?iW3HZmxaYc3mv7gvnqD;FHZqt+{7Fvd!kQ4&_n)n`63LYz6c$9=Iz+g$VESylS0%P z#y-y-n7j^oEu!-@RFwL?>;NGKD!PQL4n-~G&wMz*J}1i-^vNiw_64;))CdWqTBybS5c}@VW06H?qYKF zT`QxeM{0g|{5lC~br)&JDKff~MtZEyPf{s6jY!HLF8L1YedI0 zld71=fN5{4p#KqQNO;Ge%84%Ncok`tF36OWI!fm#lzMO({-PY_GvKRihbgHVeTm#PU z%BY_`FcHZxp0R@P?p`X@osTNmRVDK7Yj!?WS7^tlZ63GR__rZUWOR->6Mf zgoV9!^D;NB4Wrs#|NP$c!@#I~Wcn%a*i?!qJSNNm?BzmlZXRNiQP6XD2Ej;AqE z$@O;sB1tLcYPo8f{vGqtUy(p7V(A0MdyrI)BNpe`ND^HSCCKk-n#!udTy=w_I7cYH zz2kqMn6ZE_06w@6#v!H-c!yGc#nYIQKw%}Ai)j27$cBW&C8M)Vq8#^ig%R)#H&c42 z$E7vOue*)B#Q^=%so5x?g!+}$Bs4hCb1Q6RGOz*om&%tjCD`ZX&xnkR%_OT}9q{b+ zqLs3tqx5)8(LI!D>3EE;L*Xlrf$Rm-Y0AXEz7^o^n${jF-d1tpH`;4_O2#Y139l+$ z*{&x5`$;M3`|?i7fot01Fxi(Hveli#0MW`Lm36QhlUzRIDZo{R&AfGg{9Hj#vS+CxgmQb{H2Eqo4cDAq5U2`Na+XoqB zS|S_`oGg*#k=|sIqk;6`l>R+cO(k^tRIH7sjj6h3i#6q2@6-?8uJbzS?V%P|y@C#U zc$8`VMhdm9u**-$?HiiOl4ZcS6^0Da(cDXx+NfJaqw3XVMQ_SJCH3q;wmu@fxQHc9 zrWyZkZ?j=_?v`?zu~NZeG&7AH)39pxf%p|Po_G7YQtq_oNU0}#b8%Q7qwer8$%wSr3`t~0-H9Sm1jpei#d4OB4?CjsM0)M zR;@5@mp516+k=(m#XcYf1OmSh#;2N4FkbfLPF&66n&_%Q{MzF10{1tDF$kBP$xx^W zR&dOClfI*AY?%t^=ZyJ5?M?23#eU(D&o{TQj$3Ps&@9hSHqvHtLeHLy$^DsNgTQ(x zG5UgKYFsu3WNWY%>Cw2kbU3q{*E+xr4etE5aKnSTQg9X(%!^7P8TbKAsPd-QSOBx4 z3FS}|NO!-fzl2_XbvdYz;7DKoxx8_XY}T@ii8IVb&8!FS#PP;e=^>FBD}8oh9G1Y% zlc4$I8Z0A>fLXz*C>KXe(gOX@h^xy2Jr{ZBM}> zuh=)_mc-g?M@eK{MX}#X-g@AKZxZe@RD?mOaJU%;BRMY7Q@Z+wHN686v(19u2$q>bzK8u6 zxbifDLbXJ|{x##-*P;a$QM>?JfLXN@I&;t*CN`HdVdkwSSI*qhzr8Tj3Ay?Q#K-5K zkNDW;>{vzladM;cVXm(!0iEkg0teKAH{}Ptq5(ZaS~B3aYa*i~{DH_b{tC@mxXM)D zK);U2&k)jNF65eIG!@jx!_V-uB!a9`V#dx4S#88Q+Sjh4N{V=gy}(d{bGNYQd!Pvs zVkDtmM2MG}CK^w$PbT_PV?W4*cM zAxjt?m6o{j#MN;2KvEJ3=HO@MYu-YDdiUSXtv2}S;0=7x9SYDaZLB{COqdVAQlj(y zy0ZDG6#lSv4ilE4un%od{y>7_wg$$L5Un2(M;e#wny; z<|RGa=6t#(N;mjj)Qt#>=iU7Szc_YDYooSG<%7CP-x(Jebs4ahWl2Ah1e15qd23{>rSnN z1JM@%CUZ5KD}3JAYg#n7dvzybsWb)kYh}4mw^3bt{l-STpE1QM zV)2-UUjYivqMz8NS6s$|VM3@lWr?lKz;wKR_Gc?#Md|?6bLZ8n-IliulusKD$JZ5~3+Qd{FK*|a98=Qc6O=jG`l_VS2J`GaFQ)t|_8dn?bn&xGbn z>KIWSl1PWI&*e(6aWm5s)Cj|6CE_8OJI~zU>UvUv&cWR3s!s%z59&`Sp9;qRRLX}E z7_266$;Nv21$Q+#IlrU^t1*{!lxJCPTXzgM_h?L2zEq%qtb|}IU9C&~I{KZ=Dm&d< z&bRhxjWhpe9?K}3#m`x{)?9b_Nmd7}sLLPO_-#T^?OYh{H7aL~K_e{gK5Q{86?lg> zEPXbY>TrRrJdf{}vtHZMYl6SsF<}P9bTu{hiW!mT>&B;5xOl@y2=B)KRorpsnAc6D z*SW+`RqYwBpKB8MpHlCb;5!fwftv`la{`ZD_44MsF1dPwCE6}om6fXxW z#cx<&t0;%1RjZ&>jAqIS_adj(?ZpwYg${!UD7uq`@87^UxXoFGkJU?4Hy-!o(0_i8 zPIHN3U%L__b#3N5Ty^IJVjr-02p6V76-$u@3=vx+xNxx$+|?fLIs--7WJ%!9a-wX? zN+cvF-Dy&9GwId_iLogiOpI+bj`u0QqQu(Sqj5FBV|3oKv7WX$C)+xB!@#h{+r35~ z>*3xKJvv$@kz&{t-1=Su^ORg*KC%CMF;2PWxW!8DgshpoPt{fp(J-j`ZUA<6-6-2y zGw2juT#lgdWE*uqzs^Jf-t=|t~qIp5m!dQ2Xa0k}5RAEU~poK9X z%{$4xPvPeq-N-X?G31&|?9OfKw&rjQr_aEFEXRAyqO4(;!P=MFcw)+Ed=dMqlXla| zLiUqEN6bQQ8270+g=t9W1lcaUT3~ka0f%tS_!PZpv91Ib9AOCzj4GA_YURu|VX)+| zGv#&4DX(b2Axf?Qhie|g!EO;{c169^nA+|C`Pkm^){!V%+=z$LvjSj!DVL< z9;>2^TMS>(`b{(hCZ@CL<7|PyXy&O@tcyW;WhAz-kvrIuT2ulbMWjia<*j<&-+n^+0uuI15sg=2N12#^CMA_J+T!gw7+k&( z+%-Q{vrv39cA#EsNK6G~bjH!f>3+k!YCue>dH; zT)UQK6{#__UOJnibk2~X?lxa zQ2#7Iwu3Rh!fol9QoHN3qj7ly%JkJ6Kj+r86>&kDIV`yMD4ycJHlFJ-D84YGjr0Qw z9GFUc$1}xGRoyh@g@}5xws~rfNhV@aqB9SzdVgb*&Bm>Z0;^U0`X2RmjF0FU0pFR{ zE5@x<#;LWN-cQ|#5Nhl4UVa@Upx9F@=asei52L!h$K`q%ST_k{HIYyYj`giZu<7;8 zmvZ$2571r@d>715&m3SLrS${#fbzJ(@)zBjzqKJmc^D`thetByOAvb9GQ5|4Y;+5Y=6Un8zoMG#o?j*Oz;9oE^0x#-m+}8@ z!O+Z)=1yHi`A%}+xzzl;Sg@p$AGAPR-E8j)^7j6Yr`6Vk3M^e}kz4ZUSgUImSy~KV|E3slGZuwymQTMp)#OH(|Uw7PUf=ZceFnd+rTf(yQ(^6glP7Y8I|h2JuadThl6S_%Ih3*~eeIZX=U^I>Xy9X;nN zN7V~0cibfE+^gDnoqK^J1V0I~E1+rEeR}y@4Uf#5IL_OB7{4}xRleFlY@(exU~mnj zdAWV2KKAg%F6wWz)h45@)GqUhtsgtK^Jh*qrHN6!zizyK_EkJL7$v|#PR`P+M58pP z(@5$D;u8v5p08($xjrwCjzlYH;e#?`s=uDpR4kZ{+}}gYc+NlPb^?yX{^;W>w`yFR za9CA^JJX1(W-!;@mK>YCQofMvr$4Hvp=D>M7nmy(CvY}M8Hz25b;kzK`e$7wXtnZa#00pwC&U6KU|}s@&a?;ON9Ce-;@j4= z87H9XK5vFQsU(gor*Ry9h%i1>____-c2=%p+~uIcqoBKICv9rZG&|blXN;8AX83QM zbFYIuV(P4CB8Yl@5s#K{b=$a9wMED$;=OThU?cNxGNv|TnE~p#B4?C0Qmu>8F=@dw zb8Jl$oZDW`H32C$WE*2mm%!U`Gm_w$3l3H6Yu(u{Om9hnK_`+&kEnnGA7hyMt%(&n z#QuCZKMKg{zy|l&7Yov)@zz%}`zU*Ra7E05IN2a*`Hmcu(c>B+4Spxgyw@H3^EcZx z2S~XxZW9*9Q8G42X0q7*QLC9BDoXpK#ZuLLQlw=}HsGArSK_Xn_GRF-6kCm6sQiZ^ zpGvjbV{d!?yqAaMXbnU9Htcm05DDRlJNslF7|q~Nu`k8lFY?dgGshWkg^L+v{AQt} zMk(T1vEeon7Bl8PHr<+RwUrbgA~V>CGmJBkaoebUWzZrpERhN|5Op|$38V>`mF$4c zqD{f_4re(w_$^C<`c!89@Aavvu~(Sp$)v0peD1)2j4+;PLN-mEwVqCr3q+Mm$f3W& zEWF8@3vU<1bfWY6Xf7RkiZzz}^ua~W&*QxL@x>+V%snZ&UzVxW;HnuG-CI|0tLV%% z3HY#SRu&iG^+D0he}`*1Bn?P9|H(zO4d3rFlF;KL9aP<0C;0Z?3Hx})NKz87^}u`v zGz+QjV-L!*xz^6`5N$_?iHEWN^4B(gb3eIzq5?g)U&{N8KY+>woLCy;9L!TO%vO#> z)$vQC4S44puL(!jX;o$5>!)>L6cJr-fivkGLol-rVLqskq~^`2URPVQPTQVc`*mD| zjm;4x$K3V&08GswXLIaI2QSLIv2=6QiLrcQDQP6V4uE1$H2v&9H#$WKwlUtJf8O$MrOk}Km49I!{8r<*U@zz!y*lXur>X&{ z#**;~xY%$-7q!SKFv$0f5$(`Xac%FWtnpMxeh|bj??|3ok7!rnqCgBUOu(G=imbTR8;R>V%v#O1aJ|>v7xPfejoqJZ zLoz@oK9!Q{ta1Yyk1^Ald$!q$p~0HZveA%|mUJlh+-x$g3}?90$wnvkI|PrFxD9cfZ}ocQv33IiYH7++yAdAp04Ms z`tPhU{}#nVaJC<}K%^&1l}@b>8TjRvKe2#Z&@qjUM8@Qmv?a`Vz!*Nt@XqGOPw>e{ z53Ln55%t`^8naWC>J^e+57^4LATe_u&XJB?fq)w6nh^xM*yF|F{|3k7EPpFdOwjBS zpgj0ftjEFa`9=IwtYlbGA->$FU?sh7Ai2cQoh4VA<|+4jtcPG#=qNVHNph1Jn`9_*(sxO4!Z}c z0u)+=u4@FxL4+%^bIC~x{@_Yq_V4~6yw(z4DZy( zUt28Zr^ue;1?3&IlIu;6Wu%e6qC}$5rnFKESAy5M-frqjah6(7=KWxmk1x?6aEa>4 zSA10=*^rAuyI$9JflVso2tUU=dc8c3_G7N@2Q9Pcp}!03z_e#!rR)MjMqw$tmV6hz zEQ!(FfpJ%VD^HVUm3H7oH6`&pYw;*2PR)G{MndTLyq*3FK$0Q>x}cy&%JV8 zT^C={%p2NiSNp$pIQx@Jfhxu!gB^~#{zYy|eJN`}+NsyZ7xp8}GgQ;P(az;rHJED5 zL^%W(wnPoEaY)1ukwwe-afTF`R>dNE^-gM??#&sDOsaKgS-Rs3&Hi?WxuaD@#DV-U zto3-|F6vpF8-0)w)|s5;&9!5ENDl3uxUo&>Fa?<}0im;tvy)T*QH1WO{inUsv*PQ= zvp3m$S?k2`vcZD_L}#5Z${Bh1v5x0_SET1Yp2(l;c!rrJ(ZdR;EvLl)N9gQ7={^oW0neZ3%d@TUu8 z+`uj&TnRLH%bMxhO$8i4NH%}2KKj|#>pOrr`9eX9#@_WhKC-@iH;VDWks`B}+zx1Zh zJv0z!*i1Dtq$Q&AHeoLMf+@%U2_3M0On&V94p{4Kcd*iWu|rZsHsYbQ8CFO4Z;>gr zitc9PwqR_Z=Ej@%KNX%zX%R~ONk`LqVvm#4g8q2 z`i$E6z{T8EvyUA=@zptw6>j*2RysVR<8=OEk5JaboQN;T)>|5xR9yx1ur>& zQD;~YqN61B!2sMt%}b2WtL{jT^jz;N_EOZ+M9DJsKgmt$Qi=HHa!nosqs~O7bd|)p%!7lDATnM8>%z1T8 z!iMR}QR3BD!8pJS-@v>7hK%UOI3zz1Op|+|Y4X3R8nhrN=#DJ@^0`Y7iEsP4@$CJ} zi?Xx*dwtU!=6DSsocv3_FZyJ_KbZGaAp0ru0qRtGAhTmU9hN=!b#Yiv@NZBaHJ^_u zUp}`v*^w_ganRpWbdn|G{TXQ&nA>&``6tSFUbagWk*N_vM*8PAPPqkf;i+sfjMfwN z9|7Q!U&0d#>+6D85*BxLwnvtWB7C$?!JMj!(BV#Lca}Y`5SyuJ^4&*~u!(yZoWz8= z+qms0y)exV;aIzRrT*1M+0GGn`Ex2YaclQhvmilTm)A~-Rxvl5Nr?i3 z44UD;3<4x&mi;m|X#1<-*<;x2#vD2Ub zHl3YS64ue$ZZSBpLDfQJ;d%P70MoDLO^o15QN6GBbjs|Q$SE=96lk7o%Abj1B!}?! z9V;fWLs5v}dCzFVX>Xt{iZXFpo|`JlRCBeA#i8!&riznwJ5#UMyuOUxbLRQr#lE&4 zAuAQ1;Hb*e-Ws_BSL8cwdb?YqA=qg%gzA}Duv77d|H+sg<>>~dPYa!jeQW5UiD1Pn z6y~bCwN$^Rt2l2WAyYJdE$?@hiIKWvJE4}k&}Wx5zAIHxQ*I;bbB&fyPhZn`@jJy7 zUg?zhdpk}1x_Ir_4z_+}$zib>-6Agq+XMx+Q&Z9MBmOlU+Yh?~Ae{R$cez%`w2d#7 zG9|;rOFi&%b|mXQ$=o)4UVc^rQf#>n2^vnKpqxseHK2WHb9j05R|1m?Nl|M-O8R-p zV5Fu&%#Ult?ya=gkQW#EC*Q*rM6$k5*QpSBt+DZUa7Bci&r7w$%{Lp|mPz8~S2b4R zA<}O|V(cJD(QiZE1_-d!pq1szv*>arn86$&Kven1i55nm35r>4g(U;oB<$qr_{BSr z;C}^ke*KStIiKqMp9khB4PgdP!(pv~&)X{!th+JZ6=*B$XATGPiN7*gt3M4u*KW0> zN!nkWm_S(e`pocSCK<*V$CL)4PTV`Z-P|V`j+L7gTJ%=S!}tqQk)4L<^k49teXiFt z9`P#^0bkC2YG~tnD!==%&rByj(}9zxRm(ps8!E0NJE|*-WId|fi*~2k!K9EwgvCZe*mGDhq*Gt<(}9D z-jBF8cv#_uBd7S*SXN_%vSRBrtY{$};+hGu!#Y<)%(26hfN8blOgdm~x^P8&rFj0q zagD_+!d|XUF9MsDvH10WGn(^gE&I}cKbR9sTOn$}LzBy>_4;y8!(4Sz=s7SrQ6Bk( z-L7AnmMIT(Wgb6@VV19W%_=1St;`n^8(+{FH7Qe2(${y(zP7r`@LKr!5coB5q8m3o z=VT@(?>ELdEYWD=xb#7P8s1E8;9GA`8}~b9gw}HRVZM5c>S9ZPua~ScT-wsK%EfNwX|pUehvu} zq!+~LL-KBSHHdo6B`aRK`z7+<3V$`TBh>8hMf4x*8L~4kDP=0K5wKCZ2?2- zynl?itDzkgr^^JJ$(O&eI0Y$Gc7E!%-9F!2Q#+;W;|WI`dFas8FL29Pc_SqbXKRv* zvx-3Oi1JM2=Jj`821!z!##!?@cl#Z($hr4vr{P(3kd6Bk+@y(*lh>v-Z=A<`0Yd)^H2O}# zTCSKYkI8@R6O3tzr8v)GT$g5#GEF89&viEqGe7AmaYIys_1pvH3l&7BF<8mInTm11 z##Tq{H*d0H+XAnCmz3s|fk4vKC0fLI=4`+6uVZ2*U3Ak1gK3kvYq7z&bB0uS$w1(7 zaLjSNP9TIHV&@-8u%_jbh|1uj6Uv$1mrJ;cR%%IB8@Q(8$WZjuwV_~qn)e1RIZKS{P|)Ea_360HSxs0n6p21FKoGd`T$J^H?UWt7dL!c zEBzrJSJ!A@XW1@6U#qkP-&F8SDbpV=tio+n@pJzJp8^^%u#X)q1R1xQX*}?+9LCn2 zH(EIgDWi>F@6HbcxB=|GYh^`C^U=x19mTi$kK@U7|9Fx6t~#<{hAs>*8Hn_PPsxwn z^Y02ISG$`{_smLf9H{wwRL-3HqU&|hD4{?II~NnkQbFdB+@RI$dah7PnM%~Chx0eU zY_fX#U8d=UEyxPaQPCBg6;%qI`POqxE3l=A>hL?5k%+|CS-+F+Bqk8T(jVw<-EBle z{y-ynd8r6WVjoZ%cH)HhHWq@p@y91E95sQkn_8teAC->Q=BGo$0Qw?Hp>miF6gTLaT`R#U8tX@&DJ6GfKC&z42Z7z*f;2Ye$vGQ-dm5c2m3z$N$sz1 z7v^vKKQ;UzkQHT~@>lORr8(#){QoiGulM|w-YfALKxQNR?B4m6Jnsjbk0p4!D3h|; zT_cl$KMOi%Uw|`e%UF`O+o_WhLyft<^#(Mt7XY9uS>-oUk}mVkR6k z6lqA8kS?+@Z(mGHGC3tP*Arp!ckvthJ`EP62`%_H?n@0>Uu^l2a(gpFhbFw=%WI2k z9{C&DqNm0q`G(z`lmHVSADL|qthKEl0C-)8&#X&FS)T+95+V2bz$Eyu6-F%`J72VF z9J=yOt)`-=6ghINTk{pPY9&6G)qScGo7ZDk_L@)rLBs3RkwHyg?M%v83lWFzTSdO z^^R}c?u%gMOeFB`4oUnoTlYawF_S` zVVlbtZAf=nL|3A$j+eoF=A`Gw@RrfjRTs}{B3AAQc0lTK?BJG@oA*p?_Sy)*DMKOO z=udL%9`g8ER*LBqWbkC3{CxedWj1Tm5elwnVH}q^;7OFi<{Oo|%YQ~iblcQ*X)IW? zUu)sN@+i0*ZH&<>mXVrkE7R7V`Ty8^@31D*gi&8)kQo_>WspuRvlbYdNEf0q%8ZJj zn9!RDL3+T@6F|fuO&vv=bftxanh;6?2n3}oB(%__B~&4l(9Rp3`BrE5`*zRo>^Z+Z z*ZKaLYc7QF^48>e?&rRT#GBXgElOj5S0FP^v5^D@sQLeDkfFkI$|f%8YaUt#tcyYR zwp*TGnB(F2G=@;=Ms>cN_fs#3h3b2hP3 z701^v2~0mk_{t>}7t<|W%_vckI!uAbsga)|Te5|`a2D6l6t?ALZH%E2fR1D|0*ND* zws+0^6#ujYw`(>E8GSa`DW_f3qMIlzYTkX{?ykF2rTMW!0$pahx5mi=$ZQQVN_U#J zsH8ca3qtaL*E=Muax6+Y0Q`k^=;X4O5(4MiB>SW2J%Tsp@<4 zejp2$nRu!zGB6nxCO5`A(aYoq%)LIOhYZxljVLN=% zBxZ1pTmjw6bgpv0uF}$$bWc<=+om!%<^McZbC|q@C=X#~ z=geOQ20HBJVN(MKca>M7^-3lO0a`clVXYs&Yu&w-Z_~_~kED{%wu}z=+3lR&`}RA0 zDh1EM!8ocb`~PIY>qAeZz$IrN;h65sYS_OMa^nvcjiPZnO!XkdY@U2A|B3_pR1llU8G*KL*!@G2F@660R z70Z%be22HogNU{AjaZb;NsW$1Q|AWUp*vIJ1tsE+*x6(IBFE^WaC}MchQ5kpP?}QD zgVhpbyd@mleQr|FyXOTsM{^+sIa}LjSoT_-Kn`Qt59JxLD>u91x)|rk^Jjof|K1he zrfpIw4#4A)D@nO^fEYw2Yvp?N^A#9F*(4!SxYSDM!yG5MwuNJ9ufoqY>KNmuFy50` z5<6=pmV^%isd>|K$4F*53JJg}I_#`urf|SFR60UxA;g_)sp3I7Fs}fvYSB2qsG9hw zUY6H`J!^-vN;HU$hw+psXulpX=J9$^TtX9=sW+3LUBO)g&-VG$GZUwHOKGNJ`IRsrkF>x`oL6sIltTHAu=ggDcM!vzpXm|(5kcZ?_|{p z5w>Pba=ki!hFeZogV81J50$5AF3|aR><0Bxt$mp*-+c+%EEPsKdOBnD!!fNE@X8A zol?~;C#vw5`|1OF2?QzOzo2S_s~~dVJnU679ma*Ro=xF{#oIhPCg~;1>** zdOa-Ew)3n?LyiH0)zVrj@ym%>lJxK194CceRqxcn=3Hlohk5-GzC5#Y!}Wy9x+poo znq#bSlq!>%$jjcVDk(R&I0t5Y-pU~!mk0G=qOS4Bx;!X_Y!ZEh*<;0>fMm;FRNtx+FqUU z=qz^o)~nN`pI8&U(JOeJiQpPvHd#C&84~4rPm|jF;?{E%wsvhvYNv9mJ^tnAE&JhO z4GpsWD_!ZBNP!oC@PxMKE;+4w36t7Z9lDsN*N+%5d#W96UZWmvJefhABrRluRPtQ0 z3f8iCp(u@yfWklVi5S@&sC`8mTa=|)e*tkEv3T&lrWNJlb}RE>QVI2XJhEIoJH=T6sOB&CizLQ% zvnQwCK~_y8q+i|29(nAUomNtDh6r(R50UD~g?b!RF@Ri^+sEoFS*$wX1CUS4%f|iB zH4FdNiW4ce{r=7LtyW+1Xn#^9(4-ozt zd>b}Db6i$zIt=GyQ<(82sfOlre9sJEJ{>J?b-m#gm0%&GjPN4^rLa%!DXaIav0!)i z+0t`?%i5#T;Dfh9{I`feYFFQS8^o8Kq}? zTyE0!FT15l`}LfW5CBA+000)%+ZQ#SRS+2+Um?cm>v{EnE*h-Kt#h&I-kb_`<)~3< zC`LL;E7SI(J0Ts25&vf2tjtu*Y0boAECIpgBMTR` z0^9R`{ql(Te!^;yUPu8TBFm(nRPzn^*I+edkJ<7y|6ek?s$xF!navNr2=PCxD>@X7 z0%$KBQ#-?f=JV2)we^)mrvId1&&zxXW>3#q&!q!JcS~-n-6*f^*Hz0aV2Tb52Ojh!xA_^Ss?U}qF@0&cfUNP4-4ZgJDm&qtm83TWSPxR z_l!lZ66iSsbF%Er`Kzr?Rhj23>I>Fg-&JNiw26!<2JyL)zv3ESYxGbTjE1uafAp2ID~ui)9IpLsECuA` z+4IAf1l|#bCQ*rTjJ-4R-`<;l5AV%qs>UvP=mtfv*x!9{bhB*jAuxAGLMO{vU;r%% zshZ{0@dq{X(LpDcqtWpxYgNE5zwxK673COAu!LX|&GPK$9n<`mXRSi!I5Qz2C!N4W zE>?q5?EDvZt@?lI@N?O!wf?GWl{s-QDNzpnp7R&%(jU&2>znnHZF_&`cZ!dfd;c)W ze`$qmQS2ZZx?UR3mXcC^@~*b^$=6-08NitxEyhhYEPB;#zFLobjLDu{wdS1Fd2{3k z)jEh;Mm6{v$`uV+iW6QN4_ma1Xs*kW!kkL+Bkv+)IJgaii+HNlU}LoOvOp#9Oy zE3cn8Z?)I$2VL7b+_2FL`CvSSa$I$(-jD9V|8rfddr1k9^>VF$l(jlBRB5tmlC4q} z%SIcxl&k%r4Z3&M=QR!iB(wf!ahg3qge1~+f8@TIJz83WF+aeLT(*?ULtiqMQel($fW$P3>hg{Do2GbD!MiQ z6C@4bcyGQXu?kjw>);b$8!u8DRox&iqY8?*?W#qb{Ur~s zI+yHdfp_jFEL-0GERk?V_zf4@Zfky)CS7k=6Ve!dY^}KUc7p|sa&H--vEti`Z@7LE zy3ph84@)06Im>Gcv#AW0C$8@J*-Zzq*)r5AN#|1Ru*{(>m|15{qr>?7O zUwYg)XXzyg3G+%rPxfC}%5cZJ#dR1?bH^k-&`u}FBu6jQnbGnq_WG z(|f1#F5*|4?D@-#CWC;v{6Dq4q`CXy{{TW0ywW!`PTc$dSH;R0s94oyu+!XYP1>j? z;s(fEg&CUh*8VG7-z_1)ngQwmE)^@Wt&x&qG!{p_7{pQbnYgs$-%6of^B8z9WD1?ohjk*&3O?QR#3NSwsBW=EP zggiO;BrQv_q>b-P7ZK<2J-l;sKok4-3RYgL9Z_5dP)v4t1=pJsFx`;3haN{^n{Bk* zu-k9JLY3b5+t8Q3N>?H+Af+Oh`RceaFPpZ#jJ3Jiqf#bK_66Dlmgxh%Z5Jjp!dQ)G z%UU3lZ+jV4b3E(Q6^D^{J!Fm-Ri$nPrai?kC>}bivf^T~D>Z?!QWMXejC%^yh`J{N z2J#2Jd^;l}TNfwGi0;)4Ux>@v@{n)O?|5G<2`pZKP1kbQ620TQ_M%`F!6#WN>9e4k z+ONd2Wfx8#WC$3zv)8=W=;hsW(}fQ~j6oI1i-rHvZ*r%n*IL{2wHZVzu^a zR-5u*$)h^lccpf%UEcOrfcU@|(is=Z?2l|#XIF5fQJ^EwW3XLCm%3#O6`bA&aG|S0 zkQG+HmbB1`9PVrDI=8Pf2CHb*j9*^PkXlIOVCNm(ER+4a5V2z9YmGS$a2~lX=y`cu z;&q@AsQ93SK<^bZguh~5N>Q<{%7#_tLHh3E{n^9u!fT_@#(K63qxX+f<$p`KD%V=b zzk4o3->KR(iI>}$DSDwwWXIF)e$zGQV)KgPYcC}Cr-T!}1tIHZkn{y@0IQo3$>6qq zM0NXRr}!ZsYKgrsWXx6e0Yw+Ahl}xQg2p#w(Os%I^H;*%y>0K)fD4otm~<^9+;VT7 z{fV4u0ZWt;D)qHVA{V z_s|Qv(k@A@xy4XkV3K{0XgjU=cfi7ZS(~sMpu=PVsSYt!u++w4%6sJymmVj4EWe5G z3STUp=)-TzF{=$_Oc(a01YdM4!w57bpoE96zG+@x?#blQS-1#>17)E90H=%fR+s$bl_Jtk|kC5TO5;bbc|M3cE9XgbqMfiUqZfH5w_-=E#$5D(6Px9k`yRF zR^_>mSH$Vgb}!*ntH9U0mo7^={8x>#>u!$o154&(ZKsVeKxJ+AWXv1)uLv44JHori zS*!QJ>IsO?*JUmi7m|Z*IKDXaXK9ajjW5~kx0HDx$M(**TgN3ax7YMs^YbFTL|a}t zbIUk*M^a=8?gnZRUEgAs0pMJ6 zaeUTVRlg{IHInNy^FsJcXxrr5&R0cG-%(#JykpMI5$g)=cV7mWTVCWRg2o1EeZK?0 z^^C@UnoCYxxi2yH26T`7PXjb>{x1S(u3N}JVmi5bs^cKeGF@zAbrd?}RA-hqd;1C_ z({qfye>G|B#;W1(d=Lr#_76EvD1x;%>>{YVIIZl}zDm8MHO!_X>3PsdP5kLhALvkm z&DAJD!M4E*-H9d2-F*}@mi98U;>J=1egR#75<=%G>V?^@58-lAYRI4&m z!4Ng(_zrH_bqTFGSI6pHY7VG!=ZAp6v-1*5NjtrzeB?0h0X0IL5UN}0pcR%%Q-&41 zW7LP&4zlEeHrl6DN(EJ1S_CD_q(Oiqv+IR#Wz$&m@HL<1~vpRDYpX9#1tt78D+2;hBS z6`1u-+}cHjgE@5nc6#l23`LUP5R()~XFJYzd3R?KY?4Cl$}RR6*c-v<6~XMd3h3rAM<+RG#m#?x4u_Y zZOr?>2%(9??IM$XA?G*9122bYr{)(1xYd5&jF%gXwIZmx+<+a(d0v?)Y@TZsb&Ng~3Q|DqcPqFu|m z(mz0|S>IjuFkPuDhzPuj;-yx57Vu~B3{2&;Oa#Bcy4q_4Of~H?go@8ma9ro1%`h{z z2xJxay~R-j1z#J1u8D;aLSuYYa0q@^4c#m zR;}+=Jri6jbKY#SV?CfKX08s*zpM)bRtq`a**thh0vJfO*ABJ=e$c~tbbL(2L)@r5 zsV>V$2c5pGT|L=-qIG_xb#i*6k557U2Z*k2L$5b)nCN-2HGP;jUJ#9ZvYoQ&oEv+_S1xOg-^TkE7s=AC1b_*@@TCED* z1!mcqSpE~t$>ER~>8gEQoIW<*FTcy>m5?mL>#IptlHAo}s#--4-wIwc3?dW?Q)i`A z5lf~B^zBRT1Y<^)oN5FCJZL;nnwgx`kOnR2Wx&REN9*In8`%lzPC6MvC5{$xsVwEp zGR`ghNZn#7sNbx{Y79T9+x2cq<6d4I=12z?oyJ+op;++ByM>ikYA}1{FSfeAa*#!u zJdE`3`fB2sB>gHRsWK)(uycQp|A;SQyw<6 zw2@QqA_VdPuIJPGE}^vxPfPYYU20LDOE*=NUpg*tFxmalb=}z7qBG0uk$<7(Rxix< z1&#%e&2%Q3VR}j7VXDy&S1kfERlf3&G~N)0sihc?I7|Vdt3S$8ttJD9-MH|IXlC3A zppSf+F=&Ei!(gLxxw?;1ww?NK@UPTrzF#@CaPcSjG5C(Uefcwr>U#54KW zWgl&$tWtglcV>HitsYha=!!oF>v~112kz}!2N(cx_lnv`m=#0kF05%gOhZ;4=tx;c zXqP9C_uDo6O*o2Q;zLhWJ!I>{jBLK!{nxmMP8Z|3Kk)tuIPkWAaeLJJH70}j1CzP?H<-+xA^?*KZ)ee|b1f5!NcH~97EIji zG7{k++Mu2HJE}A22hR&e-c*kxEI%cBT4M^IIQ~3dru5E4Wdbj8y zZZ66jZatEp$kRHx;wRJ3brqkN{hZeVB(sdl+VpXl^?-& zwVF=q+GK(`>A0AN?LNwHzy`S*@5>a1A6oHoY=M$X6!FcVE>emAcVqPEjYBw1tN}q5 zr>Q6pM47^@1a}E(b`7oX1E^~(zf7#zJZuICMOD*-1mLvq^Sf%-_eE>HuVkaY+8|e8 z_uQZhB3)dh%l#gHN<=lt@}G_(Tr6N2p%i17-yn}AtY}J)eXdqv1q=7!)MZ<`d?E+M z&fuHd*$MB=QAqu0k42gl;eEC-FK3L_Yh0sllAS;Bi|!S7+*>CL!}%j3B56BOsTW#h zy}ECysFk*MMdbsDP5C_VxuniN-|T@llmz4dgL&pXKP!WMCF9FW{xeS#%6}y$Kqu$# z<)1trk#@S9$>8@NzT!9cT_7R<7oMhHJ_xzbop>Np_0?t_xvv=N8i)$qo@A#60bK(B zlRBUR&?AF_b_Ipe)sLKyn`@kf*7-kDg~56r`8kU^pW$Pxl7Fz_@89`g(sgSc-Rtj$a8_}8t^@gVl^?C2Ei zZ293jRPSxKtr*ov9@gu6+bc_;l(jgyL86BW(eu$ml;iDGnoqkjYbL;P-v1oL>Bnxg zs$C%c#pm!*N;wcu8aQ3{@vQT}7zsb54KF<{&Cv`PKcUlYs_@CH;=@p$yoJOKNqmuo z`fKuN&V7)T`DGTWeHUtu9T>tyu87ay=$a3Vp9Hij5)(gf$!4~<=GhVt!5fmp5XqlE z&u&FCt3hgsz~@<1+-Qn#&l#*pms!X$)!|8Cv5_jdROl@gNNQC`!{Q6(hxm^)(KSq4 zXY<*?yf0TQurb?-Y0uGC5mcS&!aK|7d{8TfJBBqFTPd%V+N(kEjZw43K> z@6G0&H|@Uce(d~Y5t~0~9LDtcF+Zea>$V$0zCAHg&UtIlB)L6v0<5&Jxi@C?gpLpF z++5A)X4YFmraDCNQ!%Sfv?aSCUqJ^l#hST&*Td__uZvLd21{G*6u^(77<6a*=!cQ% z^{qIaMGqo?LEM%GC~?VI*|Xg}M)xY3V=cN%RY;8qIfa5)JQYkDp<^U=+^ zkF?DkRPu2cl`Pi6+NN6oL~^~v4QvJW%PB@A=TCUI`LAhyI6uQ*t^h7nRT`XBnUq>b z(?ZY+u5wkWDPS_$mpm6O(X+Pv$JirKvkw_G7x zNcG6zU$DSl>-kR;NM>W{$?ZDX_BWM#b`|V502z4Tw&CZZk!P75lS-_@aj|H04zD#W z%T7bk7aBC4PXgYB{!80Wb?;GyY%UIzgX;nedPhH1s$Aa6-B&IL*p(HsM%^Jn9A}W} zwKK)`fThTKA+{iaZU4wDV_9eqTtwz`t$&;>hq+o$pe9xIYCbm(FGj8Po%1u`mY;M^ z*zgjzek069L)mQ4*^teE)T9>%faN~T+PSJ^GzG}?1oixwG%%|_9Ttr^xN6W}i_s5Q zmL~F8n?zsP$rSRd;a0A`3qpr^)$=q?hFo1L%q_b1dz!~d+i*<1O-fpoEgd6#_T)4` zHILN=gQe5ZqRJGG@-;S}w#2*m`t>_M(M%wRfA~N}2PY;gq&>H4(KZY%0mgwwQ8txMWS+e$zOKQ@f}ttWWsX$0KCRp+?&Ce%a${+C+aqzbsR&a1%w)| zL-%1ch6yWh3bgs1xa2Z|2<6IBaj$x)z`r6QLfm3#n8_wwnI50|7;#I`= z;eoHOr727AMz?}Vxi$RU)u<7dbI#vGx^yQ~(Z1G`yy!RQ$No|LDTI}zxvG=-rlsV5 zOIMU4WH|)1aR)pxJSXxWkt|*)uZKpkX1kjU^+IJEHi@793d8AM#F8_%yYe^|;B!H6 zEndgkc>JYO4Q(9qq75(=A*)Bn3H$Il-F41$ni}uBhg#^%<6jDSL|k>@-iWt^Cnt-d z`$0)-0^j%c%r_)TH`Z_U&Fr)_LTrztM1Gez0G4BQm-uQSu9@k0VAoQ#u8{f%k*h%; zw>j`-vz=!{c@H4pd-68sV+g!2A?sD#$C^&y#);Jm3s&KGpv7LyAyjN%7I<%4s*W!b zG>{VAqH|ZQc|8-y(Sp2xU#BBKXtmbxJ=mp}Bx?2lnJPIRD>^Y;rAxz-g z%$q?i)Y);B(653=T?!#3D%fofhVSd(QTc`B6AieML|(Sm(4j;TlsU5U{)5W}z&K;b zl9ASQ!^I?)ZcLZ?1_3y!ZroJ1BLpW?-qfz2T$XC^?lZ*~ zo5WzrgytDD$bmnBF%^X0aiNc$DO$2$<*!0@u3VxJD9rh}{mhFHviBC!^+kI6X|t#D zhjLNOv!{TeMrw|ZFxy9o>qESP!c%Q-mM9PR<+9IL(v4h$*ozuz$9%Asijpn_y?kgI?#Tc zo{kiW#zt2uRaD_t+V5eWD+trLwj*y2(Y>5ok`@4_Dn@hIcPGdhwFZPYIgIt7OE{-B zn-|<<*qbAX1Y=)ycJj!P8G=v}Oxi5aIvp6pjd>Jgjdv~BsWyA z1nqClDdr(u)*~xHyrYbGu*YKXKEgTbic?MvSiL`Du*u0HIh9$k97trk(#%3YXPhK9 zHBvr@l%NbSvZ;xKBZ`Y`O}8@=B(_*88!)X}78{N zyf{+q6VTnRzFRYW?)HN>lP1zxp|dVVNjE0rRcwM{8rZaZi!WSX8rxNBxUIjQ3)GOl zRn*!5wOsaE4unm(hQ(@j7;klz5tZdt4p^}K0rO{xy5|hedFv$b5jfY2$CcnJ3&dpZ z>L8N=pOAO{!@Mg?9iIwSs=x1}@&HGwF#s7gc3N0k&@NV!mq)MjvaSsI<4_K{Mc?At zK)>aGFB4;mC<9a%{~LK2Am;L4^Dra2O})k}tk!_Z6_0D&0fKa6_k1R7&JOa40FD5;U&=nJivkLH?P5b#7S!BsS{c2UrxFka@4RFGBuCyg8{CakGD+>b7Sy8zcl(~*f1{+N zh6F5$Isbx2K>zq6m5%cIrsYmMId>^)b!N=NdH1Y|GQkpvuocStYiJa7#UJZ0YWtX(L1Z|nuW z^E?Mk;wnzb(uoU}ngBInF1wv6xFkFdNTH956^jR;8uK~_C zdfPG5i;&0P*is`>fVgTgw14$vRIZD7Z;$#n5ob4jKX4mSzoCSkwc9k^^goMeVw)zz z2M6~A>gi@x7X>ZYWYqprm+seJ(zUR_Y42;WUKrjLF*`|U@vNLUH&MTXIaP?O3y@OP z*RlB+7fNF@YZEt+u6x&Z+hcE?a&<9(N4v|{g?F*JVO@0^vsV}hM8)>MeMd5$cwS&k zd8&aQ#h2!;H7Q(x*!Pd;o$*$=TRo%i^nAl6swd^Mk_GA2BuP7`jxW|9>B*Z**7yc1 zx{GL1c2(H8ckcbZ5z(eS$NiNHp+%1-yyFHfNW`Zb-& zS5A~+nWTGOIRH0wq#a^~AYW)WuXStO+w;s^Z234Vwji;;1$ecjh=R?d%SJ>?{AD|1 z-Br`mxu$cOI+aI1gF|lD5;DcVZG@`U_~t!Nh;@ex)w)#;tag1L7*ediEMx9gE#X*8 z>cGtiSGIrGlNUJnNkdaKQKZc={@^zzsh4W0-GdoBN;hxN=CzYex}@p`f?D|v1O~~d z4zcv7X~#J_a}k$=CetS42Sqd`7Rsbm7tf{p*(j!Gj{tF4lRGB3-fe#?rFNXQy9CLh zt)Vx6XjRi?RChnf!2Zz(%s4X6k8UD1L+`xc5;O-k8sN1r9m)+R6ApZKw0ty0 zT>9i_Y0?R^!&dlA?90EktH(3>DAdBf@(S;CmrsTouY@7-SouJ%Pn-a9L}eqNlOeUj zz_0y;(f_79yjdv^p}BM@mx>+kX(D2;xTLOK>JZX8_x(7pCUV%sw{^@O^^^q_Dj#Z6 z>t8_YS+zgayt_QXidFc$iN~T|xoB-US-jGEWIYWfHu=O!^ck;iz9U&`BHwfv^d7HQ zlC6*^jJW1zOe-2?axFBi+gL6`U{x04b2D&UBt5lw))Ok8EpB73edV}HGwRlh zh^bHEt7q*I%Adkl*8zqGYxdipkMD2STIY6~#YB-izn92y@v|JPb&`h>udXy~NF@_~ z9ttFn{=!ghv3pCMnAohF54tvUXK|z{5^u8R03yzPrjy?rR)R;8EP!A35 zt~QdN#ytNOWxQDIPQ+0HB_9Sr-Kr)eeFC-Yp1$qV(4~vM75DCjkCYrnBQ$@TciKKJ zc@R*R$j{L|e%y|raUCsq>LAxba~-hI%;$#OWoo@kGVR4m!V>4YDkI6o%5L_o7QUQ0 zxcsl$G+F z6IViWV|_xY$EV>*dvuJIm61_nnX>BJ^53pSoprmdV!*6u7DmQLwIUBpmN6l@8#Eh> zG?#;-@QcWKYd)55w|cT)M!QpT(T!AE5@~$da{g~l{KRJ)F4y3N&J{;-5lp9 zrm5$TL$pnM_2V2H##tSbx$X%al;-ND6IZhXwuD1*G9O7#Vg_?5TD9?_65`LuNAW85 zaC|w+jHR5<)Xep>GWM;uivzQQdt928e$X3D2^o_CuE;sH$!f$Bxg=z#;`+|of^(+M zW_9d#PnCP)*2>t%SZBL$kmP1yz@43PrJ%J`eTgZ33nEXdV}q;YgJj!W+fr$(5e08N zz|cssG=kRKr-b{6;zAR9@aCYiC;hF0)^-dU0EQ;D-%98)dO&0dyuBHepj`%;nTrH2 zBl-0vK0h8nl=^R&h`#&b=du6-^>Z;oXfOYe!JaqfVIyyze$JB z&SzAg*-SJs>M==`49$13;VNjnRm*y+#jmVin}|o0fX60QG7ldzB<9=tXrHGAmp81R z^|V$L;y5JjraU>@mMp#xgqERR&rzGappW=zs-Vwv=S^E1;R$N+25+!jBkbz3LytkP zsG9R_1_NDY)pvaeFSUqWeAD!NgaH#j&Qj}#t(vDqwjtZH^T3-O_)_T2BAH=Viz*UFBk9Q4BVE9CIZA$>d%{m7iG+E7t^7x zGS<|KsZ)S~CL(D|RINPw&iIJ#Y~&&%`m7W1P<1xrBW#{@zMouieqb? z+lmKKV^l3BiQh$$o5M6h)l*-L_2B8>y3ox2A}bNkEk7l<%B`dVDF1_M?}lkqkx1a7WMkcBF|Djr*@BTG$Kc$Ui@d1>|AL+yKw@3 z;#&#=kkH(Ua%|3Lh7g@L&pJ%tNtU7py=RIM0o>}-Z>8y$9LUzXY$Te|-Rjf2X><5J zt#WzmEkX(y52*fVe9=$%T=3~aTRGp-;wz`D8;c~Wj6TU&=DyLPSsfM=jIydhYo$iP1JwQn-$l^Y71SX+>{ z-;nE{=_DC{*mLVD=M-*uN>X;Ks5;LWljpFXe!{uO(qPymLI!>@-*jcZMLB#CS~c@j zBQu?)7Uq>1&Ta@8*R=#bl=|CT?bD!qZt|(YX%jk7ygi^YXnb36z%TOfr5FikSnI|6WA;Gu=@u|`e9}5i zRq0mL)DTV=@Xx#{mn65{hU`<*JUsyTXNdZA-B%(%0xUB7VIB+mkFgA7Ng*2+QxzbE(=aoQ9`j8o>s($2KPw z&J?%*IM;4{_v|adwMhV)8Jl{5PRm@)9@57MlmO7oDIoR~dRIl#6g1pdmj&-i^z~oo zvxpK~KZDVS_^pS%PhHpEUXxzhSeaScY9|P!congy_j5PY6Wx{Rd}ZNORr)DvM!Wf)U3vOS?;8*G>@EcrCkebR$InS+jLuT62HZ_|=EMsu1xv?Qq{15zUQt@zrmN^k@moRr>r@P|resa*v01ldRrWc0mpea5T<9Ila z5*6F;!*$SDeQG`(?LH1PpI$v$>G5g2tDpa=`Si!Rq;!efd_peYt?`Ze)o|WIYn)#b zm`n_RAHDq|AdOl;l6B*2G%u3Y1>S8YMJes5mPx86tODFj8(I z+*;W`7ugOQdT0gl(Gjv<*GU=Htnx6H3T&?)){M^#9EU$k)tbm0ng1rhH)xUL*xJEg z^sF_0^`a_eyKhXyc)Qbu8sFYDv~x8TltkILR(zm(Iq)xYO|3!U3BXTp%pPNiq~a(C z$>_O2Nu7SpN`dZ?uuA4|z&2lZt>id9VCk5Lu^VNm~vL65D5?EEt?i(GK(-WXgOnkY-_TsxEJ4#DdYZQ`5cXxb{z|fKcNr+k{MH!0H z0D+$W08Bb{Us4sfi&ApH?CewadAg%$I#QPv#QW#(E`PAOvr|hR-)IU%-Of~^iUL3K z*Z)ZUOTRQm*j!cKiCZgxKXZSu-L%rX-c+8jI=-X5RNd|o9kSz>RWWZ9NtsGQWs;9_ zLM44NY{xea6{0(yxI$4S;J4c(7~19r=2wd^x3L`5>njJIE&Oh=@I2PV{jF4aLcsb- zXE>jbB#HRL`d~BW_!D2>^qD6XaZtvT8XF}rsw@jWnmcLwBj;YA?>Bk6>Ay-HM?`<| zS5H{o9z%I3v$cbYf)|&lBqb>w0HX;B<*0lQIx#x(>)H5-?X!l{ADT#uX~$NK5sUuy ztGF6cp4Be@jzfe|?PXc=!Yhc^4a|j7B&#FZ`O7`JdQ4(IRH7K6%*Ks8z!!^^jFuEg zO#yFDg-)Ma!5N~}zd4_zxp{8umf#7BJI}Js)u|A93)$-LQ|@T((9$~+G}j8>esPSJ zDB+h}ENbfM)&uC=1biA#tH<^K?~g6I!TAA zS#(i7_Vi;F>+2YKC47?sdwQys67Rg`QPqQS6)v14bq0iM!gP!uvyZx$o_Am71*CuF zZ50WR@&btZf%ALDJkHXOPr_;`=X!ZquY|(dx@?8Jzt||dxf3cLMn>tXFS9*(DSc=7 zE{A^PwIutEL7uA7j1igMR46bQpmbiXOm_6%HL`I&(>qJZ5{3YtJ=i!!c{SFta6;sF zQ*m}Oc>&?5wk{_=&qw8#BnS-vViRliy6;1u=TFGBCmUC}+kz0v!ssuzvyQy3$g^nw zJ6mE?cf-|e)W2+zVatVUn)bv@h3z=$iuQ@BF@J7h#I65q&Eeskg6TXZ6=^faCy;LQ z#gP(fBfx8gMF>25@Nm$D7Xc`0K4Pq21o4fXH*jf05{?^-KRUoV-?jEeh3+>l0Spi1 z`ll(lBIJ97dUO`a2V)wGOseNz7&@n{e%olM17{XSe0>@`6(>zc3Bp`#tjKXpEG(d-!>x-Smsos z&tvU)L-j&yxL>1o7vI=v_l%D>(XsCL3D}R#i{EZEs60W1e!`^?bW-(S!@}0m2vnE)gQZ}c$30|i0z37$R;|OCr zeU?;0y|xiXSaz$|`=!uK=fF#h!mU#hay@K?|0MVxkX9-I9D;KdSWTW=o2Ns#oR+#Ds z9*MwWSAGtUee>18_8J7N-dcM<9_uC*Bfa-}XfY4>e)`wTa!{mq?F7RFmV`5cN%TYv z9LL_u79r9?YJVP^+h-@t>z(HEl;mP0(JGnw^hy?x-vWO7J%pC{asRh@k9JeA1f!6( zselIHx1R-L*G+r#)(2~KpIiejVR)^&)<*_(RyN+qha7TEDyClLuXk5!JnI+1o{koY zH;lU}E_tx4K=tV)a1z8RVWT~zzt8Cjf4Ell;86sjFXu#8Iq-7cOC7eWz5$(_za znlTpy{jSAB-Vl1vw?eP!F?V_Y>DG6HS4~y8Ga7o4k@FWsiMkseRxb#(KM`x?R;c&! z0pa5j#FE%n$MeQpJ5FPyd*%A0)Rk{<-9%IrRv)FlV9vfK$v_^Dc>@`@S-1Db$QYeo z>0EB`3A*wfR(zM_)t`gLb&C~-YabL}l#!BYe5jBfW3huB-bz&QU_|7@C&AtIFNL0x zzSv2kUJAK963=>iqLQBX>>_)P9Vi;ja$>wlmVBc~-%rjnSgBRN4k$8bN+%aZAw&bu zr21CX8u6Qm<9C?>Kf7Z@jmNV&cb6T8NLk)FYwuDB@8M8@y@B?gu#wC+qPu0Mzc;7d z*J(O*o{?iK>@|8N$M3Yy!F?i&OD`CJE+{vY`~Kmt+XLqBb)@WNkYL32UF?7{#_dA+ zkF_yhd;>Lp^_*&A`yTZ8E>8*74+U$4mVlAE@T~)33hsyW)3&s<54xbhu8= zqU~4zCc5egc_M*pvy0Q_Hzp5CXY`v^8nr#Kcw`+k zJRLaKan@T_9~ZCzzKKUtfPZ$x6rEFY!yGXJPJI;)MrQ+-p$Lo ze#~zku1tluKK!`RlH;ePEV67&eAJ@nmjYc?$1nM`$H1t%*7ogz4U?LzS1#5GkJhAC z^UBX%i+WX~3ER?9?gZYrwFNa82hLp`u7Ij8=>$GkdAL!zp^bz?n$77hY_Z^Zt;O=P zjVj=-tKAqN+l98H7d5-CnvynR;#G3YB;N%SNp9H1N3TguSx=(->$2FF*h3hJS38}l zluVMEv?`K)&qCItVNalf+gRaruI7*Zovp4F(#My%y{BJ3Oue}3Rr#s+l)}ofBOlMP zPd5IMN&)bjRYfSO?eQo4Mp_xNAUjpyVjX5tAZ4_(7wA3NS4Ln<(Xhu_hTN@wpt62w zbs{EH{3fcsOL8l?*s`{l?J=>06S#G4;wX!!X(@sSJ-c}5p{KPDQ_J}72P0W*CN`Jzk4HfGRY6D)13IB z@8L;EVM*$^obv;vc!P`GK0ZA^4}SCtBi#I1s*=$bsAq8}9Bm}XbpJkhF=I$f)Cd?V zSnPhVZUoqA+=swH+wUhw%+>F|c33Njo`Qg~t<&<&NKVDdi0XUrLdfSMs0+BRW zSBv1Ltm<$(bv(UMOwLlUgsqrqiNQ3c5>qk9s=H^)z@W1?HAvU8$_GlMtw4VJHq1#1 z)wZs;s;ochY0UUJ+B7p?h_;Snz-SX;dm}x`fW5fQJm^=*>9FeEzPNT?EdhfCGDpVf za}F^d99oir!|%6B?i~q#kVgTt%Y)edB-!@27Q>G+WxY7zcPC~u@#V7R{#z;Mxw)t4 z*|ks<;YrPligMIT&G@Cj@qjpIVGWrLLWQ*6?%bDZ~Qfg*0`W2TD);?tyQ7Ty~Cb7QW~j%M(9(X_a^7TRl(ZiEF?S zuG$!!@%UL6I`oLcsP@-p#7^Fp?*iYnj&7?$b*cFL1w8HdoF%0uRXo4 z(SC!yWk;Q-sn8tX{laSPd~64)rntKk>eN|$vb=^u+GdWeXSM#h7bg6M<7(v!@nB93 zX6qJsBDkB8y6zxn>}=Sf*PHdh4`yd%>;@m|xi%D$qeYF@9J~>l@$sR!xBq#(+#8+4 zRBcai-RfTQk1IyrkL^y&(><5%^7r;kHwQRfj1qkI;IbOZcvYf5CyYp2a`_k)Q3Kj~ z7Om(Od+?3Qt|kR)AFjm7A_h<;V-theK<3F9S);B2S%ZyTosgrxf{b;eHt%#=ibufeCxlc(G*1~7Y& zp(S$g@xjr9^jS1Az%}&t#QeX{8quy5|^(y^+G}dtOsEk|7cZTlt z^uV`LzP8wmtf0#ad<(F)S`=3Hs3M;lkOtdYdNS$yUMoswv}E%apPv(A_coM`qeB83;6#@xYnp)0ii;e#NHD zp*Ph_5&&@`yYqr9Bzzp-%+NpFKWng`(B>)BRS+n9%W4_NTsb6u8q2x1yn9?^_O`Hy zYaXclhWngx@A(+vhlyfXAQyI&fBazjygc!Jo96#$?!Duh%G$ke5d|9{qX+^aDl@1s zQe@~=X=4FpAcT$(0i{Zpk^rKDR1r{mQxO6L0@5L%Ku|hLr~#Bf0)*a^P~H{hzR&H< zIqx~=dEfJVp2t5mhP~Hbd$ISrzSs9xFfYd=U7^(hb7sM3S>^acJ!T2!7dTpDuw94LJQIgIHL{)bt`kZ#r)qIwVlns>gU_QL$6DO zF-e@x3E%uJbvaC{F3lF2x!mepX@rE|oKYV`%f=-)`=j2nI7^Y!a+eau4)p%qa)3{) zpQ<;ml9V_9_M*;$ZW*TJ*3Xz{q9Va*xVXxsqIF`@Ux#AJq+|uQA-GLYxmq{HTLjy! zl%4N>K3I5-Ct^a?H~pLrU~xXFCQTLDX#2lGORVMbhgfvX2RWYaXP?8S_plkO*|HY1 z*dokr5p(EDgIwXhR4=^PuyN=yPs5cb%DJTg;L`U*iW_2NxXzYKDflAzoqZDASVQ+RK9^2u%ZMSKz! zM>IdgMz_kvwDF6oq)Ojz?wAe86z)HTprGP>BZh2*+>pg-&ON6-d$jWxtF?^N(RY+V zWg-eo5QO83#r8Zk25zs#_g?IMQzY|Pk*}>*JO1?4xT&P(4O*DX$Szy|s)ZSZXma?9 zOLA4sT@ch591p;!R(ZQC^=bC;tc1-Sm;*256Th1mmn^s&Y}{^oRY7f4*o}j1>@aV`%zVNbXguC&JtZ@dJPA}a>Y$i{X6un# ze`m#p_$cIIh7GUynldc>UToN+sVevOb|NK~Mp3?)lH4zSroK}A_jIZSWO*<23C+Hp zc@~5|MN0KFC}u?=q~G0py{j)@UUL5~V&(XS4$V)2cD80&}R0wRL)Hl>EZdVBf3gRx! zmvw>UCuUup%SibF;#ko7fW!;L{GDHI5d{TTqOf0X>=IGuX(6K0g|4Y5o}lH1V*OoW zKv7o9r4~cxyF2z-5bidJSytR{)9fbfTL5-__i~@mB6t3f{s@+W@OOM zGE;rN$qfJAS;xe4(5rX7S5AE<%>_x^mc_U76Zi-K&v_f_UGillkCjeIaT!TTdC zOqFJN{|(QHrb+5FJwwG>Kops|PfED_%E-8)Qddmr{&haF7mNi=jiDdQ5KB=$z;lRu zc#cEll+PPGpI|otKLmMCKs_4#$xr*ilUQm7VTJT-Vhw>W-~Z6unf@^3$(~o4NO+ep zsI7p^EmQVEAD&lk%U7y;|rRw0vV zEv0WVYxZL!M6_bzCs0B1ONa&w)PB=ku@G}K(AI$PCyhb0mp6NF(&y?}`%d14Jf5N+ zsIwc!2xx0gZ;mG*^nPo?`OZyL+N%p@{_jF=rrN-a@mssov0W_-WCA5HY`koF;gm!5 zeh0X$WS)d&lE#zp5bvhexruv3F^{%>vAuZ$ANm1MsNt`+T4(YJlG;vPnb580?8DUx zw$Tn}1mS}@51e2doq~^G$M%^OnB|1$JNeuXdvMH_^+et20rY^5N6n1|ao+chxT|Yg zN;VYl=J}Y-&-7nrORLA+$40z1PV-nFvu!i1GJFsKc%VE}y#VRin&GVh0t=!6_eim1 zl(2SpuAuMcqyVx~^-jbbH(p;>vL^+gL6J2!ygqNEBwv=yt>(KW%qyeb!UsjiL@VZ1 z)S{H$8iXF(j-L}XDqHySc@C|Jxg*}nX2kCf0h?buqMSqAVij?(-HO^T0eXi(wDWfR z`x$q=xYpqgT^r>28)U?SHz;c_2vG7kxXomxm?#GbVUs=R76cXw()#Vmg#M0=cQz|^ zqw$u`*$X60?y3)`U-UFGtCS^>YJGz1WHLM32|xbMj^XHpr-bCWA_~t6acHGH*pY0W z+CfJaHHxV2Q?fE%4)OA;^DD*(k;OncU40O3d|hDJ$WtcGc7x@x$l6;8%hFx+=)F*t zia0R+JDtH!9p4_f^aE-{GGuj1j2nq(krUur|D;p3EB; z9k$^A{T-;JoE``&fqb6RtE*u;YXxV?hYxk_#GX#J?KNfp1#SRQOKDz~t( zVKm8sL@*Yc3()R7)|QWsLK4&z^CI_8+eWy-ryEMke(Akgum2Oas)pE&tER-@=f31= zJVmv7^S~$inHSo~9O()YU zpS@lV$4K^UK5}vb+M2B$N>lnNpsiWn_c&!ZaQjR4>54ZywgwT{QMmYU(5=<+^mhM7 z%f>;#%zb&VVEq+K6gT5~C3p_i-I(*4eP;fKW-VF6SjDqPh@mCIjD3o~L=|?3oUZiw z(sMa)VUI@XmE60Ctx^4{q-a-rr^_Zv_MiR%qy(I5lDMn0Y|&x$6ag$JEzKkR(M?uZ zegS|q5(a9jo^YtPvJLF2$UN8kb2U$By#6)K&j$?|H^HM$Q+7?S3fLpi!#-IHX5mwn%^3$mpT@V0w$FRj^G6;9w`g?d;K@F$A3pW1 z^sQBsLg{s@<~A?~lF3Xk3|6&74`=8u8EpVb+a6A&-}OS6h9OHdO7*al$V(qa_!XC? zzgAJ;h}V!s`jle60}T8$&sDv(k;C|=f$q#%9=F1aN**0^UrjTtVg1)SR;oWTAT?XY z>`g}NH~VfAGFAqoYFM#*wQhVy!<*=;hEr4Bj@)SZ z&{gqjJwsq66nJZbN1JQ~7M~dr@zw*UU-D~Hzw_+aPyiV*hD~32wHs-f<(&Whj?1^| zwT6&v%3A2|u3C)cA9SJ%h(evPAWxl#LS5)~tuVG!8w4fURHTt2yENhgErV@<%*(@$v zfRt7>?+}bzeJ-~|?olhygW%L`Iclhf(QNIPIw`S?M{eD6!zUM`Cfw$GgB~s#NJU?* zQ{Z(7M_dUcykzKjdt;(zS6%{bjY7Fiwp=5s6<=v04A7mLwJ*_h@3}CmC>^8nW0dkQ zk|ye+2Jr2D?qX+edoibt;LuQ=KN``A^Z7IGu?HpqFHk_ zI&OH?nJHCSlr@_c>wdr*V#45q=iMMx>&dmum}{CEPj3sZO&4Qtm|KQU~#K zf)bABdwGTlmFl$pWzV<~n!7F#H1OASPEL7=FoG+4VyA(>rqsDaNVM*Z!&@lAK3gJp zOTD*X<|jtWy2I$Xc7>J^3!fz5uStk$<+xk_`)XS6thk7WRB#onR?FpEdtKEdkKSkc zgmYbiGQ5lVeMfFP^6k98MY4<)2$PzA_x@2Te3Jc6n_E6eH-Vs&wTv^E5L+>4$fp=+B5{Q|5(_1v)ynLY2LLp0OyDx?KqQD3k38$CfS_vi4sgw^t`(*6L%UUO{EI zXIB^4dsmLG;DnHU$=>K*4u8K-;uzwDyQr6aH}9CZ(li#4YfIR6A+v&zDEDw5>Ur+UDK<0X6)h=K~QV@0&M@qj2OCeRh6 znnnj`?;+CV7zkgS6dXCGJU$7aQmj3$OO>w3|v;2B{) z5%Y|v`;l>2-YhrWsEK}237<<2lB|eM=j4l#Iy{r+_-;#GNBSF}y1>peSMTO(ii3J8 zpIMYzuy4WbAuDE)Lj=)Y-*G8amyG=WDt|2$Pnp0FH-krM!(e#1LdKNw4&4$U#p!l z>0QP#AhOA@M>9Cfry_6(uF+ABlkCTZTktFDG`sqQ(TX62tDz-6GyAsRiy3BIL#ml} z{8%8}6)jmZRo(O1NW%w5UnsdtZ!GJBTqr@k2hJCl>v*|jFVixGp)MDoC5rbxBaQg8 z$IQ4hOlFGn%tEKFzrMji3+;ZWYbFMs9()KTo6({3P^92Jdjcp!7F)|J_LI3kQ`6c;HTyE2SX`|$lb4|^Gk>Zielzsm_2 zq`LsfwKllRv+1QY6fe{rs%sx|7|Dk*AKlcncu95thl``d!Dh+`gCmtE@Xh zt+uQBzK)Vy=#~HPFpgZKuMxY@`@G82WDNNp*xZh6Try4>NiCQG1izwD!8qzo@{e>w zyA=m9*U(co+UEf)s*D@`+Xc05LW@elIt#dMqWWK1DC_9+VafCocyW(?x>D+q$E~aIxG7mxjgiTym2I5Ot@l}CmBh+3+$!s;K|z3s8^XW0jboZPYRfC<@-ZW?@dyv^1&$MSgKXn| zR{|%w>g9Kj;6u^}qNxtLnBRnc=?lqSighelX_ecrwB??DhI|6?f-l7_^K7TU@D-=} zqkDJ`bHsv-+pl0-9l@pS{qa&FEPv-9dg9r{WW@>El+%3mHQcRH2DYp^s~@4;@f;6A z%$vs_(i9oq%a2v?;Wx)ItL~A)0$pj9Am%-k2WI|U=H@Chmc0Dpx1+kP+PSBt7Aixl z@0gQy4bxkTmZKK$XiT_85edd${d7d! z0L0XMJ+K^-$I37ipr776_p&6mW+CgfWPdIY;hb_jF+BKkT2S`lu#337_-w)g;lb~p~o+g5mDL&eeV$D6wdfI3mvo!s#q?bCK>n`-V>Y{I3>Jx2vRFMwwkQ9uiY zN*VcHep#YBj=?LIE-60)-Em?@^wPQCAp`Gf%D)3T`sD{>AEJi+CmB2bW>U7$MoTyoDn(FxejnoyaP7fana&Hj!=11{JMKdX7aqrOvN@ELW6F zyhnuC6xcLy#ykJ?%Gtpn8R6;v*>ilEeiLFO>7vu9DPgjv-{4c`EqWY^EUFCcd`>~g zsMS=NL7qxl+2;3Mf4UUY>wg44sNXRHuo`bK`JXl}{q3yZ0QC#Gvd?bRRi)uX^+GL(`TxX(N(*bUipK4GtG~+= zlFjd@KH}p$@2D=YrekGp-vPTM90@1rt-<;Cc^Z=7OQ(+xC2arNTbvRHgLKPP2zME8 z+>4l<@qhD|Y2d+?7o3t1?(o%jzs*FYTceCiqEShwC4tj`V*) z>U92lQpe^`Qin@j)CMI6dKj*+poJncsDV7SRtZmCAl@Fa{f%#MIu3uC9vjd3i<3S_ zxjxeEsJ-aN))X%A)~)oR`z}9&A=Q(b>2S3BrK8NVqtSo8mFKU>CKv6Odz~;Rt-kGm7~L9P4AL6>aZ=-Je_?Vs zixG#O4;P;;LM1JkELz9uFT!!*9kaq_chI_V24)N6>ysbo&bLQ7V>Lhfe!nh zck8Vm++G>vx*srb_s4(62YCLE!nFSZt4oRrZQ0%0>rSDv#0b*wEJ0qs$-Meoa ztEB0r?=2N-Tb8tS@Kis2u_<6nJb>M;9=W@;I!tjDg1*>&p3XGolp^Z8pD`9mORoya zOD&C1q==_SN>Qk493ACvS<^+@>04BGy%nQ|3pBu4d}0_ z7OxUVWyB_t*9n`veGB#^E2}u4*E9a)B(Dt>Pj8h>sLYFvKV>-=>boM7E2KTH1llq_ z1;kTKykba-fg7{;Y?W7G>+SFWOsVA_lrrX4lpgVVquKthpDU_~RnAf!0jfQrW zh`0s%)W4gbwfAjHKI=-3`(S@HV7@TmN^kzG!l+rS+OTAoNsG8#`?b}sS)8yP2l|xG zZ<&`c!)cutBlE#7dZmh!sjJ~GvQi(kuuLn3`;)8<;1Zw8_QRbTHpXQ+Q_|5IB`PDt z9IExzdR!p%v1WAV=ie%7`dB%i&s0S#_BlQ>?eo>LnN8tL@#ZohP>#{K4hHa%rB+z$ zA2C6uPisR`YenjN98KkQsyz_q*h()n+8f8Yy1CBFqpuJ)Zg%u=T^Qy`wcxT>9jiV= zzAIiLl)0rU?J&C2yufeYzW}?y1pn=f0yA5D3uJ$~41LQ9|Hyw~7)}=MoJ{a=Tg7b_ z{Qt&vX!~x^QlGdb=n4x2Iwz_n)E}a)Z!b|KkV3d88W)_N?hb|RtUQ?UT4BW27c@vn zJYKjU&XuAkm~)Tr$Ks&XL2RhEVcl^u7xGK(`9hF1ENK<0~no{5-~-S`V$J8A2Co0zcg{ zM&7HRWT)E)K|SMqEBCZHt=0>N1(7M#d(^}#w*9q zxZ2#}j4&kDaHi-@)p@e2O2)LYmU`5ShBNV%;loD!_ZW;jRJ!Tbo7iDnYL;nS;0KL7 z)+$cQObKz6#F_?uxpa06yB)FE=>A6x6IUAH8W0xz+>xk61BW{AU)spc0Yj<*ch1b( z19*B|)$PFBNSS|}eZU)v3C4%$`3WcX{Dh4zt_JlFQfXrfxn!c??rJHs3q(XbN11a%*JOJw8ViUxGE*lM;>a50%7JytO3z;T3rVK`p7%fV@; zb#7K*c-oBHtEE!E#mwQhp{jnP9#|I+4zf2@>dYfHEwf%Vp;|fJZ{nLxqfwDC7A2(b zt->&D@R!@uISVr=gg{w$xnH_&x<2u65C^Az?mht$uY)n?D1vwJ;|jp@%ZjFJY(usY!^<>XraR<=a2_Byp`q!9AFZ2x3S zWt=bd_}cu;aU?>Ck++l;v;8|pD{>fYbb3;3_OE=%FRA~F;{3i|P%f*Jd zIUZ9Xit)((URv=P+Ok4>iBcc&JEBJZg4`QkrMtH*cMg|!`+PcG_sY59yd6lCdT?jw z)SvLz+w?AT_Xcj%hgGG8{y^;_`n#msv+D^J^7z6I*W)&QyFiT^qroHFka)C^T882P6! z{>wEo3TL|vGz&{pS=YqI!V`Is`PFq#p0=Yw{Y#yurDOuEIWroV-V_E5U|(9m-!&E8WOZv4WJ0z4zL+ z0B#3o#~W&P^a^}-)(>T#nFnV#Z$HPD=w6(ZXD@nykoD#0xOsO>wY2Z3s9JYazPkZx zkUh87T>t|c3iNGU{Wqv>ikACqzNkxpO*J?MSCS6&$;Ou z{)1l%D5G>9;4z@(@lVK_=i`)&yu!L(q`6{TSK%9n*0QZ?jkZZcl^Pm$6(X=-Uoa+o z!v!tu`GgsFv+Ki8&&8r~z3&{3dCSzWV$#f^?_2z|1@3_NvIe&JD7S>DvixzqtY~A8 zzSBic2*~Ab{SMyc%ucjWyH3Y_OD>(?o{RN!%IGlX3osGD~su$1T4xhc_DfKDEbxkS)OG1I-{!LR&OJqRoF({F#$PQ(T{Z0dkVyR_osX0evDHya%47@q<^169b08o8u8#(fd4D z?RNRI(e~e5QZr_uCNx2U89{MB^j@U%BIO}AEfcaGf2mO|XdKD?e*vcBT_B9mHtNFV ztLrLQrXP*W7>@oZ6@RJoovD2l#PJ+8+_#Bo&oPj7|5@C4(5LEqg0n**9H$RooqQ+- z=aHMq22RJN+~BphuDEDq^@Q<({w1O0-pD4IL_}Uygc}QEnXbpdO&2rkeeU>j!FOiA zzJyF3zvkFL2I_R=VgGjLX3jFS&HCHv|cB3*}^vGT^r!Llo8YrOi5FUH5~ z(_X3+T4*)oDPPE(6y8wd@lvM7V9ZA>^SLM`R6Q0H-)l{fm#=iZf_|>w zYe*Gk{ZlOHcg9m4qOYu{wY%}WniDM&K4T;(_o8LU;?^Cj-UBO{4*UN}qbx=ko3o?m z^f(S+S1NS?iQ?aD)^QeL=y6nlE-G+`xY2ujkN^xAr6q8;{M64Dem{%2_bC8f1g0iCi9_AUzKL3Fw4*g{^&H7}tPG z#l4o~&vUW=4$`Tk3tcUcclkM{40J!^azD=iE6QnWXsw;u;|-^O4GK3te>N82ORSa# zf$Jk3h3K#0$}VqS!>0!bv*MXgsxN z*w0^J5yFwQgWvfO@{CQGBb4mi^+#hPRn3t%@Iwgeh4PI4%WZSXtVt(!p*aoZoC{t9 zztMuSB%sy#)UAf-36#ZOs0|C2G9U}+dx&Csk`CjHmX3rT*!MDgOc2I7NN=C$4c05Oocju59I^>pjHzFEwo-J*en5vL||= z$^y#}MlmU_g82%fkIx8FTMKYZmto>Of>r=1LBBjPc8oDEtLE+1z*k(984V_eiiuLb zi~&uXnJZ=E|JW^_@80R*mc(Ts}L1b!*Y0$-dHewOQVbgUqX*W`BKEOR!eQvemx% zG{<7F1pNZzWOU)oC>VlpmOE`;;f8JrbK)Ft491>~9&=J3jGpj*2UJMCPx@y|feUHC zqsC@KXA3Qxz_xBO;###ZpZtyAO+i~m&wlhA;UN#``}6jCHg{s|(2zBISXO}gJ7&t1 zZ*76AdtK|<4KtdZ!05s!s_!46(W3!%%eF+G8ZbW}W8X~Q$C;f9MtU-HP@h?T0ng3qZLwG%5H^)?si;lKxT6{HI=F}RuS;J} zOsu7v-(Rkx_=C>iK1TgJwl8@Lnyz-XMefG;s|^;!*IPR)6AGnyGpoA} z$=q}|TRZD3?%jW+DKzHGXi~+eUMN7pMCTkL9f%e?#VqtWvTH(rYbdF^JmuZPj!NdD zo_b?4rRB~TkB+JwKrVH4A!D5bKU1zNN&#=p9(@7twZ1Ide=T(kz58Y!9>T;d@8Hll z9g5Qts<8^9^GnisdI5-=0vy1YlZNh|Q1yNY{E?#QfGklc_bwz``ZP7Epx}#PIC{&`#yVPg4Z7_ zwIFY*MVy&8m7r4UuPS9p5lU+ahzGyRH&Rx{DruvG4S^%MfdQLk`p})R>w!+A6(q1# zvAr~HU#rH6!qvUsB_^tEb>(oZiZEfU_h3t?v9q{W(v+)ckyi;W_+Tq~Ewz5je)Wj6 z83#KcybIZdPO22CZSB6ZK^doXIZJ?dkokxOuE3r8_Vi(>_eL}57hqA9T*{2$(yfhw zn$kX0SCd1*Zy!|R#JbZ0YD(&52@HB*hBeBs=R8B>MuEm-tSIATIQYi_6??6SS497e&Rv#>3DjHy{L^i{bp(+p z6b!jA?2z8;#k<~ygh6r~Ay^Lq628pz{sJH`65~pAtqT%$HgJ{uBipdc(ro=z&ifH9 zGFby7DOig_?g4b!bt75HsECbwN-1WuYE@%=@DZI)SDOE$aq$2Mv-}8S-@hb6+K*A9 zA{d0g+9LqNj~f#Nzknz02@e0%d>(+Yl>@|kNyxb-Su2;9Cdzm$OkyRQtc7qa@{?}M zg~@~!>lvsV!M8eEbZ`fds#%Mh`FdWO5LlfFn{MsW{Kmt?-Cui|nmn-EIIit+ZKzv( zJ7Z@nK}&6(O0Ka(Ngkh`v7qlvR7T%7r(1!zDa_NdG3jO&9ZGSYRcOD)RV~=7w5Ru7 zkaZdl`F{RhO&sBkXw2GBcKR3+$0c!ad->JVd4RQO#h=>zDx`K?bn^}n=7eezFhQ5- zWYq-Z5t5f`YV}$n2%AD{s}orN!l}IWC|GUzZ$K(zCz+ZU-h~7%FPKOEOt92_%4RE9 z?UScdrTGh*pn*K&Pz!>Xs*x}Z@e{t+g~QsxTo&`(S+46u3QTWtcVSQFFTuUq=sx`q ztzu<7DVHAcDYz7v$nbJUzbZR16F%o)o*584)D$L_9azB$bKw)FxUVY4ZL1TfK6!em zcMi&$a3xqCFZ&XMmG0p+M4fRk@_M$7@RxDrCZ{reCpTw;Hd=Ki*-ET_rt5*{#<;3P zcQURryymwD(TY;?T}jHj1oMU>Q!({I>&>sKQGj$mgMX9p<%qjw0udswPSESEKH@cX_y+#Nt51N!d5pU{)vzrrCkUb?2<= zI%wzeE`eQ)_h(LdUhsd65-pEA69nfD=}H?e5$#2Ijyv-2o* zgio993JgA^S%2+PWwzmd)ZXl}x_39@8Zo1O>fv>P#jgI%gq9wTZFQZ6TlFcNykPgE z`wx&Z#XjvIUn0~{7OLBoE3>q^e6*5Jdig$Q3Tk=z=v%cF|E(nz^2XdCg^}UNtNcCF zQ)b;?NR}Iwd0hD}i8YeTLiJal{xkT;5ZOuIXR5P0!xLF%Q5w1q0jp7nkeN2lI|W-T zUMHKvY4cNkault)R=J-@A^#prk^k>wDdnY}RDt@`FYih_4FiUZ-|GY2jKp0kK%{c%vc+e{9RlmacYnABJ+n-&;4^gVCeZs;k1pK|~ZMSP2YrSQ< zUk>}6In~l3HE(V|!nouRwtXe?gKX_WUsg?LPXC!sf!X4VYR|pCsZgsQm1WHzB2BD^ z=e|OgcJ6h(UF+nSV2Cr1UASDD!c$-oT~jmv6T@q!+k$$gB@&r!^|mhDpFOTh{;H+Y z!$6Ty?h>|Q265nndAx2`AD>LKZ>MPw*W7<0%=vFPgmUDwgLCPZjaQ_myda>JVZK@>K@A-}LxVsRWg)W8t8QsfCXqc%0nV^Oc1pC32&DtI6xr^#5Gx_N+$1JTHet4Eu4oUhg9*|MGP%~WDS3O)PY&CsHN zdgNoqcqw@I>+ed7X@uiq&R23u5Mk_l9vbomE81y*LeuVTDt!npzrNX=u=`tz6uESHL+vZ2F-PJ6uAoUm#p+p&`wG;C`M*(&L!f8_)`p@CC1_|@ zdFT8`uZQ0;iks*Yck}qk9$y&lxygM9HO%^k+Q;u^9RJN|aB*vyxy^1OEHu{zs z?0AEFq3&5xiH^I)a@Jva_kq<@Yw5i#1)Tow(cMK`>Z`*vd}aPpFuL@|`Y18fsg}zn zhukq-ae!3c%vMhG3wT-<62i9Dc3NoX+gh0GLF5Vx`_SnW_Rcts=c4nFo74shpCtl1 zaeD2t>tv{RZ3)MAH(R``xlb|WFZF+4HKvm%PS$k>_si%T86DcJBRf5~#I_m-Q4>nM z$r)Wu8q@9L=6aHyVdHh@sn^N=7m7jWe3kzy`cOTU&7G1q&@{nz{?o4Qu0EfS*FY1; zP}nkf5*$p|PbnWb6TQ8tO3i`H5r1Wa)m2yNk{??@z;8i#_gapz zmqld*Er&Ivlk^tO&KT3oue^o#EKQm(cz!MNALs?KGZTup1uS1@JX%5bS(PsxztP;m z-+jK~FKiAYmcNn(J>`U%Zy;Z=$?Je;@8LOZZeq(hbD}Q2ot3RnXDN{>vbUCbciu)g zY^1X{t4&n=ldl(WY)rCTJ%II=V%c`TspZ9m?MgB&RG*iEPilzZ3Zl@?kJsjaM1VAU z4aQ!6mX~}mQ-Q(@w)}4^TsCqd@#(Y9u14Ld-6%-<;Tn>yevx_b^|EWUJ3^)$VqN|0 zAI2OtCg34ZrGMUrjB7tn|H9RM3%i`u6g8(WAy@6;|H{I=wqy6cvYE$_8`VF$;|Pc#D|tLMydiYVjJR99{6 z4f8lBb}VFplziTV)zOd}!8BO?bIP`FzhQInv=QbJNKNbbemHV$&s9vt36|m zZVwQ#;*m$)Ax2&%gO?XsYjL9UO@DDaVXnyT9F_~hS{KZo$Pvh5HfmtM9vz-(xRdSX z6NU|E0UZ4`^YXl?faspjzkCImVA5X3A1Wb$*1Aj3VHqO~<*5g(){P4(Hr`b=!Vv=C zo?Iz8Zex$k**o91zRk~A3v;{=TlidyY~ug%>f;oF1t!5a?u9@a98Btesd74uT}1Nt zR5=(~Z>5yR|5sB^?}y;4&0V=(jc#Z#8|4p5N$T|l$?`Ar<5 z=V=I*rSd5&+Bxsl2du2GrKBO zH}c~9kdk`6hnI7t=X{m%F>`T5 z5w}thRRv2uoJJSw89s}L(ehNmYAA=vgNUNu+ZV2y5Q27?{vJMdYJ3@ZncMt{e!=Xn z*BMs#fby1d9vw1f{CCLm3c%e@tuAc#zzfqr=lZ@O(Yn7)*PZ~L%Fl&P87wm!Y}KFd zSlmHg_{&fxR%q^da}rqG1p?=e538L3!n-3|%&;o@ljlDU`k$Y~&t&|{XlIuJpZf79 zAygFs&7_JAqod?$J6w|6I5a{JEy3r-`|G H=h6QI?OrQk literal 0 HcmV?d00001 diff --git a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/production.md b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/production.md index dc70defb89..8aa802bb1e 100644 --- a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/production.md +++ b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/production.md @@ -1,62 +1,93 @@ --- -title: 'Production Environment' +title: 'Production' sidebar_position: 8 +slug: /gettingStarted/production --- +# Production +This page only describes the impact of **runtime failures** in production and how to recover from them. +## 1. General Recovery Order -In production environments, cluster deployments are typically used to ensure high availability of components and services. However, with limited resources, some developers may opt for single-machine deployment (using source code or Docker containers) in production. This document covers data backup, failure recovery, and potential risks in single-machine deployment environments. +1. Recover external components first. +2. Recover OpenIMServer next. +3. Recover ChatServer last. -## 1. Scheduled MongoDB Backup -IMServer stores its core data in MongoDB, so backing up MongoDB data can recover most of the data. Set up the MongoDB backup directory and scheduled tasks before starting containers. +## 2. Runtime Failures of External Components -### Data Backup +| Failed Component | Runtime Impact | Recovery Action | +| --- | --- | --- | +| MongoDB unavailable | OpenIMServer `10002` may still respond, but ChatServer and APP Administrator APIs often fail | Recover MongoDB first; immediately retest `10002/10008/10009`; if still abnormal, restart OpenIMServer / ChatServer | +| Redis unavailable | OpenIMServer authentication becomes abnormal; source deployment often shows `auth-rpc-service down`, while all-in-one Docker deployment often shows Redis connection or resolution errors | Recover Redis first; observe for `30-60s`; if OpenIMServer authentication is still abnormal, restart OpenIMServer | +| Kafka unavailable | Basic probes may still pass, but message transfer and push paths become abnormal | Recover Kafka first; then run end-to-end message send / consume / push verification | +| Etcd unavailable | Running instances can usually keep serving for a short period, but service restarts may fail | Recover Etcd first; if service registration does not recover, restart OpenIMServer / ChatServer | +| MinIO unavailable | File upload/download fails; in source deployment the basic probes usually remain healthy, while all-in-one Docker deployment may also break `10002/10008/10009` | Recover MinIO and verify `externalAddress`; if all-in-one Docker deployment still does not recover within `30-60s`, restart the OpenIMServer / ChatServer service stack | -IMServer stores its core data in MongoDB, so backing up MongoDB data enables recovery of most data. Here are the backup steps: +### External Component Recovery Commands -1. **Modify Backup Directory** +For `openim-docker` deployment: - - Edit the `MONGO_BACKUP_DIR` path in the `.env` file (default: `components/backup/mongo/`). It is recommended to set the backup directory to a different disk than the `components` directory to avoid losing both original data and backups due to a single disk failure. +```bash +cd /path/to/openim-docker +docker compose up -d mongo redis kafka etcd minio +``` -3. **Scheduled Backup Configuration** - - Configure a scheduled backup task in Linux by editing crontab: - ```sh - crontab -e - ``` - - Add the following scheduled task to run a backup at 2:00 AM daily, keeping the 2 most recent backups. Adjust the `cron` expression as needed for different schedules: - ```sh - 0 2 * * * docker exec mongo mongodump --uri="mongodb://openIM:openIM123@localhost:27017/openim_v3" --out="/data/backup/$(expr $(date +\%s) / 86400 \% 2)" - ``` - - Use `crontab -l` to verify the scheduled task was set up correctly. +For `open-im-server` source deployment: +```bash +cd /path/to/open-im-server +docker compose up -d mongodb redis kafka etcd minio +``` +> The default service name is `mongo` in `openim-docker`, and `mongodb` in `open-im-server`. -## 2. Handling Component Failures +## 3. OpenIMServer Runtime Failures -1. If `mongo`, `redis`, `kafka`, `etcd`, or other components stop unexpectedly, first try restarting all components and the IMServer service. +| Failed Service | Runtime Impact | Recovery Action | +| --- | --- | --- | +| `openim-api` | `10002` is usually unavailable | Run `mage stop && mage start` in the `open-im-server` directory | +| `openim-rpc-auth` | OpenIMServer auth probes fail, while ChatServer probes may still be available | Run `mage stop && mage start` in the `open-im-server` directory | +| `openim-msggateway` | Real-time WebSocket connectivity is interrupted | Run `mage stop && mage start` in the `open-im-server` directory | +| `openim-msgtransfer` / `openim-push` | Message delivery and push degrade; basic probes may still pass | Run `mage stop && mage start` in the `open-im-server` directory, then run message-path verification | +| `openim-crontask` | Scheduled tasks stop running | Run `mage stop && mage start` in the `open-im-server` directory | -2. If services fail to start due to data issues (e.g., disk failure, disk full), first stop all components and the IMServer service. - - If `redis` fails to start, delete the `components/redis/` directory. - - If `kafka` fails to start, delete the `components/kafka/` directory. - - If `mongo` fails to start: - - 1. Delete the `components/redis/`, `components/mongodb/`, and `components/kafka/` directories. - - 2. Restore from backup: `docker exec -it mongo mongorestore --uri="mongodb://openIM:openIM123@localhost:27017/openim_v3" /data/backup/your_backup_name/openim_v3` - - **your_backup_name is either 0 or 1 — choose the more recent directory** - - If `etcd` fails to start, delete the `components/etcd/` directory. +OpenIMServer recovery command: -3. After performing the above steps, restart all components and the IMServer service. +```bash +cd /path/to/open-im-server +mage check +mage stop +mage start +mage check +``` -## 3. Potential Risks +## 4. ChatServer Runtime Failures -1. **Single-Machine Deployment Risk** - If a machine failure makes both the original data disk and backup disk inaccessible, data cannot be directly recovered. In this case, you may need to use your cloud provider's snapshot service for data recovery. +| Failed Service | Runtime Impact | Recovery Action | +| --- | --- | --- | +| `chat-api` | The APP Business Server API `10008` is usually unavailable | Run `mage stop && mage start` in the `chat` directory | +| `chat-rpc` | Core ChatServer business calls fail, while the basic HTTP port may still exist | Run `mage stop && mage start` in the `chat` directory | +| `admin-api` | The APP Administrator API `10009` is usually unavailable, while `10008` may still work | Run `mage stop && mage start` in the `chat` directory | +| `admin-rpc` | APP Administrator business calls fail | Run `mage stop && mage start` in the `chat` directory | +| `bot-api` / `bot-rpc` | Bot-related capabilities fail | Run `mage stop && mage start` in the `chat` directory | -2. **Backup Directory Recommendation** - To prevent data loss from a single disk failure, it is recommended to set MongoDB's backup directory `MONGO_BACKUP_DIR` on a separate disk from the `components` directory. +ChatServer recovery command: -3. **Data Recovery Risk** - When restoring MongoDB data, any data created after the backup time will be lost. However, backing up too frequently may significantly impact MongoDB performance. +```bash +cd /path/to/chat +mage check +mage stop +mage start +mage check +``` -4. **Impact of Deleting Redis Data** - Deleting Redis data may cause **incorrect unread message counts**. +## 5. Post-Recovery Verification + +After recovery, verify at least the following: + +1. `mage check` or `docker ps` is healthy. +2. The three basic probes `10002`, `10008`, and `10009` are restored. +3. For Kafka and MinIO incidents, run additional message-path and file-path verification. + +> Basic probes for OpenIMServer and ChatServer must include the `operationID` request header. diff --git a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/quickTestServer.md b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/quickTestServer.md index f9ba92d731..20b3e4a21f 100644 --- a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/quickTestServer.md +++ b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/quickTestServer.md @@ -2,25 +2,95 @@ title: 'Quick Verification' sidebar_position: 9 --- + +import Image3 from './assets/pc-web.png'; + ## 📌 1. Deploy the Server -Refer to [Docker Deployment](./dockerCompose) or [Source Code Deployment](./imSourceCodeDeployment) for deployment instructions. +Refer to [Docker Deployment](./dockerCompose) or [Source Code Deployment](./imSourceCodeDeployment). --- ## 📌 2. Open Ports -Refer to [Ports & Firewall](./ports) - -## 3. PC Web Verification +Refer to [Ports and Firewall](./ports). +## 📌 3. PC Web Verification :::tip -Enter `http://your_server_ip:11001` in your browser to access the PC Web interface. Replace `your_server_ip` with the public IP address of your IMServer. +Enter `http://your_server_ip:11001` in your browser to access PC Web. `your_server_ip` is the server IP where the frontend is deployed. ::: +PC Web Interface +## 📌 4. Service Process Verification -import Image3 from './assets/pc-web.png'; +Confirm that OpenIMServer and ChatServer are running normally. -PC Web Interface +```bash +docker ps | grep -E 'openim-server|openim-chat' +``` + +> In the `docker deployment` scenario, `openim-server` and `openim-chat` may initially show `health: starting`. Wait `20-30s` until they become `healthy` before continuing with API verification. + +If you use source deployment, run: + +```bash +# Run in the open-im-server directory +mage check + +# Run in the chat directory +mage check +``` + +## 📌 5. Domain and Gateway Verification + +When using a domain name and SSL, call the real interfaces directly to verify the OpenIMServer and ChatServer (APP Business Server) gateway routes. + +```bash +curl -sS -X POST "https://your_domain/api/auth/get_admin_token" \ + -H "Content-Type: application/json" \ + -H "operationID: verify-openim" \ + -d '{"secret":"your_openim_secret","userID":"imAdmin"}' +``` + +```bash +curl -sS -X POST "https://your_domain/chat/application/latest_version" \ + -H "Content-Type: application/json" \ + -H "operationID: verify-chat" \ + -d '{}' +``` + +> If the interface returns JSON with a business error, it usually still means the reverse-proxy path is already connected. + +> If you have deployed only the backend services and not the frontend pages, the `11001` PC Web step does not apply. It is mainly for the `docker deployment` scenario where the frontend image is started by default, or for cases where you deployed the web frontend yourself. + +To verify the WebSocket gateway, use any WebSocket client to test: + +```text +wss://your_domain/msg_gateway +``` + +> In production, it is recommended to access everything through port `443`. OpenIMClientSDK should use: +> - `apiAddr`: `https://your_domain/api` +> - `wsAddr`: `wss://your_domain/msg_gateway` + +## 📌 6. API Verification Without a Domain + +If you have not configured a domain name or SSL yet, verify directly through the server IP and default ports: + +```bash +curl -sS -X POST "http://your_server_ip:10002/auth/get_admin_token" \ + -H "Content-Type: application/json" \ + -H "operationID: verify-openim-local" \ + -d '{"secret":"your_openim_secret","userID":"imAdmin"}' +``` + +```bash +curl -sS -X POST "http://your_server_ip:10008/application/latest_version" \ + -H "Content-Type: application/json" \ + -H "operationID: verify-chat-local" \ + -d '{}' +``` + +> ChatServer (APP Business Server) POST APIs also require the `operationID` request header. Without it, the server returns a parameter error. From 46915e9e5f292f1b6032e1b6ec6d403f4ef6b0f7 Mon Sep 17 00:00:00 2001 From: dsx137 <70027572+dsx137@users.noreply.github.com> Date: Tue, 10 Mar 2026 19:17:31 +0800 Subject: [PATCH 8/9] refactor(docs): remove redundant text and fix links - Remove "APP business server" redundancy from nginx config - Fix relative link format in deployment docs - Clean up duplicate content in test server guide --- docs/guides/gettingStarted/env-comp.md | 13 ++++++------- .../guides/gettingStarted/imSourceCodeDeployment.md | 2 +- docs/guides/gettingStarted/nginxDomainConfig.md | 4 ++-- docs/guides/gettingStarted/quickTestServer.md | 4 +--- .../current/gettingStarted/env-comp.md | 3 +-- .../gettingStarted/imSourceCodeDeployment.md | 2 +- .../current/gettingStarted/nginxDomainConfig.md | 4 ++-- .../current/gettingStarted/quickTestServer.md | 4 +--- 8 files changed, 15 insertions(+), 21 deletions(-) diff --git a/docs/guides/gettingStarted/env-comp.md b/docs/guides/gettingStarted/env-comp.md index 3f2e151a4d..cc49216721 100644 --- a/docs/guides/gettingStarted/env-comp.md +++ b/docs/guides/gettingStarted/env-comp.md @@ -16,7 +16,6 @@ sidebar_position: 1 - **OpenIMServer**:IM 基础服务端。 - **ChatServer**:业务扩展服务端,文档中不再使用 `Chat` 作为独立产品名称。 - **APP 管理员**:调用管理类接口(如 `10009`)的后台管理角色。 -- **APP 业务服务器**:调用业务扩展接口(如 `10008`)的应用服务端。 ## 二、版本与分支策略 @@ -38,12 +37,12 @@ sidebar_position: 1 ## 四、外部组件要求 -| 组件 | 建议版本 | OpenIMServer 支持模式 | ChatServer 接入方式 | 支持云服务 / 备注 | -| --- | --- | --- | --- | --- | -| MongoDB | `v7.0` | `standalone`、`replicaSet` | `address` 或 `uri` | 支持;如接副本集,建议优先使用 `uri` | +| 组件 | 建议版本 | OpenIMServer 支持模式 | ChatServer 接入方式 | 支持云服务 / 备注 | +| --- | --- |-----------------------------------| --- | --- | +| MongoDB | `v7.0` | `standalone`、`replicaSet`、`sharded` | `address` 或 `uri` | 支持;如接副本集或分片集群,建议优先使用 `uri` | | Redis | `v7.0.0` | `standalone`、`cluster`、`sentinel` | `standalone`、`clusterMode` | 支持;`sentinel` 仅在 OpenIMServer 配置层有显式支持 | -| Etcd | `v3.5.13` | 单机、多节点集群 | 多地址接入 | 不支持云服务 | -| Kafka | `v3.5.1` | 单机、分布式集群 | 不直接依赖 | 支持;需按文档预建 topic | -| MinIO | `RELEASE.2024-01-11T07-46-16Z` | 单机 | 不直接依赖 | 可替换为 S3 兼容存储(`COS`、`OSS`、`Kodo`、`AWS S3`) | +| Etcd | `v3.5.13` | 单机、多节点集群 | 多地址接入 | 不支持云服务 | +| Kafka | `v3.5.1` | 单机、分布式集群 | 不直接依赖 | 支持;需按文档预建 topic | +| MinIO | `RELEASE.2024-01-11T07-46-16Z` | 单机 | 不直接依赖 | 可替换为 S3 兼容存储(`COS`、`OSS`、`Kodo`、`AWS S3`) | --- diff --git a/docs/guides/gettingStarted/imSourceCodeDeployment.md b/docs/guides/gettingStarted/imSourceCodeDeployment.md index 25da913f76..9bb11c30ac 100644 --- a/docs/guides/gettingStarted/imSourceCodeDeployment.md +++ b/docs/guides/gettingStarted/imSourceCodeDeployment.md @@ -237,4 +237,4 @@ docker compose --profile m up -d ### 10.3 单机生产环境数据备份及恢复 -请参考:[单机生产环境数据备份及恢复](production.md) +请参考:[单机生产环境数据备份及恢复](./production) diff --git a/docs/guides/gettingStarted/nginxDomainConfig.md b/docs/guides/gettingStarted/nginxDomainConfig.md index a53d6ae94c..88824022d2 100644 --- a/docs/guides/gettingStarted/nginxDomainConfig.md +++ b/docs/guides/gettingStarted/nginxDomainConfig.md @@ -32,7 +32,7 @@ upstream im_api { } upstream im_chat_api { - # ChatServer (APP business server) API + # ChatServer API server 127.0.0.1:10008; } @@ -72,7 +72,7 @@ server { } location ^~ /chat/ { - # ChatServer (APP business server) API reverse proxy + # ChatServer API reverse proxy proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; diff --git a/docs/guides/gettingStarted/quickTestServer.md b/docs/guides/gettingStarted/quickTestServer.md index b6e437a80d..37826bf3e8 100644 --- a/docs/guides/gettingStarted/quickTestServer.md +++ b/docs/guides/gettingStarted/quickTestServer.md @@ -3,8 +3,6 @@ title: '快速验证' sidebar_position: 9 --- -import Image3 from './assets/pc-web.png'; - ## 📌 一、部署服务端 请参考 [docker部署](./dockerCompose) 或 [源码部署](./imSourceCodeDeployment) 来进行部署。 @@ -21,7 +19,7 @@ import Image3 from './assets/pc-web.png'; 在浏览器中输入 `http://your_server_ip:11001` 来访问 PC Web。`your_server_ip` 为部署前端服务的服务器 IP 地址。 ::: -PC Web Interface +![PC Web Interface](./assets/pc-web.png) ## 📌 四、服务进程验证 diff --git a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/env-comp.md b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/env-comp.md index dc8bb8ebc7..39ffc0471d 100644 --- a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/env-comp.md +++ b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/env-comp.md @@ -16,7 +16,6 @@ Applies to the OpenIMServer and ChatServer deployment documents under `docs/guid - **OpenIMServer**: The IM core server. - **ChatServer**: The business extension server. This documentation no longer uses `Chat` as a standalone product name. - **APP Administrator**: The backend management role that calls management APIs such as `10009`. -- **APP Business Server**: The application-side server that calls business extension APIs such as `10008`. ## 2. Version and Branch Strategy @@ -40,7 +39,7 @@ Applies to the OpenIMServer and ChatServer deployment documents under `docs/guid | Component | Recommended Version | Supported Modes in OpenIMServer | ChatServer Access Mode | Cloud Support / Notes | | --- | --- | --- | --- | --- | -| MongoDB | `v7.0` | `standalone`, `replicaSet` | `address` or `uri` | Supported; for replica sets, `uri` is preferred | +| MongoDB | `v7.0` | `standalone`, `replicaSet` | `address` or `uri` | Supported; for replica sets or sharded clusters, `uri` is preferred | | Redis | `v7.0.0` | `standalone`, `cluster`, `sentinel` | `standalone`, `clusterMode` | Supported; `sentinel` is explicitly supported only in OpenIMServer config | | Etcd | `v3.5.13` | Single node, multi-node cluster | Multi-address access | No managed cloud support | | Kafka | `v3.5.1` | Single node, distributed cluster | Not directly used by ChatServer | Supported; required topics must be created in advance | diff --git a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/imSourceCodeDeployment.md b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/imSourceCodeDeployment.md index 8bcbde1725..9b59aa8ac5 100644 --- a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/imSourceCodeDeployment.md +++ b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/imSourceCodeDeployment.md @@ -237,4 +237,4 @@ With a domain and SSL, refer to [Domain and SSL Certificate Configuration](./ngi ### 10.3 Backup and Recovery for Single-Node Production -Please refer to [Backup and Recovery for Single-Node Production](production.md) +Please refer to [Backup and Recovery for Single-Node Production](./production) diff --git a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/nginxDomainConfig.md b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/nginxDomainConfig.md index 17bffb414f..c5bbca3d61 100644 --- a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/nginxDomainConfig.md +++ b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/nginxDomainConfig.md @@ -32,7 +32,7 @@ upstream im_api { } upstream im_chat_api { - # ChatServer (APP business server) API + # ChatServer API server 127.0.0.1:10008; } @@ -72,7 +72,7 @@ server { } location ^~ /chat/ { - # ChatServer (APP business server) API reverse proxy + # ChatServer API reverse proxy proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; diff --git a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/quickTestServer.md b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/quickTestServer.md index 20b3e4a21f..30178c2751 100644 --- a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/quickTestServer.md +++ b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/quickTestServer.md @@ -3,8 +3,6 @@ title: 'Quick Verification' sidebar_position: 9 --- -import Image3 from './assets/pc-web.png'; - ## 📌 1. Deploy the Server Refer to [Docker Deployment](./dockerCompose) or [Source Code Deployment](./imSourceCodeDeployment). @@ -21,7 +19,7 @@ Refer to [Ports and Firewall](./ports). Enter `http://your_server_ip:11001` in your browser to access PC Web. `your_server_ip` is the server IP where the frontend is deployed. ::: -PC Web Interface +![PC Web Interface](./assets/pc-web.png) ## 📌 4. Service Process Verification From 60adda06f0001d5465df541b1c60fa8da62628f3 Mon Sep 17 00:00:00 2001 From: dsx137 <70027572+dsx137@users.noreply.github.com> Date: Tue, 10 Mar 2026 19:57:53 +0800 Subject: [PATCH 9/9] refactor: docs - Update environment configuration documentation The documentation for environment configuration has been updated. This includes clarifying naming conventions, versioning strategies, and environment requirements. Also updated external component requirements and default port configurations. --- docs/guides/gettingStarted/env-comp.md | 18 ++++------ .../gettingStarted/imSourceCodeDeployment.md | 35 +------------------ .../current/gettingStarted/env-comp.md | 18 ++++------ .../gettingStarted/imSourceCodeDeployment.md | 35 +------------------ 4 files changed, 16 insertions(+), 90 deletions(-) diff --git a/docs/guides/gettingStarted/env-comp.md b/docs/guides/gettingStarted/env-comp.md index cc49216721..a3af137f86 100644 --- a/docs/guides/gettingStarted/env-comp.md +++ b/docs/guides/gettingStarted/env-comp.md @@ -5,10 +5,6 @@ sidebar_position: 1 # 🧩 平台及组件要求 -适用于 `docs/guides/gettingStarted` 下的 OpenIMServer、ChatServer 部署文档。 - ---- - ## 一、名词约定 - **OpenIMSDK**:项目统称,包含 OpenIMClientSDK 与 OpenIMServer。 @@ -37,12 +33,12 @@ sidebar_position: 1 ## 四、外部组件要求 -| 组件 | 建议版本 | OpenIMServer 支持模式 | ChatServer 接入方式 | 支持云服务 / 备注 | -| --- | --- |-----------------------------------| --- | --- | -| MongoDB | `v7.0` | `standalone`、`replicaSet`、`sharded` | `address` 或 `uri` | 支持;如接副本集或分片集群,建议优先使用 `uri` | -| Redis | `v7.0.0` | `standalone`、`cluster`、`sentinel` | `standalone`、`clusterMode` | 支持;`sentinel` 仅在 OpenIMServer 配置层有显式支持 | -| Etcd | `v3.5.13` | 单机、多节点集群 | 多地址接入 | 不支持云服务 | -| Kafka | `v3.5.1` | 单机、分布式集群 | 不直接依赖 | 支持;需按文档预建 topic | -| MinIO | `RELEASE.2024-01-11T07-46-16Z` | 单机 | 不直接依赖 | 可替换为 S3 兼容存储(`COS`、`OSS`、`Kodo`、`AWS S3`) | +| 组件 | 建议版本 | 支持模式 | 支持云服务 / 备注 | +| --- | --- |----------------------------------- |-------------------------------------------------| +| MongoDB | `v7.0` | `standalone`、`replicaSet`、`sharded` | 支持;如接副本集或分片集群,建议优先使用 `uri` | +| Redis | `v7.0.0` | `standalone`、`cluster`、`sentinel` | 支持 | +| Etcd | `v3.5.13` | 单机、多节点集群 | 不支持云服务 | +| Kafka | `v3.5.1` | 单机、分布式集群 | 支持;需按文档预建 topic | +| MinIO | `RELEASE.2024-01-11T07-46-16Z` | 单机 | 可替换为 S3 兼容存储(`COS`、`OSS`、`Kodo`(社区维护)、`AWS S3`) | --- diff --git a/docs/guides/gettingStarted/imSourceCodeDeployment.md b/docs/guides/gettingStarted/imSourceCodeDeployment.md index 9bb11c30ac..986142caf2 100644 --- a/docs/guides/gettingStarted/imSourceCodeDeployment.md +++ b/docs/guides/gettingStarted/imSourceCodeDeployment.md @@ -176,40 +176,7 @@ mage ## 九、监控告警(可选) -### 9.1 组件组成 - -- `Prometheus`:采集 OpenIMServer 暴露的 Prometheus 指标。 -- `Alertmanager`:处理规则命中后的告警路由与通知。 -- `Grafana`:展示仪表盘。 -- `node-exporter`:采集主机 CPU、内存、磁盘、网络等资源指标。 - -### 9.2 启动方式 - -在 `open-im-server` 目录执行: - -```bash -docker compose --profile m up -d -``` - -### 9.3 关键配置文件 - -- `config/prometheus.yml`:Prometheus 抓取配置 -- `config/instance-down-rules.yml`:实例存活告警规则 -- `config/alertmanager.yml`:Alertmanager 路由配置 -- `config/email.tmpl`:邮件告警模板 - -### 9.4 默认端口 - -- `19091`:Prometheus -- `19093`:Alertmanager -- `13000`:Grafana -- `19100`:node-exporter - -### 9.5 使用建议 - -- 先确保 OpenIMServer 已正常启动并能访问 `10002`,再启动监控栈。 -- 如需邮件告警,请先补齐 `config/alertmanager.yml` 中的收件人与 SMTP 信息。 -- ChatServer 需要统一纳入监控时,建议沿用现有 Prometheus 体系继续扩展抓取配置。 +待补充 ## 十、重要指引 diff --git a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/env-comp.md b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/env-comp.md index 39ffc0471d..808697eaa8 100644 --- a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/env-comp.md +++ b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/env-comp.md @@ -5,10 +5,6 @@ sidebar_position: 1 # 🧩 Platform & Component Requirements -Applies to the OpenIMServer and ChatServer deployment documents under `docs/guides/gettingStarted`. - ---- - ## 1. Terminology - **OpenIMSDK**: The overall project name, including OpenIMClientSDK and OpenIMServer. @@ -37,12 +33,12 @@ Applies to the OpenIMServer and ChatServer deployment documents under `docs/guid ## 4. External Component Requirements -| Component | Recommended Version | Supported Modes in OpenIMServer | ChatServer Access Mode | Cloud Support / Notes | -| --- | --- | --- | --- | --- | -| MongoDB | `v7.0` | `standalone`, `replicaSet` | `address` or `uri` | Supported; for replica sets or sharded clusters, `uri` is preferred | -| Redis | `v7.0.0` | `standalone`, `cluster`, `sentinel` | `standalone`, `clusterMode` | Supported; `sentinel` is explicitly supported only in OpenIMServer config | -| Etcd | `v3.5.13` | Single node, multi-node cluster | Multi-address access | No managed cloud support | -| Kafka | `v3.5.1` | Single node, distributed cluster | Not directly used by ChatServer | Supported; required topics must be created in advance | -| MinIO | `RELEASE.2024-01-11T07-46-16Z` | Single node | Not directly used by ChatServer | Can be replaced with S3-compatible storage such as `COS`, `OSS`, `Kodo`, or `AWS S3` | +| Component | Recommended Version | Supported Modes | Cloud Support / Notes | +| --- | --- | --- | --- | +| MongoDB | `v7.0` | `standalone`, `replicaSet`, `sharded` | Supported; for replica sets or sharded clusters, `uri` is preferred | +| Redis | `v7.0.0` | `standalone`, `cluster`, `sentinel` | Supported | +| Etcd | `v3.5.13` | Single node, multi-node cluster | No managed cloud support | +| Kafka | `v3.5.1` | Single node, distributed cluster | Supported; create the required topics in advance according to the docs | +| MinIO | `RELEASE.2024-01-11T07-46-16Z` | Single node | Can be replaced with S3-compatible storage such as `COS`, `OSS`, `Kodo` (community-maintained), or `AWS S3` | --- diff --git a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/imSourceCodeDeployment.md b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/imSourceCodeDeployment.md index 9b59aa8ac5..9b8db3060b 100644 --- a/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/imSourceCodeDeployment.md +++ b/i18n/en/docusaurus-plugin-content-docs-guides/current/gettingStarted/imSourceCodeDeployment.md @@ -176,40 +176,7 @@ For `openim-msggateway` and `openim-api`: ## 9. Monitoring & Alerting (Optional) -### 9.1 Components - -- `Prometheus`: collects Prometheus metrics exposed by OpenIMServer -- `Alertmanager`: routes and notifies alerts triggered by rules -- `Grafana`: displays dashboards -- `node-exporter`: collects host CPU, memory, disk, and network metrics - -### 9.2 How to Start - -Run the following in the `open-im-server` directory: - -```bash -docker compose --profile m up -d -``` - -### 9.3 Key Configuration Files - -- `config/prometheus.yml`: Prometheus scrape configuration -- `config/instance-down-rules.yml`: instance-down alert rules -- `config/alertmanager.yml`: Alertmanager routing configuration -- `config/email.tmpl`: email alert template - -### 9.4 Default Ports - -- `19091`: Prometheus -- `19093`: Alertmanager -- `13000`: Grafana -- `19100`: node-exporter - -### 9.5 Recommendations - -- Make sure OpenIMServer is already running and `10002` is reachable before starting the monitoring stack. -- If email alerts are required, complete the recipient and SMTP settings in `config/alertmanager.yml` first. -- If ChatServer also needs to be included in monitoring, extend the existing Prometheus setup accordingly. +WIP ## 10. Important Guidance