客户端消息只能到解码器,到不了IOHandler
MINA启动代码片段如下:
IoFilter protocol = new ProtocolCodecFilter(codecFactory); fcb.addLast("codec", protocol); OrderedThreadPoolExecutor threadpoll = new OrderedThreadPoolExecutor(500); fcb.addLast("threadPool", new ExecutorFilter(threadpoll));
为了多线程处理,在解码器的下面,我们加了一个线程池,mina自己写的线程池, 这个线程池的作用是对于同一个session来的请求,它能够按照请求到达的时间顺序的执行。举个例子,在一个session中,如果先接收到request A,然后再接收到request B,那么,OrderedThreadPoolExecutor能够保证一定处理完A之后再处理B。而一般的thread pool,会将A和B传递给不同的thread处理,有可能request B会先于request A完成。
为什么消息能过解码器,但是到不了逻辑, 怀疑这个线程池出问题,我简单分析一下这个类的源码:
private final Set<Worker> workers = new HashSet<Worker>(); private final AtomicInteger idleWorkers = new AtomicInteger();
workers是一个Worker的集合,每一个Worker都实现了Runnable接口,线程池管理这些Worker线程执行并发的事件处理, idleWorkers则负责及时的统计空闲的Worker数量便于提高性能, 非常形象的命名,workers是一个工厂,里面全部是worker工人,工人负责处理扔过来的Runnable, idleWrokers统计有多少工人空闲, 很显然,idleWorkers的数量必然小于等于workers的数量
private void addWorkerIfNecessary() { if (idleWorkers.get() == 0) { synchronized (workers) { if (workers.isEmpty() || (idleWorkers.get() == 0)) { addWorker(); } } } }
这个方法是专门给workers添加worker的,但是大家注意,在加之前有个判断,如果idleWrokers大于0肯定就加不进去了
也就是说,如果我们的workers里数量是0了,但是idleWorkers计数却大于0,那这个线程池肯定跪了
问题就出现在下面这段代码:
public void run() { thread = Thread.currentThread(); try { for (;;) { IoSession session = fetchSession(); idleWorkers.decrementAndGet(); if (session == null) { synchronized (workers) { if (workers.size() > getCorePoolSize()) { // Remove now to prevent duplicate exit. workers.remove(this); break; } } } if (session == EXIT_SIGNAL) { break; } try { if (session != null) { runTasks(getSessionTasksQueue(session)); } } finally { idleWorkers.incrementAndGet(); } } } finally { synchronized (workers) { workers.remove(this); OrderedThreadPoolExecutor.this.completedTaskCount += completedTaskCount; workers.notifyAll(); } } }
上面的是worker的run方法,
这段代码什么情况下会导致 workers里数量是0,但idleWorkers计数却大于0的情况呢
代码看起来似乎不会出现这种情况,因为能出异常的地方,都进行了try处理,
问题恰恰就发生在try里,假如我们的worker里有一个Runnable里出现一个Throwable的异常呢
当代码运行到这行时
runTasks(getSessionTasksQueue(session));
这里抛出了一个Throwable的异常,代码里没有处理,所以导致for循环直接退出,到了最后的finally
于是出现了我们不愿意看到的情况,workers数量比idleWorkers少了1,这个异常发生几次,就会导致workers是0,而idleWorkers数量大于0
这时候再有Runnable过来,全部都执行不了了
让我们来看看咱们的JDK的线程池这个地方是怎么做的,会不会出现同样的情况呢
try { beforeExecute(wt, task); Throwable thrown = null; try { task.run(); } catch (RuntimeException x) { thrown = x; throw x; } catch (Error x) { thrown = x; throw x; } catch (Throwable x) { thrown = x; throw new Error(x); } finally { afterExecute(task, thrown); } } finally { task = null; w.completedTasks++; w.unlock(); }
哈哈哈,看来JDK还是牛逼些,所有错误全部处理了
总结一下,mina这个地方确实是个坑,但是如果框架里try了Throwable也不会出现这个问题