引语

IM (Instant Messaging)是网络上最流行的通信方式,与日常生活息息相关。IM软件也层出不穷,例如:微信、QQ、易信等。通过多年深耕和技术沉淀,云信产出了一套成熟稳定的IM SDK架构。它提供了IM的主要功能,大大降低了第三方实现IM功能的难度。本文主要对IM接口设计实践展开论述。

1 对外接口的设计准则

SDK对外提供接口设计的基本原则是易用,易懂,易扩展,易监控。展开来可归纳为以下几个特性:

  1. API按照业务功能分类,但所有业务具有统一的调用风格。
  2. API不包含方法实现,接口的实现对调用者隐藏。
  3. API调用可跟踪。

在功能形式上,SDK需要提供以下类型的API:

  1. 功能接口:主动调用使用其提供的功能

1.1.    同步接口:在调用线程完成函数调用,并立即返回结果。

1.2.    普通异步接口:在后台线程完成函数调用,可以添加回调函数。

1.3.    可中断异步接口:在后台线程完成函数调用,可以添加回调函数,可以中断调用。

  1. 回调接口:监听数据和状态改变。

2 业务的分类

SDK包含多种业务,一部分是基础业务,另一部分是可选业务。比如用户认证服务、群服务等是基础业务;超大群服务、第三方推送服务等是可选业务。另外,不同的业务之间难免有相似的功能,如群服务和超大群服务中,都有添加群成员,拉取群消息等功能。因此,需要将不同的业务进行隔离,一方面方便业务功能的扩展与调整,另一方面方便函数的命名与用户的理解。

同一个业务下有多个API,按照调用的主动性,分为回调接口和功能接口,分别放在与业务一一对应的接口类Observer和Service中。例如:用户认证服务下的所有回调接口和功能接口都位于AuthServiceObserver和AuthService中。其中由用户主动调用来实现功能的功能接口在AuthService中,如登录、登出等;而用于注册回调的回调接口都在AuthServiceObserver中,如监听在线状态、监听数据同步等。

每个Service中的功能接口根据执行的性质又分为三种,同步接口、普通异步接口和可中断异步接口。其中同步接口在调用线程立即执行;异步接口在后台线程执行,在调用线程返回可设置回调的InvocationFuture类型,最终结果在主线程回调;可中断异步接口和普通异步接口相似,但是返回的是继承自InvocationFuture的AbortableFuture类型,支持中断操作,用户可以通过主动调用来中断功能接口的执行。接口调用的线程切换流程如图2.1所示,业务功能和接口的分类如图2.2所示。

图 2.1 接口调用的线程切换流程

图 2.2 业务功能和接口的分类

3 API的实现

3.1 API的实现方式

为了实现这些目标,并考虑到实现简单,我们选用Java的动态代理类模型。外部调用者调用API时,得到一个动态代理(Proxy)对象,通过Proxy对象,将功能接口的调用全部转接到一个实现了InvocationHandler 接口的类ProxyHandler上。再根据调用方法,执行注册/注销回调或者将调用请求分派到真正的实现类上,最后根据接口的返回类型进行返回或回调,如图3.1所示。

图 3.1 功能调用流程

和用户服务的接口类AuthService和AuthServiceObserver一样,SDK也为其他的所有业务定义了接口类。所有接口类和实现类呈一一对应关系,可以方便地找到API对应的实现。

3.2 获取Proxy对象的方法

SDK对外提供了静态方法NimClient.getService(Class<T> clazz)来获取业务接口类对应的动态代理类。参数填入对应的接口类即可。例如:获取用户认证服务的接口类的方式为NimClient.getService(AuthService.class),获取用户认证服务观察者的接口类的方式为NimClient.getService(AuthServiceObserver.class)。

NimClient.getService方法同步返回一个Proxy对象。该对象的构造方式为懒加载,业务被调用的时候才构造对应实例。如图3.2所示。

图 3.2 获取业务Proxy对象流程

生成Proxy对象基于Proxy.newProxyInstance方法,所有生成的Proxy对象都由专门的容器类来管理。以用户认证服务为例,第一次调用NimClient.getService(AuthService.class)获取用户认证服务的Proxy对象时,容器理类创建一个对应的Proxy对象并缓存。之后再次获取用户认证服务Proxy对象时,容器类将缓存直接返回。

3.3 事务跟踪类

invoke函数中有一个重要的事务跟踪类(Transaction),它记录了方法的同步异步属性、方法体和返回值要求等。Transaction对象和他的实现方法是一一对应的。每次执行invoke方法,都会创建一个Transaction实例,并传输到真正的执行点去执行

执行完毕后,invoke方法根据同步异步特性以及返回值,来确定功能函数的返回结果。如果是同步函数,则直接返回结果;如果是异步函数,则根据业务需求返回InvocationFuture或者AbortableFuture。

3.4 API的执行方式

NimClient.getService返回的是动态代理类ProxyHandler ,它实现了InvocationHandler接口的 Object invoke(Object who, Method method, Object[] args)方法,用于代理所有功能接口。

API执行方式,即ProxyHandler对invoke方法的实现方式,其大体步骤是加载事务、初始化判断、执行事务和返回,如图3.3所示。

图 3.3 代理执行的简要流程

执行事务这一环节还可以细分为回调接口的执行和功能接口的执行,如果是执行回调接口,则判断是否需要回调当前状态,如果是,则立即回调。

Transaction的执行函数是TransactionExecutor. execute方法,送出Transaction后,invoke方法会根据同步异步属性,决定是在当前线程执行,还是在后台线程执行。用于异步执行Transaction的后台线程是唯一的,如果有多个Transaction需要被异步执行,则会阻塞。

接口类的每个方法都被存进一个Map中,SDK以方法签名实现了同名函数重载。执行Transaction时,通过方法所在的接口类找到对应的实现类,再调用对应的invoke方法即可。对于异步方法,invoke方法的返回值不会被返回到上层,因此异步API函数的实现不用关心返回值。代理的详细执行流程如图3.4所示

图 3.4 代理的详细流程

3.5 异步方法的回调

对于异步方法,在TransactionExecutor. execute执行前,先基于Transaction生成对应的AbortableFuture的实现类TransactionFuture,然后将其缓存,用于稍后的回调。异步方法执行完毕后,将子类返回到调用层。

在调用可中断异步接口后,同步返回的是AbortableFuture实例。调用方可以直接调用abort方法中止执行。例如:调用了用于下载消息附件的downloadAttachment函数后,由于文件太大,网络状况不好等情况需要取消下载时,可以主动调用abort方法来终止。调用方式如图3.5所示。可中断异步接口对应的abort方法由SDK内部实现,调用者不需要关心其实现方式。

图 3.5 abort函数调用示例

TransactionFuture回调的基本类型为RequestCallback,SDK在此基本类型中定义了onSuccess、onFailed和onException函数,分别用于在成功、失败和异常情况下根据执行结果的不同,回调到不同的函数。到此整个流程结束。

4. 总结

云信IM中,用户可以通过NimClient.getService方法选择业务的动态代理,然后根据业务需求在调用线程或者后台线程执行功能,最后直接返回或者回调结果。如果调用的是可中断异步接口,用户还可以中断操作。基于动态代理的实现方式,隔离了其他业务下的对外接口,并解耦了接口和实现,配合代码混淆,更进一步的提升隔离效果。

了解网易云信IM即时通讯>>>

了解网易云信,来自网易核心架构的通信与视频云服务>>

更多技术干货,欢迎关注vx公众号“网易智慧企业技术+”。系列课程提前看,精品礼物免费得,还可直接对话CTO。

听网易CTO讲述前沿观察,看最有价值技术干货,学网易最新实践经验。网易智慧企业技术+,陪你从思考者成长为技术专家。

网易实战分享|云信IM SDK接口设计实践的更多相关文章

  1. 短视频 SDK 架构设计实践

    作者简介 孔维乐,七牛云客户端团队 Android 平台高级开发工程师,专注音视频,图形图像领域.OpenGL 专家,先后参与直播推流及连麦 SDK 的开发,主导短视频 SDK 的架构设计与实现, 对 ...

  2. 总结常见的违背Rest原则的接口设计做法

    此文已由作者郑华斌授权网易云社区发布. REST这词我们常常挂在嘴边,比如"开发一个rest接口",又比如Spring项目的代码: @RestControllerpublic cl ...

  3. 呼叫到达率100%,网易云信信令SDK免费上线!

    近期,网易云信推出一款稳定可靠.到达率高.扩展性较强的信令通道产品--信令SDK.它能够提供可靠的消息通道,可用于搭建音视频场景下的呼叫邀请机制.信令SDK目前兼容市面上所有主流的音视频SDK,呼叫到 ...

  4. Spring+SpringMVC+MyBatis+easyUI整合进阶篇(二)RESTful API实战笔记(接口设计及Java后端实现)

    写在前面的话 原计划这部分代码的更新也是上传到ssm-demo仓库中,因为如下原因并没有这么做: 有些使用了该项目的朋友建议重新创建一个仓库,因为原来仓库中的项目太多,结构多少有些乱糟糟的. 而且这次 ...

  5. RESTful API实战笔记(接口设计及Java后端实现)

    写在前面的话 原计划这部分代码的更新也是上传到ssm-demo仓库中,因为如下原因并没有这么做: 有些使用了该项目的朋友建议重新创建一个仓库,因为原来仓库中的项目太多,结构多少有些乱糟糟的. 而且这次 ...

  6. SSM实战——秒杀系统之Service层接口设计与实现、Spring托管、声明式事务

    一:Service层接口设计 准备工作:新建三个包:service包.exception包.dto包,分别用来存放业务接口.自定义异常类.dto类. 1:定义接口 package org.myseck ...

  7. .NET Core实战项目之CMS 第九章 设计篇-白话架构设计

    前面两篇文章给大家介绍了我们实战的CMS系统的数据库设计,源码也已经上传到服务器上了.今天我们就好聊聊架构设计,在开始之前先给大家分享一下这几天我一直在听的<从零开始学架构>里面关于架构设 ...

  8. .NET Core实战项目之CMS 第十章 设计篇-系统开发框架设计

    这两天比较忙,周末也在加班,所以更新的就慢了一点,不过没关系,今天我们就进行千呼万唤的系统开发框架的设计.不知道上篇关于架构设计的文章大家有没有阅读,如果阅读后相信一定对架构设计有了更近一部的理解,如 ...

  9. 支付宝支付 微信支付SDK接口不统一? 盘他!

      开发过支付宝.微信支付的同学都知道,微信的支付 API 设计感觉是 Java 开发工程师写的,远不如支付宝 SDK 的接口设计用起来顺手.在这里,统一封装微信支付和支付宝支付的API,使两种支付方 ...

随机推荐

  1. GitHub 热点速览 Vol.24:程序员自我增值,优雅赚零花钱

    摘要:升职加薪,出任 CTO,迎娶白富美/高帅富,走向人生巅峰是很多人的梦想.在本期的热点速览中你将了解自由作者 Easy 如何优雅赚取零花钱的方法,以及定投改变命运 -- 让时间陪你慢慢变富.说到程 ...

  2. C++ 基于多态的职工管理系统

    职工管理系统 1.管理系统需求 职工管理系统可以用来管理公司内所有员工的信息 本教程主要利用C++来实现一个基于多态的职工管理系统 公司中职工分为三类:普通员工.经理.老板,显示信息时,需要显示职工编 ...

  3. vue 深度拷贝 除去空的参数

    // 去除数组里面为空的属性及子数组 export function deepCopy (source) { var result = [] //var result = {} for (var ke ...

  4. Linux安装Redis 6.0.5 ./install_server.sh报错

    Linux安装Redis 6.0.5 ./install_server.sh报错 linux 安装Redis6.0.5时 进行到./install_server.sh时报错, This systems ...

  5. 基于TCP与UDP协议的socket通信

    基于TCP与UDP协议的socket通信 C/S架构与初识socket 在开始socket介绍之前,得先知道一个Client端/服务端架构,也就是 C/S 架构,互联网中处处充满了 C/S 架构(Cl ...

  6. java后端无法接收到前端传递的json对象

    java后端无法接收到前端传递的json对象 一·可能是因为未使用@RequestBody 在Controller层中,要么使用@RestController要么使用@Controller+@@Req ...

  7. python设计模式之策略模式

    每次看到项目中存在大量的if else代码时,都会心生一丝不安全感. 特别是产品给的需求需要添加或者更改一种if条件时,生怕会因为自己的疏忽而使代码天崩地裂,哈哈,本文的目的就是来解决这种不安全感的, ...

  8. C#状态机Stateless

    最近在折腾一些控制相关的软件设计,想起来状态机这个东西,对解决一些控制系统状态切换还是挺有用的. 状态机(有限状态自动机)网上有很多介绍.简单理解就是定义一系列状态,通过一系列的事件,可以使得状态可以 ...

  9. Java NIO之Buffer的使用

    目录 Buffer简介 Buffer的核心属性 Buffer的创建与使用(ByteBuffer为例) 总结 参考资料 Buffer简介 缓冲区(Buffer):本质上是一个数组,用于临时保存.写入以及 ...

  10. Mac安装文件已勾选“允许任何来源”,还是提示“文件已损坏”的解决方案

    Mac安装文件已勾选"允许任何来源",还是提示"文件已损坏"的解决方案 打开终端,在终端中粘贴下面命令:[sudo xattr -r -d com.apple. ...