spring feign http客户端连接池配置以及spring zuul http客户端连接池配置解析

背景

Feign和网关Zuul的RPC调用,实际上都是HTTP请求。HTTP请求,如果不配置好HTTP连接池参数的话,会影响性能,或者造成堆积阻塞,对于其中一个微服务的调用影响到其他微服务的调用。

源代码类比解析

本文基于Spring Cloud Dalston.SR4,但是基本思路上,这块比较稳定,不稳定的是Feign本身HttpClient的配置实现上。

不过个人感觉,未来Feign可能也会转去用底层Ribbon的HttpClient。因为可以配置,并且实现的连接池粒度更细一些。

Feign Http客户端解析

Feign调用和网关Zuul调用都用了HttpClient,不同的是,这个HttpClient所在层不一样。Feign调用,利用的是自己这一层的HttpClient,并没有用底层Ribbon,只是从Ribbon中获取了服务实例列表。Zuul没有自己的Httpclient,直接利用底层的Ribbon的HttpClient进行调用。

先看看Feign,Feign的Http客户端默认是ApacheHttpClient。这个可以替换成OkHttpClient(参考:https://segmentfault.com/a/1190000009071952 但是,由于我们其他组件的配置,例如重试等等,导致我们这里只能用默认的ApacheHttpClient)。

打断点,看下核心实现的源代码feign.httpclient.ApacheHttpClient:

1public final class ApacheHttpClient implements Client { 2 private static final String ACCEPT_HEADER_NAME = "Accept"; 3 private final HttpClient client; 4 5 public ApacheHttpClient() { 6 this(HttpClientBuilder.create().build()); 7 } 8 9 public ApacheHttpClient(HttpClient client) { 10 this.client = client; 11 } 12 13 public Response execute(Request request, Options options) throws IOException { 14 HttpUriRequest httpUriRequest; 15 try { 16 httpUriRequest = this.toHttpUriRequest(request, options); 17 } catch (URISyntaxException var6) { 18 throw new IOException("URL '" + request.url() + "' couldn't be parsed into a URI", var6); 19 } 20 21 HttpResponse httpResponse = this.client.execute(httpUriRequest); 22 Response response = this.toFeignResponse(httpResponse).toBuilder().request(request).build(); 23 HttpResponseConvertUtil.convert5XXToException(httpUriRequest, httpResponse); 24 return response; 25 } 26 //其他代码略 27}

打断点确认,在某个微服务被调用时,确实HTTP请求在这里的execute方法中发出。我们看下构造方法,发现就是用默认配置的HttpClientBuilder构造的。这样不太好,默认情况下,没有连接池,而是依靠对于不同实例地址的共用不同的一个长连接。而又没找到,可以配置参数的地方,所以选择覆盖这里的源代码,将其无参构造器改成:

1public ApacheHttpClient() 2{ 3 HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); 4 // 长连接保持30秒 5 PoolingHttpClientConnectionManager pollingConnectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS); 6 // 总连接数 7 pollingConnectionManager.setMaxTotal(1000); 8 // 同路由的并发数 9 pollingConnectionManager.setDefaultMaxPerRoute(100); 10 // 保持长连接配置,需要在头添加Keep-Alive 11 httpClientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy()); 12 httpClientBuilder.setConnectionManager(pollingConnectionManager); 13 this.client = httpClientBuilder.build(); 14}

但是,这么改只是简单的改了下,首先没有做成可配置的,其次就是没有做成对于每个实例隔离连接池(每个实例用不同的HttpClient)。只是整体上对于服务器做了每个实例最多用100个连接的配置。
个人感觉未来feign未来会更改这部分逻辑,所以没大改,而且,都是内网调用,配置成这样也基本可以接受了。

Zuul Http客户端解析

Zuul利用底层的Ribbon Http客户端,更好用些;同样的,我们先看下核心源码RibbonLoadBalancingHttpClient:

1public class RibbonLoadBalancingHttpClient extends AbstractLoadBalancingClient<RibbonApacheHttpRequest, RibbonApacheHttpResponse, CloseableHttpClient> { 2 3 public RibbonLoadBalancingHttpClient(IClientConfig config, ServerIntrospector serverIntrospector) 4 { 5 super(config, serverIntrospector); 6 } 7 8 public RibbonLoadBalancingHttpClient(CloseableHttpClient delegate, IClientConfig config, 9 ServerIntrospector serverIntrospector) 10 { 11 super(delegate, config, serverIntrospector); 12 } 13 14 protected CloseableHttpClient createDelegate(IClientConfig config) 15 { 16 return HttpClientBuilder.create() 17 // already defaults to 0 in builder, so resetting to 0 won't hurt 18 .setMaxConnTotal(config.getPropertyAsInteger(CommonClientConfigKey.MaxTotalConnections, 0)) 19 // already defaults to 0 in builder, so resetting to 0 won't hurt 20 .setMaxConnPerRoute(config.getPropertyAsInteger(CommonClientConfigKey.MaxConnectionsPerHost, 0)) 21 .disableCookieManagement().useSystemProperties() // for proxy 22 .build(); 23 } 24 25 @Override 26 public RibbonApacheHttpResponse execute(RibbonApacheHttpRequest request, final IClientConfig configOverride) 27 throws Exception 28 { 29 final RequestConfig.Builder builder = RequestConfig.custom(); 30 IClientConfig config = configOverride != null ? configOverride : this.config; 31 builder.setConnectTimeout(config.get(CommonClientConfigKey.ConnectTimeout, this.connectTimeout)); 32 builder.setSocketTimeout(config.get(CommonClientConfigKey.ReadTimeout, this.readTimeout)); 33 builder.setRedirectsEnabled(config.get(CommonClientConfigKey.FollowRedirects, this.followRedirects)); 34 35 final RequestConfig requestConfig = builder.build(); 36 if (isSecure(configOverride)) 37 { 38 final URI secureUri = UriComponentsBuilder.fromUri(request.getUri()).scheme("https").build().toUri(); 39 request = request.withNewUri(secureUri); 40 } 41 final HttpUriRequest httpUriRequest = request.toRequest(requestConfig); 42 final HttpResponse httpResponse = this.delegate.execute(httpUriRequest); 43 44 return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI()); 45 } 46 47 @Override 48 public URI reconstructURIWithServer(Server server, URI original) 49 { 50 URI uri = updateToHttpsIfNeeded(original, this.config, this.serverIntrospector, server); 51 return super.reconstructURIWithServer(server, uri); 52 } 53 54 @Override 55 public RequestSpecificRetryHandler getRequestSpecificRetryHandler(RibbonApacheHttpRequest request, 56 IClientConfig requestConfig) 57 { 58 return new RequestSpecificRetryHandler(false, false, RetryHandler.DEFAULT, requestConfig); 59 } 60}

从createDelegate这个方法可以看出通过HttpClientBuilder建立HttpClient,并且是可配置的,配置类是CommonClientConfigKey,我们可以配置这几个参数实现对于连接池大小和每个路由连接大小的控制,就是:

1ribbon.MaxTotalConnections=200 2ribbon.MaxConnectionsPerHost=100

由于是CommonClientConfigKey下的配置,所以也可以对于每个微服务配置:

1service1.ribbon.MaxTotalConnections=200 2service1.ribbon.MaxConnectionsPerHost=100 3service2.ribbon.MaxTotalConnections=200 4service2.ribbon.MaxConnectionsPerHost=100

通过配置以及打断点,可以看出,对于每个微服务的调用,都走的是不同的CloseableHttpClient,我们可以对每个微服务单独配置;例如,假设service1有两个实例,service2有三个实例,service1访问压力大概一共需要100个连接,service2访问压力大概一共需要300个连接.我们假设平均分配没有问题,则可以这么配置:

1service1.ribbon.MaxTotalConnections=100 2service1.ribbon.MaxConnectionsPerHost=50 3service2.ribbon.MaxTotalConnections=300 4service2.ribbon.MaxConnectionsPerHost=100

但是,考虑如果某台服务器如果出异常了,这么配置会导致连接也许不够用,所以,最好PerHost的就设置为总共需要多少个连接:

1service1.ribbon.MaxTotalConnections=200 2service1.ribbon.MaxConnectionsPerHost=100 3service2.ribbon.MaxTotalConnections=900 4service2.ribbon.MaxConnectionsPerHost=300
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Nepxion Discovery 5.5.0 发布

!(https://oscimg.oschina.net/oscnet/f81c043194ef4732880459d00c1a720e.png)发布日志功能更新:增加基于Opentracing调用链的支持,目前支持UberJaeger,实现在SpringCloudGateway、Zuul和服务上的灰度