IM 平滑迁移方案
更新时间: 2024/03/14 19:21:17
网易云信多年来的稳定IM即时通讯服务,在开发者中积累了良好的口碑。部分开发者希望接入云信的IM服务,但正在使用自研或友商提供的即时通讯服务。针对这一场景,网易云信为客户贴身打造了一套迁移方案,并且成功为多家客户实现了平滑迁移。
基本概念
- 应用服务器:客户方自有,服务于应用层功能的服务器。
- 云信IM服务器:网易云信提供的服务于IM功能的服务器。
- 原IM服务器:用户原先实现IM功能的服务器,可以是自有服务器或友商提供的云服务
- 老应用:与原IM服务器链接的老版本app。
- 新应用:迁移后与云信IM服务器连接的新版本app。
前期准备
进行IM平滑迁移,需要提前进行以下准备:
- 注册网易云信账号,创建应用并开通IM功能。
- 如原IM服务器为友商提供的云服务,需要导出用户资料和用户关系等。
迁移方式
强制升级迁移
强制升级迁移的方式,是指在完成云信IM接入后,新应用上架,强制所有的老应用升级至新应用的迁移方式。此方式下不存在新老应用的兼容问题。
新老兼容迁移
新老兼容迁移的方式,是指在迁移过程中,云信IM服务器和原IM服务器同时提供服务,新应用和旧应用并存,支持新旧应用互通。待用户逐步更新至新应用,旧应用逐步无人使用后,原IM服务器停止服务。
新老兼容迁移过程中,会涉及到新老应用的增量消息发送的保障。因此需要原IM服务器可以提供消息抄送和服务端发送消息的功能。一条消息由老应用的用户发送到新应用的用户,需要经历以下几个步骤:
- 老应用用户发送消息至原IM服务器
- 原IM服务器提供消息抄送功能,将消息抄送给应用服务器
- 应用服务器收到消息抄送后,调用云信提供的服务端消息发送API发送消息
- 云信服务器将消息发送给新应用的指定用户
反之,一条消息由新应用发送到老应用,也通过类似的4个步骤实现。
优势对比
不同的迁移方式适应不同的场景,各有优劣,需要依赖的条件也不同。详情如下:
迁移流程
迁移步骤
注:以下接口调用时,请将调用频次控制在1秒100次以下。以免触发频控导致调用失败。
1、用户资料迁移
使用注册账号完成对应用户账号的注册与用户资料迁移。后续也可以通过设置用户资料完成用户资料的二次修改。
注:请先完成用户迁移,再进行后续操作。请在自身业务服务器上维护记录好所有用户的account id。
2、用户关系迁移
根据云信官网提供的用户关系托管接口完成好友、黑名单等关系的迁移。
3、群组迁移
根据云信官网提供的群组功能相关接口完成群组的迁移。
注:请在自身业务服务器上维护记录好所有群组tid。
4、历史消息迁移
若图片、语音等消息里的文件也要迁移并存储到云信服务器,需先使用文件上传接口完成上传来拿到云信返回的 url,然后替换原消息体中的 url。
4.1 历史存量消息(迁移前已产生)
历史存量消息,一般为某个约定时刻之前(约定时刻之后的参见下文增量消息)的历史消息,请按照要求提供以下信息:
- 按照要求构造消息体,存放在文本文件中。(注:文件后缀建议为 .txt,每条消息独占一行,详见消息体格式介绍章节。单聊与群聊消息请分开单独提供。建议各自一个文本文件,文件数量请尽可能少。)
- 迁入对应的云信应用appkey。
- 是否需要漫游。
另外:针对群消息,若新进群用户希望能查阅历史消息,请在云信控制台打开「新进群用户获取历史消息开关」,配置方法:应用 > IM专业版/IM免费版 > 功能配置 > 新进群用户获取历史消息配置 > 新进群用户获取历史消息开关(默认关闭)。
4.2 增量消息
若应用迁移模式是新老兼容迁移,则在迁移过程中,会产生增量消息,考虑到历史消息迁移存在实时性的问题,可使用如下方式:
- 提供一个对云信开放的 HTTP 接口,由云信这边发起 GET 请求。需提前约定 GET 的频率,如 1 小时一次。
- 收到GET后返回{"code":200, "data":[msg1,msg2,……]}。data 为 JSONArray,内容为增量消息,消息格式参见消息体格式介绍章节。
存量消息与增量消息迁移之后,可通过客户端 SDK 和服务端 API 的 查询云端历史消息接口 拉取验证。
注:免费版应用可查最近30天内的消息,正式版应用默认可查一年内的消息。
5、最近联系人列表迁移
最近联系人列表(最近会话列表)的迁移,通过接收云端主动下推的离线消息与漫游消息,自动触发产生最近会话列表。
由于离线和漫游消息有限,可通过如下方法维护更多最近会话列表:
- 对于迁移之前的会话列表,需要自行根据原IM服务提供的老数据构造并维护会话列表;
- 对于迁移之后的会话列表,可以使用云信的消息抄送功能,在自身服务器进行全量会话列表的维护;或者开通会话服务功能,可从云端获取最多3000个最近会话。
消息体格式介绍
注1:不能同时存在两条消息的 msgid 、 from 、 to/tid 、 createTime 这四者相同,否则会被覆盖。
注2:ext字段为消息扩展字段,若原先无该字段,可忽略。
注3:createTime请使用毫秒级别。
单聊消息
aaa 发给 bbb 一条文本消息
json{"data":{"ext":"ext..","clientType":"2","createTime":1513685265481,"msgid":1,"from":"aaa","to":"bbb","body":"测试"},"type":0}
aaa 发给 bbb 一条图片消息
json{"data":{"ext":"ext..","clientType":"2","createTime":1513685265528,"msgid":11,"from":"aaa","to":"bbb","attach":"{\"h\":780,\"ext\":\"jpg\
“,\"size\":2589,\"w\":1040,\"name\":\"haha\",\"url\":\"https://a1.ease5fb40/2c93186d\"}"},"type":1}
aaa 发给 bbb 一条语音消息(dur 单位是 ms)
json{"data":{"ext":"ext..","clientType":"2","createTime":1513685265528,"msgid":101,"from":"aaa","to":"bbb","attach":"{\"size\":5232,\"ext\":\" amr\",\"dur\":8000,\"url\":\"https://a1.easemob.files/f3bdaffe174cee99f\"}"},"type":2}
aaa 发给 bbb 一条视频消息(dur 单位是 ms)
json{"data":{"ext":"ext..","clientType":"2","createTime":1513685265528,"msgid":1001,"from":"aaa","to":"bbb","attach":"{\"h\":780,\"w\":1040,\"size\":5232,\"ext\":\" mp4\",\"dur\":8000,\"url\":\"https://a1.easeatfiles/f3-ffe174cee99f\"}"},"type":3}
aaa 发给 bbb 一条地理位置消息
json{"data":{"attach":"{\"lng\":\"116.655294\",\"title\":\"新华南路 145 号
\",\"lat\":\"39.89611\"}","clientType":"1","createTime":"1537262375000","ext":"","msgid":100561,"from":"aaa","to":"bbb"},"type":4}
aaa 发给 bbb 一条文件消息
json{"data":{"ext":"ext..","clientType":"2","createTime":1513685265528,"msgid":10501,"from":"aaa","to":"bbb","attach":"{\"size\":5232,\"ext\":\" ttf\",\"url\":\"https://a1.easemob.com/hywtfiles/f3bda-ffe174cee99f\"}"},"type":6}
aaa 发给 bbb 一条自定义消息
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,"from": "aaa","to": "bbb"},"type":100}
群聊消息
A 发了一条文本消息
json{"data":{"ext":"ext..","createTime":1511513944827,"clientType":16,"msgid":1,"accid":"aaa","body":"哈哈","tid":1},"type":0}
A 发了一条图片消息
json{"data":{"ext":"ext..","createTime":1511513944827,"clientType":16,"msgid":1231,"accid":"aaa","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 发了一条语音消息(dur 的单位是 ms)
json{"data":{"ext":"ext..","createTime":1511513944827,"clientType":16,"msgid":13,"accid":"aaa","attach":"{\"size\":5232,\"ext\":\" amr\",\"dur\":8000,\"url\":\"https://a1.easemob.com/hywwjugh/jugh/chatfiles/f3bda300-e469-11e7-9508-ffe174ce e99f\"}","tid":1},"type":2}
其他类型消息参考单聊
群操作通知消息
B 被 A 拉进群
json{"data":{"accid2":"aaa","createTime":1511513944871,"clientType":16,"msgid":3,"accid":"bbb","tid":1},"type":101}
A 把 B 踢出群
json{"data":{"accid2":"bbb","createTime":1511513944871,"clientType":16,"msgid":4,"accid":"aaa","tid":1},"type":102}
A 禁言 C
json{"data":{"accid2":"ccc","createTime":1511513944871,"clientType":16,"msgid":5,"accid":"aaa","tid":1},"type":103}
A 取消禁言 C
json{"data":{"accid2":"ccc","createTime":1511513944871,"clientType":16,"msgid":6,"accid":"aaa","tid":1},"type":104}
A 任命 C 为管理员
json{"data":{"accid2":"ccc","createTime":1511513944871,"clientType":16,"msgid":7,"accid":"aaa","tid":1},"type":105}
A 取消 C 为管理员
json{"data":{"accid2":"ccc","createTime":1511513944871,"clientType":16,"msgid":8,"accid":"aaa","tid":1},"type":106}
A 修改了群介绍
json{"data":{"createTime":1511513944871,"clientType":16,"tinfo":{"14":"new","tid":1},"msgid":9,"accid":"aaa","tid":1},"type":107}
A 修改了加群认证方式
json{"data":{"createTime":1511513944872,"clientType":16,"tinfo":{"16":"0","tid":1},"msgid":10,"accid":"aaa","tid":1},"type":107}
A 把群转让给了 C
json{"data":{"accid2":"ccc","createTime":1511513944872,"clientType":16,"msgid":11,"accid":"aaa","tid":1},"type":108}
A 退出了群
json{"data":{"createTime":1511513944872,"clientType":16,"msgid":12,"accid":"aaa","tid":1},"type":109}
C 解散了群
json{"data":{"createTime":1511513944872,"clientType":16,"msgid":13,"accid":"ccc","tid":1},"type":110}
群相关参数介绍
对于修改群信息这类通知:
tinfo这个子json串,必须包含 tid,且和父串中的 tid 保持一致。例子中可以看到,14这个key表示的是群介绍,16这个key 表示的是加群方式,其他项如下:
key | 数据类型 | 含义 |
---|---|---|
14 | string | 表示群介绍 |
15 | string | 表示群公告 |
16 | int | 表示入群权限,0 表示不需要申请,1 表示需要申请,2 表示不允许申请 |
17 | int | 表示群状态,0 表示正常状态,1 表示禁言 |
18 | string | 表示第三方扩展字段 |
19 | string | 表示第三方服务器扩展字段 |
20 | string | 群 icon |
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 |