10.1 需要进行HTTP连接复用的高并发场景
10.1.1 反向代理Nginx与Java Web应用服务之间的HTTP高并发通信
10.1.2 微服务网关与微服务Provider实例之间的HTTP高并发通信
10.1.3 分布式微服务Provider实例之间的RPC的HTTP高并发通信
10.1.4 Java通过HTTP客户端访问REST接口服务的HTTP高并发通信
10.2 详解传输层TCP
10.2.1 TCP/IP的分层模型
10.2.2 HTTP报文传输原理
10.2.4 TCP的三次握手
一段简单的服务端监听新连接请求,并且被动打开(Passive Open)传输套接字的Java示例代码,具体如下:
public class SocketServer {public static void main(String[] args) {try {//创建服务端socketServerSocket serverSocket = new ServerSocket(8080);//循环监听等待客户端的连接
一段简单的客户端连接主动打开的Java示例代码如下:
public class SocketClient {public static void main(String[] args) throws InterruptedException {try {//和服务器创建连接Socket socket = new Socket("localhost",8080);//写入给监听方的输出流OutputStream os = socket.getOutputStream();…//读取监听方的输入流InputStream is = socket.getInputStream();…} catch (Exception e) {e.printStackTrace();}}}
10.2.5 TCP的四次挥手
10.2.6 三次握手、四次挥手的常见面试题
10.3 TCP连接状态的原理与实验
10.3.2 通过netstat指令查看连接状态
10.4 HTTP长连接原理
HTTP长连接和HTTP短连接,指的是传输层的TCP连接是否被多次使用。
10.4.1 HTTP长连接和短连接
10.4.2 不同HTTP版本中的长连接选项
10.5 服务端HTTP长连接技术
10.5.1 应用服务器Tomcat的长连接配置
独立部署Tomcat的长连接配置
一个使用HTTP长连接的Connector连接器的配置示例大致如下(Tomcat版本假定8.0或以上): ```java
2. 内嵌式部署Tomcat的长连接配置<br />一段简单的定制化TomcatServletWebServerFactory容器工厂的配置代码大致如下:```javapackage com.crazymaker.springcloud.standard.config;//省略import@Configuration@ConditionalOnClass({Connector.class})public class TomcatConfig{@Autowiredprivate HttpConnectionProperties httpConnectionProperties;@Beanpublic TomcatServletWebServerFactorycreateEmbeddedServletContainerFactory(){TomcatServletWebServerFactory tomcatFactory =new TomcatServletWebServerFactory();//增加连接器的定制配置tomcatFactory.addConnectorCustomizers(connector ->{Http11NioProtocol protocol =(Http11NioProtocol) connector.getProtocolHandler();//定制keepAliveTimeout,确定下次请求过来之前Socket连接保持多久//设置600秒内没有请求则服务端自动断开Socket连接protocol.setKeepAliveTimeout(600000);//当客户端发送的请求超过10000个时强制关闭Socket连接protocol.setMaxKeepAliveRequests(1000);//设置最大连接数protocol.setMaxConnections(3000);//省略其他配置});return tomcatFactory;}}
10.5.2 Nginx承担服务端角色时的长连接配置
一段简单的Nginx承担服务端角色时的长连接配置代码如下:
#…http {include mime.types;default_type application/octet-stream;#长连接保持时长keepalive_timeout 65s;#长连接最大处理请求数keepalive_requests 1000;#…server {listen 80;server_name openresty localhost;
10.5.3 服务端长连接设置的注意事项
- 单个客户端的HTTP请求数较少时
2. 单个客户端的请求数较多时10.6 客户端HTTP长连接技术原理与实验
10.6.1 HttpURLConnection短连接技术
```java
package com.crazymakercircle.util;
//省略import
//HTTP 客户端处理帮助类 @Slf4j public class HttpClientHelper { /**
* 使用JDK的 java.net.HttpURLConnection 发起HTTP请求*/public static String jdkGet(String url){InputStream inputStream = null; //输入流HttpURLConnection httpConnection = null; //HTTP连接实例StringBuilder builder = new StringBuilder();try{URL restServiceURL = new URL(url);//打开HttpURLConnection连接实例httpConnection =(HttpURLConnection) restServiceURL.openConnection();//设置请求头httpConnection.setRequestMethod("GET");httpConnection.setRequestProperty("Accept", "application/json");//建立连接,发送请求httpConnection.connect();//读取响应码if (httpConnection.getResponseCode() != 200){throw new RuntimeException("Failed with Error code : "+ httpConnection.getResponseCode());}//读取响应内容(字节流)inputStream = httpConnection.getInputStream();byte[] b = new byte[1024];int length = -1;while ((length = inputStream.read(b)) != -1){builder.append(new String(b, 0, length));}} catch (MalformedURLException e){e.printStackTrace();} catch (IOException e){e.printStackTrace();} finally{//关闭流和连接quietlyClose(inputStream);httpConnection.disconnect();}return builder.toString();}//…
}
<a name="lgFEs"></a>## 10.6.2 HTTP短连接的通信实验<a name="xImQT"></a>## 10.6.3 Apache HttpClient客户端的HTTP长连接技术```javapackage com.crazymakercircle.util;//省略import//HTTP 协议处理帮助类@Slf4jpublic class HttpClientHelper{//长连接的保持时长,单位为ms(毫秒)private static final long KEEP_ALIVE_DURATION = 600000;//客户端和服务器建立连接的超时时长,单位msprivate static final int CONNECT_TIMEOUT = 2000;//建立连接后,客户端从服务器读取数据的超时时长,单位msprivate static final int SOCKET_TIMEOUT = 2000;//从连接池获取连接的超时时长,单位msprivate static final int REQUEST_TIMEOUT = 2000;//无效长连接的清理间隔,单位msprivate static final int EXPIRED_CHECK_GAP = 6000;//连接池内对不活跃连接的检查间隔,单位msprivate static final int VALIDATE_AFTER_INACTIVITY = 2000;//最大的连接数private static final int POOL_MAXTOTAL = 500;//每一个路由(可以理解为IP+端口)的最大连接数private static final int MAX_PER_ROUTE = 500;//单例:HTTP长连接管理器,也就是连接池private static PoolingHttpClientConnectionManagerhttpClientConnectionManager;//单例:全局的池化HTTP客户端实例private static CloseableHttpClient pooledHttpClient;//线程池:负责HTTP连接池的无效连接清理private static ScheduledExecutorService monitorExecutor = null;//创建全局连接池:HTTP连接管理器public static void createHttpClientConnectionManager(){//DNS解析器DnsResolver dnsResolver = SystemDefaultDnsResolver.INSTANCE;//负责HTTP传输的套接字工厂ConnectionSocketFactory plainSocketFactory =PlainConnectionSocketFactory.getSocketFactory();//负责HTTPS传输的安全套接字工厂LayeredConnectionSocketFactory sslSocketFactory =SSLConnectionSocketFactory.getSocketFactory();//根据应用层协议,为其注册传输层的套接字工厂Registry<ConnectionSocketFactory> registry =RegistryBuilder.<ConnectionSocketFactory>create().register("http", plainSocketFactory).register("https", sslSocketFactory).build();//创建连接管理器httpClientConnectionManager =new PoolingHttpClientConnectionManager(registry, //传输层套接字注册器null,null,dnsResolver, //DNS解析器KEEP_ALIVE_DURATION, //长连接的连接保持时长TimeUnit.MILLISECONDS); //保持时长的时间单位//连接池内,连接不活跃多长时间后,需要进行一次验证//默认为2秒 TimeUnit.MILLISECONDShttpClientConnectionManager.setValidateAfterInactivity(VALIDATE_AFTER_INACTIVITY);//最大连接数,高于这个值时的新连接请求,需要阻塞和排队等待httpClientConnectionManager.setMaxTotal(POOL_MAXTOTAL);//设置每个route默认的最大连接数,路由是对MaxTotal的细分//每个路由实际最大连接数默认值是由DefaultMaxPerRoute控制的//MaxPerRoute设置过小,无法支持大并发httpClientConnectionManager.setDefaultMaxPerRoute(MAX_PER_ROUTE);}//省略其他方法}
客户端关闭异常连接的定时执行代码,大致如下:
package com.crazymakercircle.util;//省略import//HTTP 协议处理帮助类@Slf4jpublic class HttpClientHelper{//省略其他方法/*** 定时处理线程:对异常连接进行关闭*/private static void startExpiredConnectionsMonitor(){//空闲监测,配置文件默认为6秒,生产环境建议稍微放大一点int idleCheckGap = IDLE_CHECK_GAP;//设置保持连接的时长,根据实际情况调整配置long keepAliveTimeout = KEEP_ALIVE_DURATION;//开启监控线程,关闭异常和空闲线程monitorExecutor = Executors.newScheduledThreadPool(1);monitorExecutor.scheduleAtFixedRate(new TimerTask(){@Overridepublic void run(){//关闭异常连接,包括被服务端关闭的长连接httpClientConnectionManager.closeExpiredConnections();//关闭keepAliveTimeout(保持连接时长)超时的不活跃连接httpClientConnectionManager.closeIdleConnections(keepAliveTimeout, TimeUnit.MILLISECONDS);//获取连接池的状态PoolStats status =httpClientConnectionManager.getTotalStats();//输出连接池的状态,仅供测试使用/*log.info(" manager.getRoutes().size():" +manager.getRoutes().size());log.info(" status.getAvailable():" + status.getAvailable());log.info(" status.getPending():" + status.getPending());log.info(" status.getLeased():" + status.getLeased());log.info(" status.getMax():" + status.getMax());*/}}, idleCheckGap, idleCheckGap, TimeUnit.MILLISECONDS);}}
下面是一段创建带连接池的全局客户端实例pooledHttpClient的代码,大致如下:
package com.crazymakercircle.util;//省略import//HTTP 协议处理帮助类@Slf4jpublic class HttpClientHelper{//省略其他方法/*** 创建带连接池的 pooledHttpClient 全局客户端实例*/public static CloseableHttpClient pooledHttpClient(){if (null != pooledHttpClient){return pooledHttpClient;}createHttpClientConnectionManager();log.info(" Apache httpclient 初始化HTTP连接池 starting===");//请求配置实例RequestConfig.Builder requestConfigBuilder =RequestConfig.custom();//读取数据的超时设置requestConfigBuilder.setSocketTimeout(SOCKET_TIMEOUT);//建立连接的超时设置requestConfigBuilder.setConnectTimeout(CONNECT_TIMEOUT);//从连接池获取连接的等待超时时间设置requestConfigBuilder.setConnectionRequestTimeout(REQUEST_TIMEOUT);RequestConfig config = requestConfigBuilder.build();//httpclient建造者实例HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();//设置连接池管理器httpClientBuilder.setConnectionManager(httpClientConnectionManager);//设置HTTP请求配置信息httpClientBuilder.setDefaultRequestConfig(config);//httpclient默认提供了一个Keep-Alive策略//这里进行定制:确保客户端与服务端在长连接的保持时长一致httpClientBuilder.setKeepAliveStrategy(new ConnectionKeepAliveStrategy(){@Overridepublic long getKeepAliveDuration(HttpResponse response, HttpContext context){//获取响应头中HTTP.CONN_KEEP_ALIVE中的Keep-Alive部分值//如果服务端响应"Keep-Alive: timeout=60",表示保持时长为60秒//则客户端也设置连接的保持时长为60秒//目的:确保客户端与服务端在长连接的保持时长一致HeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE));while (it.hasNext()){HeaderElement he = it.nextElement();String param = he.getName();String value = he.getValue();if (value != null && param.equalsIgnoreCase("timeout")){try{return Long.parseLong(value) * 1000;} catch (final NumberFormatException ignore){}}}//如果服务端响应头中没有设置保持时长,则使用客户端统一定义时长为600秒return KEEP_ALIVE_DURATION;}});//实例化:全局的池化HTTP客户端实例pooledHttpClient = httpClientBuilder.build();log.info(" Apache httpclient 初始化HTTP连接池 finished===");//启动定时处理线程:对异常和空闲连接进行关闭startExpiredConnectionsMonitor();return pooledHttpClient;}}
10.6.4 Apache HttpClient客户端长连接实验
10.6.5 Nginx承担客户端角色时的长连接技术
keepalive指令的使用示例大致如下:
upstream memcached_backend {server 127.0.0.1:11211;server 10.0.0.2:11211;//可以理解为连接池可以缓存32个连接keepalive 32;}
综合以上两点,在Nginx上负责下游HTTP请求路由和转发的location配置区块中,需要使用proxy_http_version指令和proxy_set_header指令完成HTTP请求头的配置优化,具体代码如下:
server {listen 8080 default_server;server_name "";//…//处理下游客户端请求转发的location配置区块location / {proxy_pass http://memcached_backend;//转发之前,进行请求头重置,重置HTTP协议的版本为1.1proxy_http_version 1.1;


