package org.hdl.anima.fronend; import java.io.IOException; import java.net.InetSocketAddress; import java.util.Collection; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.hdl.anima.AppConf; import org.hdl.anima.Application; import org.hdl.anima.blacklist.BlackListMgr; import org.hdl.anima.common.NamedThreadFactory; import org.hdl.anima.common.module.BasicModule; import org.hdl.anima.common.utils.StringUtils; import org.hdl.anima.handler.RequestDispatcher; import org.hdl.anima.handler.RequestMappingMethodHandler; import org.hdl.anima.protocol.AbstractMessage; import org.hdl.anima.protocol.HandSnakeReq; import org.hdl.anima.protocol.HandSnakeResp; import org.hdl.anima.protocol.Kick; import org.hdl.anima.protocol.Request; import org.hdl.anima.remoting.Channel; import org.hdl.anima.remoting.ChannelHandler; import org.hdl.anima.remoting.Constants; import org.hdl.anima.remoting.RemotingException; import org.hdl.anima.remoting.Transporters; import org.hdl.anima.remoting.support.AbstractChannelHandlerDelegate; import org.hdl.anima.remoting.support.AbstractServer; import org.hdl.anima.remoting.support.ChannelHandlerAdapter; import org.hdl.anima.remoting.support.HeartBeatTask; import org.hdl.anima.route.Router; import org.hdl.anima.session.ClientSession; import org.hdl.anima.session.ClientSessionMgr; import org.hdl.anima.session.ISession; import org.hdl.anima.session.ReconnectionTokenFactory; import org.hdl.anima.session.ClientSessionReconnectionException; import org.hdl.anima.surrogate.ServerSurrogateMgr; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Frontend Server * @author qiuhd * @since 2014-8-1 * @version V1.0.0 */ public class FrontendServer extends BasicModule { private static final Logger logger = LoggerFactory.getLogger(FrontendServer.class); private AbstractServer server; private AppConf conf ; private BlackListMgr blackListMgr; private ClientSessionMgr clientSessionMgr; private Router router; private ServerSurrogateMgr serverSurrogate; private RequestMappingMethodHandler requestMappingMethodHandler; private RequestDispatcher dispather; private static final ScheduledThreadPoolExecutor scheduled = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("FronentServer-heartbeat", true)); // 心跳定时器 private ScheduledFuture<?> heatbeatTimer; // 心跳超时,毫秒。缺省0,不会执行心跳。 private int heartbeat; private int heartbeatTimeout; public FrontendServer(String moduleName) { super(moduleName); } @Override public void initialize(Application application) { super.initialize(application); this.conf = application.getAppConf(); this.application = application; this.blackListMgr = application.getMoulde(BlackListMgr.class); this.clientSessionMgr = application.getMoulde(ClientSessionMgr.class); this.heartbeat = conf.getInt(Constants.HEARTBEAT_KEY, Constants.DEFAULT_HEARTBEAT); this.heartbeatTimeout = conf.getInt(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3); this.router = application.getMoulde(Router.class); this.serverSurrogate = application.getMoulde(ServerSurrogateMgr.class); this.requestMappingMethodHandler = application.getMoulde(RequestMappingMethodHandler.class); this.dispather = application.getMoulde(RequestDispatcher.class); } @Override public void start() throws IllegalStateException { try { FrontendCodec codec = new FrontendCodec(this.application); server = Transporters.bind(conf, new HandSnakeHandler(channelHandler),codec); startHeatbeatTimer(); } catch (RemotingException e) { throw new IllegalStateException("Failed to start the fronent server,cause:"+ e.getMessage(),e); } } private void startHeatbeatTimer() { stopHeartbeatTimer(); if (heartbeat > 0) { heatbeatTimer = scheduled.scheduleWithFixedDelay(new HeartBeatTask( new HeartBeatTask.ChannelProvider() { @Override public Collection<Channel> getChannels() { return clientSessionMgr.getChannels(); } @Override public boolean isClientSide() { return false; } }, heartbeat, heartbeatTimeout), heartbeat, heartbeat, TimeUnit.MILLISECONDS); } } private void stopHeartbeatTimer() { try { ScheduledFuture<?> timer = heatbeatTimer; if (timer != null && ! timer.isCancelled()) { timer.cancel(true); } } catch (Throwable t) { logger.warn(t.getMessage(), t); } finally { heatbeatTimer =null; } } @Override public void stop() { stopHeartbeatTimer(); if (server != null) { server.close(); } } @Override public void destroy() { stop(); this.server = null; } private ChannelHandlerAdapter channelHandler = new ChannelHandlerAdapter(){ @Override public void connected(Channel channel) throws RemotingException { InetSocketAddress socketAddress = channel.getRemoteAddress(); String address = socketAddress.getAddress().toString(); boolean result = blackListMgr.contains(address); //黑名单处理 if (result) { //通知客户端关闭连接 channel.send(new Kick("BackList")); } ClientSessionMgr.createSession(channel); } @Override public void caught(Channel channel, Throwable cause) throws RemotingException { if (cause instanceof IOException) { //ignore logger.debug("Caugth exception:{}",cause.getMessage()); }else { logger.error("Caugth exception:{}",cause.getMessage(),cause); } } @Override public void disconnected(Channel channel) throws RemotingException { ClientSession clientSession = ClientSessionMgr.getClientSession(channel); if (clientSession != null) { //冻结session if (clientSession.getReconnectionSeconds() > 0 && clientSession.isWorking()) { clientSessionMgr.freezeSession(clientSession); }else { clientSession.close(); } } } @Override public void received(Channel channel, Object message)throws RemotingException { ClientSession session = ClientSessionMgr.getClientSession(channel); if (session != null) { if (message instanceof AbstractMessage) { Request request = (Request) message; request.setSid(session.getId()); if (!requestMappingMethodHandler.supportRequest(request)) { router.route(request); return; } // 处理请求消息 dispather.dispatch(request, session); return ; } throw new IllegalStateException("Unsupported request: "+ message == null ? null : (message.getClass().getName() + ": " + message)); } else { logger.warn("Failed to dispatch message",new IllegalStateException("Client channel maybe closed :" + channel.toString())); } } }; /** * * @author qiuhd * @since 2014年9月9日 * @version V1.0.0 */ protected final class HandSnakeHandler extends AbstractChannelHandlerDelegate { public HandSnakeHandler(ChannelHandler channelHandler) { super(channelHandler); } @Override public void received(Channel channel, Object message) throws RemotingException { if (message instanceof HandSnakeReq) { ISession session = ClientSessionMgr.getClientSession(channel); if(session != null) { handleHandSnake((ClientSession)session,(HandSnakeReq)message); } return ; } handler.received(channel, message); } /** * @param session * @param req * @throws RemotingException */ private void handleHandSnake(ClientSession session,HandSnakeReq handsnake) throws RemotingException { String apiVersion = application.getVersion().getVersionString(); if (!handsnake.getApiVersion().equals(apiVersion)) { HandSnakeResp response = new HandSnakeResp(false); session.getChannel().send(response); return ; } session.setClientType(handsnake.getClientType()); session.setStatus(ISession.STATUS_WORKING); int heartbeat = application.getAppConf().getInt(Constants.HEARTBEAT_KEY, Constants.DEFAULT_HEARTBEAT); int payload = application.getAppConf().getInt(Constants.PAYLOAD_KEY, Constants.DEFAULT_PAYLOAD); String reconnectToken = handsnake.getReconnectToken(); if (StringUtils.isEmpty(reconnectToken)) { //通知所有后端服务器创建 BackenSession InetSocketAddress remoteAddress = session.getRemoteAddress(); InetSocketAddress localAddress = session.getLocalAddress(); int identity = session.getId(); //通知所有后端服务器创建 BackenSession try { serverSurrogate.clientSessionCreated(identity,remoteAddress.getHostName(), remoteAddress.getPort(), localAddress.getHostName(), localAddress.getPort(),handsnake.getClientType()); }catch(Exception e) { e.printStackTrace(); } reconnectToken = ReconnectionTokenFactory.getInstance().getUniqueSessionToken(session.getRemoteAddress().toString()); session.setReconnectToken(reconnectToken); clientSessionMgr.addSession(session); }else { //实现断线重连 try { logger.debug("Reconnecting on token {}",reconnectToken); ClientSession resumedSession = clientSessionMgr.reconnectSession(reconnectToken, session); reconnectToken = resumedSession.getReconnectToken(); //通知后台服务器重连成功 serverSurrogate.clientSessionUFreeze(resumedSession.getId()); } catch (ClientSessionReconnectionException e) { HandSnakeResp response = new HandSnakeResp(false); session.getChannel().send(response); logger.error("Reconnection failure on token " + reconnectToken, e); return ; } } HandSnakeResp handSnakeResp = new HandSnakeResp(true); handSnakeResp.setHeartbeatTime(heartbeat); handSnakeResp.setReconnectToken(reconnectToken); handSnakeResp.setPayload(payload); session.getChannel().send((handSnakeResp)); } } }