东莞网站建设流程图,阿里云网站备案好了 怎么建站,想要网站推广版,如何用html制作网站随着并发数量的提高#xff0c;传统nio框架采用一个Selector来支撑大量连接事件的管理和触发已经遇到瓶颈#xff0c;因此现在各种nio框架的新版本都采用多个Selector并存的结构#xff0c;由多个Selector均衡地去管理大量连接。这里以Mina和Grizzly的实现为例。 在Mina 2…随着并发数量的提高传统nio框架采用一个Selector来支撑大量连接事件的管理和触发已经遇到瓶颈因此现在各种nio框架的新版本都采用多个Selector并存的结构由多个Selector均衡地去管理大量连接。这里以Mina和Grizzly的实现为例。 在Mina 2.0中Selector的管理是由org.apache.mina.transport.socket.nio.NioProcessor来处理每个NioProcessor对象保存一个Selector负责具体的select、wakeup、channel的注册和取消、读写事件的注册和判断、实际的IO读写操作等等核心代码如下 public NioProcessor(Executor executor) { super(executor); try { // Open a new selector selector Selector.open(); } catch (IOException e) { throw new RuntimeIoException(Failed to open a selector., e); } } protected int select(long timeout) throws Exception { return selector.select(timeout); } protected boolean isInterestedInRead(NioSession session) { SelectionKey key session.getSelectionKey(); return key.isValid() (key.interestOps() SelectionKey.OP_READ) ! 0; } protected boolean isInterestedInWrite(NioSession session) { SelectionKey key session.getSelectionKey(); return key.isValid() (key.interestOps() SelectionKey.OP_WRITE) ! 0; } protected int read(NioSession session, IoBuffer buf) throws Exception { return session.getChannel().read(buf.buf()); } protected int write(NioSession session, IoBuffer buf, int length) throws Exception { if (buf.remaining() length) { return session.getChannel().write(buf.buf()); } else { int oldLimit buf.limit(); buf.limit(buf.position() length); try { return session.getChannel().write(buf.buf()); } finally { buf.limit(oldLimit); } } } 这些方法的调用都是通过AbstractPollingIoProcessor来处理这个类里可以看到一个nio框架的核心逻辑注册、select、派发具体因为与本文主题不合不再展开。NioProcessor的初始化是在NioSocketAcceptor的构造方法中调用的 public NioSocketAcceptor() { super(new DefaultSocketSessionConfig(), NioProcessor.class); ((DefaultSocketSessionConfig) getSessionConfig()).init(this); } 直接调用了父类AbstractPollingIoAcceptor的构造函数在其中我们可以看到默认是启动了一个SimpleIoProcessorPool来包装NioProcessor protected AbstractPollingIoAcceptor(IoSessionConfig sessionConfig, Class? extends IoProcessorT processorClass) { this(sessionConfig, null, new SimpleIoProcessorPoolT(processorClass), true); } 这里其实是一个组合模式,SimpleIoProcessorPool和NioProcessor都实现了Processor接口一个是组合形成的Processor池而另一个是单独的类。调用的SimpleIoProcessorPool的构造函数是这样 private static final int DEFAULT_SIZE Runtime.getRuntime().availableProcessors() 1; public SimpleIoProcessorPool(Class? extends IoProcessorT processorType) { this(processorType, null, DEFAULT_SIZE); } 可以看到默认的池大小是cpu个数1也就是创建了cpu1个的Selector对象。它的重载构造函数里是创建了一个数组启动一个CachedThreadPool来运行NioProcessor通过反射创建具体的Processor对象这里就不再列出了。 Mina当有一个新连接建立的时候就创建一个NioSocketSession并且传入上面的SimpleIoProcessorPool当连接初始化的时候将Session加入SimpleIoProcessorPool protected NioSession accept(IoProcessorNioSession processor, ServerSocketChannel handle) throws Exception { SelectionKey key handle.keyFor(selector); if ((key null) || (!key.isValid()) || (!key.isAcceptable()) ) { return null; } // accept the connection from the client SocketChannel ch handle.accept(); if (ch null) { return null; } return new NioSocketSession(this, processor, ch); } private void processHandles(IteratorH handles) throws Exception { while (handles.hasNext()) { H handle handles.next(); handles.remove(); // Associates a new created connection to a processor, // and get back a session T session accept(processor, handle); if (session null) { break; } initSession(session, null, null); // add the session to the SocketIoProcessor session.getProcessor().add(session); } } 加入的操作是递增一个整型变量并且模数组大小后对应的NioProcessor注册到session里 private IoProcessorT nextProcessor() { checkDisposal(); return pool[Math.abs(processorDistributor.getAndIncrement()) % pool.length]; } if (p null) { p nextProcessor(); IoProcessorT oldp (IoProcessorT) session.setAttributeIfAbsent(PROCESSOR, p); if (oldp ! null) { p oldp; } } 这样一来每个连接都关联一个NioProcessor也就是关联一个Selector对象避免了所有连接共用一个Selector负载过高导致server响应变慢的后果。但是注意到NioSocketAcceptor也有一个Selector这个Selector用来干什么的呢那就是集中处理OP_ACCEPT事件的Selector主要用于连接的接入不跟处理读写事件的Selector混在一起因此Mina的默认open的Selector是cpu2个。 看完mina2.0之后我们来看看Grizzly2.0是怎么处理的Grizzly还是比较保守它默认就是启动两个Selector其中一个专门负责accept另一个负责连接的IO读写事件的管理。Grizzly 2.0中Selector的管理是通过SelectorRunner类这个类封装了Selector对象以及核心的分发注册逻辑你可以将他理解成Mina中的NioProcessor核心的代码如下 protected boolean doSelect() { selectorHandler transport.getSelectorHandler(); selectionKeyHandler transport.getSelectionKeyHandler(); strategy transport.getStrategy(); try { if (isResume) { // If resume SelectorRunner - finish postponed keys isResume false; if (keyReadyOps ! 0) { if (!iterateKeyEvents()) return false; } if (!iterateKeys()) return false; } lastSelectedKeysCount 0; selectorHandler.preSelect(this); readyKeys selectorHandler.select(this); if (stateHolder.getState(false) State.STOPPING) return false; lastSelectedKeysCount readyKeys.size(); if (lastSelectedKeysCount ! 0) { iterator readyKeys.iterator(); if (!iterateKeys()) return false; } selectorHandler.postSelect(this); } catch (ClosedSelectorException e) { notifyConnectionException(key, Selector was unexpectedly closed, e, Severity.TRANSPORT, Level.SEVERE, Level.FINE); } catch (Exception e) { notifyConnectionException(key, doSelect exception, e, Severity.UNKNOWN, Level.SEVERE, Level.FINE); } catch (Throwable t) { logger.log(Level.SEVERE,doSelect exception, t); transport.notifyException(Severity.FATAL, t); } return true; } 基本上是一个reactor实现的样子在AbstractNIOTransport类维护了一个SelectorRunner的数组而Grizzly用于创建tcp server的类TCPNIOTransport正是继承于AbstractNIOTransport类在它的start方法中调用了startSelectorRunners来创建并启动SelectorRunner数组 private static final int DEFAULT_SELECTOR_RUNNERS_COUNT 2; Override public void start() throws IOException { if (selectorRunnersCount 0) { selectorRunnersCount DEFAULT_SELECTOR_RUNNERS_COUNT; } startSelectorRunners();} protected void startSelectorRunners() throws IOException { selectorRunners new SelectorRunner[selectorRunnersCount]; synchronized(selectorRunners) { for (int i 0; i selectorRunnersCount; i) { SelectorRunner runner new SelectorRunner(this, SelectorFactory.instance().create()); runner.start(); selectorRunners[i] runner; } } } 可见Grizzly并没有采用一个单独的池对象来管理SelectorRunner而是直接采用数组管理默认数组大小是2。SelectorRunner实现了Runnable接口它的start方法调用了一个线程池来运行自身。刚才我提到了说Grizzly的Accept是单独一个Selector来管理的那么是如何表现的呢答案在RoundRobinConnectionDistributor类这个类是用于派发注册事件到相应的SelectorRunner上它的派发方式是这样 public FutureRegisterChannelResult registerChannelAsync( SelectableChannel channel, int interestOps, Object attachment, CompletionHandler completionHandler) throws IOException { SelectorRunner runner getSelectorRunner(interestOps); return transport.getSelectorHandler().registerChannelAsync( runner, channel, interestOps, attachment, completionHandler); } private SelectorRunner getSelectorRunner(int interestOps) { SelectorRunner[] runners getTransportSelectorRunners(); int index; if (interestOps SelectionKey.OP_ACCEPT || runners.length 1) { index 0; } else { index (counter.incrementAndGet() % (runners.length - 1)) 1; } return runners[index]; } getSelectorRunner这个方法道出了秘密如果是OP_ACCEPT那么都使用数组中的第一个SelectorRunner如果不是那么就通过取模运算的结果1从后面的SelectorRunner中取一个来注册。 分析完mina2.0和grizzly2.0对Selector的管理后我们可以得到几个启示1、在处理大量连接的情况下多个Selector比单个Selector好2、多个Selector的情况下处理OP_READ和OP_WRITE的Selector要与处理OP_ACCEPT的Selector分离也就是说处理接入应该要一个单独的Selector对象来处理避免IO读写事件影响接入速度。3、Selector的数目问题mina默认是cpu2而grizzly总共就2个我更倾向于mina的策略但是我认为应该对cpu个数做一个判断如果CPU个数超过8个那么更多的Selector线程可能带来比较大的线程切换的开销mina默认的策略并非合适幸好可以设置这个数值。 原文地址http://click.aliyun.com/m/21432/ 转载于:https://www.cnblogs.com/iyulang/p/6878559.html