Spring Cloud Zuul改造与连接池原理深度解析
Spring Cloud Zuul改造与连接池原理深度解析
最近在项目中需要实现一个代理逻辑,虽然NGINX是最佳选择,但考虑到额外功能的开发需求,我选择了Zuul作为Java开发的网关解决方案。本文将详细介绍如何改造Zuul,使其支持HTTPS转发,并研究其连接池的实现原理。
Zuul改造
Zuul最初是为微服务网关设计的,但要实现TCP或WebSocket转发则需要自定义实现。开源的goproxy是一个高性能的代理方案,但考虑到开发语言的限制,我最终选择了Zuul,因为它具有极强的定制性。
剥离注册中心相关操作
Zuul默认需要注册到注册中心,但我们可以将其剥离为插件,按需加载。查看Zuul Starter源码,发现它默认加载两个配置类:
Cloud模式支持负载均衡和熔断等功能,而域名或Host模式则支持基于域名的负载均衡和限流等。
Zuul源码分析
Zuul通过EnableZuulProxy
和EnableZuulServer
注解来启用不同的功能。EnableZuulProxy
的能力更强,因为它包含了熔断器相关的配置,但这些配置已经过时,需要自己实现限流熔断功能。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ZuulServerMarkerConfiguration.class)
public @interface EnableZuulServer {
}
@EnableCircuitBreaker
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ZuulProxyMarkerConfiguration.class)
public @interface EnableZuulProxy {
}
HTTPS转发逻辑
剥离注册中心相关操作后,我们可以实现HTTP到HTTPS的转发。以下是一个简单的配置示例:
@SpringBootApplication
//@EnableDiscoveryClient
@EnableZuulProxy
public class ZuulMain {
public static void main(String[] args) {
SpringApplication.run(ZuulMain.class, args);
}
}
配置转发规则:
zuul:
routes:
rule1:
path: /demo/**
url: https://blog.csdn.net/
Zuul通过过滤器链处理请求,关键的路径匹配逻辑在PreDecorationFilter
中实现:
请求经过一系列过滤器处理后,最终通过HttpClient发送出去:
连接池研究
我使用的是Apache HttpClient 4.5版本,研究其连接池的实现原理。
连接池初始化
在SimpleHostRoutingFilter
中初始化连接池:
@PostConstruct
private void initialize() {
if (!customHttpClient) {
this.connectionManager = newConnectionManager();
this.httpClient = newClient();
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
public void run() {
if (SimpleHostRoutingFilter.this.connectionManager == null) {
return;
}
SimpleHostRoutingFilter.this.connectionManager
.closeExpiredConnections();
}
}, 30000, 5000);
}
}
创建连接池
连接池的创建过程封装在newConnectionManager
方法中:
protected HttpClientConnectionManager newConnectionManager() {
return connectionManagerFactory.newConnectionManager(
!this.sslHostnameValidationEnabled,
this.hostProperties.getMaxTotalConnections(),
this.hostProperties.getMaxPerRouteConnections(),
this.hostProperties.getTimeToLive(), this.hostProperties.getTimeUnit(),
null);
}
HttpClient的创建
HttpClient的创建过程如下:
protected CloseableHttpClient newClient() {
final RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(
this.hostProperties.getConnectionRequestTimeoutMillis())
.setSocketTimeout(this.hostProperties.getSocketTimeoutMillis())
.setConnectTimeout(this.hostProperties.getConnectTimeoutMillis())
.setCookieSpec(CookieSpecs.IGNORE_COOKIES).build();
return httpClientFactory.createBuilder().setDefaultRequestConfig(requestConfig)
.setConnectionManager(this.connectionManager).disableRedirectHandling()
.build();
}
获取连接与发送请求
发送请求时需要封装HTTP方法,由于JDK8自带的URLConnection不支持PATCH方法,因此需要使用HttpClient或OkHttp。
关闭连接
异常情况下会关闭连接,正常情况下在读取响应流结束时也会关闭连接。Zuul通过EofSensorWatcher
机制确保连接及时释放:
"http-nio-8766-exec-4@8603" daemon prio=5 tid=0x1c nid=NA runnable
java.lang.Thread.State: RUNNABLE
at org.apache.http.impl.execchain.ResponseEntityProxy.releaseConnection(ResponseEntityProxy.java:76)
at org.apache.http.impl.execchain.ResponseEntityProxy.eofDetected(ResponseEntityProxy.java:121)
at org.apache.http.conn.EofSensorInputStream.checkEOF(EofSensorInputStream.java:199)
at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:136)
at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:148)
at org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter.writeResponse(SendResponseFilter.java:259)
at org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter.writeResponse(SendResponseFilter.java:162)
at org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter.run(SendResponseFilter.java:112)
at com.netflix.zuul.ZuulFilter.runFilter(ZuulFilter.java:117)
总结
对于技术实现而言,无论是使用Zuul(基于Servlet)还是Gateway(基于Netty),或者NGINX,本质处理逻辑都是类似的。关键在于如何实现定制化需求。对于简单的应用场景,Zuul仍然是一个不错的选择,因为它提供了强大的定制能力。