ConnectionManagerImpl源码分析
openfire使用的是mina框架,关于mina框架的源码分析可以参考本人博客的另外几篇文章。这里转载一个mina的框架图,
如图所示,简单来说,IoService用于管理Socket的连接,IoFilterChain是一串过滤器,IoHandler用于业务逻辑处理。应用程序需要实现IoHandler接口并注册到mina框架中。
下面来看ConnectionManagerImpl模块,由第三章的分析知道,每个模块被调用了三个函数,一个是构造函数,一个是initialize函数,一个是start函数。首先看其构造函数,
public ConnectionManagerImpl() {
super("Connection Manager");
ports = new ArrayList<ServerPort>(4);
}
该构造函数没有实质性内容,主要是设置了模块名称Connection Manager,并初始化ports。
initialize()
public void initialize(XMPPServer server) {
super.initialize(server);
serverName = server.getServerInfo().getXMPPDomain();
router = server.getPacketRouter();
routingTable = server.getRoutingTable();
deliverer = server.getPacketDeliverer();
sessionManager = server.getSessionManager();
if (JiveGlobals.getBooleanProperty("xmpp.socket.heapBuffer", true)) {
IoBuffer.setUseDirectBuffer(false);
IoBuffer.setAllocator(new SimpleBufferAllocator());
}
}
这里也没有特别的地方,主要是一些成员变量的设置和缓存的设置。
start()
public void start() {
super.start();
createListeners();
startListeners();
SocketSendingTracker.getInstance().start();
CertificateManager.addListener(this);
}
这里的CertificateManager用来监听ssl的相关时间,下面主要分析另外3个函数,createListeners、startListeners和SocketSendingTracker.getInstance().start()。
createListeners()
private synchronized void createListeners() {
if (isSocketStarted || sessionManager == null || deliverer == null || router == null || serverName == null) {
return;
}
createServerListener(localIPAddress);
createConnectionManagerListener();
createComponentListener();
createClientListeners();
createClientSSLListeners();
}
这里第一个if里,由于是第一次进入该函数,isSocketStarted 为false,因此进入下面的函数。这里调用不同的create函数创建服务器端Socket,本文重点分析createClientListeners,该函数创建的服务器端Socket用于处理客户端的连接。createClientListeners就定义在ConnectionManagerImpl中,
private void createClientListeners() {
if (isClientListenerEnabled()) {
socketAcceptor = buildSocketAcceptor(CLIENT_SOCKET_ACCEPTOR_NAME);
int maxPoolSize = JiveGlobals.getIntProperty(ConnectionSettings.Client.MAX_THREADS, 16);
ExecutorFilter executorFilter = new ExecutorFilter(getCorePoolSize(maxPoolSize), maxPoolSize, 60, TimeUnit.SECONDS);
ThreadPoolExecutor eventExecutor = (ThreadPoolExecutor)executorFilter.getExecutor();
ThreadFactory threadFactory = eventExecutor.getThreadFactory();
threadFactory = new DelegatingThreadFactory("C2S-Thread-", threadFactory);
eventExecutor.setThreadFactory(threadFactory);
socketAcceptor.getFilterChain().addFirst(EXECUTOR_FILTER_NAME, executorFilter);
socketAcceptor.getFilterChain().addAfter(EXECUTOR_FILTER_NAME, XMPP_CODEC_FILTER_NAME, new ProtocolCodecFilter(new XMPPCodecFactory()));
socketAcceptor.getFilterChain().addAfter(XMPP_CODEC_FILTER_NAME, CAPACITY_FILTER_NAME, new StalledSessionsFilter());
int maxBufferSize = JiveGlobals.getIntProperty(ConnectionSettings.Client.MAX_READ_BUFFER, 10 * MB);
socketAcceptor.getSessionConfig().setMaxReadBufferSize(maxBufferSize);
}
}
首先通过buildSocketAcceptor创建mina框架中的NioSocketAcceptor,定义在ConnectionManagerImpl中
private NioSocketAcceptor buildSocketAcceptor(String name) {
NioSocketAcceptor socketAcceptor;
int processorCount = JiveGlobals.getIntProperty("xmpp.processor.count", Runtime.getRuntime().availableProcessors());
socketAcceptor = new NioSocketAcceptor(processorCount);
socketAcceptor.setReuseAddress(true);
socketAcceptor.setBacklog(JiveGlobals.getIntProperty("xmpp.socket.backlog", 50));
SocketSessionConfig socketSessionConfig = socketAcceptor.getSessionConfig();
int receiveBuffer = JiveGlobals.getIntProperty("xmpp.socket.buffer.receive", -1);
if (receiveBuffer > 0 ) {
socketSessionConfig.setReceiveBufferSize(receiveBuffer);
}
int sendBuffer = JiveGlobals.getIntProperty("xmpp.socket.buffer.send", -1);
if (sendBuffer > 0 ) {
socketSessionConfig.setSendBufferSize(sendBuffer);
}
int linger = JiveGlobals.getIntProperty("xmpp.socket.linger", -1);
if (linger > 0 ) {
socketSessionConfig.setSoLinger(linger);
}
socketSessionConfig.setTcpNoDelay(
JiveGlobals.getBooleanProperty("xmpp.socket.tcp-nodelay", socketSessionConfig.isTcpNoDelay()));
if (JMXManager.isEnabled()) {
configureJMX(socketAcceptor, name);
}
return socketAcceptor;
}
简单来说,这里就是构造了一个NioSocketAcceptor,进行相应的设置,configureJMX里面配置JMX(Java Management Extensions),JMX用来管理和监视java应用程序,本文不分析它。
回到createClientListeners函数中,接下来构造3个filter,ExecutorFilter、ProtocolCodecFilter和StalledSessionsFilter并将它们添加到NioSocketAcceptor中,其中,在构造ExecutorFilter时,创建了DelegatingThreadFactory用于创建线程,最后就是继续设置NioSocketAcceptor。下一章会分析刚才添加的3个filter的源码,这里继续往下看。
startListeners
private synchronized void startListeners() {
isSocketStarted = true;
localIPAddress = InetAddress.getLocalHost().getHostAddress();
startServerListener();
startConnectionManagerListener(localIPAddress);
startComponentListener();
startClientListeners(localIPAddress);
startClientSSLListeners(localIPAddress);
startHTTPBindListeners();
}
这里的代码删掉了插件相关的代码,然后调用各个函数启动Socket,这里主要看startClientListeners,
private void startClientListeners(String localIPAddress) {
if (isClientListenerEnabled()) {
int port = getClientListenerPort();
try {
String interfaceName = JiveGlobals.getXMLProperty("network.interface");
InetAddress bindInterface = null;
if (interfaceName != null) {
if (interfaceName.trim().length() > 0) {
bindInterface = InetAddress.getByName(interfaceName);
}
}
socketAcceptor.setHandler(new ClientConnectionHandler(serverName));
socketAcceptor.bind(new InetSocketAddress(bindInterface, port));
ports.add(new ServerPort(port, serverName, localIPAddress, false, null, ServerPort.Type.client));
}
catch (Exception e) {
}
}
}
这里主要做了两件事,一是创建了ClientConnectionHandler并将其添加到socketAcceptor中,ClientConnectionHandler包含了处理客户端请求的主要逻辑代码,下一章再来分析;二是调用socketAcceptor的bind函数构造服务器端Socket并进行监听。setHandler和bind函数请参考本博客《mina框架源码分析》。
SocketSendingTracker.getInstance().start()
SocketSendingTracker的构造函数为空函数,因此这里直接看start函数,
public void start() {
shutdown = false;
checkingThread = new Thread("SocketSendingTracker") {
@Override
public void run() {
while (!shutdown) {
checkHealth();
synchronized (this) {
try {
wait(10000);
}
catch (InterruptedException e) {
}
}
}
}
};
checkingThread.setDaemon(true);
checkingThread.start();
}
该线程每隔10秒调用checkHealth检查连接的Socket的状态,如下
private void checkHealth() {
for (SocketConnection connection : SocketConnection.getInstances()) {
connection.checkHealth();
}
}
这里获取每个连接的Socket并调用其checkHealth,checkHealth是管理客户端Socket连接的核心函数,定义在SocketConnection中,
boolean checkHealth() {
long writeTimestamp = writeStarted;
if (writeTimestamp > -1 && System.currentTimeMillis() - writeTimestamp >
JiveGlobals.getIntProperty("xmpp.session.sending-limit", 60000)) {
forceClose();
return true;
}
else {
if (idleTimeout > -1 && socketReader != null &&
System.currentTimeMillis() - socketReader.getLastActive() > idleTimeout) {
forceClose();
return true;
}
}
return false;
}
checkHealth做了两件事情,如果某个Socket发送数据的事件大于60秒,或者长时间处于idle状态(表示长时间没有接收到客户端发来的心跳数据包),就调用forceClose将其关闭。