http client realized keep-alive source inquiry

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.1order to improve HTTP 1.0network performance, increased keepalivefeatures. Then the browser at the time of the request will add Connection: Keep-Aliveheader information, it is how to achieve it?
We know that the server (nginx) can be set keepalive_timeoutto control the timeout time, then httpkeep the connection requires a browser (client) support? Today we come together through java.net.HttpURLConnectionthe source code to see how the client is maintaining these httpconnections.

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()

HttpClientWill 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: Closethat server is also able to prevail.

The client request is complete

When first run time bufferedInputStream.read(buf)when, HttpClientit 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 KeepAliveKeyandClientVector

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+ portto establish, for the same connection. Therefore, the use KeepAliveKeyas KeepAliveCachea key.
ClientVectorIs 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();
    }
    ...
}

ClientVectorWhen 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 httpof multiple connections.

summary

Storage structure as
image.png
multiplexed tcpconnection standard is protocol+ host+ port, the number of connecting clients connected to the server is not too much to maintain, HttpURLConnectionby default can store five different connections, more directly disconnected (see above HttpClient#finishedmethods), too many connections to maintain the client and server will increase of pressure.
While KeepAliveCachealso detecting a scan every 5 seconds, remove expired httpClient.

Guess you like

Origin yq.aliyun.com/articles/733974
Recommended