代码示例
public static void main(String[] args) throws Exception { ConnectingIOReactor ioReactor = new DefaultConnectingIOReactor(); PoolingNHttpClientConnectionManager cm = new PoolingNHttpClientConnectionManager(ioReactor); cm.setMaxTotal(100); CloseableHttpAsyncClient httpAsyncClient = HttpAsyncClients.custom().setConnectionManager(cm).build(); httpAsyncClient.start(); String[] urisToGet = { "http://www.chinaso.com/", "http://www.so.com/", "http://www.qq.com/", }; final CountDownLatch latch = new CountDownLatch(urisToGet.length); for (final String uri: urisToGet) { final HttpGet httpget = new HttpGet(uri); httpAsyncClient.execute(httpget, new FutureCallback() { public void completed(final HttpResponse response) { latch.countDown(); System.out.println(httpget.getRequestLine() + "->" + response.getStatusLine()); } public void failed(final Exception ex) { latch.countDown(); System.out.println(httpget.getRequestLine() + "->" + ex); } public void cancelled() { latch.countDown(); System.out.println(httpget.getRequestLine() + " cancelled"); } }); } latch.await(); }
流程分析
简要流程总结如下:
HttpAsyncClient有一个AbstractMultiworkerIOReactor和AbstractIOReactor, 前者和后者类似于netty的bossGroup和workerGroup, AbstractMultiworkerIOReactor负责channel的连接, AbstractIOReactor负责channel的读写
先要明白连接池的结构
AbstractNIOConnPool 下各个变量的含义
// 一个可复用的ioreactor, 负责生成SessionRequest并唤醒selector去做连接到目标网站的操作 private final ConnectingIOReactor ioreactor; // 用来构造连接池的entry的工厂 private final NIOConnFactoryconnFactory; // 验证并生成目标连接socketAddress的类 private final SocketAddressResolver addressResolver; // 一个可复用的callBack类, 里面提供了一个调用SessionRequest的complete的方法 private final SessionRequestCallback sessionRequestCallback; // 用域名区分的连接池 private final Map > routeToPool; // 没有成功拿到连接的请求列表 private final LinkedList > leasingRequests; // 已经拿到连接权利, 但是还没连接成功的连接集合 private final Set pending; // 已经连接成功, 并被租借出去的连接集合 private final Set leased; // 当前连接池可用的连接集合 private final LinkedList available; // 已经连接完成, 但是不可用的连接集合, 例如因为异常连接失败等待, 他们会在队列中等待被调用回调方法做后续处理 private final ConcurrentLinkedQueue > completedRequests; // 每个route的最大连接数 private final Map maxPerRoute; // 锁对象 private final Lock lock; // 是否关闭 private final AtomicBoolean isShutDown; // 每个route最大连接数默认值 private volatile int defaultMaxPerRoute; // 整个连接池最大连接数 private volatile int maxTotal;
1. 发起请求
a. 根据请求route查看连接池, 如果连接池不为空, 直接返回跟池中connection绑定的future, 并把该conn放入leased列表
b. 如果因为某些原因导致当前请求无法取得连接, 但是没有发生致命错误的, 请求将被放入一个 leasing 列表, 这个列表会在后续动作中被取出来做连接重试
c. 如果实在连接过程中出现了移除等不可恢复的错误, 则将request标记为completed, 退出方法后调用fireCallBack, 进行回调清理, 这次请求就算是失败结束了
d. 如果是因为连接池没有可用连接, 但是可以新建连接的情况, 则会将request 加入pending列表, 并调用 selector的wakeup()方法, selector在wakeup以后会使用AbstractMultiworkerIOReactor(bossGroup)来进行连接操作, 并注册到selector中, 后续的connectable事件监听和channel连接成功注册也是由他完成的
2. AbstractIOReactor监听读写事件
3. 通过decoder检测response已经完成, 最后将连接release到连接池中, 此时将连接从leased列表除去, 并加入到available中
连接阶段
调用
Futureexecute( HttpUriRequest request, FutureCallback callback)
请求开始, 里面会调用 execute(request, new BasicHttpContext(), callback)
调用
Futureexecute( final HttpUriRequest request, final HttpContext context, final FutureCallback callback)
context 代表了一次请求的上下文, 里面实际上就是一个用来存储 attribute 的结构, 默认的实现 BasicHttpContext 实际上就是一个 ConcurrentHashMap
context 是可以嵌套的, 代码如下
@ThreadSafe public class BasicHttpContext implements HttpContext { private final HttpContext parentContext; private final Mapmap; public BasicHttpContext() { this(null); } public BasicHttpContext(final HttpContext parentContext) { super(); this.map = new ConcurrentHashMap (); this.parentContext = parentContext; } public Object getAttribute(final String id) { Args.notNull(id, "Id"); Object obj = this.map.get(id); if (obj == null && this.parentContext != null) { obj = this.parentContext.getAttribute(id); } return obj; } public void setAttribute(final String id, final Object obj) { Args.notNull(id, "Id"); if (obj != null) { this.map.put(id, obj); } else { this.map.remove(id); } } public Object removeAttribute(final String id) { Args.notNull(id, "Id"); return this.map.remove(id); } /** * @since 4.2 */ public void clear() { this.map.clear(); } @Override public String toString() { return this.map.toString(); } }
接着看执行流程
public Futureexecute( final HttpUriRequest request, final HttpContext context, final FutureCallback callback) { final HttpHost target; try { target = determineTarget(request); // 这一步是取出目标host } catch (final ClientProtocolException ex) { final BasicFuture future = new BasicFuture (callback); future.failed(ex); return future; } return execute(target, request, context, callback); }
调用
public Futureexecute( final HttpHost target, final HttpRequest request, final HttpContext context, final FutureCallback callback) { return execute( HttpAsyncMethods.create(target, request), HttpAsyncMethods.createConsumer(), context, callback); }
位于HttpAsyncClient接口下的 /** * Initiates asynchronous HTTP request execution using the given context. * * The request producer passed to this method will be used to generate * a request message and stream out its content without buffering it * in memory. The response consumer passed to this method will be used * to process a response message without buffering its content in memory. * * Please note it may be unsafe to interact with the context instance * while the request is still being executed. * * @paramthe result type of request execution. * @param requestProducer request producer callback. * @param responseConsumer response consumer callaback. * @param context HTTP context * @param callback future callback. * @return future representing pending completion of the operation. */Future execute( HttpAsyncRequestProducer requestProducer, HttpAsyncResponseConsumer responseConsumer, HttpContext context, FutureCallback callback);
这里会通过原来的请求信息生成一个requestProducer跟responseConsumer, 默认会调用HttpAsyncClient的InternalHttpAsyncClient的实现, 如下
publicFuture execute( final HttpAsyncRequestProducer requestProducer, final HttpAsyncResponseConsumer responseConsumer, final HttpContext context, final FutureCallback callback) { final Status status = getStatus(); Asserts.check(status == Status.ACTIVE, "Request cannot be executed; " + "I/O reactor status: %s", status); final BasicFuture future = new BasicFuture (callback); final HttpClientContext localcontext = HttpClientContext.adapt( context != null ? context : new BasicHttpContext()); setupContext(localcontext); @SuppressWarnings("resource") final DefaultClientExchangeHandlerImpl handler = new DefaultClientExchangeHandlerImpl ( this.log, requestProducer, responseConsumer, localcontext, future, this.connmgr, this.exec); try { handler.start(); // 请求开始 } catch (final Exception ex) { handler.failed(ex); } return future; }
这里通过生成一个ExchangeHandler来实现请求开始, 查看 handler.start()
public void start() throws HttpException, IOException { final HttpHost target = this.requestProducer.getTarget(); final HttpRequest original = this.requestProducer.generateRequest(); if (original instanceof HttpExecutionAware) { ((HttpExecutionAware) original).setCancellable(this); } this.exec.prepare(this.state, target, original); // 准备动作, 往state里设置各种状态 requestConnection(); // 实际发送请求的地方 }
接着往下
private void requestConnection() { if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.state.getId() + "] Request connection for " + this.state.getRoute()); } discardConnection(); this.state.setValidDuration(0); this.state.setNonReusable(); this.state.setRouteEstablished(false); this.state.setRouteTracker(null); final HttpRoute route = this.state.getRoute(); final Object userToken = this.localContext.getUserToken(); final RequestConfig config = this.localContext.getRequestConfig(); this.connmgr.requestConnection( // 此处调用ConenctionManager的requestConnection方法 route, userToken, config.getConnectTimeout(), config.getConnectionRequestTimeout(), TimeUnit.MILLISECONDS, new FutureCallback() { public void completed(final NHttpClientConnection managedConn) { connectionAllocated(managedConn); } public void failed(final Exception ex) { connectionRequestFailed(ex); } public void cancelled() { connectionRequestCancelled(); } }); }
再看NHttpClientConnectionManager下的
/** * Returns a {@link Future} for a {@link NHttpClientConnection}. * * Please note that the consumer of that connection is responsible * for fully establishing the route the to the connection target * by calling {@link #startRoute(org.apache.http.nio.NHttpClientConnection, * org.apache.http.conn.routing.HttpRoute, * org.apache.http.protocol.HttpContext) startRoute} in order to start * the process of connection initialization, optionally calling * {@link #upgrade(org.apache.http.nio.NHttpClientConnection, * org.apache.http.conn.routing.HttpRoute, * org.apache.http.protocol.HttpContext) upgrade} method to upgrade * the connection after having executedCONNECT
method to * all intermediate proxy hops and and finally calling * {@link #routeComplete(org.apache.http.nio.NHttpClientConnection, * org.apache.http.conn.routing.HttpRoute, * org.apache.http.protocol.HttpContext) routeComplete} to mark the route * as fully completed. * * @param route HTTP route of the requested connection. * @param state expected state of the connection ornull
* if the connection is not expected to carry any state. * @param connectTimeout connect timeout. * @param connectionRequestTimeout connection request timeout. * @param timeUnit time unit of the previous two timeout values. * @param callback future callback. */ FuturerequestConnection( HttpRoute route, Object state, long connectTimeout, long connectionRequestTimeout, TimeUnit timeUnit, FutureCallback callback);
它调用了PoolingNHttpClientConnectionManager的实现
public FuturerequestConnection( final HttpRoute route, final Object state, final long connectTimeout, final long leaseTimeout, final TimeUnit tunit, final FutureCallback callback) { Args.notNull(route, "HTTP route"); if (this.log.isDebugEnabled()) { this.log.debug("Connection request: " + format(route, state) + formatStats(route)); } final BasicFuture future = new BasicFuture (callback); final HttpHost host; if (route.getProxyHost() != null) { host = route.getProxyHost(); } else { host = route.getTargetHost(); } final SchemeIOSessionStrategy sf = this.iosessionFactoryRegistry.lookup( host.getSchemeName()); if (sf == null) { future.failed(new UnsupportedSchemeException(host.getSchemeName() + " protocol is not supported")); return future; } this.pool.lease(route, state, connectTimeout, leaseTimeout, tunit != null ? tunit : TimeUnit.MILLISECONDS, new InternalPoolEntryCallback(future)); // 这里就是实际运用连接池的地方 return future; }
看 AbstractNIOConnPool 的 lease 方法
public Futurelease( final T route, final Object state, final long connectTimeout, final long leaseTimeout, final TimeUnit tunit, final FutureCallback callback) { Args.notNull(route, "Route"); Args.notNull(tunit, "Time unit"); Asserts.check(!this.isShutDown.get(), "Connection pool shut down"); final BasicFuture future = new BasicFuture (callback); this.lock.lock(); // 同步 try { final long timeout = connectTimeout > 0 ? tunit.toMillis(connectTimeout) : 0; final LeaseRequest request = new LeaseRequest (route, state, timeout, leaseTimeout, future); final boolean completed = processPendingRequest(request); // 1) 获取连接的方法 if (!request.isDone() && !completed) { // 2) 因为连接池满而不能马上获得连接的的, 加入到一个leasing的LinkedList中, 他会在后续的某些操作中被取出来重新尝试连接发送请求 this.leasingRequests.add(request); } if (request.isDone()) { // 3) 已经完成连接动作(注意是连接动作完成, 不是请求完成获得响应, 这里的连接完成包括从连接池获取到连接, 或者是因为异常request被设置为fail)的请求, 加入到一个ConcurrentLinkedQueue中, 这个队列的唯一作用就是标记连接完成以后, 调用fireCallBack方法会从里面把这些连接完成的request做一遍回调处理 this.completedRequests.add(request); } } finally { this.lock.unlock(); } fireCallbacks(); return future; }
这里主要涉及到连接池 AbstractNIOConnPool 以及连接池下得实际存储连接的 RouteSpecificPool,
然后开始分析连接流程
private boolean processPendingRequest(final LeaseRequestrequest) { final T route = request.getRoute(); final Object state = request.getState(); final long deadline = request.getDeadline(); final long now = System.currentTimeMillis(); if (now > deadline) { request.failed(new TimeoutException()); return false; } final RouteSpecificPool pool = getPool(route); E entry; for (;;) { // 租借连接池连接 entry = pool.getFree(state); // getFree即是从available中获取一个state匹配的连接 if (entry == null) { // 没有可用连接退出循环 break; } // 清除不可用连接 if (entry.isClosed() || entry.isExpired(System.currentTimeMillis())) { entry.close(); this.available.remove(entry); pool.free(entry, false); } else { break; } } if (entry != null) { // 找到连接退出 this.available.remove(entry); this.leased.add(entry); request.completed(entry); onLease(entry); return true; } // 需要新连接的情况 // New connection is needed final int maxPerRoute = getMax(route); // 已经分配的连接超出可分配限制 // Shrink the pool prior to allocating a new connection final int excess = Math.max(0, pool.getAllocatedCount() + 1 - maxPerRoute); // 对连接池进行缩减, 将上次使用的连接关闭并删除, 直到超出的连接全被清除 if (excess > 0) { for (int i = 0; i < excess; i++) { final E lastUsed = pool.getLastUsed(); // 这个方法是取到 available 里的最后一个连接, 也就是说会出现所有连接都被租借出去了的情况, 这样的话就相当于连接池满, 到下一步的 if (pool.getAllocatedCount() < maxPerRoute) 即会 false, 最后导致request进入 leasingRequest 列表 if (lastUsed == null) { break; } lastUsed.close(); this.available.remove(lastUsed); pool.remove(lastUsed); } } // 已分配连接数 < 最大连接数限制, 开始新建 if (pool.getAllocatedCount() < maxPerRoute) { // 总共被使用的数量等于 正在等待连接数 + 已经租借出去的连接数 final int totalUsed = this.pending.size() + this.leased.size(); final int freeCapacity = Math.max(this.maxTotal - totalUsed, 0); if (freeCapacity == 0) { return false; } // 需要注意的是pool里available不为空, 也有可能拿不到可用连接, 因为state不匹配 final int totalAvailable = this.available.size(); // 总的available > 连接空位时, 会随机选择最后一次使用的连接, 并把它关掉.. 没搞明白这一步是干嘛用的 if (totalAvailable > freeCapacity - 1) { if (!this.available.isEmpty()) { final E lastUsed = this.available.removeLast(); lastUsed.close(); final RouteSpecificPool otherpool = getPool(lastUsed.getRoute()); otherpool.remove(lastUsed); } } // 创建连接监视器阶段, 创建了一个监时此次请求的监视对象 SessionRequest, 并调用selector的wakeup(), 出发实际的连接操作 final SocketAddress localAddress; final SocketAddress remoteAddress; try { remoteAddress = this.addressResolver.resolveRemoteAddress(route); localAddress = this.addressResolver.resolveLocalAddress(route); } catch (final IOException ex) { request.failed(ex); return false; }
// 重点关注一下这个connect方法 final SessionRequest sessionRequest = this.ioreactor.connect( remoteAddress, localAddress, route, this.sessionRequestCallback); final int timout = request.getConnectTimeout() < Integer.MAX_VALUE ? (int) request.getConnectTimeout() : Integer.MAX_VALUE; sessionRequest.setConnectTimeout(timout); // 加入到总pending集合 this.pending.add(sessionRequest); // 加入到route连接池pending集合 pool.addPending(sessionRequest, request.getFuture()); return true; } else { return false; } } // 检查最后一个完成的request的结果, 并设置future的状态 private void fireCallbacks() { LeaseRequestrequest; while ((request = this.completedRequests.poll()) != null) { final BasicFuture future = request.getFuture(); final Exception ex = request.getException(); final E result = request.getResult(); if (ex != null) { future.failed(ex); } else if (result != null) { future.completed(result); } else { future.cancel(); } } }
看看DefaultConnectingIOReactor的connect方法
public SessionRequest connect( final SocketAddress remoteAddress, final SocketAddress localAddress, final Object attachment, final SessionRequestCallback callback) { Asserts.check(this.status.compareTo(IOReactorStatus.ACTIVE) <= 0, "I/O reactor has been shut down"); final SessionRequestImpl sessionRequest = new SessionRequestImpl( remoteAddress, localAddress, attachment, callback); sessionRequest.setConnectTimeout(this.config.getConnectTimeout()); this.requestQueue.add(sessionRequest); this.selector.wakeup(); // 去看看wakeup()以后会发生什么事情 return sessionRequest; }
在AbstractMultiworkerIOReactor中有一个execute()方法
/** * Activates the main I/O reactor as well as all worker I/O reactors. * The I/O main reactor will start reacting to I/O events and triggering * notification methods. The worker I/O reactor in their turn will start * reacting to I/O events and dispatch I/O event notifications to the given * {@link IOEventDispatch} interface. ** This method will enter the infinite I/O select loop on * the {
@link Selector} instance associated with this I/O reactor and used * to manage creation of new I/O channels. Once a new I/O channel has been * created the processing of I/O events on that channel will be delegated * to one of the worker I/O reactors. ** The method will remain blocked unto the I/O reactor is shut down or the * execution thread is interrupted. * *
@see #processEvents(int) * @see #cancelRequests() * * @throws InterruptedIOException if the dispatch thread is interrupted. * @throws IOReactorException in case if a non-recoverable I/O error. */ public void execute( final IOEventDispatch eventDispatch) throws InterruptedIOException, IOReactorException { Args.notNull(eventDispatch, "Event dispatcher"); synchronized (this.statusLock) { if (this.status.compareTo(IOReactorStatus.SHUTDOWN_REQUEST) >= 0) { this.status = IOReactorStatus.SHUT_DOWN; this.statusLock.notifyAll(); return; } Asserts.check(this.status.compareTo(IOReactorStatus.INACTIVE) == 0, "Illegal state %s", this.status); this.status = IOReactorStatus.ACTIVE; // Start I/O dispatchers for (int i = 0; i < this.dispatchers.length; i++) { final BaseIOReactor dispatcher = new BaseIOReactor(this.selectTimeout, this.interestOpsQueueing); dispatcher.setExceptionHandler(exceptionHandler); this.dispatchers[i] = dispatcher; } for (int i = 0; i < this.workerCount; i++) { final BaseIOReactor dispatcher = this.dispatchers[i]; this.workers[i] = new Worker(dispatcher, eventDispatch); this.threads[i] = this.threadFactory.newThread(this.workers[i]); } } try { // 启动所有worker线程, 连接的事情是交给 AbstractMultiworkerIOReactor 来做的, 但是连接成功后的事情则是交给 AbstractIOReactor Worker 线程来处理, 前者类似于 bossGroup, 后者类似于 workerGroup for (int i = 0; i < this.workerCount; i++) { if (this.status != IOReactorStatus.ACTIVE) { return; } this.threads[i].start(); } // 使用无限循环监听事件 for (;;) { final int readyCount; try { // 阻塞, 直到超时或者调用 wakeup() readyCount = this.selector.select(this.selectTimeout); } catch (final InterruptedIOException ex) { throw ex; } catch (final IOException ex) { throw new IOReactorException("Unexpected selector failure", ex); } // 如果有需要处理的事件, 则进入processEvents流程, 实际的连接过程就在这里 if (this.status.compareTo(IOReactorStatus.ACTIVE) == 0) { processEvents(readyCount); } // Verify I/O dispatchers for (int i = 0; i < this.workerCount; i++) { final Worker worker = this.workers[i]; final Exception ex = worker.getException(); if (ex != null) { throw new IOReactorException( "I/O dispatch worker terminated abnormally", ex); } } if (this.status.compareTo(IOReactorStatus.ACTIVE) > 0) { break; } } } catch (final ClosedSelectorException ex) { addExceptionEvent(ex); } catch (final IOReactorException ex) { if (ex.getCause() != null) { addExceptionEvent(ex.getCause()); } throw ex; } finally { doShutdown(); synchronized (this.statusLock) { this.status = IOReactorStatus.SHUT_DOWN; this.statusLock.notifyAll(); } } }
首次连接的时候, 触发的是 DefaultConnectingIOReactor 的 processEvents 方法
@Override protected void processEvents(final int readyCount) throws IOReactorException { processSessionRequests(); // 这里就是实际连接的地方 if (readyCount > 0) { final SetselectedKeys = this.selector.selectedKeys(); for (final SelectionKey key : selectedKeys) { processEvent(key); } selectedKeys.clear(); } final long currentTime = System.currentTimeMillis(); if ((currentTime - this.lastTimeoutCheck) >= this.selectTimeout) { this.lastTimeoutCheck = currentTime; final Set keys = this.selector.keys(); processTimeouts(keys); } } private void processSessionRequests() throws IOReactorException { SessionRequestImpl request; // wakeup 一次将队列的所有request处理(发起连接)掉 while ((request = this.requestQueue.poll()) != null) { if (request.isCompleted()) { continue; } final SocketChannel socketChannel; try { socketChannel = SocketChannel.open(); } catch (final IOException ex) { throw new IOReactorException("Failure opening socket", ex); } try { socketChannel.configureBlocking(false); validateAddress(request.getLocalAddress()); validateAddress(request.getRemoteAddress()); if (request.getLocalAddress() != null) { final Socket sock = socketChannel.socket(); sock.setReuseAddress(this.config.isSoReuseAddress()); sock.bind(request.getLocalAddress()); } prepareSocket(socketChannel.socket()); final boolean connected = socketChannel.connect(request.getRemoteAddress()); if (connected) { // 马上连接成功, 处理下一个 final ChannelEntry entry = new ChannelEntry(socketChannel, request); addChannel(entry); continue; } } catch (final IOException ex) { closeChannel(socketChannel); request.failed(ex); return; } // 还未连接成功, 则注册到selector, 等待connect事件的触发, 再用processEvent来处理 final SessionRequestHandle requestHandle = new SessionRequestHandle(request); try { final SelectionKey key = socketChannel.register(this.selector, SelectionKey.OP_CONNECT, requestHandle); request.setKey(key); } catch (final IOException ex) { closeChannel(socketChannel); throw new IOReactorException("Failure registering channel " + "with the selector", ex); } } } // 这个方法是连接成功以后注册channel的方法 private void processEvent(final SelectionKey key) { try { if (key.isConnectable()) { final SocketChannel channel = (SocketChannel) key.channel(); // Get request handle final SessionRequestHandle requestHandle = (SessionRequestHandle) key.attachment(); final SessionRequestImpl sessionRequest = requestHandle.getSessionRequest(); // Finish connection process try { channel.finishConnect(); } catch (final IOException ex) { sessionRequest.failed(ex); } key.cancel(); key.attach(null); if (!sessionRequest.isCompleted()) { // 注册新channel, 这些channel后来会被worker线程处理, 他们来进行io读写 addChannel(new ChannelEntry(channel, sessionRequest)); } else { try { channel.close(); } catch (IOException ignore) { } } } } catch (final CancelledKeyException ex) { final SessionRequestHandle requestHandle = (SessionRequestHandle) key.attachment(); key.attach(null); if (requestHandle != null) { final SessionRequestImpl sessionRequest = requestHandle.getSessionRequest(); if (sessionRequest != null) { sessionRequest.cancel(); } } } }
接下来看连接成功后的IOReactor如何处理, 如下 BaseIOReactor 的 execute 方法
/** * Activates the I/O reactor. The I/O reactor will start reacting to * I/O events and triggering notification methods. ** This method will enter the infinite I/O select loop on * the {
@link Selector} instance associated with this I/O reactor. ** The method will remain blocked unto the I/O reactor is shut down or the * execution thread is interrupted. * *
@see #acceptable(SelectionKey) * @see #connectable(SelectionKey) * @see #readable(SelectionKey) * @see #writable(SelectionKey) * @see #timeoutCheck(SelectionKey, long) * @see #validate(Set) * @see #sessionCreated(SelectionKey, IOSession) * @see #sessionClosed(IOSession) * * @throws InterruptedIOException if the dispatch thread is interrupted. * @throws IOReactorException in case if a non-recoverable I/O error. */ protected void execute() throws InterruptedIOException, IOReactorException { this.status = IOReactorStatus.ACTIVE; try { for (;;) { final int readyCount; try { readyCount = this.selector.select(this.selectTimeout); } catch (final InterruptedIOException ex) { throw ex; } catch (final IOException ex) { throw new IOReactorException("Unexpected selector failure", ex); } if (this.status == IOReactorStatus.SHUT_DOWN) { // Hard shut down. Exit select loop immediately break; } if (this.status == IOReactorStatus.SHUTTING_DOWN) { // Graceful shutdown in process // Try to close things out nicely closeSessions(); closeNewChannels(); } // Process selected I/O events if (readyCount > 0) { processEvents(this.selector.selectedKeys()); } // Validate active channels validate(this.selector.keys()); // Process closed sessions processClosedSessions(); // If active process new channels if (this.status == IOReactorStatus.ACTIVE) { processNewChannels(); } // Exit select loop if graceful shutdown has been completed if (this.status.compareTo(IOReactorStatus.ACTIVE) > 0 && this.sessions.isEmpty()) { break; } if (this.interestOpsQueueing) { // process all pending interestOps() operations processPendingInterestOps(); } } } catch (final ClosedSelectorException ignore) { } finally { hardShutdown(); synchronized (this.statusMutex) { this.statusMutex.notifyAll(); } } } private void processEvents(final SetselectedKeys) { for (final SelectionKey key : selectedKeys) { processEvent(key); } selectedKeys.clear(); } /** * Processes new event on the given selection key. * * @param key the selection key that triggered an event. */ protected void processEvent(final SelectionKey key) { final IOSessionImpl session = (IOSessionImpl) key.attachment(); try { if (key.isAcceptable()) { acceptable(key); } if (key.isConnectable()) { connectable(key); } if (key.isReadable()) { session.resetLastRead(); readable(key); } if (key.isWritable()) { session.resetLastWrite(); writable(key); } } catch (final CancelledKeyException ex) { queueClosedSession(session); key.attach(null); } }
这个就跟AbstractMultiworkerIOReactor类似, 只不过两个人的兴趣集事件不太一样, 看看 AbstractIOReactor 的实现类 BaseIOReactor就知道了
/** * This I/O reactor implementation does not react to the * {@link SelectionKey#OP_ACCEPT} event. ** Super-classes can override this method to react to the event.
*/ @Override protected void acceptable(final SelectionKey key) { } /** * This I/O reactor implementation does not react to the * {@link SelectionKey#OP_CONNECT} event. ** Super-classes can override this method to react to the event.
*/ @Override protected void connectable(final SelectionKey key) { } /** * Processes {@link SelectionKey#OP_READ} event on the given selection key. * This method dispatches the event notification to the * {@link IOEventDispatch#inputReady(IOSession)} method. */ @Override protected void readable(final SelectionKey key) { final IOSession session = getSession(key); try { this.eventDispatch.inputReady(session); if (session.hasBufferedInput()) { this.bufferingSessions.add(session); } } catch (final CancelledKeyException ex) { queueClosedSession(session); key.attach(null); } catch (final RuntimeException ex) { handleRuntimeException(ex); } } /** * Processes {@link SelectionKey#OP_WRITE} event on the given selection key. * This method dispatches the event notification to the * {@link IOEventDispatch#outputReady(IOSession)} method. */ @Override protected void writable(final SelectionKey key) { final IOSession session = getSession(key); try { this.eventDispatch.outputReady(session); } catch (final CancelledKeyException ex) { queueClosedSession(session); key.attach(null); } catch (final RuntimeException ex) { handleRuntimeException(ex); } }
它实际上只会处理 read 跟 write 事件
这里特别注意一下 AbstractNIOConnPool requestComplete 并不是整个请求结束, 而是连接成功的意思, 看看他的调用的地方和触发的东西
最开始他是从 AbstractIOReactor 的processNewChannels中来的, 这个方法在execute里被触发
rivate void processNewChannels() throws IOReactorException { ChannelEntry entry; while ((entry = this.newChannels.poll()) != null) { // 记得上面连接成功后调用addChannel加到的这个队列, 现在取出来 final SocketChannel channel; final SelectionKey key; try { channel = entry.getChannel(); channel.configureBlocking(false); key = channel.register(this.selector, SelectionKey.OP_READ); } catch (final ClosedChannelException ex) { final SessionRequestImpl sessionRequest = entry.getSessionRequest(); if (sessionRequest != null) { sessionRequest.failed(ex); } return; } catch (final IOException ex) { throw new IOReactorException("Failure registering channel " + "with the selector", ex); } final SessionClosedCallback sessionClosedCallback = new SessionClosedCallback() { public void sessionClosed(final IOSession session) { queueClosedSession(session); } }; InterestOpsCallback interestOpsCallback = null; if (this.interestOpsQueueing) { interestOpsCallback = new InterestOpsCallback() { public void addInterestOps(final InterestOpEntry entry) { queueInterestOps(entry); } }; } final IOSession session; try { session = new IOSessionImpl(key, interestOpsCallback, sessionClosedCallback); int timeout = 0; try { timeout = channel.socket().getSoTimeout(); } catch (final IOException ex) { // Very unlikely to happen and is not fatal // as the protocol layer is expected to overwrite // this value anyways } session.setAttribute(IOSession.ATTACHMENT_KEY, entry.getAttachment()); session.setSocketTimeout(timeout); } catch (final CancelledKeyException ex) { continue; } try { this.sessions.add(session); final SessionRequestImpl sessionRequest = entry.getSessionRequest(); if (sessionRequest != null) { // 就是在这里调用了completed, 最后进入连接池的completed方法 sessionRequest.completed(session); } key.attach(session); sessionCreated(key, session); } catch (final CancelledKeyException ex) { queueClosedSession(session); key.attach(null); } } }
看看连接池 AbstractNIOConnPool 的completed 方法做了什么
protected void requestCompleted(final SessionRequest request) { if (this.isShutDown.get()) { return; } @SuppressWarnings("unchecked") final T route = (T) request.getAttachment(); this.lock.lock(); try { this.pending.remove(request); // 从peding列表中去掉这个连接 final RouteSpecificPoolpool = getPool(route); final IOSession session = request.getSession(); try { final C conn = this.connFactory.create(route, session); final E entry = pool.createEntry(request, conn); this.leased.add(entry); // 连接加入到被租借集合 pool.completed(request, entry); // 调用 perRoute 连接池的complte onLease(entry); // 这个 onLease 就是设置了一下超时时间 } catch (final IOException ex) { pool.failed(request, ex); } } finally { this.lock.unlock(); } fireCallbacks(); }
主要做的事情就是设置了一下连接的sotimeout, 还有就是将连接从 pending列表移到了 lease 集合
最后, 来看看 连接是如何归还的
通过对AbstractNIOConnPool的release方法的跟踪, 最后找了还是在 BaseIOReactor 监听到 readable 时间的时候, 调用了HttpAsyncRequestExecutor的inputReady
方法
public void inputReady( final NHttpClientConnection conn, final ContentDecoder decoder) throws IOException, HttpException { final State state = ensureNotNull(getState(conn)); final HttpAsyncClientExchangeHandler handler = ensureNotNull(getHandler(conn)); handler.consumeContent(decoder, conn); state.setResponseState(MessageState.BODY_STREAM); if (decoder.isCompleted()) { // 检测到内容已结束, 进入complete流程 processResponse(conn, state, handler); } }
请求完成阶段
当检测到response已经完了, 就会进入complete流程, 最后回到releaseConnection流程, 最后到达连接池的release
public void release(final E entry, final boolean reusable) { if (entry == null) { return; } if (this.isShutDown.get()) { return; } this.lock.lock(); try { if (this.leased.remove(entry)) { // 从租借集合中删除 final RouteSpecificPoolpool = getPool(entry.getRoute()); pool.free(entry, reusable); // 重新加入到pool的available中 if (reusable) { this.available.addFirst(entry); // 加入到available onRelease(entry); // 重新这是soTimeout } else { entry.close(); } processNextPendingRequest(); // 处理下一个在leasing队列中等待的请求 } } finally { this.lock.unlock(); } fireCallbacks(); }
完