之前一篇关于《Openfire集群源码分析》提到了session的一些内容。其中也提到了session是不会向每一台服务器进行同步复制的,这就有一个问题,如果A用户先是连接了服务器1,但是接下来的操作又到服务器2,这不就会造成session无法找到吗?同样的问题,如果想要获取到当前所有的client session怎么办?
1、如何在集群中发消息
对于消息最终还是通过session来发送的,前后代码太多,就直接看一下sessionManager中的getSession方法吧。
public ClientSession getSession(JID from) { // Return null if the JID is null or belongs to a foreign server. If the server is // shutting down then serverName will be null so answer null too in this case. if (from == null || serverName == null || !serverName.equals(from.getDomain())) { return null; } // Initially Check preAuthenticated Sessions if (from.getResource() != null) { ClientSession session = localSessionManager.getPreAuthenticatedSessions().get(from.getResource()); if (session != null) { return session; } } if (from.getResource() == null || from.getNode() == null) { return null; } return routingTable.getClientRoute(from); }
先是获取本地的session,如果能找到直接返回,找不到则跳到routingTable里获取客户端的路由信息。
@Override public ClientSession getClientRoute(JID jid) { // Check if this session is hosted by this cluster node ClientSession session = (ClientSession) localRoutingTable.getRoute(jid.toString()); if (session == null) { // The session is not in this JVM so assume remote RemoteSessionLocator locator = server.getRemoteSessionLocator(); if (locator != null) { // Check if the session is hosted by other cluster node ClientRoute route = usersCache.get(jid.toString()); if (route == null) { route = anonymouSUSErsCache.get(jid.toString()); } if (route != null) { session = locator.getClientSession(route.getNodeID().toByteArray(), jid); } } } return session; }
这里更直接的可以看到,查找本地路由不null则会通过RemoteSessionLocator来完成。当然这里最大的奥秘其实是usersCache和anonymousUsersCache这两个cache。之前写的集群源码分析中提过,最终openfire集群后会对缓存进行同步,这样每台服务器上都会有缓存的副本。所以usersCache是拥有所有��户信息的,有了user的信息就有了jid的信息,这样不管是哪台服务器都可以对数据包处理并发送给客户端。
这里的RemoteSessionLocator是由于适配不同的集群组件所抽象的接口,使得加入不同集群组件提供了透明处理。
2、如何获利所有的在线用户
对于获取所有在线用户这个功能思路也挺简单,一样是找本地所有的缓存。看getSessions的代码:
public Collection<ClientSession> getSessions() { return routingTable.getClientsRoutes(false); }
其实就是访问路由表,因为路由表里有所有的cache,和获取单个的session不一样,需要对所有的路由都遍历返回。
@Override public Collection<ClientSession> getClientsRoutes(boolean onlyLocal) { // Add sessions hosted by this cluster node Collection<ClientSession> sessions = new ArrayList<ClientSession>(localRoutingTable.getClientRoutes()); if (!onlyLocal) { // Add sessions not hosted by this JVM RemoteSessionLocator locator = server.getRemoteSessionLocator(); if (locator != null) { // Add sessions of non-anonymous users hosted by other cluster nodes for (Map.Entry<String, ClientRoute> entry : usersCache.entrySet()) { ClientRoute route = entry.getValue(); if (!server.getNodeID().equals(route.getNodeID())) { sessions.add(locator.getClientSession(route.getNodeID().toByteArray(), new JID(entry.getKey()))); } } // Add sessions of anonymous users hosted by other cluster nodes for (Map.Entry<String, ClientRoute> entry : anonymousUsersCache.entrySet()) { ClientRoute route = entry.getValue(); if (!server.getNodeID().equals(route.getNodeID())) { sessions.add(locator.getClientSession(route.getNodeID().toByteArray(), new JID(entry.getKey()))); } } } } return sessions; }
总结