圈组

IM 平滑迁移方案

更新时间: 2025/05/09 14:09:55

网易云信稳定的 IM 即时通讯服务,在开发者中积累了良好的口碑。部分开发者希望接入网易云信的 IM 服务,但正在使用自研或友商提供的即时通讯服务。针对这一场景,网易云信为客户贴身打造了一套迁移方案,并且成功为多家客户实现了平滑迁移。

基本概念

  • 应用服务器:客户方自有,服务于应用层功能的服务器。
  • 网易云信 IM 服务器:网易云信提供的服务于 IM 功能的服务器。
  • 原 IM 服务器:用户原先实现 IM 功能的服务器,可以是自有服务器或友商提供的云服务。
  • 老应用:与原 IM 服务器链接的老版本 App。
  • 新应用:迁移后与网易云信 IM 服务器连接的新版本 App。

前期准备

进行 IM 平滑迁移,需要提前进行以下准备:

迁移方案

强制升级迁移

强制升级迁移的方案,是指在完成网易云信 IM 接入后,新应用上架,强制所有的老应用升级至新应用的迁移方案。此方案下不存在新老应用的兼容问题。

image-netease

新老兼容迁移

新老兼容迁移的方案,是指在迁移过程中,网易云信 IM 服务器和原 IM 服务器同时提供服务,新应用和旧应用并存,支持新旧应用互通。待用户逐步更新至新应用,旧应用逐步无人使用后,原 IM 服务器停止服务。

image-netease

新老兼容迁移过程中,会涉及到新老应用的增量消息发送的保障。因此需要原 IM 服务器可以提供消息抄送和服务端发送消息的功能。一条消息由老应用的用户发送到新应用的用户,需要经历以下几个步骤:

  1. 老应用中的用户发送消息至原 IM 服务器。
  2. 原 IM 服务器提供消息抄送功能,将消息抄送给应用服务器。
  3. 应用服务器收到消息抄送后,调用网易云信提供的服务端消息发送 API 发送消息。
  4. 网易云信服务器将消息发送给新应用的指定用户。

反之,一条消息由新应用发送到老应用,也通过类似的四个步骤实现。

方案对比

不同的迁移方案适应不同的场景,各有优劣,需要依赖的条件也不同。详情如下:

迁移方案 优势 劣势
强制升级迁移
  • 实施简单。
  • 无需原 IM 服务器提供消息抄送。
  • 无需原 IM 服务器实时同步用户资料和用户关系的数据变更。
  • 不存在新老应用互通可能出现的延迟、丢消息的现象。
  • 无需停服迁移。
  • 要求老应用强制更新,否则无法使用。
    新老兼容迁移
  • 支持新老应用互通。
  • 无需老应用强制更新。
  • 无需停服迁移。
  • 实施较为复杂。
  • 需要原 IM 服务器提供消息抄送功能。
  • 需要原 IM 服务器实时同步用户资料和用户关系的数据变更。
  • 新老应用共存期间可能存在延迟、丢消息现象。
  • 迁移流程

    flowchart LR
      classDef default fill:#337EFF,stroke:#337EFF,stroke-width:0px,color:#FFFFFF;
      A("开始") --> B("用户资料迁移") --> C("用户关系迁移")--> D("群组迁移")--> E("历史消息迁移")--> F("(可选)漫游消息迁移")--> G("完成")
    

    迁移流程中接口调用时,请将调用频次控制在 1 秒 100 次以下。以免触发频控导致调用失败。

    第一步:迁移用户资料

    调用 注册 IM 账号 完成对应用户账号的注册与用户资料的迁移。后续也可以通过 更新用户资料名片 完成用户资料的二次修改。

    • 请先完成用户迁移,再进行后续操作。
    • 请在自身业务服务器上维护记录好所有用户的账号 ID。

    第二步:迁移用户关系

    调用 好友管理黑名单管理 相关接口完成好友、黑名单等关系的迁移。

    第三步:迁移群组

    调用 群组管理 相关接口完成群组的迁移。

    请在自身业务服务器上维护记录好所有群组 ID。

    第四步:迁移历史消息

    若图片、语音等消息中的文件也需要迁移并存储到网易云信服务器,需先使用 文件上传 接口上传,调用成功后将网易云信返回的 url 替换原消息体中的 url。

    历史存量消息

    历史存量消息(迁移前已产生),一般为某个约定时刻前(约定时刻后的参考下文 增量消息)的历史消息,请按照要求提供以下信息:

    • 按照要求构造消息体,存放在文本文件中。

      文件后缀建议为 .txt,每条消息独占一行,请参考 消息体格式章节。单聊与群聊消息请分开,单独提供。建议各自一个文本文件,文件数量尽可能少。

    • 迁入对应的网易云信应用 AppKey。

    • 根据业务需求确定是否需要 漫游消息 功能。

      针对群消息,若新进群用户希望能查阅历史消息,请在 网易云信控制台 打开 新进群用户获取历史消息开关,配置方法:应用 > IM 即时通讯 > 功能配置 > 群聊 > 新进群用户获取历史消息。

    增量消息

    若应用迁移模式是 新老兼容迁移,则在迁移过程中,会产生增量消息,考虑到历史消息迁移存在实时性的问题,可使用如下方案:

    • 提供一个对网易云信开放的 HTTP 接口,由网易云信发起 GET 请求。需提前约定 GET 的频率,如 1 小时一次。
    • 收到 GET 后返回 {"code":200, "data": [msg1,msg2,……]}。data 为 JSON 数组,内容为增量消息,消息格式参考 消息体格式章节

    存量消息与增量消息迁移之后,可通过客户端 SDK 和服务端 API 的 查询云端历史消息接口 拉取验证。

    网易云信 IM 支持云端历史消息,历史消息的存储时长在不同的套餐包中不同,各套餐包中的限制具体请参考 增值服务

    第五步:迁移最近联系人列表

    最近联系人列表(最近会话列表)的迁移,通过接收云端主动下推的 离线消息与漫游消息,自动触发产生最近会话列表。

    由于离线和漫游消息有限,可通过如下方法维护更多最近会话列表:

    • 对于迁移前的会话列表,需要自行根据原 IM 服务提供的老数据构造并维护会话列表。
    • 对于迁移后的会话列表,可以使用网易云信的消息抄送功能,在自身服务器进行全量会话列表的维护。或者开通 会话服务 功能,可从云端获取最多 3000 个最近会话。

    消息体格式

    • 不能同时存在两条消息的 msgidfromto/tidcreateTime 这四个字段相同,否则会被覆盖。
    • ext 字段为消息扩展字段,若原先无该字段,可忽略。
    • createTime 请使用毫秒级别。
    • msgClientId 为客户端消息 ID,不能同时存在 msgClientId 相同的多条消息。如果为空,导入时会自动生成一个默认的 msgClientId

    单聊消息

    A 发给 B 一条文本消息

    JSON{
      "data": {
        "ext": "ext..",
        "clientType": "2",
        "createTime": 1513685265481,
        "msgid": 1,
        "msgClientId": "bec7df36-a8e4****e6abb76d58ee",
        "from": "A",
        "subType": 100,
        "to": "B",
        "body": "测试"
      },
      "type": 0
    }
    

    A 发给 B 一条图片消息

    JSON{
        "data": {
            "ext": "ext..",
            "clientType": "2",
            "createTime": 1513685265528,
            "msgid": 11,
            "msgClientId": "bec7df36-a8e4****e6abb76d58ee",
            "from": "A",
            "subType": 100,
            "to": "B",
            "attach": {
                "h": 780,
                "ext": "jpg",
                "size": 2589,
                "w": 1040,
                "name": "haha",
                "url": "https://a1.ease5fb40/2c93186d"
            }
        },
        "type": 1
    }
    

    A 发给 B 一条语音消息

    JSON{
        "data": {
            "ext": "ext..",
            "clientType": "2",
            "createTime": 1513685265528,
            "msgid": 101,
            "msgClientId": "bec7df36-a8e4****e6abb76d58ee",
            "from": "A",
            "subType": 100,
            "to": "B",
            "attach": {
                "size": 5232,
                "ext": "amr",
                "dur": 8000, //单位为 ms
                "url": "https://a1.easemob.files/f3bdaffe174cee99f"
            }
        },
        "type": 2
    }
    

    A 发给 B 一条视频消息

    JSON{
        "data": {
            "ext": "ext..",
            "clientType": "2",
            "createTime": 1513685265528,
            "msgid": 1001,
            "msgClientId": "bec7df36-a8e4****e6abb76d58ee",
            "from": "A",
            "subType": 100,
            "to": "B",
            "attach": {
                "h": 780,
                "w": 1040,
                "size": 5232,
                "ext": "mp4",
                "dur": 8000, //单位为 ms
                "url": "https://a1.easeatfiles/f3-ffe174cee99f"
            }
        },
        "type": 3
    }
    

    A 发给 B 一条地理位置消息

    JSON{
        "data": {
            "attach": "{\"lng\":\"116.655294\",\"title\":\"新华南路 145 号\",\"lat\":\"39.89611\"}",
            "clientType": "1",
            "createTime": "1537262375000",
            "ext": "",
            "msgid": 100561,
            "msgClientId": "bec7df36-a8e4****e6abb76d58ee",
            "from": "A",
            "subType": 100,
            "to": "B"
        },
        "type": 4
    }
    

    A 发给 B 一条文件消息

    JSON{
        "data": {
            "ext": "ext..",
            "clientType": "2",
            "createTime": 1513685265528,
            "msgid": 10501,
            "msgClientId": "bec7df36-a8e4****e6abb76d58ee",
            "from": "A",
            "subType": 100,
            "to": "B",
            "attach": "{\"size\":5232,\"ext\":\"ttf\",\"url\":\"https://a1.easemob.com/hywtfiles/f3bda-ffe174cee99f\"}"
        },
        "type": 6
    }
    

    A 发给 B 一条自定义消息

    JSON{
        "data": {
            "attach": {
                "param": {
                    "ptId": "f5",
                    "pe": "http://img.u.com/zmw/upad/2014/92!/0/quty-90/fobp",
                    "artisanName": "",
                    "productPrice": "66.0",
                    "productName": "helase"
                },
                "type": "1",
                "url": "",
                "desc": "作息"
            },
            "clientType": "1",
            "createTime": "1539499209000",
            "ext": "",
            "msgid": 10534501,
            "msgClientId": "bec7df36-a8e4****e6abb76d58ee",
            "from": "A",
            "subType": 100,
            "to": "B"
        },
        "type": 100
    }
    

    群聊消息

    A 发了一条文本消息

    JSON{
        "data": {
            "ext": "ext..",
            "createTime": 1511513944827,
            "clientType": 16,
            "msgid": 1,
            "msgClientId": "bec7df36-a8e4****e6abb76d58ee",
            "accid": "A",
            "subType": 100,
            "body": "哈哈",
            "tid": 1
        },
        "type": 0
    }
    

    A 发了一条图片消息

    JSON{
        "data": {
            "ext": "ext..",
            "createTime": 1511513944827,
            "clientType": 16,
            "msgid": 1231,
            "msgClientId": "bec7df36-a8e4****e6abb76d58ee",
            "accid": "A",
            "subType": 100,
            "attach": {
                "h": 780,
                "ext": "jpg",
                "size": 257389,
                "w": 1040,
                "name": "haha",
                "url": "https://a1.easemob.com/hywwjugh/jugh/chatfiles/ba05f0e0- e469-11e7-86e2- 5b402c93186d"
            },
            "tid": 1
        },
        "type": 1
    }
    

    A 发了一条语音消息

    JSON{
        "data": {
            "ext": "ext..",
            "createTime": 1511513944827,
            "clientType": 16,
            "msgid": 13,
            "msgClientId": "bec7df36-a8e4****e6abb76d58ee",
            "accid": "A",
            "subType": 100,
            "attach": "{\"size\":5232,\"ext\":\" amr\",\"dur\":8000,\"url\":\"https://a1.easemob.com/hywwjugh/jugh/chatfiles/f3bda300-e469-11e7-9508-ffe174ce e99f\"}", // dur 的单位为 ms
            "tid": 1
        },
        "type": 2
    }
    

    群聊的其他类型消息参考 单聊

    群操作通知消息

    B 被 A 拉进群

    JSON{
        "data": {
            "accid2": "A",
            "createTime": 1511513944871,
            "clientType": 16,
            "msgid": 3,
            "msgClientId": "bec7df36-a8e4****e6abb76d58ee",
            "accid": "B",
            "subType": 100,
            "tid": 1
        },
        "type": 101
    }
    

    A 把 B 踢出群

    JSON{
        "data": {
            "accid2": "B",
            "createTime": 1511513944871,
            "clientType": 16,
            "msgid": 4,
            "msgClientId": "bec7df36-a8e4****e6abb76d58ee",
            "accid": "A",
            "subType": 100,
            "tid": 1
        },
        "type": 102
    }
    

    A 禁言 C

    JSON{
      "data": {
        "accid2": "ccc",
        "createTime": 1511513944871,
        "clientType": 16,
        "msgid": 5,
        "msgClientId": "bec7df36-a8e4****e6abb76d58ee",
        "accid": "A",
        "subType": 100,
        "tid": 1
      },
      "type": 103
    }
    

    A 取消禁言 C

    JSON{
        "data": {
            "accid2": "ccc",
            "createTime": 1511513944871,
            "clientType": 16,
            "msgid": 6,
            "msgClientId": "bec7df36-a8e4****e6abb76d58ee",
            "accid": "A",
            "subType": 100,
            "tid": 1
        },
        "type": 104
    }
    

    A 任命 C 为管理员

    JSON{
        "data": {
            "accid2": "ccc",
            "createTime": 1511513944871,
            "clientType": 16,
            "msgid": 7,
            "msgClientId": "bec7df36-a8e4****e6abb76d58ee",
            "accid": "A",
            "subType": 100,
            "tid": 1
        },
        "type": 105
    }
    

    A 取消 C 为管理员

    JSON{
        "data": {
            "accid2": "ccc",
            "createTime": 1511513944871,
            "clientType": 16,
            "msgid": 8,
            "msgClientId": "bec7df36-a8e4****e6abb76d58ee",
            "accid": "A",
            "subType": 100,
            "tid": 1
        },
        "type": 106
    }
    

    A 修改了群介绍

    JSON{
      "data": {
        "createTime": 1511513944871,
        "clientType": 16,
        "tinfo": {
          "14": "new",
          "tid": 1
        },
        "msgid": 9,
        "msgClientId": "bec7df36-a8e4****e6abb76d58ee",
        "accid": "A",
        "subType": 100,
        "tid": 1
      },
      "type": 107
    }
    

    A 修改了加群认证方式

    JSON{
      "data": {
        "createTime": 1511513944872,
        "clientType": 16,
        "tinfo": {
          "16": "0",
          "tid": 1
        },
        "msgid": 10,
        "msgClientId": "bec7df36-a8e4****e6abb76d58ee",
        "accid": "A",
        "subType": 100,
        "tid": 1
      },
      "type": 107
    }
    

    A 把群转让给了 C

    JSON{
      "data": {
        "accid2": "ccc",
        "createTime": 1511513944872,
        "clientType": 16,
        "msgid": 11,
        "msgClientId": "bec7df36-a8e4****e6abb76d58ee",
        "accid": "A",
        "subType": 100,
        "tid": 1
      },
      "type": 108
    }
    

    A 退出了群

    JSON{
        "data": {
            "createTime": 1511513944872,
            "clientType": 16,
            "msgid": 12,
            "msgClientId": "bec7df36-a8e4****e6abb76d58ee",
            "accid": "A",
            "subType": 100,
            "tid": 1
        },
        "type": 109
    }
    

    C 解散了群

    JSON{
      "data": {
        "createTime": 1511513944872,
        "clientType": 16,
        "msgid": 13,
        "msgClientId": "bec7df36-a8e4****e6abb76d58ee",
        "accid": "ccc",
        "subType": 100,
        "tid": 1
      },
      "type": 110
    }
    

    群相关参数

    对于修改群信息这类通知:

    tinfo 的子 JSON 串中必须包含 tid,且需要和父串中的 tid 保持一致。在示例中可以看到,key = 14 表示的是群介绍,key=16 表示的是申请入群的验证方式,其他项如下:

    key 数据类型 含义
    14 string 表示群组简介。
    15 string 表示群组公告。
    16 int 表示申请入群的验证方式。0 表示无需验证,直接入群。1 表示需要群主或管理员验证通过才能入群。2 表示不允许任何人申请入群。
    17 int 表示群组状态,0 表示正常状态。1 表示群组禁言状态。
    18 string 表示第三方扩展信息。
    19 string 表示第三方服务器扩展信息。
    20 string 表示群组头像。
    21 int 表示邀请入群时是否需要被邀请人的同意,0 表示需要同意。1 表示不需要同意。
    22 int 表示谁可以邀请他人入群,0 表示群主和管理员。1 表示所有人。
    23 int 表示谁可以修改群资料,0 表示群主和管理员。1 表示所有人。
    24 int 表示谁可以更新群自定义属性,0 表示群主和管理员。1 表示所有人。

    客户端类型

    对于客户端类型(ClientType),表示上述操作的来源客户端:

    枚举值 客户端类型
    1 表示 AOS(Android)
    2 表示 iOS
    4 表示 PC
    8 表示 WINPHONE
    16 表示 WEB
    32 表示 REST(Server)
    64 表示 MAC
    65 表示 HARMONY
    此文档是否对你有帮助?
    有帮助
    去反馈
    • 基本概念
    • 前期准备
    • 迁移方案
    • 强制升级迁移
    • 新老兼容迁移
    • 方案对比
    • 迁移流程
    • 第一步:迁移用户资料
    • 第二步:迁移用户关系
    • 第三步:迁移群组
    • 第四步:迁移历史消息
    • 历史存量消息
    • 增量消息
    • 第五步:迁移最近联系人列表
    • 消息体格式
    • 单聊消息
    • 群聊消息
    • 群操作通知消息
    • 群相关参数
    • 客户端类型