A few days ago in the sharing of time "to realize their wget" because our request is a one-time, http set in advance Connection: Close
. In HTTP/1.1
order to improve HTTP 1.0
network performance, increased keepalive
features. Then the browser at the time of the request will add Connection: Keep-Alive
header information, it is how to achieve it?
We know that the server (nginx) can be set keepalive_timeout
to control the timeout time, then http
keep the connection requires a browser (client) support? Today we come together through java.net.HttpURLConnection
the source code to see how the client is maintaining these http
connections.
Test code
package net.mengkang.demo;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
public class Demo {
public static void main(String[] args) throws IOException {
test();
test();
}
private static void test() throws IOException {
URL url = new URL("http://static.mengkang.net/upload/image/2019/0921/1569075837628814.jpeg");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Charset", "UTF-8");
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestMethod("GET");
connection.connect();
BufferedInputStream bufferedInputStream = new BufferedInputStream(connection.getInputStream());
File file = new File("./xxx.jpeg");
OutputStream out = new FileOutputStream(file);
int size;
byte[] buf = new byte[1024];
while ((size = bufferedInputStream.read(buf)) != -1) {
out.write(buf, 0, size);
}
connection.disconnect();
}
}
Parsing headers returned
When the client obtains from the server returns the byte stream
connection.getInputStream()
HttpClient
Will return to the header parsing, I simplified the removal of the most important logic code
private boolean parseHTTPHeader(MessageHeader var1, ProgressSource var2, HttpURLConnection var3) throws IOException {
String var15 = var1.findValue("Connection");
...
if (var15 != null && var15.toLowerCase(Locale.US).equals("keep-alive")) {
HeaderParser var11 = new HeaderParser(var1.findValue("Keep-Alive"));
this.keepAliveConnections = var11.findInt("max", this.usingProxy ? 50 : 5);
this.keepAliveTimeout = var11.findInt("timeout", this.usingProxy ? 60 : 5);
}
...
}
The need to maintain a long connection, the client application, the server decided, so to header information returned from the server prevail. For example, a request is sent by the client Connection: Keep-Alive
, the server returns Connection: Close
that server is also able to prevail.
The client request is complete
When first run time bufferedInputStream.read(buf)
when, HttpClient
it executes finished()
the method
public void finished() {
if (!this.reuse) {
--this.keepAliveConnections;
this.poster = null;
if (this.keepAliveConnections > 0 && this.isKeepingAlive() && !this.serverOutput.checkError()) {
this.putInKeepAliveCache();
} else {
this.closeServer();
}
}
}
Http added to the long connection cache
protected static KeepAliveCache kac = new KeepAliveCache();
protected synchronized void putInKeepAliveCache() {
if (this.inCache) {
assert false : "Duplicate put to keep alive cache";
} else {
this.inCache = true;
kac.put(this.url, (Object)null, this);
}
}
public class KeepAliveCache extends HashMap<KeepAliveKey, ClientVector> implements Runnable {
...
public synchronized void put(URL var1, Object var2, HttpClient var3) {
KeepAliveKey var5 = new KeepAliveKey(var1, var2); // var2 null
ClientVector var6 = (ClientVector)super.get(var5);
if (var6 == null) {
int var7 = var3.getKeepAliveTimeout();
var6 = new ClientVector(var7 > 0 ? var7 * 1000 : 5000);
var6.put(var3);
super.put(var5, var6);
} else {
var6.put(var3);
}
}
...
}
Here involved KeepAliveKey
andClientVector
class KeepAliveKey {
private String protocol = null;
private String host = null;
private int port = 0;
private Object obj = null;
}
This object design does, because only protocol
+ host
+ port
to establish, for the same connection. Therefore, the use KeepAliveKey
as KeepAliveCache
a key
. ClientVector
Is a stack, each time there is a request in the same domain have the stack.
class ClientVector extends Stack<KeepAliveEntry> {
private static final long serialVersionUID = -8680532108106489459L;
int nap;
ClientVector(int var1) {
this.nap = var1;
}
synchronized void put(HttpClient var1) {
if (this.size() >= KeepAliveCache.getMaxConnections()) {
var1.closeServer();
} else {
this.push(new KeepAliveEntry(var1, System.currentTimeMillis()));
}
}
...
}
"Disconnect
connection.disconnect();
If the connection is kept long, but the actual number of closed flow, socket does not turn off.
public void disconnect() {
...
boolean var2 = var1.isKeepingAlive();
if (var2) {
var1.closeIdleConnection();
}
...
}
public void closeIdleConnection() {
HttpClient var1 = kac.get(this.url, (Object)null);
if (var1 != null) {
var1.closeServer();
}
}
Connection multiplexing
public static HttpClient New(URL var0, Proxy var1, int var2, boolean var3, HttpURLConnection var4) throws IOException {
...
HttpClient var5 = null;
if (var3) {
var5 = kac.get(var0, (Object)null);
...
}
if (var5 == null) {
var5 = new HttpClient(var0, var1, var2);
} else {
...
var5.url = var0;
}
return var5;
}
public class KeepAliveCache extends HashMap<KeepAliveKey, ClientVector> implements Runnable {
...
public synchronized HttpClient get(URL var1, Object var2) {
KeepAliveKey var3 = new KeepAliveKey(var1, var2);
ClientVector var4 = (ClientVector)super.get(var3);
return var4 == null ? null : var4.get();
}
...
}
ClientVector
When taken out of the stack, the stack process if the connection has timed out, turn off the connection with the server and continue to perform the push operation.
class ClientVector extends Stack<KeepAliveEntry> {
private static final long serialVersionUID = -8680532108106489459L;
int nap;
ClientVector(int var1) {
this.nap = var1;
}
synchronized HttpClient get() {
if (this.empty()) {
return null;
} else {
HttpClient var1 = null;
long var2 = System.currentTimeMillis();
do {
KeepAliveEntry var4 = (KeepAliveEntry)this.pop();
if (var2 - var4.idleStartTime > (long)this.nap) {
var4.hc.closeServer();
} else {
var1 = var4.hc;
}
} while(var1 == null && !this.empty());
return var1;
}
}
...
}
This realization of the client http
of multiple connections.
summary
Storage structure as
multiplexed tcp
connection standard is protocol
+ host
+ port
, the number of connecting clients connected to the server is not too much to maintain, HttpURLConnection
by default can store five different connections, more directly disconnected (see above HttpClient#finished
methods), too many connections to maintain the client and server will increase of pressure.
While KeepAliveCache
also detecting a scan every 5 seconds, remove expired httpClient
.