您的位置:首页 > 其它

Mina 线程模型分析

2016-04-21 19:36 851 查看
想要知道Mina都在什么地方使用了线程,博主自己写了一个简单的程序代码来寻找,考虑到server端和client端是同一个链接,不妨就不要client端了,server端代码如下:
public class Myserver {

private static Logger logger = LoggerFactory.getLogger(Myserver.class);

private static final int PORT = 8877;

public static void main(String[] args) throws IOException {
// 创建一个非阻塞的serverSocket

IoAcceptor acceptor = new NioSocketAcceptor();
// 设置缓冲区大小

acceptor.getSessionConfig().setReadBufferSize(2048);
// 10秒无读写操作进入空闲状态

acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
// Mina自带默认文本解编码

acceptor.getFilterChain().addLast("codec",

new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));
// 添加线程池过滤器

acceptor.getFilterChain().addLast("threadPool", new ExecutorFilter(Executors.newCachedThreadPool()));
// 绑定handle

acceptor.setHandler(new MyIoHandler());
// 绑定端口

acceptor.bind(new InetSocketAddress(PORT));

logger.info("服务启动端口号是:" + PORT);

}

}

handler代码如下:
public class MyIoHandler extends IoHandlerAdapter {

private final static Logger logger = LoggerFactory.getLogger(MyIoHandler.class);

@Override

public void messageReceived(IoSession session, Object message) throws Exception {
String string = message.toString();

logger.info("接收到了消息");

System.out.println("接收到的消息是:" + string);

if (string.equals("quit")) {

session.close(true);

return;

}

}

}

通过JDK自带的Jvisualvm查看线程。首先到命令行窗口输入Jvisualvm,然后启动server,命令行窗口下输入telnet 127.0.0.1 8877(注意:如果提示不可用,到控制面板下的程序与功能处勾选telnet客户端) ,输入一段信息。之后对线程视图截图,截图结果:



有绑定端口accept类型线程,有监听端口IoProcessor类型线程(少圈了)以及我们创建的过滤器pool-thread类型线程。
事实上,Mina本身默认有3种线程:
1,IoAcceptor:
服务器端用于监听端口所创建的线程,一旦有请求进来,也就是调用bind方法,该线程就会创建一个IoSession对象,而且就只需要一个线程。很多朋友会问为什么不能多点呢,不是更快?事实上,线程运行解决独立任务时,单线程是最快的,因为线程虽然不占有空间,但仍然需要程序计数器,寄存器等必不可缺部分。多线程运行同一任务会引起线程间的频繁切换,而线程切换必然会保存线程本次执行的寄存器信息也就是现场信息,随后进行现场信息的复原。这些对CPU都是极大的消耗,监听本身是个独立任务,单线程下最快,所以M
ina采用单个线程来监听。
2,IoConnector:
不用多想必然和服务器端一样,单个线程连接服务器,每次连接启用线程,创建IoSession,单个线程
3,IoProcessor
用于处理Io操作,默认启用是CPU数目+1,为什么是多线程呢?因为IoService本事是IoAcceptor和IoConnector的抽象,它们绑定IoProcessor去做IO操作,而IO操作本事又很费CPU资源,因此启用线程池会更快。那为什么是CPU数目+1呢?因为线程运行独自占有CPU,同上面一样,线程多反而会引起线程切换反而浪费资源。
总结一下Mina的默认线程模型:
1,当 IoService 实例创建的时候,创建IoProcessor 线程池;

2,当 IoService 建立套接字即服务器端绑定或者客户端连接时,从IoProcessor线程池中取一个线程监听链接;

3,当 IoService 监听到套接字上有连接请求时,创建 IoSession 对象, 从 IoProcessor池中取出一个线程执行这个会话通道上的过滤器、IoHandler;

4,当这条 IoSession 通道进入空闲状态或者关闭时,IoProcessor 被回收。

以上就是Mina框架默认的线工作方式,那实际开发当中会采用吗?显然不是。
可以发现,IoProcessor线程池执行的都是一条链路上顺序的操作,从链接的监听到filter如解编码过滤器,再到IoHandler。并且看Mina官网文档发现事实上IoSession的事件中除了created,close,write事件与IoProcessor必须在同一线程运行外,其它所有的事件都可以独立分配线程池运行。看IoProcessor的API也可以发现,它还有个构造方法 acceptor.getFilterChain().addLast("exceutor",
new ExecutorFilter());这样你再处理如sent,recieved事件时,丢在executor线程池中调用excute()方法执行,代码性能会更好。除此之外,实际项目开发当中不可避免的的数据库增删改查操作等一切的非IO操作都可以用这个线程池去执行。最好采用默认的构造方法,因为如果修改,线程可能会变得不再是异步,事件的发生顺序有可能混乱,比如messageReceived()、messageSent()方法被同时步执行,就会变得非常危险。
另外,这段代码切记一定要放在codeFilter之前,因为解编码的操作就出在IoProcessor线程池处理的整个链路上,在同一线程池中进行,独立的运行任务CPU无需进行上下文切换。从Mina的线程模型分析,得出总结,实际项目开发当中一定要避免独立任务由不同线程池去处理亦或是不同任务尤同一线程处理,这样代码的性能会被抑制。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: