/**
*
*/
package com.chamago.cometserver.connection;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.chamago.cometserver.PullEvent;
import com.chamago.cometserver.StreamConstants;
/**
* @author Gavin.peng
*
* 2014-2-24 下午03:08:15 × cometserver
*/
public class CometConnectionManager {
private final static Log LOG = LogFactory
.getLog(CometConnectionManager.class);
private volatile boolean containExit = false;
private ConcurrentHashMap<String, CometConnection> connectCollect;
private TimeOutThread toThread;
public CometConnectionManager() {
this.init();
}
/**
* 注册请求连接,如果两个相同的客户端同时注册, 只有一个连接生效。即新的连接会踢掉老的连接,同时关闭老的连接
*
* @param connect
*/
public void registerConnection(CometConnection connect) {
String appKey = connect.getAppkey();
// 同一个客户端重复的连接,则启用新的连接
if (this.connectCollect.containsKey(appKey)) {
LOG.info("系统接收到一个新的客户端连接[" + connect.getAppkey()
+ "],同时发现该客户端已经建立了连接,踢掉老的连接");
PullEvent hb = new PullEvent(connect.getAppkey(),StreamConstants.CLIENT_KICKOFF,null,"相同的appkey已经建立了连接,退换掉老的连接");
connect.pullMessage(hb);
CometConnection oldConnect = this.connectCollect.get(appKey);
oldConnect.closeConnection();
this.connectCollect.remove(appKey);
this.connectCollect.put(appKey, connect);
} else {
LOG.info("系统接收到一个新的客户端连接[" + connect.getAppkey() + "]");
this.connectCollect.put(appKey, connect);
}
//processDiscardMsg(connect);
}
private void init() {
this.connectCollect = new ConcurrentHashMap<String, CometConnection>(50);
this.toThread = new TimeOutThread("Connect-Monitor-Thread");
this.toThread.start();
}
/**
* 根据appkey找到链接
* @param appkey
* @return
*/
public CometConnection findCometConnection(String appkey){
return this.connectCollect.get(appkey);
}
/**
* 根据appkey从链接集合中移除
* @param appkey
* @return
*/
public void closeCometConnection(String appkey){
CometConnection connect = this.connectCollect.remove(appkey);
connect = null;
}
public int getConnectSize(){
return this.connectCollect.size();
}
/**
* 踢掉所有链接,并发消息告诉所有客户端,让其在一定的时间重连,在服务端更新时会用,
*/
public void killAllConnection(){
//停止心跳线程,防止连接关闭了,还写心跳消息。
stopMonitorThread();
LOG.info("服务端升级,关闭所有客户端连接....");
Iterator<String> connectIts = CometConnectionManager.this.connectCollect
.keySet().iterator();
while (connectIts.hasNext()) {
String connectKey = connectIts.next();
CometConnection connect = CometConnectionManager.this.connectCollect
.get(connectKey);
PullEvent hb = new PullEvent(connect.getAppkey(),StreamConstants.SERVER_DEPLOY,null,"服务端在升级,客户端请稍后重连!");
connect.pullMessage(hb);
connect.closeConnection();
}
}
public void processDiscardMsg(final CometConnection connetion){
connetion.streamMsgPullFactory.commit(new Runnable(){
@Override
public void run() {
RedisClientManager rcm = RedisClientManager.getInstance();
Map<String,String> msgMap = rcm.findDiscardMsg(connetion.getAppkey());
if(msgMap!=null&&msgMap.size()>0){
Iterator<String> it = msgMap.keySet().iterator();
List<PullEvent> peList = new ArrayList<PullEvent>();
while(it.hasNext()){
String msgId = it.next();
String content = msgMap.get(msgId);
PullEvent pe = new PullEvent(connetion.getAppkey(),StreamConstants.DISCARD_MESSAGE,null,content);
peList.add(pe);
}
LOG.info("发现客户端连接["+connetion.getAppkey()+"]丢弃的消息数为:"+peList.size());
((StreamCometConnection)connetion).batchPullEvent(peList);
}
}
});
}
public boolean isContainExit() {
return containExit;
}
public void setContainExit(boolean containExit) {
this.containExit = containExit;
}
public void clear(){
this.connectCollect.clear();
this.connectCollect = null;
}
public void stopMonitorThread(){
this.toThread.stopThread();
}
public class TimeOutThread extends Thread {
private boolean running = false;
private long sleepTime = 30 * 1000;
public TimeOutThread(String name) {
this.setName(name);
this.running = true;
}
public void run() {
while (running) {
long nowTime = System.currentTimeMillis();
Iterator<String> connectIts = CometConnectionManager.this.connectCollect
.keySet().iterator();
LOG.info("系统维持的连接数为:"+CometConnectionManager.this.connectCollect.size());
while (connectIts.hasNext()) {
String connectKey = connectIts.next();
CometConnection connect = CometConnectionManager.this.connectCollect
.get(connectKey);
long connectTime = nowTime - connect.getConnectionTime();
if (connectTime > StreamConstants.CONNECT_MAX_TIME) {
LOG.info("客户端连接[" + connectKey + "] 连接时间 是["
+ connectTime / (1000 * 60 * 60)
+ "]小时,达到连接的最大有效时间24小时,系统将主动关闭该连接,客户端需要重连");
PullEvent hb = new PullEvent(connect.getAppkey(),StreamConstants.CONNECT_REACH_MAX_TIME,null,"connect is reached max time 24h");
connect.pullMessage(hb);
connect.closeConnection();
}
//检查30s内是否有下发数据,没有则发送心跳消息,防止超时
if((nowTime-connect.getActiveTime())>=StreamConstants.PARAM_HEARBEAT_TIME){
if(connect.needWorkFlag.get()){
PullEvent hb = new PullEvent(connect.getAppkey(),StreamConstants.HEAT_BEAT,null,"heatbeak msg");
connect.pullMessage(hb);
}
}
}
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
LOG.info("监控线程"+this.getName()+"退出");
}
public void stopThread() {
this.running = false;
interrupt();
}
}
}