本文为博主原创,转载请注明出处:

  引用 的 spring cloud gateway 的版本为 2.2.5 ;

  SpringCloud gateway 在实现服务路由并请求的具体过程是在 org.springframework.cloud.gateway.filter.NettyRoutingFilter 的过滤器中,该过滤器封装了具体的请求参数,以及根据路由规则请求的对应服务,然后根据 HttpClient 进行微服务之间的请求; 该 httpClient 类是 用netty 封装的 客户端,其包路径为 : reactor.netty.http.client.HttpClient ;

  查看 NettyRoutingFilter 中的 filter 实现过程:

    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI requestUrl = (URI)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
String scheme = requestUrl.getScheme();
if (!ServerWebExchangeUtils.isAlreadyRouted(exchange) && ("http".equals(scheme) || "https".equals(scheme))) {
ServerWebExchangeUtils.setAlreadyRouted(exchange);
ServerHttpRequest request = exchange.getRequest();
HttpMethod method = HttpMethod.valueOf(request.getMethodValue());
String url = requestUrl.toASCIIString();
HttpHeaders filtered = HttpHeadersFilter.filterRequest(this.getHeadersFilters(), exchange);
DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders();
filtered.forEach(httpHeaders::set);
boolean preserveHost = (Boolean)exchange.getAttributeOrDefault(ServerWebExchangeUtils.PRESERVE_HOST_HEADER_ATTRIBUTE, false);
Route route = (Route)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
Flux<HttpClientResponse> responseFlux = ((RequestSender)this.getHttpClient(route, exchange).headers((headers) -> {
headers.add(httpHeaders);
headers.remove("Host");
if (preserveHost) {
String host = request.getHeaders().getFirst("Host");
headers.add("Host", host);
} }).request(method).uri(url)).send((req, nettyOutbound) -> {
if (log.isTraceEnabled()) {
nettyOutbound.withConnection((connection) -> {
log.trace("outbound route: " + connection.channel().id().asShortText() + ", inbound: " + exchange.getLogPrefix());
});
} return nettyOutbound.send(request.getBody().map(this::getByteBuf));
}).responseConnection((res, connection) -> {
exchange.getAttributes().put(ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR, res);
exchange.getAttributes().put(ServerWebExchangeUtils.CLIENT_RESPONSE_CONN_ATTR, connection);
ServerHttpResponse response = exchange.getResponse();
HttpHeaders headers = new HttpHeaders();
res.responseHeaders().forEach((entry) -> {
headers.add((String)entry.getKey(), (String)entry.getValue());
});
String contentTypeValue = headers.getFirst("Content-Type");
if (StringUtils.hasLength(contentTypeValue)) {
exchange.getAttributes().put("original_response_content_type", contentTypeValue);
} this.setResponseStatus(res, response);
HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(this.getHeadersFilters(), headers, exchange, Type.RESPONSE);
if (!filteredResponseHeaders.containsKey("Transfer-Encoding") && filteredResponseHeaders.containsKey("Content-Length")) {
response.getHeaders().remove("Transfer-Encoding");
} exchange.getAttributes().put(ServerWebExchangeUtils.CLIENT_RESPONSE_HEADER_NAMES, filteredResponseHeaders.keySet());
response.getHeaders().putAll(filteredResponseHeaders);
return Mono.just(res);
});
Duration responseTimeout = this.getResponseTimeout(route);
if (responseTimeout != null) {
responseFlux = responseFlux.timeout(responseTimeout, Mono.error(new TimeoutException("Response took longer than timeout: " + responseTimeout))).onErrorMap(TimeoutException.class, (th) -> {
return new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, th.getMessage(), th);
});
} return responseFlux.then(chain.filter(exchange));
} else {
return chain.filter(exchange);
}
}

  

  该方法中 有一个 getHttpClient 方法获取 httpClient 客户端实例的过程,由于在  GatewayAutoConfiguration 中 定义了 springCloud gateway 使用的 httpclient 实例,其声明并自动加载的代码如下:

@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({HttpClient.class})
protected static class NettyConfiguration {
protected final Log logger = LogFactory.getLog(this.getClass()); protected NettyConfiguration() {
} @Bean
@ConditionalOnProperty(
name = {"spring.cloud.gateway.httpserver.wiretap"}
)
public NettyWebServerFactoryCustomizer nettyServerWiretapCustomizer(Environment environment, ServerProperties serverProperties) {
return new NettyWebServerFactoryCustomizer(environment, serverProperties) {
public void customize(NettyReactiveWebServerFactory factory) {
factory.addServerCustomizers(new NettyServerCustomizer[]{(httpServer) -> {
return httpServer.wiretap(true);
}});
super.customize(factory);
}
};
} @Bean
@ConditionalOnMissingBean
public HttpClient gatewayHttpClient(HttpClientProperties properties, List<HttpClientCustomizer> customizers) {
Pool pool = properties.getPool();
ConnectionProvider connectionProvider;
if (pool.getType() == PoolType.DISABLED) {
connectionProvider = ConnectionProvider.newConnection();
} else if (pool.getType() == PoolType.FIXED) {
connectionProvider = ConnectionProvider.fixed(pool.getName(), pool.getMaxConnections(), pool.getAcquireTimeout(), pool.getMaxIdleTime(), pool.getMaxLifeTime());
} else {
connectionProvider = ConnectionProvider.elastic(pool.getName(), pool.getMaxIdleTime(), pool.getMaxLifeTime());
} HttpClient httpClient = HttpClient.create(connectionProvider).httpResponseDecoder((spec) -> {
if (properties.getMaxHeaderSize() != null) {
spec.maxHeaderSize((int)properties.getMaxHeaderSize().toBytes());
} if (properties.getMaxInitialLineLength() != null) {
spec.maxInitialLineLength((int)properties.getMaxInitialLineLength().toBytes());
} return spec;
}).tcpConfiguration((tcpClient) -> {
if (properties.getConnectTimeout() != null) {
tcpClient = tcpClient.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, properties.getConnectTimeout());
} Proxy proxy = properties.getProxy();
if (StringUtils.hasText(proxy.getHost())) {
tcpClient = tcpClient.proxy((proxySpec) -> {
Builder builder = proxySpec.type(reactor.netty.tcp.ProxyProvider.Proxy.HTTP).host(proxy.getHost());
PropertyMapper map = PropertyMapper.get();
proxy.getClass();
map.from(proxy::getPort).whenNonNull().to(builder::port);
proxy.getClass();
map.from(proxy::getUsername).whenHasText().to(builder::username);
proxy.getClass();
map.from(proxy::getPassword).whenHasText().to((password) -> {
builder.password((s) -> {
return password;
});
});
proxy.getClass();
map.from(proxy::getNonProxyHostsPattern).whenHasText().to(builder::nonProxyHosts);
});
} return tcpClient;
});
Ssl ssl = properties.getSsl();
if (ssl.getKeyStore() != null && ssl.getKeyStore().length() > 0 || ssl.getTrustedX509CertificatesForTrustManager().length > 0 || ssl.isUseInsecureTrustManager()) {
httpClient = httpClient.secure((sslContextSpec) -> {
SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();
X509Certificate[] trustedX509Certificates = ssl.getTrustedX509CertificatesForTrustManager();
if (trustedX509Certificates.length > 0) {
sslContextBuilder = sslContextBuilder.trustManager(trustedX509Certificates);
} else if (ssl.isUseInsecureTrustManager()) {
sslContextBuilder = sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE);
} try {
sslContextBuilder = sslContextBuilder.keyManager(ssl.getKeyManagerFactory());
} catch (Exception var6) {
this.logger.error(var6);
} sslContextSpec.sslContext(sslContextBuilder).defaultConfiguration(ssl.getDefaultConfigurationType()).handshakeTimeout(ssl.getHandshakeTimeout()).closeNotifyFlushTimeout(ssl.getCloseNotifyFlushTimeout()).closeNotifyReadTimeout(ssl.getCloseNotifyReadTimeout());
});
} if (properties.isWiretap()) {
httpClient = httpClient.wiretap(true);
} if (!CollectionUtils.isEmpty(customizers)) {
customizers.sort(AnnotationAwareOrderComparator.INSTANCE); HttpClientCustomizer customizer;
for(Iterator var7 = customizers.iterator(); var7.hasNext(); httpClient = customizer.customize(httpClient)) {
customizer = (HttpClientCustomizer)var7.next();
}
} return httpClient;
} @Bean
public HttpClientProperties httpClientProperties() {
return new HttpClientProperties();
} @Bean
public NettyRoutingFilter routingFilter(HttpClient httpClient, ObjectProvider<List<HttpHeadersFilter>> headersFilters, HttpClientProperties properties) {
return new NettyRoutingFilter(httpClient, headersFilters, properties);
} @Bean
public NettyWriteResponseFilter nettyWriteResponseFilter(GatewayProperties properties) {
return new NettyWriteResponseFilter(properties.getStreamingMediaTypes());
} @Bean
public ReactorNettyWebSocketClient reactorNettyWebSocketClient(HttpClientProperties properties, HttpClient httpClient) {
ReactorNettyWebSocketClient webSocketClient = new ReactorNettyWebSocketClient(httpClient);
if (properties.getWebsocket().getMaxFramePayloadLength() != null) {
webSocketClient.setMaxFramePayloadLength(properties.getWebsocket().getMaxFramePayloadLength());
} webSocketClient.setHandlePing(properties.getWebsocket().isProxyPing());
return webSocketClient;
} @Bean
public ReactorNettyRequestUpgradeStrategy reactorNettyRequestUpgradeStrategy(HttpClientProperties httpClientProperties) {
ReactorNettyRequestUpgradeStrategy requestUpgradeStrategy = new ReactorNettyRequestUpgradeStrategy();
Websocket websocket = httpClientProperties.getWebsocket();
PropertyMapper map = PropertyMapper.get();
websocket.getClass();
map.from(websocket::getMaxFramePayloadLength).whenNonNull().to(requestUpgradeStrategy::setMaxFramePayloadLength);
websocket.getClass();
map.from(websocket::isProxyPing).to(requestUpgradeStrategy::setHandlePing);
return requestUpgradeStrategy;
}
}

  上面 代码中的 gatewayHttpClient 为 spring cloud gateway 使用的 HttpClient 实例,在spring cloud gateway 进行服务请求时,会自动配置使用该 实例。

  如果需要自定义的 HttpClient 实例,如在 httpClient 中自定义 ip 白名单校验,https 请求证书预置,或是添加特殊认证请求头等,这种场景下需要在代码中显示的定义 gatewayHttpClient 实例,代码如下:

    @Configuration
public class GatewayAutoConfiguration { @Bean
@ConditionalOnMissingBean
public HttpClient gatewayHttpClient(HttpClientProperties properties, List<HttpClientCustomizer> customizers) {
Pool pool = properties.getPool();
ConnectionProvider connectionProvider;
if (pool.getType() == PoolType.DISABLED) {
connectionProvider = ConnectionProvider.newConnection();
} else if (pool.getType() == PoolType.FIXED) {
connectionProvider = ConnectionProvider.fixed(pool.getName(), pool.getMaxConnections(), pool.getAcquireTimeout(), pool.getMaxIdleTime(), pool.getMaxLifeTime());
} else {
connectionProvider = ConnectionProvider.elastic(pool.getName(), pool.getMaxIdleTime(), pool.getMaxLifeTime());
} HttpClient httpClient = HttpClient.create(connectionProvider).httpResponseDecoder((spec) -> {
if (properties.getMaxHeaderSize() != null) {
spec.maxHeaderSize((int)properties.getMaxHeaderSize().toBytes());
} if (properties.getMaxInitialLineLength() != null) {
spec.maxInitialLineLength((int)properties.getMaxInitialLineLength().toBytes());
} return spec;
}).tcpConfiguration((tcpClient) -> {
if (properties.getConnectTimeout() != null) {
tcpClient = tcpClient.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, properties.getConnectTimeout());
} Proxy proxy = properties.getProxy();
if (StringUtils.hasText(proxy.getHost())) {
tcpClient = tcpClient.proxy((proxySpec) -> {
Builder builder = proxySpec.type(reactor.netty.tcp.ProxyProvider.Proxy.HTTP).host(proxy.getHost());
PropertyMapper map = PropertyMapper.get();
proxy.getClass();
map.from(proxy::getPort).whenNonNull().to(builder::port);
proxy.getClass();
map.from(proxy::getUsername).whenHasText().to(builder::username);
proxy.getClass();
map.from(proxy::getPassword).whenHasText().to((password) -> {
builder.password((s) -> {
return password;
});
});
proxy.getClass();
map.from(proxy::getNonProxyHostsPattern).whenHasText().to(builder::nonProxyHosts);
});
} return tcpClient;
});
Ssl ssl = properties.getSsl();
if (ssl.getKeyStore() != null && ssl.getKeyStore().length() > 0 || ssl.getTrustedX509CertificatesForTrustManager().length > 0 || ssl.isUseInsecureTrustManager()) {
httpClient = httpClient.secure((sslContextSpec) -> {
SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();
X509Certificate[] trustedX509Certificates = ssl.getTrustedX509CertificatesForTrustManager();
if (trustedX509Certificates.length > 0) {
sslContextBuilder = sslContextBuilder.trustManager(trustedX509Certificates);
} else if (ssl.isUseInsecureTrustManager()) {
sslContextBuilder = sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE);
} try {
sslContextBuilder = sslContextBuilder.keyManager(ssl.getKeyManagerFactory());
} catch (Exception var6) {
this.logger.error(var6);
} sslContextSpec.sslContext(sslContextBuilder).defaultConfiguration(ssl.getDefaultConfigurationType()).handshakeTimeout(ssl.getHandshakeTimeout()).closeNotifyFlushTimeout(ssl.getCloseNotifyFlushTimeout()).closeNotifyReadTimeout(ssl.getCloseNotifyReadTimeout());
});
} if (properties.isWiretap()) {
httpClient = httpClient.wiretap(true);
} if (!CollectionUtils.isEmpty(customizers)) {
customizers.sort(AnnotationAwareOrderComparator.INSTANCE); HttpClientCustomizer customizer;
for(Iterator var7 = customizers.iterator(); var7.hasNext(); httpClient = customizer.customize(httpClient)) {
customizer = (HttpClientCustomizer)var7.next();
}
} return httpClient;
} }

   这样服务在启动的时候就会优先加载自定的 httpClient 实例。

  

SpringCloud gateway自定义请求的 httpClient的更多相关文章

  1. 微服务实战系列(八)-网关springcloud gateway自定义规则

    1. 场景描述 先说明下项目中使用的网关是:springcloud gateway, 因需要给各个网关服务系统提供自定义配置路由规则,实时生效,不用重启网关(重启风险大),目前已实现:动态加载自定义路 ...

  2. 使用springcloud gateway搭建网关(分流,限流,熔断)

    Spring Cloud Gateway Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 ...

  3. SpringCloud gateway (史上最全)

    疯狂创客圈 Java 分布式聊天室[ 亿级流量]实战系列之 -25[ 博客园 总入口 ] 前言 ### 前言 疯狂创客圈(笔者尼恩创建的高并发研习社群)Springcloud 高并发系列文章,将为大家 ...

  4. SpringCloud gateway 3

    参考博客:https://www.cnblogs.com/crazymakercircle/p/11704077.html 1.1 SpringCloud Gateway 简介 SpringCloud ...

  5. SpringCloud Gateway高阶之Sentinel限流、熔断

    前言 为什么需要服务熔断和降级?微服务是当前业界的一大趋势,原理就是将单一职责的功能模块独立化为子服务,降低服务间的耦合,服务间互相调用.但是这样也会出现一些问题: 上图中大量微服务互相调用,存在大量 ...

  6. SpringCloud Gateway快速入门

    SpringCloud Gateway cloud笔记第一部分 cloud笔记第二部分Hystrix 文章目录 SpringCloud Gateway Zull的工作模式与Gateway的对比 Rou ...

  7. API网关才是大势所趋?SpringCloud Gateway保姆级入门教程

    什么是微服务网关 SpringCloud Gateway是Spring全家桶中一个比较新的项目,Spring社区是这么介绍它的: 该项目借助Spring WebFlux的能力,打造了一个API网关.旨 ...

  8. 万字长文:SpringCloud gateway入门学习&实践

    官方文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/# ...

  9. SpringCloud Gateway微服务网关实战与源码分析-上

    概述 定义 Spring Cloud Gateway 官网地址 https://spring.io/projects/spring-cloud-gateway/ 最新版本3.1.3 Spring Cl ...

随机推荐

  1. kali linux安装后乱码的解决方法

    操作系统是5.3 解决方法是在终端执行命令: sudo apt-get install ttf-wqy-zenhei

  2. 详解CVE-2022-0847 DirtyPipe漏洞

    摘要:本文详细介绍了CVE-2022-0847漏洞形成根因,相应补丁修复方法,通过本文让读者对CVE-2022-0847漏洞有更清晰的了解. 本文分享自华为云社区<CVE-2022-0847 D ...

  3. 103_Power Pivot 透视表中空白标签处理及百分比

    焦棚子的文章目录 请点击下载附件 1.案例来源于不断变化的需求 事实表:销售表 维度表:城市表 销售表和城市建立多对一的关系 如图1: 图1 2.插入透视表 如图2: 图2 3.问题 1.销售表中,城 ...

  4. MongoDB 分片规则

    每日一句 生命本身毫无意义,只有死亡才能让你邃晓人性的真谛! 每日一句 Ideal is the beacon. Without ideal, there is no secure direction ...

  5. Python数据分析--Numpy常用函数介绍(4)--Numpy中的线性关系和数据修剪压缩

    摘要:总结股票均线计算原理--线性关系,也是以后大数据处理的基础之一,NumPy的 linalg 包是专门用于线性代数计算的.作一个假设,就是一个价格可以根据N个之前的价格利用线性模型计算得出. 前一 ...

  6. 《Mybatis 手撸专栏》第10章:使用策略模式,调用参数处理器

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 你这代码写的,咋这么轴呢! 说到轴,让我想起初中上学时老师说的话:"你那脑 ...

  7. AT32F415 修改时钟和晶振方法(原创)

    1. 简介 我们几乎是国内第一批使用AT32F415芯片的客户,那个时候芯片还没涨价,岁月一切静好.使用AT32F415 做了几个小产品,也在持续出货.后来大家都知道,涨价缺货愈演愈烈.好在我们提前囤 ...

  8. 2.如何正确理解古典概率中的条件概率《zobol的考研概率论教程》

    写本文主要是帮助粉丝理解考研中的古典概率-条件概率的具体定义. "B事件发生的条件下,A事件发生的概率"? "在A集合内有多少B的样本点"? "在B约 ...

  9. C# 读写文件从用户态切到内核态,到底是个什么流程?

    一:背景 1. 一个很好奇的问题 我们在学习 C# 的过程中,总会听到一个词叫做 内核态 ,比如说用 C# 读写文件,会涉及到代码从 用户态 到 内核态 的切换,用 HttpClient 获取远端的数 ...

  10. 重学ES系列之Set实现数组去重、交集、并集、差集

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...