HttpClient(三)连接池获取连接

源码:httpcomponents-core-4.4.13,org.apache.http.pool.AbstractConnPool

一、源码+注释

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
public abstract class AbstractConnPool<T, C, E extends PoolEntry<T, C>>
implements ConnPool<T, E>, ConnPoolControl<T> {
private E getPoolEntryBlocking(
final T route, final Object state,
final long timeout, final TimeUnit timeUnit,
final Future<E> future) throws IOException, InterruptedException, ExecutionException, TimeoutException {
// 1. 根据connectionRequestTimeout计算等待终止时间
Date deadline = null;
if (timeout > 0) {
deadline = new Date (System.currentTimeMillis() + timeUnit.toMillis(timeout));
}
this.lock.lock();
try {
// 2. 获取该路由的连接池,如果没有则新建
final RouteSpecificPool<T, C, E> pool = getPool(route);
E entry;
// 3. 循环用来处理等待逻辑
for (;;) {
Asserts.check(!this.isShutDown, "Connection pool shut down");
if (future.isCancelled()) {
throw new ExecutionException(operationAborted());
}
// 4. 尝试从该路由的连接池获取连接
for (;;) {
// 若pool.available有可用连接,则移动至pool.leased并返回
entry = pool.getFree(state);
if (entry == null) {
break;
}
// entry若过期,设置为close
if (entry.isExpired(System.currentTimeMillis())) {
entry.close();
}
// entry若已close,则从全局可用队列this.available移除
if (entry.isClosed()) {
// 全局可用available移除
this.available.remove(entry);
// 从pool.leased移动至pool.available
pool.free(entry, false);
} else {
break;
}
}
// 5.1 若从该路由的连接池获取到连接,则从全局可用队列移动到待释放队列,然后返回
// this.available ---> this.leased
if (entry != null) {
this.available.remove(entry);
this.leased.add(entry);
onReuse(entry);
return entry;
}

// 5.2 若从该路由的连接池没获取到连接,则开始新建连接逻辑
// 1)获取该路由允许使用连接的最大值
final int maxPerRoute = getMax(route);
// 2)计算该路由超出的连接数(由于最少需要释放一个,所以总数+1)
final int excess = Math.max(0, pool.getAllocatedCount() + 1 - maxPerRoute);
// 3)按LRU淘汰从该路由的pool.available移除空闲连接,同样在全局this.available里删除
if (excess > 0) {
for (int i = 0; i < excess; i++) {
final E lastUsed = pool.getLastUsed();
if (lastUsed == null) {
break;
}
lastUsed.close();
this.available.remove(lastUsed);
pool.remove(lastUsed);
}
}
// 4.1)如果该路由当前已分配的连接小于设置的该路由的最大连接数,并且全局连接数未达到上限,为该路由创建新连接。然后返回。
// 当前全局连接数大于上限时,则根据LRU原则从全局可用连接里淘汰连接(会从其路由里也删除)
if (pool.getAllocatedCount() < maxPerRoute) {
final int totalUsed = this.leased.size();
final int freeCapacity = Math.max(this.maxTotal - totalUsed, 0);
if (freeCapacity > 0) {
final int totalAvailable = this.available.size();
if (totalAvailable > freeCapacity - 1) {
if (!this.available.isEmpty()) {
final E lastUsed = this.available.removeLast();
lastUsed.close();
final RouteSpecificPool<T, C, E> otherpool = getPool(lastUsed.getRoute());
otherpool.remove(lastUsed);
}
}
final C conn = this.connFactory.create(route);
entry = pool.add(conn);
this.leased.add(entry);
return entry;
}
}
// 4.2)加入连接等待队列,当有连接释放时,若等待队列不为空则会唤醒等待的线程,进入下次循环重新尝试获取连接。
// 此处会根据设置的connectionRequestTimeout设置等待超时时间
boolean success = false;
try {
pool.queue(future);
this.pending.add(future);
if (deadline != null) {
success = this.condition.awaitUntil(deadline);
} else {
this.condition.await();
success = true;
}
if (future.isCancelled()) {
throw new ExecutionException(operationAborted());
}
} finally {
pool.unqueue(future);
this.pending.remove(future);
}
if (!success && (deadline != null && deadline.getTime() <= System.currentTimeMillis())) {
break;
}
}
throw new TimeoutException("Timeout waiting for connection");
} finally {
this.lock.unlock();
}
}
}
阅读更多

slf4j加载实现

一、源码

1、入口

1
2
3
4
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Logger logger = LoggerFactory.getLogger(Demo.class);
阅读更多

HttpClient(一)连接池

一、代码示例

1)引入httpclient依赖

1
2
3
4
5
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
阅读更多

Tomcat类加载器

  1. springboot实现定义了一个符合双亲委派的类TomcatEmbeddedWebappClassLoader extends WebappClassLoader,因为springboot只针对一个应用运行的场景,所以无需打破双亲委派。
  2. 使用IDEA调试Tomcat代码时,在Project Settings - Libraries里把tomcat/lib加进来就可以了。
阅读更多

while(true)和for(;;)分析

由字节码看,两者一致。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class WhileForTest {
public void forFunc() {
for (;;) {
System.out.println("for loop");
}
}

public void whileFunc() {
while (true) {
System.out.println("while loop");
}
}
}
阅读更多