模拟一个http server, 且能动态展示当前连接数
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", abc)
server := &http.Server{
Addr: ":8000",
Handler: mux,
ConnState: connStateHandler,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
go func() {
for {
fmt.Println("当前活跃的连接数:", atomic.LoadInt32(&activeConnections))
time.Sleep(1 * time.Second)
}
}()
server.ListenAndServe()
}
func connStateHandler(conn net.Conn, state http.ConnState) {
switch state {
case http.StateNew:
atomic.AddInt32(&activeConnections, 1)
case http.StateActive:
case http.StateIdle:
case http.StateClosed:
atomic.AddInt32(&activeConnections, -1)
}
}
func abc(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html;charset=utf-8")
w.Write([]byte("为了新中国,前进!"))
}
1 使用webclient,查看Connection不同取值的耗时
System.setProperty("reactor.netty.ioWorkerCount", "8");
WebClient client = webClient();
int n = 10000;
CountDownLatch latch = new CountDownLatch(n);
long start = System.nanoTime();
for (int i = 0; i < n; i++) {
client.get()
.uri("/api/classmates")
.header("Connection", *sample*)
.retrieve()
.bodyToFlux(String.class)
.subscribe(v -> {
latch.countDown();
}, v -> {
System.out.println(v);
latch.countDown();
});
latch.countDown();
}
latch.await();
System.err.println((System.nanoTime() - start) / 1000000);
public static WebClient webClient() {
ConnectionProvider provider = ConnectionProvider.builder("pool")
.maxConnections(50) // 自定义最大连接数
.pendingAcquireMaxCount(-1) // 自定义挂起获取的最大数量
.pendingAcquireTimeout(Duration.ofSeconds(100)) // 自定义挂起获取的超时时间
.build();
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(HttpClient.create(provider)))
.baseUrl("http://127.0.0.1:8000")
.build();
}
运行10000个请求,总耗时结果如下
| close | keep-alive |
|---|---|
| 1462ms | 4435ms |
2 使用java.net.http.HttpClient,对比WebClient
System.setProperty("jdk.httpclient.connectionPoolSize", "50");
HttpClient client = HttpClient.newHttpClient();
int n = 1000;
CountDownLatch latch = new CountDownLatch(n);
long start = System.nanoTime();
for (int i = 0; i < n; i++) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://127.0.0.1:8000"))
.GET()
.build();
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenAccept(v -> latch.countDown()).exceptionally( v -> {
System.out.println(v);
latch.countDown();
return null;
});
}
latch.await();
System.err.println((System.nanoTime() - start) / 1000000);
总耗时结果如下
| HttpClient | WebClient |
|---|---|
| 764ms | 432ms |
- 在可用率上, HttpClient明显表现比较差, 有比较多的java.util.concurrent.CompletionException: java.net.ConnectException。相比WebClient是要差很多。经过分析, 找到了一部分原因, 目前设置System.setProperty("jdk.httpclient.connectionPoolSize", "5");起初认为不生效,进一步研究后, 发现设计上有所不同。
它只是returnToPool的时候, 会用到connectionPoolSize这个参数的判断。在执行过程中, 如果没有可用的connection, 它会并发的创新大量的连接,导致建连失败。从下图观测可得知
- 参数的可控性上, HttpClient一些header是不能手动设置的,由内部产生。 包括它解析协议的I/O线程数, 也无法设置