问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Spring Cloud Zuul改造与连接池原理深度解析

创作时间:
作者:
@小白创作中心

Spring Cloud Zuul改造与连接池原理深度解析

引用
CSDN
1.
https://blog.csdn.net/fenglllle/article/details/132761316

最近在项目中需要实现一个代理逻辑,虽然NGINX是最佳选择,但考虑到额外功能的开发需求,我选择了Zuul作为Java开发的网关解决方案。本文将详细介绍如何改造Zuul,使其支持HTTPS转发,并研究其连接池的实现原理。

Zuul改造

Zuul最初是为微服务网关设计的,但要实现TCP或WebSocket转发则需要自定义实现。开源的goproxy是一个高性能的代理方案,但考虑到开发语言的限制,我最终选择了Zuul,因为它具有极强的定制性。

剥离注册中心相关操作

Zuul默认需要注册到注册中心,但我们可以将其剥离为插件,按需加载。查看Zuul Starter源码,发现它默认加载两个配置类:

Cloud模式支持负载均衡和熔断等功能,而域名或Host模式则支持基于域名的负载均衡和限流等。

Zuul源码分析

Zuul通过EnableZuulProxyEnableZuulServer注解来启用不同的功能。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仍然是一个不错的选择,因为它提供了强大的定制能力。


© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号