MINA2为何取消了ThreadModel
在MINA2的官方文档中,apache提到了ThreadModel被废除,原因是该类的使用容易引起错误。由于我一开始就用的是mina2,所以对这个问题没什么想法。
?
ThreadModel的作用其实就是在处理链的最后(handler之前)添加一个ExecutorFilter过滤器。
?
前年我进入了现在的项目组,组里由于使用mina比较早,因此在使用mina1.1.7,并且对该mina进行了封装使其更易用(在我看来,封装的主要原因在于对mina的API为操作链不太习惯,而将它封装为添加连接池、添加解码器等API,我使用起来觉得比mina的API还有点麻烦),但是正式1.X版本的ThreadModel引起了问题,才让我对他的废除有点感想。
?
mina1.X中的ThreadModel使用有两种方式,一种为采用手动方式设置连接池:
SocketAcceptorConfig acceptorConfig = acceptor.getDefaultConfig();acceptorConfig.setThreadModel(ThreadModel.MANUAL);
?
?
另外一种为使用默认ThreadModel设置连接池:
((ExecutorThreadModel)acceptorConfig.cfg.getThreadModel()).setExecutor(executor);
?
正常情况下,第二种情况没什么问题,但是如果你的程序中启动两个或两个以上的server,并且连接池的设置都使用第二种方式,那就悲剧了。
?
我们来看一下SocketAcceptorConfig类的getThreadModel方法,内容如下:
?
public ThreadModel getThreadModel() { if (threadModel == null) { threadModel = getDefaultThreadModel(); } return threadModel; }?
?
由于采用默认ThreadModel,我们是没有设置过ThreadModel的,所以会调用getDefaultThreadModel方法,再来看看这个方法的代码:
?
private synchronized ThreadModel getDefaultThreadModel() { if (defaultThreadModel == null) { defaultThreadModel = ExecutorThreadModel.getInstance("AnonymousIoService"); } return defaultThreadModel; }?
?
看到了吧, ExecutorThreadModel.getInstance("AnonymousIoService")这句,如果信息的人可能会知道悲剧在什么地方了,让我们接着看看这个语句中方法是什么样的
?
private static final Map<String, ExecutorThreadModel> service2model = new HashMap<String, ExecutorThreadModel>();public static ExecutorThreadModel getInstance(String serviceName) { if (serviceName == null) { throw new NullPointerException("serviceName"); } ExecutorThreadModel model; synchronized (service2model) { model = service2model.get(serviceName); if (model == null) { model = new ExecutorThreadModel(serviceName); service2model.put(serviceName, model); } } return model; }?
在代码中我们可以看到,service2model是一个静态变量,ExecutorThreadModel.getInstance("AnonymousIoService")拿到的ExecutorThreadModel也是一个静态变量,那么当两个server都没有acceptorConfig.setThreadModel时,两个server会共用同一个ExecutorThreadModel(名为AnonymousIoService的那个)。
当一个server业务量大设置了一个100大小的线程池,而另外一个server业务量小设置了大小为2的线程池,而业务小的server启动晚于业务大的server,那么业务小的线程池就会覆盖了业务大的线程池。
?
??? 我觉得还是老老实实的在链后面添加ExecutorFilter比较靠谱。
?
SocketAcceptor acceptor = ...;DefaultIoFilterChainBuilder filterChainBuilder = acceptor.getDefaultConfig().getFilterChain();filterChainBuilder.addLast("threadPool", new ExecutorFilter(Executors.newCachedThreadPool());?
?
恩恩。