package jane.core;
import java.io.IOException;
import java.net.SocketAddress;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import org.apache.mina.core.filterchain.IoFilter;
import org.apache.mina.core.filterchain.IoFilterChain;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.future.DefaultWriteFuture;
import org.apache.mina.core.future.IoFuture;
import org.apache.mina.core.future.IoFutureListener;
import org.apache.mina.core.future.WriteFuture;
import org.apache.mina.core.service.IoHandler;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.core.write.DefaultWriteRequest;
import org.apache.mina.transport.socket.SocketSessionConfig;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import jane.core.map.IntHashMap;
import jane.core.map.LongConcurrentHashMap;
import jane.core.map.LongMap.MapIterator;
/**
* 网络管理器
* <p>
* 即可用于服务器监听也可用于客户端连接,一般都要继承后使用<br>
* <li>服务器监听: 用于监听端口,并管理连接到此的所有连接处理
* <li>客户端连接: 用于连接到服务器的一条连接处理
*/
public class NetManager implements IoHandler
{
public static final int DEFAULT_SERVER_IO_THREAD_COUNT = Runtime.getRuntime().availableProcessors() + 1;
public static final int DEFAULT_CLIENT_IO_THREAD_COUNT = 1;
private static final LongConcurrentHashMap<RpcBean<?, ?, ?>> _rpcs = new LongConcurrentHashMap<>(); // 当前管理器等待回复的RPC
private static final ConcurrentLinkedQueue<IoSession> _closings = new ConcurrentLinkedQueue<>(); // 已经closeOnFlush的session队列,超时则closeNow
private static final ScheduledExecutorService _rpcThread; // 处理重连及RPC和事务超时的线程
private final String _name = getClass().getSimpleName(); // 当前管理器的名字
private volatile Class<? extends IoFilter> _pcf = BeanCodec.class; // 协议编码器的类
private volatile IntHashMap<BeanHandler<?>> _handlers = new IntHashMap<>(0); // bean的处理器
private volatile NioSocketAcceptor _acceptor; // mina的网络监听器
private volatile NioSocketConnector _connector; // mina的网络连接器
private int _ioThreadCount; // IO线程池的最大数量(<=0表示默认值)
static
{
_rpcThread = Executors.newSingleThreadScheduledExecutor(new ThreadFactory()
{
@Override
public Thread newThread(Runnable r)
{
Thread t = new Thread(r, "RpcThread");
t.setDaemon(true);
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
});
scheduleAtFixedRate(Const.rpcCheckInterval, Const.rpcCheckInterval, new Runnable()
{
@Override
public void run()
{
try
{
int now = (int)(System.currentTimeMillis() / 1000);
for(MapIterator<RpcBean<?, ?, ?>> it = _rpcs.entryIterator(); it.moveToNext();)
{
RpcBean<?, ?, ?> rpcbean = it.value();
if(now - rpcbean.getReqTime() > rpcbean.getTimeout() && _rpcs.remove(it.key()) != null)
{
RpcHandler<?, ?, ?> onclient = rpcbean.getOnClient();
IoSession session = rpcbean.getSession();
rpcbean.setSession(null); // 绑定期已过,清除对session的引用
if(onclient != null)
{
rpcbean.setOnClient(null);
if(session != null)
{
IoHandler manager = session.getHandler();
try
{
onclient.timeout((NetManager)manager, session, rpcbean);
}
catch(Exception e)
{
Log.log.error(manager.getClass().getName() + '(' + session.getId() + "): onTimeout exception:", e);
}
}
}
}
}
}
catch(Throwable e)
{
Log.log.error("NetManager: RPC timeout fatal exception:", e);
}
}
});
scheduleAtFixedRate(1, 1, new Runnable()
{
@Override
public void run()
{
try
{
IoSession session = _closings.peek();
if(session == null) return;
long now = System.currentTimeMillis();
do
{
if(!session.isClosing())
{
Object v = session.getAttribute("closeOnFlushTime");
if(v != null && v instanceof Long && now < (long)v) break;
session.closeNow();
}
_closings.poll();
}
while((session = _closings.peek()) != null);
}
catch(Throwable e)
{
Log.log.error("NetManager: IoSession timeout fatal exception:", e);
}
}
});
}
/**
* 获取当前通信中的RPC数量
*/
public static int getRpcCount()
{
return _rpcs.size();
}
/**
* 在当前的RPC调用记录中移除某个RPC
* <p>
* 只在RPC得到回复时调用
*/
static RpcBean<?, ?, ?> removeRpc(int rpcId)
{
return _rpcs.remove(rpcId);
}
/**
* 获取此网络管理器的名字
* <p>
* 目前仅仅是完整类名
*/
public final String getName()
{
return _name;
}
/**
* 判断某个session是否在连接状态
*/
public final boolean hasSession(IoSession session)
{
long sid = session.getId();
if(_acceptor != null && _acceptor.getManagedSessions().containsKey(sid)) return true;
if(_connector != null && _connector.getManagedSessions().containsKey(sid)) return true;
return false;
}
/**
* 获取监听器管理的当前全部sessions
* @return 返回不可修改的map容器
*/
public final Map<Long, IoSession> getServerSessions()
{
return _acceptor != null ? _acceptor.getManagedSessions() : Collections.<Long, IoSession>emptyMap();
}
/**
* 获取连接器管理的当前全部sessions
* @return 返回不可修改的map容器
*/
public final Map<Long, IoSession> getClientSessions()
{
return _connector != null ? _connector.getManagedSessions() : Collections.<Long, IoSession>emptyMap();
}
/**
* 设置当前的协议编码器
* <p>
* 必须在连接或监听之前设置
*/
public final void setCodec(Class<? extends IoFilter> pcf)
{
_pcf = (pcf != null ? pcf : BeanCodec.class);
}
/**
* 设置能够响应beans的处理器
* <p>
* 设置后,参数的容器不能再做修改<br>
* 最好在网络连接前设置
*/
public final void setHandlers(IntHashMap<BeanHandler<?>> handlers)
{
if(handlers != null) _handlers = handlers;
}
/**
* 获取当前响应beans的处理器
* <p>
* 获取到的容器不能做修改
*/
public final IntHashMap<BeanHandler<?>> getHandlers()
{
return _handlers;
}
/**
* 设置IO线程池的最大数量
* <p>
* 必须在创建连接器和监听器之前修改. <=0表示使用默认值(作为服务器时是CPU核数+1,作为客户端时为1)
*/
public final synchronized void setIoThreadCount(int count)
{
_ioThreadCount = count;
}
/**
* 获取监听器
*/
public final NioSocketAcceptor getAcceptor()
{
if(_acceptor == null || _acceptor.isDisposed())
{
synchronized(this)
{
if(_acceptor == null || _acceptor.isDisposed())
{
NioSocketAcceptor t = new NioSocketAcceptor(_ioThreadCount > 0 ? _ioThreadCount : DEFAULT_SERVER_IO_THREAD_COUNT);
t.setReuseAddress(true);
t.setHandler(this);
_acceptor = t;
}
}
}
return _acceptor;
}
/**
* 获取连接器
*/
public final NioSocketConnector getConnector()
{
if(_connector == null || _connector.isDisposed())
{
synchronized(this)
{
if(_connector == null || _connector.isDisposed())
{
NioSocketConnector t = new NioSocketConnector(_ioThreadCount > 0 ? _ioThreadCount : DEFAULT_CLIENT_IO_THREAD_COUNT);
t.setHandler(this);
t.setConnectTimeoutMillis(Const.connectTimeout * 1000);
_connector = t;
}
}
}
return _connector;
}
/**
* 获取用于服务器端的mina网络配置并可以修改
*/
public final SocketSessionConfig getServerConfig()
{
return getAcceptor().getSessionConfig();
}
/**
* 获取用于客户端的mina网络配置并可以修改
*/
public final SocketSessionConfig getClientConfig()
{
return getConnector().getSessionConfig();
}
/**
* 开启服务器端的连接监听
* <p>
* 监听的参数要提前设置. 此操作是异步的,失败会抛出IOException异常
*/
public void startServer(SocketAddress addr) throws IOException
{
getAcceptor();
Log.log.info("{}: listening addr={}", _name, addr);
_acceptor.bind(addr);
}
/**
* 启动客户端的连接
* <p>
* 此操作是异步的,失败会在另一线程回调onConnectFailed
* @param ctx 此次连接的用户对象,用于传回onConnectFailed的回调中
*/
public ConnectFuture startClient(final SocketAddress addr, final Object ctx)
{
getConnector();
Log.log.info("{}: connecting addr={}", _name, addr);
return _connector.connect(addr).addListener(new IoFutureListener<ConnectFuture>()
{
private int _count;
@Override
public void operationComplete(ConnectFuture future)
{
if(!future.isConnected())
{
try
{
++_count;
Log.log.warn("{}: connect failed: addr={},count={}", _name, addr, _count);
int delaySec = onConnectFailed(addr, _count, ctx);
if(delaySec == 0)
{
Log.log.info("{}: reconnecting addr={},count={}", _name, addr, _count);
_connector.connect(addr).addListener(this);
}
else if(delaySec > 0)
{
final IoFutureListener<ConnectFuture> listener = this;
schedule(delaySec, new Runnable()
{
@Override
public void run()
{
try
{
Log.log.info("{}: reconnecting addr={},count={}", _name, addr, _count);
_connector.connect(addr).addListener(listener);
}
catch(Throwable e)
{
Log.log.error("NetManager.startClient.operationComplete: scheduled exception:", e);
}
}
});
}
}
catch(Throwable e)
{
Log.log.error("NetManager.startClient.operationComplete: exception:", e);
}
}
}
});
}
/**
* 启动客户端的连接
* <p>
* 此操作是异步的,失败会在另一线程回调onConnectFailed
*/
public ConnectFuture startClient(final SocketAddress addr)
{
return startClient(addr, null);
}
/**
* 停止服务器端的监听并断开相关的连接
* @param addr 指定停止的地址/端口. 如果为null则停止全部监听地址/端口
*/
public void stopServer(SocketAddress addr)
{
getAcceptor();
if(addr != null)
_acceptor.unbind(addr);
else
_acceptor.unbind();
}
/**
* 停止全部相关客户端的连接
* @param force 是否立即强制关闭(丢弃当前的发送缓存)
*/
public void stopAllClients(boolean force)
{
if(_connector != null)
{
for(IoSession session : _connector.getManagedSessions().values())
{
if(force)
session.closeNow();
else
closeOnFlush(session);
}
}
}
/**
* 使用网络工作线程调度一个延迟处理
* <p>
* 所有的网络管理器共用一个工作线程,同时运行RPC超时处理,因此只适合简单的处理,运行时间不要过长
* @param delaySec 延迟调度的秒数
*/
public static ScheduledFuture<?> schedule(long delaySec, Runnable runnable)
{
return _rpcThread.schedule(runnable, delaySec, TimeUnit.SECONDS);
}
/**
* 向网络工作线程调度一个定时间隔任务
* @param periodSec 定时间隔周期的秒数
*/
public static ScheduledFuture<?> scheduleWithFixedDelay(int delaySec, int periodSec, Runnable runnable)
{
return _rpcThread.scheduleWithFixedDelay(runnable, delaySec, periodSec, TimeUnit.SECONDS);
}
/**
* 向网络工作线程调度一个定时触发任务(同一任务不会并发)
* @param periodSec 定时触发周期的秒数
*/
public static ScheduledFuture<?> scheduleAtFixedRate(int delaySec, int periodSec, Runnable runnable)
{
return _rpcThread.scheduleAtFixedRate(runnable, delaySec, periodSec, TimeUnit.SECONDS);
}
/**
* 发送对象的底层入口
*/
public static boolean write(IoSession session, Object obj)
{
if(session.isClosing() || obj == null) return false;
IoFilterChain ifc = session.getFilterChain();
DefaultWriteRequest dwr = new DefaultWriteRequest(obj, null, null);
synchronized(session)
{
ifc.fireFilterWrite(dwr);
}
return true;
}
/**
* 发送对象的底层入口. 可带监听器,并返回WriteFuture
*/
public static WriteFuture write(IoSession session, Object obj, IoFutureListener<?> listener)
{
if(session.isClosing() || obj == null) return null;
IoFilterChain ifc = session.getFilterChain();
WriteFuture wf = new DefaultWriteFuture(session);
if(listener != null) wf.addListener(listener);
DefaultWriteRequest dwr = new DefaultWriteRequest(obj, wf, null);
synchronized(session)
{
ifc.fireFilterWrite(dwr);
}
return wf;
}
/**
* 向某个连接发送数据
* <p>
* 小心使用此接口,一般情况不要使用<br>
* 此操作是异步的
* @return 如果连接已经失效则返回false, 否则返回true
*/
public boolean sendRaw(IoSession session, Object obj)
{
if(!write(session, obj)) return false;
if(Log.hasTrace) Log.log.trace("{}({}): send: raw: {}", _name, session.getId(), obj);
return true;
}
/**
* 向某个连接发送bean
* <p>
* 此操作是异步的
* @param bean 如果其type==0,则认为是RawBean类型,仅仅发送RawBean的数据部分. 考虑到性能问题,在发送完成前不能修改RawBean的数据部分
* @return 如果连接已经失效则返回false, 否则返回true
*/
public boolean send(IoSession session, Bean<?> bean)
{
if(!write(session, bean)) return false;
if(Log.hasTrace) Log.log.trace("{}({}): send: {}:{}", _name, session.getId(), bean.getClass().getSimpleName(), bean);
return true;
}
public boolean sendSafe(final IoSession session, final Bean<?> bean)
{
if(session.isClosing() || bean == null) return false;
final RawBean rawbean = new RawBean(bean);
SContext.current().addOnCommit(new Runnable()
{
@Override
public void run()
{
send(session, rawbean);
}
});
return true;
}
/**
* 向某个连接发送bean
* <p>
* 此操作是异步的
* @param bean 如果是RawBean类型,考虑到性能问题,在发送完成前不能修改其中的data对象
* @param callback 可设置一个回调对象,用于在发送成功后回调. null表示不回调
* @return 如果连接已经失效则返回false, 否则返回true
*/
public <A extends Bean<A>> boolean send(final IoSession session, final A bean, final BeanHandler<A> callback)
{
if(session.isClosing() || bean == null) return false;
if(callback == null)
{
if(!write(session, bean)) return false;
}
else
{
if(write(session, bean, new IoFutureListener<IoFuture>()
{
@Override
public void operationComplete(IoFuture future)
{
try
{
callback.process(NetManager.this, session, bean);
}
catch(Throwable e)
{
Log.log.error(_name + '(' + session.getId() + "): callback exception: " + bean.getClass().getSimpleName(), e);
}
}
}) == null) return false;
}
if(Log.hasTrace) Log.log.trace("{}({}): send: {}:{}", _name, session.getId(), bean.getClass().getSimpleName(), bean);
return true;
}
public <A extends Bean<A>> boolean sendSafe(final IoSession session, final A bean, final BeanHandler<A> callback)
{
if(session.isClosing() || bean == null) return false;
SContext.current().addOnCommit(new Runnable()
{
@Override
public void run()
{
send(session, bean, callback);
}
});
return true;
}
/**
* 向某个连接发送RPC
* <p>
* 此操作是异步的
* @param handler 可设置一个回调对象,用于在RPC回复和超时时回调. null表示使用注册的处理器处理回复和超时(RpcHandler.onClient/onTimeout)
* @return 如果连接已经失效则返回false且不会有回复和超时的回调, 否则返回true
*/
@SuppressWarnings("unchecked")
public <A extends Bean<A>, R extends Bean<R>, B extends RpcBean<A, R, B>> boolean sendRpc(final IoSession session, final RpcBean<A, R, B> rpcBean, RpcHandler<A, R, B> handler)
{
if(session.isClosing() || rpcBean == null) return false;
rpcBean.setRequest();
rpcBean.setReqTime((int)(System.currentTimeMillis() / 1000));
rpcBean.setSession(session);
rpcBean.setOnClient(handler != null ? handler : (RpcHandler<A, R, B>)_handlers.get(rpcBean.type()));
if(_rpcs.putIfAbsent(rpcBean.getRpcId(), rpcBean) != null)
{
rpcBean.setSession(null);
rpcBean.setOnClient(null);
throw new RuntimeException("do not send the same RPC in the same time");
}
if(!send(session, rpcBean))
{
rpcBean.setSession(null);
rpcBean.setOnClient(null);
_rpcs.remove(rpcBean.getRpcId());
return false;
}
return true;
}
private static final class FutureRPC<R> extends FutureTask<R>
{
private static final Callable<?> _dummy = new Callable<Object>()
{
@Override
public Object call()
{
return null;
}
};
@SuppressWarnings("unchecked")
public FutureRPC()
{
super((Callable<R>)_dummy);
}
@Override
public void set(R v)
{
super.set(v);
}
}
/**
* 向某个连接发送RPC请求并返回Future对象
* <p>
* 此操作是异步的
* @return 如果连接已经失效则返回null, 如果RPC超时则对返回的Future对象调用get方法时返回null
*/
public <A extends Bean<A>, R extends Bean<R>, B extends RpcBean<A, R, B>> Future<R> sendRpcSync(final IoSession session, final RpcBean<A, R, B> rpcBean)
{
if(session.isClosing() || rpcBean == null) return null;
rpcBean.setRequest();
rpcBean.setReqTime((int)(System.currentTimeMillis() / 1000));
rpcBean.setSession(session);
final FutureRPC<R> ft = new FutureRPC<>();
rpcBean.setOnClient(new RpcHandler<A, R, B>()
{
@Override
public void onClient(NetManager m, IoSession s, B b)
{
ft.set(b.getRes());
}
@Override
public void onTimeout(NetManager m, IoSession s, B b)
{
ft.set(null);
}
});
if(_rpcs.putIfAbsent(rpcBean.getRpcId(), rpcBean) != null)
{
rpcBean.setSession(null);
rpcBean.setOnClient(null);
throw new RuntimeException("do not send the same RPC in the same time");
}
if(!send(session, rpcBean))
{
rpcBean.setSession(null);
rpcBean.setOnClient(null);
_rpcs.remove(rpcBean.getRpcId());
return null;
}
return ft;
}
/**
* 同sendRpc, 区别仅仅是在事务成功后发送RPC
*/
public <A extends Bean<A>, R extends Bean<R>, B extends RpcBean<A, R, B>> boolean sendRpcSafe(final IoSession session, final RpcBean<A, R, B> rpcBean, final RpcHandler<A, R, B> handler)
{
if(session.isClosing() || rpcBean == null) return false;
SContext.current().addOnCommit(new Runnable()
{
@Override
public void run()
{
sendRpc(session, rpcBean, handler);
}
});
return true;
}
/**
* 对连接器管理的全部连接广播bean
* <p>
* 警告: 不能广播RPC. 连接数很大的情况下慎用,必要时应自己在某一工作线程中即时或定时地逐一发送
*/
public void clientBroadcast(Bean<?> bean)
{
if(bean == null) return;
for(IoSession session : getClientSessions().values())
write(session, bean);
}
/**
* 对监听器管理的全部连接广播bean
* <p>
* 警告: 不能广播RPC. 连接数很大的情况下慎用,必要时应自己在某一工作线程中即时或定时地逐一发送
*/
public void serverBroadcast(Bean<?> bean)
{
if(bean == null) return;
for(IoSession session : getServerSessions().values())
write(session, bean);
}
/**
* 安全地关闭session
* <p>
* 主要适合刚发送最后消息后即关闭连接时. 如果最后的消息发送超时则强制关闭
*/
public static boolean closeOnFlush(IoSession session)
{
if(session.setAttributeIfAbsent("closeOnFlushTime", System.currentTimeMillis() + Const.closeOnFlushTimeout * 1000L) != null)
return false;
session.closeOnFlush();
_closings.offer(session);
return true;
}
/**
* TCP连接建立成功后的回调
* @param session 建立的连接对象
*/
protected void onAddSession(IoSession session)
{
}
/**
* TCP连接断开后的回调
* @param session 断开的连接对象
*/
protected void onDelSession(IoSession session)
{
}
/**
* 作为客户端连接失败后的回调
* @param addr 连接失败的地址
* @param count 重试次数(从1开始)
* @param ctx startClient时传入的用户对象. 没有则为null
* @return 返回下次重连的时间间隔(秒)
*/
@SuppressWarnings("static-method")
protected int onConnectFailed(SocketAddress addr, int count, Object ctx)
{
return -1;
}
/**
* 当收到一个没有注册处理器的bean时的回调
*/
protected void onUnhandledBean(IoSession session, Bean<?> bean)
{
Log.log.warn("{}({}): unhandled bean: {}:{}", _name, session.getId(), bean.getClass().getSimpleName(), bean);
// session.closeNow();
}
/**
* 当连接在最近一段时间内没有任何通信时的回调
* <p>
* 默认情况不会执行此回调. 需要自己设置mina的网络配置(getServerConfig, getClientConfig)
* @param session 指定的地址
*/
protected void onIdleSession(IoSession session)
{
}
@Override
public void sessionCreated(IoSession session) throws Exception
{
session.getFilterChain().addLast("codec", _pcf.newInstance());
}
@Override
public void sessionOpened(IoSession session)
{
if(Log.hasDebug) Log.log.debug("{}({}): open: {}", _name, session.getId(), session.getRemoteAddress());
onAddSession(session);
}
@Override
public void sessionClosed(IoSession session)
{
if(Log.hasDebug) Log.log.debug("{}({}): close: {}", _name, session.getId(), session.getRemoteAddress());
onDelSession(session);
}
@Override
public void inputClosed(IoSession session)
{
session.closeNow();
}
@Override
public void messageReceived(IoSession session, Object message)
{
if(Log.hasTrace) Log.log.trace("{}({}): recv: {}:{}", _name, session.getId(), message.getClass().getSimpleName(), message);
Bean<?> bean = (Bean<?>)message;
BeanHandler<?> handler = _handlers.get(bean.type());
if(handler != null)
{
try
{
handler.process(this, session, bean);
}
catch(Throwable e)
{
Log.log.error(_name + '(' + session.getId() + "): process exception: " + message.getClass().getSimpleName(), e);
}
}
else
onUnhandledBean(session, bean);
}
@Override
public void messageSent(IoSession session, Object message)
{
}
@Override
public void sessionIdle(IoSession session, IdleStatus status)
{
if(Log.hasTrace) Log.log.trace("{}({}): idle: {}", _name, session.getId(), status);
onIdleSession(session);
}
@Override
public void exceptionCaught(IoSession session, Throwable cause)
{
if(cause instanceof IOException)
Log.log.error(_name + '(' + session.getId() + ',' + session.getRemoteAddress() + "): exception: {}", cause.getMessage());
else
Log.log.error(_name + '(' + session.getId() + ',' + session.getRemoteAddress() + "): exception:", cause);
session.closeNow();
}
}