我们知道Netty作为高性能通信框架,优点在于内部封装了管道的连接通信等操作,用户只需要调用封装好的接口,便可以很便捷的进行高并发通信。类似,在Http请求时,我们通过调用HttpClient,内部使用java NIO技术,通过引入连接池概念,来提高Http的并发能力,本文主要讲解该客户端内部是如何实现并发能力提高的原理。Http客户端分为同步和异步方式,以下示例展示了最基本的异步使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
RequestConfig.Builder requestConfigBuilder = RequestConfig.custom()
.setConnectTimeout(5000)
.setSocketTimeout(0)
.setConnectionRequestTimeout(3000);
HttpAsyncClientBuilder httpClientBuilder = HttpAsyncClientBuilder.create().setDefaultRequestConfig(requestConfigBuilder.build())
// 这些参数会用来生成PoolingNHttpClientConnectionManager,若PoolingNHttpClientConnectionManager自定义了,那么这些参数也就无效了
.setMaxConnPerRoute(10).setMaxConnTotal(30);
//配置io线程
IOReactorConfig ioReactorConfig = IOReactorConfig.custom().
setIoThreadCount(Runtime.getRuntime().availableProcessors())
.setSoKeepAlive(true)
.build();
DefaultConnectingIOReactor ioReactor = new DefaultConnectingIOReactor(ioReactorConfig);
ioReactor.setExceptionHandler(new IOReactorExceptionHandler() {
@Override
public boolean handle(IOException e) {
System.out.println("dsdsdsd");
return true;
}
@Override
public boolean handle(RuntimeException e) {
System.out.println("dsssd");
return true;
}
});
// 设置channel连接池并发参数
PoolingNHttpClientConnectionManager poolingNHttpClientConnectionManager = new PoolingNHttpClientConnectionManager(ioReactor);
poolingNHttpClientConnectionManager.setDefaultMaxPerRoute(5);
poolingNHttpClientConnectionManager.setMaxTotal(80);
httpClientBuilder.setConnectionManager(poolingNHttpClientConnectionManager);
// 初始化Client并启动
CloseableHttpAsyncClient client = HttpAsyncClients.custom().
setConnectionManager(poolingNHttpClientConnectionManager)
.build();
client.start();
final HttpGet request = new HttpGet("http://1.1.1.2:9200/indexName/_search");
// 异步查询
client.execute(request, new FutureCallback<HttpResponse>() {
@Override
public void completed(HttpResponse result){
try {
System.out.println(EntityUtils.toString(result.getEntity()));
} catch (Exception e) {
e.fillInStackTrace();
}
}
@Override
public void failed(Exception ex) {
ex.fillInStackTrace();
}
@Override
public void cancelled() {
System.out.println("cancelled");
}
});
Thread.sleep(10000);
client.close();
|
使用上没啥好说的,我们就直接以数据流流向为主线,看内部是如何使用连接池进行请求处理的。需要注意的是,若我们自定义了poolingNHttpClientConnectionManager对象,那么在requestConfigBuilder中设置的连接并发将不生效。
客户端内部初始化
pom文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.12</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.10</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore-nio</artifactId>
<version>4.4.12</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpasyncclient</artifactId>
<version>4.1.4</version>
</dependency>
|
目前httpclient已经升级到了5.x,本文源码基于4.X
InternalHttpAsyncClient客户端
我们需要关注下InternalHttpAsyncClient及其基类CloseableHttpAsyncClientBase,这里把重要的属性都罗列出来:
1 2 3 4 5 6 |
// 线程池管理者 private final NHttpClientConnectionManager connmgr; //MainClientExec 请求发送接收时的处理 private final InternalClientExec exec; // 类似netty的boss线程,负责管道建立连接 private final Thread reactorThread; |
下图是客户端初始化时创建的一些重要的对象:

PoolingNHttpClientConnectionManager:根据名称就可以看到,是连接池管理者。
CPool:连接池,存放了当前连接池的连接信息,比如全局空闲连接available、每个route独自的Pool,后面会详细介绍。
连接建立线程+请求处理线程
客户端内部会创建两类线程,类似netty的boss和worker线程,分别用来创建连接管道:AbstractMultiworkerIOReactor、以及请求发送线程:BaseIOReactor。本文中,也复用netty的称呼,分别将这两类线程称呼为boss线程和worker线程。boss线程在CloseableHttpAsyncClientBase构造函数初始化时初始化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
if (threadFactory != null && handler != null) {
this.reactorThread = threadFactory.newThread(new Runnable() {
@Override
public void run() {
try {
// 比如当线程接收到数据,就跑到IOEventDispatch里面了
final IOEventDispatch ioEventDispatch = new InternalIODispatch(handler);
// 将跑到PoolingNHttpClientConnectionManager.execute()
connmgr.execute(ioEventDispatch);
} catch (final Exception ex) {
log.error("I/O reactor terminated abnormally", ex);
} finally {
status.set(Status.STOPPED);
}
}
});
} else {
this.reactorThread = null;
}
|
boss线程真正工作的地方是在AbstractMultiworkerIOReactor,我们需要注意的是selector选择器(会在AbstractMultiworkerIOReactor构造时产生),每当需要构建管道时,都会向该selector上注册OP_CONNECT事件。AbstractMultiworkerIOReactor初始化代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
public void execute(//eventDispatch=InternalIODispatch
final IOEventDispatch eventDispatch) throws InterruptedIOException, IOReactorException {
synchronized (this.statusLock) {
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);
// 产生的线程名称都是"I/O dispatcher 120"
this.threads[i] = this.threadFactory.newThread(this.workers[i]);
}
}
try {
// I/O dispatcher开头的线程名称
for (int i = 0; i < this.workerCount; i++) {
if (this.status != IOReactorStatus.ACTIVE) {
return;
}
this.threads[i].start();
}
// 无线死循环了,除非管道关闭
for (;;) {
final int readyCount;
try {
// 默认睡眠1s
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 Throwable ex = worker.getThrowable();
if (ex != null) {
throw new IOReactorException(
"I/O dispatch worker terminated abnormally", ex);
}
}
}
} finally {
doShutdown();
synchronized (this.statusLock) {
this.status = IOReactorStatus.SHUT_DOWN;
this.statusLock.notifyAll();
}
}
}
|
具体做了如下事情:
1.构建n个worker线程,线程名称是I/O dispatcher n开头的, n可以在IOReactorConfig初始化时设置,默认为cpu的个数。
2.启动n个worker线


184

被折叠的 条评论
为什么被折叠?



