【软件架构】IM架构设计(安卓版)

1. 架构总览

Alt 架构纵览

2. 模块介绍

2.1 协议封装与任务流程

2.1.1 协议与任务的封装

Alt

  1. 协议有协议头(协议头由于格式相同,被抽象出来)和协议体组成,协议有两类:请求协议(request)和回复协议(response);
  2. 任务(action)由请求协议、回复协议和任务回调(callback)组成;
  3. callback是针对客户端主动请求协议的相应处理,分别是成功回调、超时回调和失败回调;

2.1.2 消息(任务)流程

Alt

  1. 由UI或SYSTEM触发一个消息的生成,随之将其投递到发送队列中,等待发送;
  2. 消息发送线程会不停的从发送队列中拉取一个消息并发送出去,同时放入超时监测队列;而当网络断开时会等待若干时间并从新循环,若检测该消息在队列等待时间过长,则丢弃该消息并触发相应的失败回调;
  3. 对于客户端主动请求,在收到服务端给予回复时,调用该消息的成功回调处理相应回复;
  4. 对于超时线程监测超时的消息,移除超时监测队列,并调用超时回调。

2.2 定时任务

    定时任务的实现主要由TimerHelper类与ITimerProcessor两个类,第一个类主要实现了Timer的功能,ITimerProcessor为一个接口,当要创建一个Timer的时候,须要新创建一个类实现ITimerProcessor中得process接口来处理具体的业务。缓存

    TimerHelper类的内部封装了系统的Timer及TimerTask来实现,对外提供startTimer以及stopTimer接口。startTimer须要一个布尔类型的参数标志启动定时任务是须要执行一次,仍是永久执行。
    新生成TimerHelper实例的时候须要制定定时任务的时间以及定时触发处理接口ITimerProcessor的具体实现。服务器

    固然在Timer的设计中,并无彻底采用系统提供的Timer类实现,有些Timer是采用线程模拟实现,这个主要是基于Java 中Timer的一些缺陷来考虑。网络

    下面简单介绍下四种Timer架构

2.2.1 心跳Timer

    心跳Timer 是维持客户端与服务端长连一个强有力的保证。网络中接收发送都是使用socket的recv与send进行发送与接收,若是此套接字已经断开,则发送与接收数据都会出现问题,建立心跳机制,就是为了及时检测该套接字是否有效。并发

    所谓心跳就是给服务端发送一个自定义的包,来告诉服务端,本身在线,以确保长连的有效性。异步

2.2.2 发送超时Timer

    在开发网络应用程序的时候,处理业务和通信流程之间常常会出现矛盾。这种矛盾主要是因为二者之间的不一样步形成的。socket

    好比,网络的延迟较大,而实际业务处理的速度则相对比较快,那么若是处理完某一事务而后等待发送成功再处理下一个事务则会大大下降效率。因此创建了一个发送消息队列。这是一个典型的“生产者消费者”模型,业务逻辑将须要发送的数据放到消息队列中,SendPacketMonitor从消息队列中取出数据,并发送出去。ide

    因为使用了消息队列的模式,发送就变成了一种异步操做,业务逻辑将消息放入消息队列后,就能够进行其余的操做,而没法知道该消息是否真正发送成功。所以,在设计消息队列的时候采用了回调机制,业务方在放入消息队列的时候,必须实现onSuccess接口与onTimeOut接口,分别在发送成功与发送超时调用。函数

    发送超时Timer是采用线程来模拟实现的,在SendPacketMonitor类,做为消费者,会不停的从消息队列中取出数据,取出数据后,会判断该消息产生的时刻与当前时间相比较,若是发现时差已经超过系统定义的最大超时时间,则直接调用“生产者”的onTimeOut接口,通知其发送超时。spa

2.2.3 重连Timer

    重连是另外一个保证长连的机制,虽然使用了心跳机制来保证长连,可是因为网络环境的复杂性,没法保证在一个链接开启后,就永远保持链接,所以,重连就成了另一个保证。

    重连主要是为了当链接断开的时候,客户端可以自动快速的链接到服务端。为了系统的稳定性,及相应快速,重连Timer采用的是线程模拟Timer实现。

重连的逻辑中,会去检测服务端的心跳包,若是发现长时间没有收到服务端的任何数据包,则认为该socket已经失效,并进行重连。

    在重连Timer中,为了防止雪崩效应的出现,在检测到socket失效,并非立马进行重连,而是让客户端随机Sleep一段时间再去链接服务端,这样就可使不一样的客户端在服务端重启的时候不会同时去链接,从而形成雪崩效应。

2.2.4 好友状态Timer

    虽然在实现的逻辑中,服务端在好友状态变化的时候,会主动推送消息给客户端,可是仍是设计了好友状态Timer。由于在网络复杂的环境中,有太多的未知因素。

    好友状态Timer的基本实现就是每隔一段时间发送一个数据包请求得到好友的状态信息。当收到响应数据包的时候,就会去更新Cache中的好友状态信息。


2.3状态管理

2.3.1 状态管理StateManager

    状态管理就是一个多状态的状态机,其中包括Net状态管理(指硬件网络是否可用),Socket状态管理(指与MsgServer的链接是否可用)。

    状态管理主要功能就是采集Net状态与Socket状态,提供两个接口notifyNetState与notifySocketState两个接口供Net状态管理与Socket状态管理调用,当接收到状态变化的时候会调用NetDispach进行网络状态变动分发。

2.3.2 Net状态管理NetStateManager

    Net状态管理指的是物理网络状态的管理,主要管理当前物理网络是否可用以及进行变化时进行监听,当监听到网络断开或者连上事件的时候,会调用状态管理notifyNetState接口。

    在应用启动的时候咱们会注册网络变化广播接收

    public class ConnectionChangeReceiver extends BroadcastReceiver

    Override他的onReceive函数,在onReceive 函数中获取网络链接服务,而后调用NetStateManager的setState接口,通知状态变化。

2.3.3 Socket状态管理

    Socket状态管理指的是客户端与MsgServer之间的链接状态。当检测到链接不可用时,会调用该接口的setState接口设置状态。当socket的channelConnected、exceptionCaught与channelDisconnected函数被调用的时候以及在重连出现异常或失败的时候会通知进行状态变动。

2.3.4 状态变动分发

    状态变动分发对外提供三个接口register,unregister与dispachMsg接口。外界若是关心网络状态变化事件,能够注册本身的Handler到该类,当网络状态发生变化的时候,会根据注册的Handler进行事件通知。

    register接口为提供注册的接口。

    unregister接口为取消注册的忌口。

    dispachMsg为事件分发接口,当网络状态发生变化的时候,该接口会被调用,通知各个Handler进行处理。


2.4 断线重连

    断线重连机制是当IM与MsgServer断开后可以自动链接。

    断线重连为一个单独的线程,进行循环:

    当检测到NetState为不可用的时候,会随机睡眠1-9秒而后继续检测。同时会检测心跳包,当发现最后一次收到心跳包超过MAX_HEART_BEAT_TIME时间会认为Socket链接不可用而重置SocketState的状态。

    当检测到网络可用,Socket状态不可用的时候,就会启动断线重连机制,在进行断线重连以前,会进行链接次数判断,若是为第一次重连,则随机睡眠1秒多,这个机制主要是为了防止服务端出现异常而重启的时候大量的客户端同时链接上来而发生雪崩现象。若是不为第一次重连,则睡眠指定时间,该时间的计算公式以下:

  1. nSleep =(long)Math.pow(2, mnReconnectCount);
    if(nSleep >16){
        nSleep =16;
    }

    该计算公式主要是为了防止大量的客户端不停的进行重连从而对服务端形成大量的压力,另外从节省客户端的能耗考虑。

    每次进行重连都会将重连次数累加,这个主要是为了防止之后须要对重连次数进行限制。
    重连过程以下图:

Alt

2.5 登录

    登录流程主要分为如下几个流程:

    一、认证;二、获取MsgServer地址;三、登录MsgServer。

    下面依次介绍。

2.5.1 认证

    认证过程主要是对用户合法身份的验证,包括以下两个方面:

    从主客获取AppToken。该过程是一个反射调用,IM程序调用主客的获取Token接口获取到主客的AppToken,Dao等信息。

    拿从主客获取到得AppToken及Dao信息到IM的验证服务器去换取一个IMToken,该过程为一个Http调用。服务器会对上传的AppToken及Dao信息进行校验,若是校验成功,则会返回一个IMToken,以及LoginServer地址等信息,后期须要拿该Token到MsgServer进行登录验证。

    认证的过程主要在TokenManager中实现,该类中还对Token时效进行了管理,当获取IMToken的时候会先对判断IMToken是否为空,若是为空则去获取AppToken等信息,再去服务端换取IMToken,不然判断Token的时效是否失效效,若是失效则获取AppToken并换取IMToken信息,若是有效,则直接返回IMToken。

2.5.2 获取MsgServer地址

    该过程是客户端经过验证时拿到的LoginServer地址创建Socket链接,并发送获取MsgServer请求,LoginServer会返回一个可用的MsgServer的Ip及Port。

2.5.3 登录MsgServer

    当通过2.5.2获取到MsgServer的IP及Port后,会根据给定的IP与Port与MsgServer创建一个Socket链接,当链接创建成功后。会携带获取到得IMToken,用户名等信息发送一个登录请求包,若是登录请求验证经过,客户端启动一个Timer与服务端发送心跳包保持长链接。

    如下为整个登录的流程:

Alt

2.6 异步实现

    异步的封装有两种实现方式:

  1. 须要更新界面的异步
  2. 不须要更新界面的异步

    两种实现的方式是不一样的。同时异步还有一个异步管理类。

2.6.1 TaskTrigger类

    TaskTrigger类主要用来注册以及管理各个Task,每生成一个异步实例,都须要经过trigger接口,若是对于须要更新界面的异步,则直接调用AsyncTask的execute接口执行任务,不然将其放入Task的任务队列中,后台会经过process接口调用dotrigger接口执行具体的任务,执行结束后,调用task的callback接口。

2.6.2 须要更新界面的异步

    该方式主要集成Android自有的AsyncTask类,对其进行了一个简单的封装,该异步实现方式主要解决一些须要更新UI界面的异步,解决Android中非UI线程不能更新UI的问题。

2.6.3 不须要更新界面的异步

    该异步主要提供不须要更新UI的一些异步操做,该实现方式为新开启一个线程执行任务,当任务执行完成以后调用回调函数。


2.7 本地缓存

    存消息时,根据缓存中以前一条消息的ID,得到新消息的惟一自增ID,将该消息存入DB。

    存消息时依赖三张表,联系人表,主消息表和附加消息表。首先会根据当前收发用户的ID从联系人表中获得二者的惟一联系ID,并设置消息中的联系ID,将该消息存入主消息表和附加消息表。

Alt

    读消息时,直接从DB中拉取便可,根据参数的不一样拉取的消息不一样。

    能够根据消息ID,拉取某一天消息,也能够根据起止消息ID,拉取指定偏移量和指定条数的消息列表。

Alt

    存用户信息时,判断该用户信息是否为空或合法,若为空或不合法,直接返回;若合法,则更新 ,缓存中的用户信息,用户信息只保留在缓存中。

Alt

    取用户信息时,先判断缓存中是否存在该用户信息,若存在,则直接返回该用户信息;不然返回null,由业务端选择是否发起取用户信息的操做。

Alt

    收发消息时,获得好友ID,判断缓存中得最近联系人ID列表中是否存在,若不存在则添加,不然 忽略;

    一样地,在获取最近联系人列表时也如此。

Alt

    首先得到最近联系人ID列表,而后从缓存中读取必要地信息,组合成最近联系人列表。

    另外经过异步监测有新消息来通知UI主线程来更新界面

Alt

相关文章
相关标签/搜索