openfire学习笔记(四)ConnectionMultiplexerManager类解析

本文分析ConnectionMultiplexerManager类,目的是熟悉多路链接管理器的实现,与相关影响,为后续做链接分配的负载均衡做准备。根据类注释,该类的作用是“追踪链接管理器与其建立的会话。这货还能建立、获取、关闭基于链接请求的客户端会话”。下面就一步一步的将该类拆解开来。

位置

全局检索一下该类:

表面上同包的几个类有本类的实体,其他几个类没有实体也有相关引用,那除非是调用本类的静态方法,或者就是有静态方法能够获取本类实体。上类图:

类结构上更加简单,这货实现了SessionEventListener接口,说明它监听着Session事件;其他关系基本上看起来像是组合关系,同包的其他几个类拥有该类的实体,做了相关的操作。这几个类有:

  • ClientSessionConnection ——客户端会话链接类
  • MultiplexerPacketDeliverer ——多路数据包分发器
  • MultiplexerPacketHandler ——链接管理器发送IQ包使用该类实体

物以类聚,类以包分。具体有什么用,之间的关系是怎样的后续再分析。

属性

属性上分两种,静态属性和一般属性。
先静态,值得注意的有两个:

private static final ConnectionMultiplexerManager instance = new ConnectionMultiplexerManager();
/**
* Pseudo-random number generator object for use with getMultiplexerSession(String).
*/
private static Random randGen = new Random();

第一个,很明显,单例。更直白的是,实例直接在静态变量声明时new出,显然之前看到的组合关系,那几个类并没有初始化该类的实体,而是使用该类的单例,明确地说应该算是聚合关系。该类只有一个实体;

第二个是个随机工具实体,并且注释说只有getMultiplexerSession函数使用了该工具,那么就到方法分析时再看。

顺带提一句,静态代码块中,该类作为SessionEventListener被注册到系统:

static {
     // Add the unique instance of this class as a session listener. We need to react
     // when sessions are closed so we can clean up the registry of client sessions.
     SessionEventDispatcher.addListener(instance);
}

一般属性值有三:

/**
* Map that keeps track of connection managers and hosted connections.
* Key: stream ID; Value: Domain of connection manager hosting connection
*/
private Map<String, String> streamIDs = new ConcurrentHashMap<String, String>();
/**
* Map that keeps track of connection managers and hosted sessions.
* Key: Domain of connection manager; Value: Map with Key: stream ID; Value: Client session
*/
private Map<String, Map<String, LocalClientSession>> sessionsByManager =
    new ConcurrentHashMap<String, Map<String, LocalClientSession>>();

private SessionManager sessionManager;

前两个是数据属性变量,streamIDs是一个Map保存链接管理器与主机链接的对应关系,键:stream ID,值:链接管理器对应的域;sessionsByManager同样是Map,他/它持有链接管理器与会话的对应关系,另外一个链接管理器对应多个会话,所以键:链接管理器的域,值:会话映射表(键:stream ID,值:本地客户端会话);

另外一个属性应该算作工具变量,SessionManager,会话管理器。

从两个数据属性可以看出,该类的作用,追踪状态,管理会话、链接、数据流之间的关系,应当是处于统筹地位的类。

方法

静态方法有三:

public static ConnectionMultiplexerManager getInstance() {
    return instance;
}
public static String getDefaultSecret() {
    return JiveGlobals.getProperty("xmpp.multiplex.defaultSecret");
}
public static void setDefaultSecret(String defaultSecret) {
    JiveGlobals.setProperty("xmpp.multiplex.defaultSecret", defaultSecret);
}

第一个自然是单例的获取函数;
其余的两个是密码相关的设置和获取;

普通方法有一部分是实现SessionEventListener的方法,可以暂时放一边,其余的公有方法功能如下:

org.jivesoftware.openfire.multiplex.ConnectionMultiplexerManager{
    //静态方法
    getDefaultSecret()
    getInstance()
    setDefaultSecret(String)

    //关闭一个链接管理器建立的会话
    closeClientSession(String, String)
    //通过指定的链接管理器建立新的客户端会话
    createClientSession(String, String, String, String)
    //通过指定的链接管理器和数据流获取指定的本地客户端会话
    getClientSession(String, String)
    //获取所有的链接管理器域名集合
    getMultiplexers()
    //获取指定链接管理器上的随机一个会话
    getMultiplexerSession(String)
    //获取指定链接管理器制定数据流的会话
    getMultiplexerSession(String, String)
    //返回指定链接管理器上链接客户端的数量
    getNumConnectedClients(String)
    //使能指定的链接管理器
    multiplexerAvailable(String)
    //禁用指定的链接管理器
    multiplexerUnavailable(String)

    //SessionEventListener 接口方法
    anonymousSessionCreated(Session)
    anonymousSessionDestroyed(Session)
    resourceBound(Session)
    sessionCreated(Session)
    sessionDestroyed(Session)
}

可以看出核心功能除去使能函数、getter函数之外,就只有创建和关闭会话两个功能了:

  • createClientSession(String, String, String, String)
  • closeClientSession(String, String)

流程

以createClientSession函数为研究目标,找一找调用流程。调出调用树:

看起来很眼熟,没错,正是与前面文章提过的消息包传递流程十分契合:

不过在StanzaHandler.processID(IQ packet)之后的流程似乎不一样,其实从上面的调用树中可以看出,真正的调用流程实际上是MultiplexerStanzaHandler类操作的,该类继承于StanzaHandler,并且StanzaHandler本身也是一个抽象类不能直接使用。所以实际上的调用流程是这样的:

其中第二步,由于MultiplexerStanzaHandler并没有复写process方法,所以实际上运行的是其父类的方法StanzaHandler. process(String stanza, XMPPPacketReader reader);

在处理IQ节的过程中,MultiplexerStanzaHandler复写了processIQ方法,所以自此之后的流程与前面提到的流程有所不同;

最终使用了本文分析的类创建了客户端会话。

上文提到过无论是StanzaHandler还是ConnectionHandler,都是有身份有地位抽象类,不是真正干活的。不妨从MultiplexerStanzaHandler开始向上逆推一下调用的流程:
全局检索MultiplexerStanzaHandler,得到:

找到了,干活的类MultiplexerConnectionHandler,它继承于ConnectionHandler,那么再次向上,全局检索MultiplexerConnectionHandler,得到:

眼前一亮,又回到了上一篇中分析的Module ConnectionManagerImpl,定位到关联函数:

private void startConnectionManagerListener(String localIPAddress) {
            ...
            multiplexerSocketAcceptor.bind(new InetSocketAddress(bindInterface, port),
                                new MultiplexerConnectionHandler(serverName));

            ports.add(new ServerPort(port, serverName, localIPAddress, false, null, ServerPort.Type.connectionManager));
            ….
}

在分析注册、登录流程的那一篇中提到过MINA架构中SocketAcceptor提供的bind接口,再找找该函数的调用流程,不难得到如下流程:

start() –>startListeners()->startConnectionManagerListener(String localIPAddress)

也就是说ConnectionManagerImpl启动时,MultiplexerConnectionHandler就已经绑定在相应的端口上,监听处理链接了。而它对应的socketAcceptor是multiplexerSocketAcceptor:

private synchronized void createListeners() {
    …
    // Create the port listener for s2s communication
    //服务器间信息交流的
    createServerListener(localIPAddress);
    // Create the port listener for Connections Multiplexers
    createConnectionManagerListener();
    // Create the port listener for external components
    //外部元件端口监听
    createComponentListener();
    // Create the port listener for clients
    //客户端端口监听
    …
}

private void createConnectionManagerListener() {
    // Start multiplexers socket unless it's been disabled.
    if (isConnectionManagerListenerEnabled()) {
        // Create SocketAcceptor with correct number of processors
        multiplexerSocketAcceptor = buildSocketAcceptor("multiplexer");
        …
}

以上,可知Module ConnectionManagerImpl,创建链接管理器监听,对应端口的处理handler实现类是MultiplexerConnectionHandler,MultiplexerConnectionHandler复写了createStanzaHandler(NIOConnection connection)函数,所以它的StanzaHandler实体是MultiplexerStanzaHandler对象。所以在MultiplexerConnectionHandler的messageReceived回调监听到消息时,下一步就会送给MultiplexerStanzaHandler来处理,在IQ节消息的某种处理中,或许就是分析协议,接到的信息中要求建立会话,具体还要分析相关源码,最后派发到本文分析的类,调用了ConnectionMultiplexerManager.createClientSession 。

系统启动时序:

ConnectionMultiplexerManager相关时序:

感谢您赏个荷包蛋~