聊聊spring cloud gateway的NettyConfiguration

3,987 阅读3分钟

本文主要研究下spring cloud gateway的NettyConfiguration

NettyConfiguration

@Configuration
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
@EnableConfigurationProperties
@AutoConfigureBefore(HttpHandlerAutoConfiguration.class)
@AutoConfigureAfter({GatewayLoadBalancerClientAutoConfiguration.class, GatewayClassPathWarningAutoConfiguration.class})
@ConditionalOnClass(DispatcherHandler.class)
public class GatewayAutoConfiguration {

	@Configuration
	@ConditionalOnClass(HttpClient.class)
	protected static class NettyConfiguration {
		@Bean
		@ConditionalOnMissingBean
		public HttpClient httpClient(@Qualifier("nettyClientOptions") Consumer<? super HttpClientOptions.Builder> options) {
			return HttpClient.create(options);
		}

		@Bean
		public Consumer<? super HttpClientOptions.Builder> nettyClientOptions(HttpClientProperties properties) {
			return opts -> {

				// configure ssl
				HttpClientProperties.Ssl ssl = properties.getSsl();

				if (ssl.isUseInsecureTrustManager()) {
					opts.sslSupport(sslContextBuilder -> {
						sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE);
					});
				}

				// configure pool resources
				HttpClientProperties.Pool pool = properties.getPool();

				if (pool.getType() == DISABLED) {
					opts.disablePool();
				} else if (pool.getType() == FIXED) {
					PoolResources poolResources = PoolResources.fixed(pool.getName(),
							pool.getMaxConnections(), pool.getAcquireTimeout());
					opts.poolResources(poolResources);
				} else {
					PoolResources poolResources = PoolResources.elastic(pool.getName());
					opts.poolResources(poolResources);
				}


				// configure proxy if proxy host is set.
				HttpClientProperties.Proxy proxy = properties.getProxy();
				if (StringUtils.hasText(proxy.getHost())) {
					opts.proxy(typeSpec -> {
						ClientProxyOptions.Builder builder = typeSpec
								.type(ClientProxyOptions.Proxy.HTTP)
								.host(proxy.getHost());

						PropertyMapper map = PropertyMapper.get();

						map.from(proxy::getPort)
								.whenNonNull()
								.to(builder::port);
						map.from(proxy::getUsername)
								.whenHasText()
								.to(builder::username);
						map.from(proxy::getPassword)
								.whenHasText()
								.to(password -> builder.password(s -> password));
						map.from(proxy::getNonProxyHostsPattern)
								.whenHasText()
								.to(builder::nonProxyHosts);

						return builder;
					});
				}
			};
		}

		@Bean
		public HttpClientProperties httpClientProperties() {
			return new HttpClientProperties();
		}

		@Bean
		public NettyRoutingFilter routingFilter(HttpClient httpClient,
												ObjectProvider<List<HttpHeadersFilter>> headersFilters) {
			return new NettyRoutingFilter(httpClient, headersFilters);
		}

		@Bean
		public NettyWriteResponseFilter nettyWriteResponseFilter(GatewayProperties properties) {
			return new NettyWriteResponseFilter(properties.getStreamingMediaTypes());
		}

		@Bean
		public ReactorNettyWebSocketClient reactorNettyWebSocketClient(@Qualifier("nettyClientOptions") Consumer<? super HttpClientOptions.Builder> options) {
			return new ReactorNettyWebSocketClient(options);
		}
	}
    //......
}

spring cloud gateway使用的是reactor的httpclient,其通过nettyClientOptions这个bean来进行构造options,具体的配置是HttpClientProperties

HttpClientProperties

配置说明

   {
      "sourceType": "org.springframework.cloud.gateway.config.HttpClientProperties",
      "name": "spring.cloud.gateway.httpclient",
      "type": "org.springframework.cloud.gateway.config.HttpClientProperties"
    },
    {
      "sourceType": "org.springframework.cloud.gateway.config.HttpClientProperties",
      "name": "spring.cloud.gateway.httpclient.pool",
      "sourceMethod": "getPool()",
      "type": "org.springframework.cloud.gateway.config.HttpClientProperties$Pool"
    },
    {
      "sourceType": "org.springframework.cloud.gateway.config.HttpClientProperties",
      "name": "spring.cloud.gateway.httpclient.proxy",
      "sourceMethod": "getProxy()",
      "type": "org.springframework.cloud.gateway.config.HttpClientProperties$Proxy"
    },
    {
      "sourceType": "org.springframework.cloud.gateway.config.HttpClientProperties",
      "name": "spring.cloud.gateway.httpclient.ssl",
      "sourceMethod": "getSsl()",
      "type": "org.springframework.cloud.gateway.config.HttpClientProperties$Ssl"
    }

可以看到主要是pool、proxy、ssl这几个配置

配置类

spring-cloud-gateway-core-2.0.0.RC1-sources.jar!/org/springframework/cloud/gateway/config/HttpClientProperties.java

@ConfigurationProperties("spring.cloud.gateway.httpclient")
public class HttpClientProperties {

	/** Pool configuration for Netty HttpClient */
	private Pool pool = new Pool();

	/** Proxy configuration for Netty HttpClient */
	private Proxy proxy = new Proxy();

	/** SSL configuration for Netty HttpClient */
	private Ssl ssl = new Ssl();

	//......

	@Override
	public String toString() {
		return "HttpClientProperties{" +
				"pool=" + pool +
				", proxy=" + proxy +
				'}';
	}
}

Pool

	public static class Pool {

		public enum PoolType { ELASTIC, FIXED, DISABLED }

		/** Type of pool for HttpClient to use, defaults to ELASTIC. */
		private PoolType type = PoolType.ELASTIC;

		/** The channel pool map name, defaults to proxy. */
		private String name = "proxy";

		/** Only for type FIXED, the maximum number of connections before starting pending acquisition on existing ones. */
		private Integer maxConnections = PoolResources.DEFAULT_POOL_MAX_CONNECTION;

		/** Only for type FIXED, the maximum time in millis to wait for aquiring. */
		private Long acquireTimeout = PoolResources.DEFAULT_POOL_ACQUIRE_TIMEOUT;

		//......

		@Override
		public String toString() {
			return "Pool{" +
					"type=" + type +
					", name='" + name + '\'' +
					", maxConnections=" + maxConnections +
					", acquireTimeout=" + acquireTimeout +
					'}';
		}
	}

一共可以指定如下几个属性

  • spring.cloud.gateway.httpclient.pool.type,默认是ELASTIC
  • spring.cloud.gateway.httpclient.pool.name,默认是proxy

如果type是fixed类型,还可以指定如下两个参数

  • spring.cloud.gateway.httpclient.pool.maxConnections,默认是PoolResources.DEFAULT_POOL_MAX_CONNECTION
	/**
	 * Default max connection, if -1 will never wait to acquire before opening new
	 * connection in an unbounded fashion. Fallback to
	 * available number of processors.
	 */
	int DEFAULT_POOL_MAX_CONNECTION =
			Integer.parseInt(System.getProperty("reactor.ipc.netty.pool.maxConnections",
			"" + Math.max(Runtime.getRuntime()
			            .availableProcessors(), 8) * 2));
  • spring.cloud.gateway.httpclient.pool.acquireTimeout,默认是PoolResources.DEFAULT_POOL_ACQUIRE_TIMEOUT
	/**
	 * Default acquisition timeout before error. If -1 will never wait to
	 * acquire before opening new
	 * connection in an unbounded fashion. Fallback to
	 * available number of processors.
	 */
	long DEFAULT_POOL_ACQUIRE_TIMEOUT = Long.parseLong(System.getProperty(
			"reactor.ipc.netty.pool.acquireTimeout",
			"" + 45000));

Proxy

	public class Proxy {
		/** Hostname for proxy configuration of Netty HttpClient. */
		private String host;
		/** Port for proxy configuration of Netty HttpClient. */
		private Integer port;
		/** Username for proxy configuration of Netty HttpClient. */
		private String username;
		/** Password for proxy configuration of Netty HttpClient. */
		private String password;
		/** Regular expression (Java) for a configured list of hosts
		 * that should be reached directly, bypassing the proxy */
		private String nonProxyHostsPattern;

		//......

		@Override
		public String toString() {
			return "Proxy{" +
					"host='" + host + '\'' +
					", port=" + port +
					", username='" + username + '\'' +
					", password='" + password + '\'' +
					", nonProxyHostsPattern='" + nonProxyHostsPattern + '\'' +
					'}';
		}
	}

可以配置如下几个参数

  • spring.cloud.gateway.httpclient.proxy.host
  • spring.cloud.gateway.httpclient.proxy.port
  • spring.cloud.gateway.httpclient.proxy.username
  • spring.cloud.gateway.httpclient.proxy.password
  • spring.cloud.gateway.httpclient.proxy.nonProxyHostsPattern

Ssl

	public class Ssl {
		/** Installs the netty InsecureTrustManagerFactory. This is insecure and not suitable for production. */
		private boolean useInsecureTrustManager = false;

		//TODO: support configuration of other trust manager factories

		public boolean isUseInsecureTrustManager() {
			return useInsecureTrustManager;
		}

		public void setUseInsecureTrustManager(boolean useInsecureTrustManager) {
			this.useInsecureTrustManager = useInsecureTrustManager;
		}

		@Override
		public String toString() {
			return "Ssl{" +
					"useInsecureTrustManager=" + useInsecureTrustManager +
					'}';
		}
	}

主要是配置spring.cloud.gateway.httpclient.ssl.use-insecure-trust-manager属性,设置为true的话,则会使用InsecureTrustManagerFactory.INSTANCE

netty-handler-4.1.23.Final-sources.jar!/io/netty/handler/ssl/util/InsecureTrustManagerFactory.java

/**
 * An insecure {@link TrustManagerFactory} that trusts all X.509 certificates without any verification.
 * <p>
 * <strong>NOTE:</strong>
 * Never use this {@link TrustManagerFactory} in production.
 * It is purely for testing purposes, and thus it is very insecure.
 * </p>
 */
public final class InsecureTrustManagerFactory extends SimpleTrustManagerFactory {

    private static final InternalLogger logger = InternalLoggerFactory.getInstance(InsecureTrustManagerFactory.class);

    public static final TrustManagerFactory INSTANCE = new InsecureTrustManagerFactory();

    private static final TrustManager tm = new X509TrustManager() {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String s) {
            logger.debug("Accepting a client certificate: " + chain[0].getSubjectDN());
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String s) {
            logger.debug("Accepting a server certificate: " + chain[0].getSubjectDN());
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return EmptyArrays.EMPTY_X509_CERTIFICATES;
        }
    };

    private InsecureTrustManagerFactory() { }

    @Override
    protected void engineInit(KeyStore keyStore) throws Exception { }

    @Override
    protected void engineInit(ManagerFactoryParameters managerFactoryParameters) throws Exception { }

    @Override
    protected TrustManager[] engineGetTrustManagers() {
        return new TrustManager[] { tm };
    }
}

小结

spring cloud gateway底层使用的是reactor的httpclient,可以通过spring.cloud.gateway.httpclient前缀的配置来指定相关options。主要分pool、proxy、ssl三大类。其中pool默认的type是elastic,如果是fixed的话,还可以指定maxConnections及acquireTimeout参数。

doc