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

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   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
SpringCloud gateway 在实现服务路由并请求的具体过程是在 org.springframework.cloud.gateway.filter.NettyRoutingFilter 的过滤器中,该过滤器封装了具体的请求参数,以及根...【详细内容】
2022-07-30  Tags: httpClient  点击:(138)  评论:(0)  加入收藏
起因在项目中需要访问Http请求的时候,通常都是使用WebRequest,主要是老项目(你懂得).如果不是老项目,应该使用HttpClient,但在.Net Core 2.1之后,官方可推荐使用使用HttpCli...【详细内容】
2022-03-17  Tags: httpClient  点击:(241)  评论:(0)  加入收藏
简介本文将展示如何使用“全部接受”SSL支持配置Apache HttpClient 4。目标很简单 - 使用没有有效证书的HTTPS URL。SSLPeerUnverifiedException如果不使用HttpClient配置SS...【详细内容】
2021-04-01  Tags: httpClient  点击:(282)  评论:(0)  加入收藏
HttpClient有三种超时时间设置,在RequestConfig配置类中定义的,分别为connectionRequestTimeout、connectTimeout和socketTimeout,如下图,然后分开讲解。RequestConfig三种超时...【详细内容】
2020-09-10  Tags: httpClient  点击:(3821)  评论:(0)  加入收藏
httpClient 请求接口失败要重试,一般人想到的可能是 做try catch 然后循环请求,但是这种方法写起来很麻烦,而且不优雅。今天就说下另外一种重试的方法,就是引入Microsoft.Extens...【详细内容】
2020-09-07  Tags: httpClient  点击:(659)  评论:(0)  加入收藏
自从HttpClient诞生依赖,它的使用方式一直备受争议,framework版本时代产生过相当多经典的错误使用案例,包括Tcp链接耗尽、DNS更改无感知等问题。有兴趣的同学自行查找研究。在....【详细内容】
2020-08-19  Tags: httpClient  点击:(184)  评论:(0)  加入收藏
【前言】你是否也曾羡慕过有些 phython 大神有着如下的神操作: 他们就轻轻的执行一串代码,就能循环的抓取很多自己想要的数据。其实不用太羡慕他们,因为不光 phython 能实现,我...【详细内容】
2020-08-13  Tags: httpClient  点击:(86)  评论:(0)  加入收藏
一、前言为何要用http连接池那?因为使用它我们可以得到以下好处:因为使用它可以有效降低延迟和系统开销。如果不采用连接池,每当我们发起http请求时,都需要重新发起Tcp三次握手...【详细内容】
2020-06-13  Tags: httpClient  点击:(136)  评论:(0)  加入收藏
▌简易百科推荐
在了解SpringBoot项目拆分之前首先我们需要了解微服务架构什么是微服务?单个轻量级服务一般为一个单独微服务,微服务讲究的是 专注某个功能的实现,比如登录系统只专注于用户登...【详细内容】
2022-10-27  java小悠  今日头条  Tags:Spring Boot   点击:(13)  评论:(0)  加入收藏
本文整理自字节跳动基础架构的大数据开发工程师魏中佳在 ApacheCon Aisa 2022 「大数据」议题下的演讲,主要介绍 Cloud Shuffle Service(CSS) 在字节跳动 Spark 场景下的设计与...【详细内容】
2022-10-26  开源中国   网易号  Tags:Cloud Shuffle Service   点击:(10)  评论:(0)  加入收藏
几年前,在通信领域的技术咨询经历,初步了解到预分配内存管理机制,其对于性能的改善是多么的明显。最近,也从点点滴滴的金融科技的领域,看到了高频交易所需要的低延时架构技术(当然...【详细内容】
2022-10-25  科技资料库     Tags:架构   点击:(8)  评论:(0)  加入收藏
前端架构需要解决的问题前端网页就是这么简单,除去图片、视频等资源外只有三部分,即标记网页元素的HTML、设置元素样式的CSS和负责交互处理的JavaScript。在软件开发上,普遍认...【详细内容】
2022-10-24  程序员高级码农II  今日头条  Tags:架构   点击:(5)  评论:(0)  加入收藏
数据一致性前面总结了微服务的9个痛点,有些痛点没有好的解决方案,而有些痛点是有对策的,从本章开始,就来讲解某些痛点对应的解决方案。这一章先解决数据一致性的问题,先来看一个...【详细内容】
2022-10-23  互联共商     Tags:微服务   点击:(5)  评论:(0)  加入收藏
前导近期有个同事跟我说遇到一件很奇怪的事情,时不时收到售后反馈说 部分用户无法接收到聊天室(WebSocket 服务)消息,然而在测试服以各种方式测试都无法复现这种现象。于是陷...【详细内容】
2022-10-21  raylin666  今日头条  Tags:WebSocket   点击:(14)  评论:(0)  加入收藏
引言笔者在经历的的一些项目中都使用了 DDD 领域驱动设计进行架构设计,尤其是在业务梳理、中台规划以及微服务划分等方面,DDD 是重要的架构设计方法论,对业务领域建模、微服务...【详细内容】
2022-10-20  宫心职场攻略   网易号  Tags:DDD   点击:(11)  评论:(0)  加入收藏
微服务的痛点在产品研发过程中,引入一种技术来解决一个业务问题并不难,难的是能否合理评估技术风险,这个观点对微服务同样适用。因此,本节将专门讨论微服务会带来哪些问题,这部分...【详细内容】
2022-10-17  大数据架构师  今日头条  Tags:微服务   点击:(8)  评论:(0)  加入收藏
译者 | 布加迪数据在急剧增多。全球每天生成的数据量三年后将达到463 EB。相比之下,人类迄今生成的所有单词量估计总共也就5 EB。为了在当今的数字经济下取得成功,许多企业在...【详细内容】
2022-10-08    51CTO  Tags:架构   点击:(15)  评论:(0)  加入收藏
大家好!在本手册中,您将了解软件架构这一广阔而复杂的领域。当我第一次开始编码之旅时,我发现这是一个既令人困惑又令人生畏的领域。所以我会尽量避免你的困惑。在这本手册中,我...【详细内容】
2022-10-08  一个即将被退役的码农  今日头条  Tags:软件架构   点击:(7)  评论:(0)  加入收藏
站内最新
站内热门
站内头条