HTTP keep alive的一种实现

By | 2022年4月26日

最近项目需要实现HTTP的长连接,由于项目的web服务是由tomcat搭建的,所以在server端侧实现十分简单,在server.xml文件中追加一下配置即可:

<Connector port="443" 
        ...略...
        keepAliveTimeout="300000"
        maxKeepAliveRequests="100"
        ...略.../>

keepAliveTimeout:服务端对长连接的保持时间
maxKeepAliveRequests:每个长连接能发送请求的最大数,超过就需要重新建立连接。

而我们作为客户端发起请求的时候就不是特别顺利,因为一开始客户端是用HttpsURLConnection类实现的,网上查了一番资料,没有发现实现http长连接的方法。于是改为使用httpClient,apache的一个开源项目,使用中发现这货功能十分强大。
以下是客户端的实现(例):

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.Map;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import org.apache.http.HttpRequest;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.pool.PoolStats;

public class ConnectionManager {

    // get connect from connection pool time out
    private static int connectionRequestTimeout = 10000;
    // connect time out
    private static int connectionTimeout = 10000;
    // read time out
    private static int socketTimeout = 10000;

    private static CloseableHttpClient httpClient = null;
    private static PoolingHttpClientConnectionManager connectionManager;
    private static SSLConnectionSocketFactory ssf;
    private static RequestConfig defaultRequestConfig;

    private static X509TrustManager tm = new X509TrustManager() {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    };

    public static void init() {
        enableSSL(false);
        defaultRequestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(connectionRequestTimeout)
                .setConnectTimeout(connectionTimeout)
                .setSocketTimeout(socketTimeout).build();

        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
                .<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.INSTANCE)
                .register("https", ssf).build();

        connectionManager = new PoolingHttpClientConnectionManager(
                socketFactoryRegistry);

        // default : 每个路由基础上的连接不超过2个,总连接数不能超过20
        // 将最大连接数 50
        connectionManager.setMaxTotal(50);
        // 每个路由基础的连接 25
        connectionManager.setDefaultMaxPerRoute(25);

        httpClient = HttpClients.custom()
                .setConnectionManager(connectionManager)
                .setDefaultRequestConfig(defaultRequestConfig).build();
    }

    public static PoolingHttpClientConnectionManager getConnectionManager() {
        return connectionManager;
    }

    private static void checkClient() {
        if (httpClient == null) {
            httpClient = HttpClients.custom()
                    .setConnectionManager(connectionManager)
                    .setDefaultRequestConfig(defaultRequestConfig).build();
        }
    }

    private static void enableSSL(boolean trustCerts) {
        if (trustCerts) {
            // do not need to Verify certificate
            try {
                SSLContext ctx = SSLContext.getInstance("TLS");
                ctx.init(null, new TrustManager[]{tm}, null);
                ssf = new SSLConnectionSocketFactory(ctx,
                        NoopHostnameVerifier.INSTANCE);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (KeyManagementException e) {
                e.printStackTrace();
            }
        } else {
            // need to Verify certificate
            KeyStore ts = null;
            TrustManagerFactory tmf = null;
            try {
                ts = KeyStore.getInstance(("JKS"));
                ts.load(new FileInputStream(new File("truststore.jks")),
                        "aTte3yZ*8osb".toCharArray());
                tmf = TrustManagerFactory.getInstance("SunX509");
                tmf.init(ts);
                SSLContext ctx = SSLContext.getInstance("TLS");
                ctx.init(null, tmf.getTrustManagers(), null);
                ssf = new SSLConnectionSocketFactory(ctx,
                        NoopHostnameVerifier.INSTANCE);
            } catch (KeyStoreException e) {
                e.printStackTrace();
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (CertificateException e) {
                e.printStackTrace();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (KeyManagementException e) {
                e.printStackTrace();
            }
        }
    }

    public static CloseableHttpResponse doHttpsGet(String url,
            Map<String, String> headers) throws IOException {
        checkClient();
        HttpRequest httpGet = new HttpGet(url);
        if (headers != null && !headers.isEmpty()) {
            httpGet = setHeaders(headers, httpGet);
        }
        CloseableHttpResponse response = httpClient.execute((HttpGet) httpGet);
        return response;
    }

    public static CloseableHttpResponse doHttpsPost(String url,
            Map<String, String> headers, String data) throws IOException {
        checkClient();
        HttpRequest httpRequest = new HttpPost(url);
        if (headers != null && !headers.isEmpty()) {
            httpRequest = setHeaders(headers, httpRequest);
        }
        CloseableHttpResponse response = null;
        try {
            HttpPost httpPost = (HttpPost) httpRequest;
            httpPost.setEntity(new StringEntity(data,
                    ContentType.create("application/json", "UTF-8")));
            response = httpClient.execute(httpPost);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return response;
    }

    private static HttpRequest setHeaders(Map<String, String> headers,
            HttpRequest request) {
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            request.addHeader((String) entry.getKey(),
                    (String) entry.getValue());
        }
        return request;
    }

    public static void getConnectioNum() {
        PoolStats stats = connectionManager.getTotalStats();
        System.out.println(new Date() + " - " + stats.toString());
    }
}
// end
Category: 未分类 标签:

发表评论

您的电子邮箱地址不会被公开。