Openfire的启动过程与session管理
Class containerClass = loader.loadClass("org.jivesoftware.openfire.XMPPServer");
containerClass.newInstance();
- 初始化配置参数
- 检查是否需要安装
- 初始化Module
- 启动统计模块
- 启动plugin
if (!setupMode) {
verifyDataSource();
// First load all the modules so that modules may access other modules while
// being initialized
loadModules();
// Initize all the modules
initModules();
// Start all the modules
startModules();
}
public interface Module {
/**
* Returns the name of the module for display in administration interfaces.
*
* @return The name of the module.
*/
String getName();
/**
* Initialize the module with the container.
* Modules may be initialized and never started, so modules
* should be prepared for a call to destroy() to follow initialize().
*
* @param server the server hosting this module.
*/
void initialize(XMPPServer server);
/**
* Start the module (must return quickly). Any long running
* operations should spawn a thread and allow the method to return
* immediately.
*/
void start();
/**
* Stop the module. The module should attempt to free up threads
* and prepare for either another call to initialize (reconfigure the module)
* or for destruction.
*/
void stop();
/**
* Module should free all resources and prepare for deallocation.
*/
void destroy();
}
// Load this module always last since we don't want to start listening for clients
// before the rest of the modules have been started
loadModule(ConnectionManagerImpl.class.getName());
private final ConnectionListener clientListener;
private final ConnectionListener clientSslListener;
private final ConnectionListener boshListener;
private final ConnectionListener boshSslListener;
private final ConnectionListener serverListener;
private final ConnectionListener componentListener;
private final ConnectionListener componentSslListener;
private final ConnectionListener connectionManagerListener; // Also known as 'multiplexer'
private final ConnectionListener connectionManagerSslListener; // Also known as 'multiplexer'
private final ConnectionListener webAdminListener;
private final ConnectionListener webAdminSslListener;
- client:表示客户端连接
- bosh:就是HTTP绑定的连接
- server:服务器到服务器的socket连接
- component:组件到服务器的连接
- connectionManager:是指通过connectionManager连接器过来的连接
- webAdmin:是指web控制台的连接
if ( getType() == ConnectionType.SOCKET_S2S )
{
connectionAcceptor = new LegacyConnectionAcceptor( generateConnectionConfiguration() );
}
else
{
connectionAcceptor = new MINAConnectionAcceptor( generateConnectionConfiguration() );
}




@Override
StanzaHandler createStanzaHandler(NIOConnection connection) {
return new ClientStanzaHandler(XMPPServer.getInstance().getPacketRouter(), connection);
}
@Override
public void sessionOpened(IoSession session) throws Exception {
// Create a new XML parser for the new connection. The parser will be used by the XMPPDecoder filter.
final XMLLightweightParser parser = new XMLLightweightParser(StandardCharsets.UTF_8);
session.setAttribute(XML_PARSER, parser);
// Create a new NIOConnection for the new session
final NIOConnection connection = createNIOConnection(session);
session.setAttribute(CONNECTION, connection);
session.setAttribute(HANDLER, createStanzaHandler(connection));
// Set the max time a connection can be idle before closing it. This amount of seconds
// is divided in two, as Openfire will ping idle clients first (at 50% of the max idle time)
// before disconnecting them (at 100% of the max idle time). This prevents Openfire from
// removing connections without warning.
final int idleTime = getMaxIdleTime() / 2;
if (idleTime > 0) {
session.getConfig().setIdleTime(IdleStatus.READER_IDLE, idleTime);
}
}
这样每一个session在打开时都会设置handler,而具体的handler由各个派生类创建返回。这里的StanzHandler就是Openfire里的数据包处理单元。和connection类型一样,包处理也是对应的几个类:


@Override
public void messageReceived(IoSession session, Object message) throws Exception {
// Get the stanza handler for this session
StanzaHandler handler = (StanzaHandler) session.getAttribute(HANDLER);
// Get the parser to use to process stanza. For optimization there is going
// to be a parser for each running thread. Each Filter will be executed
// by the Executor placed as the first Filter. So we can have a parser associated
// to each Thread
final XMPPPacketReader parser = PARSER_CACHE.get();
// Update counter of read btyes
updateReadBytesCounter(session);
//System.out.println("RCVD: " + message);
// Let the stanza handler process the received stanza
try {
handler.process((String) message, parser);
} catch (Exception e) {
Log.error("Closing connection due to error while processing message: " + message, e);
final Connection connection = (Connection) session.getAttribute(CONNECTION);
if ( connection != null ) {
connection.close();
} }
}
在接收到数据包后获取到StanzaHandler,然后调用了它的process方法,也就是让实际的包处理者去处理数据。这样就回到了StanzeHanler,以ClientStanzaHandler为例子。只不过这个派生类中没有重写process方法,也就是说要看父类的实现:
public void process(String stanza, XMPPPacketReader reader) throws Exception {
boolean initialStream = stanza.startsWith("<stream:stream") || stanza.startsWith("<flash:stream");
if (!sessionCreated || initialStream) {
if (!initialStream) {
..........
// Found an stream:stream tag...
if (!sessionCreated) {
sessionCreated = true;
MXParser parser = reader.getXPPParser();
parser.setInput(new StringReader(stanza));
createSession(parser);
}
..........
return;
}
..........
}
由于代码较多,我省略了一些代码。看到这应该明白了吧,对于当前的连接没有创建Openfire的session对象时,会进行创建过程createSession,对于不同的StanzeHandler会有些不一样,这里ClientStanzaHandler的实现就是把创建好的session放到本地的LocalClientSession中:
@Override
boolean createSession(String namespace, String serverName, XmlPullParser xpp, Connection connection)
throws XmlPullParserException {
if ("jabber:client".equals(namespace)) {
// The connected client is a regular client so create a ClientSession
session = LocalClientSession.createSession(serverName, xpp, connection);
return true;
}
return false;
}
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的信息,这样不管是哪台服务器都可以对数据包处理并发送给客户端。
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;
}
Openfire的启动过程与session管理的更多相关文章
- cocos2dx 解释二具体的启动过程:内存管理和回调
在上一篇的第二部分中.我们有一句代码待解释的: // Draw the Scene void CCDirector::drawScene(void) { -... //tick before ...
- 启动期间的内存管理之初始化过程概述----Linux内存管理(九)
在内存管理的上下文中, 初始化(initialization)可以有多种含义. 在许多CPU上, 必须显式设置适用于Linux内核的内存模型. 例如在x86_32上需要切换到保护模式, 然后内核才能检 ...
- tomcat架构分析 (Session管理)
Session管理是JavaEE容器比较重要的一部分,在app中也经常会用到.在开发app时,我们只是获取一个session,然后向session中存取数据,然后再销毁session.那么如何产生se ...
- 使用Memcached Session Manager扩展Session管理
>>Tomcat的session管理 在请求过程中首先要解析请求中的sessionId信息,然后将sessionId存储到request的参数列表中. 然后再从request获取sessi ...
- Tomcat的Session管理机制
>>Session和Cookie请求的过程 Http连接本身是无状态的,即前一次发起的连接跟后一次没有任何关系,是属于两次独立的连接请求,但是互联网访问基本上都是需要有状态的,即服务器需要 ...
- Nginx学习笔记(六) 源码分析&启动过程
Nginx的启动过程 主要介绍Nginx的启动过程,可以在/core/nginx.c中找到Nginx的主函数main(),那么就从这里开始分析Nginx的启动过程. 涉及到的基本函数 源码: /* * ...
- How Tomcat works — 八、tomcat中的session管理
在使用shiro的session的时候感觉对于tomcat中session的管理还不是特别清楚,而且session管理作为tomcat中比较重要的一部分还是很有必要学习的. 目录 概述 session ...
- Linux X Window System运行原理和启动过程
本文主要说明X Window System的基本运行原理,其启动过程,及常见的跨网络运行X Window System. 一) 基本运行原理 X Window System采用C/S结构,但和我们常见 ...
- Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动
之前在Spring Boot启动过程(二)提到过createEmbeddedServletContainer创建了内嵌的Servlet容器,我用的是默认的Tomcat. private void cr ...
随机推荐
- 启动了VSAN服务的主机不在vCenter集群中
背景: 这个问题的来源是,某用户将该ESXi主机直接夺取到另一个vCenterA的管辖中,而这个vCenterA中集群A开启了VSAN功能,导致再次反向夺取到vCenterB中的时候带有了来自于集群A ...
- Tomcat shutdown执行后无法退出进程问题排查及解决
问题定位及排查 上周无意中调试程序在Linux上ps -ef|grep tomcat发现有许多tomcat的进程,当时因为没有影响系统运行就没当回事.而且我内心总觉得这可能是tomcat像nginx一 ...
- MVVM TextBox的键盘事件
MVVM下RichTextBox的键盘回车事件设置为发送,不是回车 xmlns:i="http://schemas.microsoft.com/expression/2010/interac ...
- 获取Canvas当前坐标系矩阵
前言 在我的另一篇博文 Canvas坐标系转换 中,我们知道了所有的平移缩放旋转操作都会影响到画布坐标系.那在我们对画布进行了一系列操作之后,怎么再知道当前矩阵数据状态呢. 具体代码 首先请看下面的一 ...
- C++随笔:.NET CoreCLR之GC探索(4)
今天继续来 带大家讲解CoreCLR之GC,首先我们继续看这个GCSample,这篇文章是上一篇文章的继续,如果有不清楚的,还请翻到我写的上一篇随笔.下面我们继续: // Initialize fre ...
- 【开源毕设】一款精美的家校互动APP分享——爱吖校推 [你关注的,我们才推](持续开源更新3)附高效动态压缩Bitmap
一.写在前面 爱吖校推如同它的名字一样,是一款校园类信息推送交流平台,这么多的家校互动类软件,你选择了我,这是我的幸运.从第一次在博客园上写博客到现在,我一次一次地提高博文的质量和代码的可读性,都是为 ...
- 缓存、队列(Memcached、redis、RabbitMQ)
本章内容: Memcached 简介.安装.使用 Python 操作 Memcached 天生支持集群 redis 简介.安装.使用.实例 Python 操作 Redis String.Hash.Li ...
- PHP设计模式(一)简单工厂模式 (Simple Factory For PHP)
最近天气变化无常,身为程序猿的寡人!~终究难耐天气的挑战,病倒了,果然,程序猿还需多保养自己的身体,有句话这么说:一生只有两件事能报复你:不够努力的辜负和过度消耗身体的后患.话不多说,开始吧. 一.什 ...
- JQuery的基础和应用
<参考文档> 1.什么是? DOM的作用:提供了一种动态的操作HTML元素的方法. jQuery是一个优秀的js库.用来操作HTML元素的工具. jQuery和DOM ...
- Mach-O 的动态链接(Lazy Bind 机制)
➠更多技术干货请戳:听云博客 动态链接 要解决空间浪费和更新困难这两个问题最简单的方法就是把程序的模块相互分割开来,形成独立的文件,而不再将它们静态的链接在一起.简单地讲,就是不对那些组成程序的目标文 ...