您当前的位置:首页 > 电脑百科 > 程序开发 > 架构

SpringCloud gateway自定义请求的 httpClient

时间:2022-07-30 10:33:39  来源:  作者:Java后端架构猛猛

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 实例。

原文链接:
https://www.cnblogs.com/zjdxr-up/p/16530423.html



Tags:httpClient   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
再也不用写请求HttpHelper了,快来试试HttpClient
前言在C#7.1之后,net推出HttpClient类代替WebRequest, HttpWebRequest, ServicePoint, and WebClient ,先来看下他们在以前的作用 &bull; HttpWebRequest和HttpWebResponse...【详细内容】
2023-08-23  Search: httpClient  点击:(235)  评论:(0)  加入收藏
.NET Core 使用 HttpClient 的正确方式
前言HttpClient 是 .NET Framework、.NET Core 或 .NET 5以上版本中的一个类,用于向 Web API 发送 HTTP 请求并接收响应。它提供了一些简单易用的方法,如 GET、POST、PUT 和 D...【详细内容】
2023-07-02  Search: httpClient  点击:(284)  评论:(0)  加入收藏
SpringCloud gateway自定义请求的 httpClient
SpringCloud gateway 在实现服务路由并请求的具体过程是在 org.springframework.cloud.gateway.filter.NettyRoutingFilter 的过滤器中,该过滤器封装了具体的请求参数,以及根...【详细内容】
2022-07-30  Search: httpClient  点击:(548)  评论:(0)  加入收藏
HttpClient使用
起因在项目中需要访问Http请求的时候,通常都是使用WebRequest,主要是老项目(你懂得).如果不是老项目,应该使用HttpClient,但在.Net Core 2.1之后,官方可推荐使用使用HttpCli...【详细内容】
2022-03-17  Search: httpClient  点击:(396)  评论:(0)  加入收藏
HttpClient高级进阶-SSL
简介本文将展示如何使用“全部接受”SSL支持配置Apache HttpClient 4。目标很简单 - 使用没有有效证书的HTTPS URL。SSLPeerUnverifiedException如果不使用HttpClient配置SS...【详细内容】
2021-04-01  Search: httpClient  点击:(411)  评论:(0)  加入收藏
HttpClient三个超时时间详解
HttpClient有三种超时时间设置,在RequestConfig配置类中定义的,分别为connectionRequestTimeout、connectTimeout和socketTimeout,如下图,然后分开讲解。RequestConfig三种超时...【详细内容】
2020-09-10  Search: httpClient  点击:(5942)  评论:(0)  加入收藏
httpClient 请求接口如何优雅的重试
httpClient 请求接口失败要重试,一般人想到的可能是 做try catch 然后循环请求,但是这种方法写起来很麻烦,而且不优雅。今天就说下另外一种重试的方法,就是引入Microsoft.Extens...【详细内容】
2020-09-07  Search: httpClient  点击:(911)  评论:(0)  加入收藏
.NET CORE HttpClient使用
自从HttpClient诞生依赖,它的使用方式一直备受争议,framework版本时代产生过相当多经典的错误使用案例,包括Tcp链接耗尽、DNS更改无感知等问题。有兴趣的同学自行查找研究。在....【详细内容】
2020-08-19  Search: httpClient  点击:(418)  评论:(0)  加入收藏
Java案例实战:Httpclient 实现网络请求 + Jsoup 解析网页
【前言】你是否也曾羡慕过有些 phython 大神有着如下的神操作: 他们就轻轻的执行一串代码,就能循环的抓取很多自己想要的数据。其实不用太羡慕他们,因为不光 phython 能实现,我...【详细内容】
2020-08-13  Search: httpClient  点击:(351)  评论:(0)  加入收藏
httpclient连接池管理,你用对了?
一、前言为何要用http连接池那?因为使用它我们可以得到以下好处:因为使用它可以有效降低延迟和系统开销。如果不采用连接池,每当我们发起http请求时,都需要重新发起Tcp三次握手...【详细内容】
2020-06-13  Search: httpClient  点击:(405)  评论:(0)  加入收藏
▌简易百科推荐
对于微服务架构监控应该遵守的原则
随着软件交付方式的变革,微服务架构的兴起使得软件开发变得更加快速和灵活。在这种情况下,监控系统成为了微服务控制系统的核心组成部分。随着软件的复杂性不断增加,了解系统的...【详细内容】
2024-04-03  步步运维步步坑    Tags:架构   点击:(7)  评论:(0)  加入收藏
大模型应用的 10 种架构模式
作者 | 曹洪伟在塑造新领域的过程中,我们往往依赖于一些经过实践验证的策略、方法和模式。这种观念对于软件工程领域的专业人士来说,已经司空见惯,设计模式已成为程序员们的重...【详细内容】
2024-03-27    InfoQ  Tags:架构模式   点击:(20)  评论:(0)  加入收藏
哈啰云原生架构落地实践
一、弹性伸缩技术实践1.全网容器化后一线研发的使用问题全网容器化后一线研发会面临一系列使用问题,包括时机、容量、效率和成本问题,弹性伸缩是云原生容器化后的必然技术选择...【详细内容】
2024-03-27  哈啰技术  微信公众号  Tags:架构   点击:(13)  评论:(0)  加入收藏
DDD 与 CQRS 才是黄金组合
在日常工作中,你是否也遇到过下面几种情况: 使用一个已有接口进行业务开发,上线后出现严重的性能问题,被老板当众质疑:“你为什么不使用缓存接口,这个接口全部走数据库,这怎么能扛...【详细内容】
2024-03-27  dbaplus社群    Tags:DDD   点击:(16)  评论:(0)  加入收藏
高并发架构设计(三大利器:缓存、限流和降级)
软件系统有三个追求:高性能、高并发、高可用,俗称三高。本篇讨论高并发,从高并发是什么到高并发应对的策略、缓存、限流、降级等。引言1.高并发背景互联网行业迅速发展,用户量剧...【详细内容】
2024-03-13    阿里云开发者  Tags:高并发   点击:(12)  评论:(0)  加入收藏
如何判断架构设计的优劣?
架构设计的基本准则是非常重要的,它们指导着我们如何构建可靠、可维护、可测试的系统。下面是这些准则的转换表达方式:简单即美(KISS):KISS原则的核心思想是保持简单。在设计系统...【详细内容】
2024-02-20  二进制跳动  微信公众号  Tags:架构设计   点击:(41)  评论:(0)  加入收藏
详解基于SpringBoot的WebSocket应用开发
在现代Web应用中,实时交互和数据推送的需求日益增长。WebSocket协议作为一种全双工通信协议,允许服务端与客户端之间建立持久性的连接,实现实时、双向的数据传输,极大地提升了用...【详细内容】
2024-01-30  ijunfu  今日头条  Tags:SpringBoot   点击:(23)  评论:(0)  加入收藏
PHP+Go 开发仿简书,实战高并发高可用微服务架构
来百度APP畅享高清图片//下栽のke:chaoxingit.com/2105/PHP和Go语言结合,可以开发出高效且稳定的仿简书应用。在实现高并发和高可用微服务架构时,我们可以采用一些关键技术。首...【详细内容】
2024-01-14  547蓝色星球    Tags:架构   点击:(125)  评论:(0)  加入收藏
GraalVM与Spring Boot 3.0:加速应用性能的完美融合
在2023年,SpringBoot3.0的发布标志着Spring框架对GraalVM的全面支持,这一支持是对Spring技术栈的重要补充。GraalVM是一个高性能的多语言虚拟机,它提供了Ahead-of-Time(AOT)编...【详细内容】
2024-01-11    王建立  Tags:Spring Boot   点击:(135)  评论:(0)  加入收藏
Spring Boot虚拟线程的性能还不如Webflux?
早上看到一篇关于Spring Boot虚拟线程和Webflux性能对比的文章,觉得还不错。内容较长,抓重点给大家介绍一下这篇文章的核心内容,方便大家快速阅读。测试场景作者采用了一个尽可...【详细内容】
2024-01-10  互联网架构小马哥    Tags:Spring Boot   点击:(135)  评论:(0)  加入收藏
站内最新
站内热门
站内头条