上一篇文章我们分析了ribbon的核心原理,接下来我们来看看springcloud是如何集成ribbon的,不同的springcloud的组件(feign,zuul,resttemplate)集成ribbon有所不同,这篇文章先来看看resttemplate。
resttemplate的类图如下
-
httpaccessor
主要根据clienthttprequestfactory
创建clienthttprequest
-
interceptinghttpaccessor
扩展了httpaccessor
,创建拦截的interceptingclienthttprequest
,这里会设置拦截器clienthttprequestinterceptor
,这是集成ribbon的核心,当resttemplate
发起http请求调用的时候,会先经过拦截器,然后才真正发起http请求。
拦截器clienthttprequestinterceptor
是如何被设置的呢?在loadbalancerautoconfiguration
类中,有如下代码:
1
2
3
|
@loadbalanced @autowired (required = false ) private list<resttemplate> resttemplates = collections.emptylist(); |
只要加入注解@loadbalanced
的resttemplate
会被注入,在没有引入spring retry组件的时候,加载如下配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
@configuration @conditionalonmissingclass ( "org.springframework.retry.support.retrytemplate" ) static class loadbalancerinterceptorconfig { @bean public loadbalancerinterceptor ribboninterceptor( loadbalancerclient loadbalancerclient, loadbalancerrequestfactory requestfactory) { return new loadbalancerinterceptor(loadbalancerclient, requestfactory); } @bean @conditionalonmissingbean public resttemplatecustomizer resttemplatecustomizer( final loadbalancerinterceptor loadbalancerinterceptor) { return new resttemplatecustomizer() { @override public void customize(resttemplate resttemplate) { list<clienthttprequestinterceptor> list = new arraylist<>( resttemplate.getinterceptors()); list.add(loadbalancerinterceptor); resttemplate.setinterceptors(list); } }; } } |
这样resttemplate
就被设置了loadbalancerinterceptor,下面来看看整个调用过程
整个过程有点复杂,核心就是经过拦截器loadbalancerinterceptor,通过ribbonloadbalancerclient发起负载均衡调用。ribbonloadbalancerclienti组合了loadbalancer,所以具备了负载均衡的能力,也就是我们在上一篇文章解读的ribbon原理。
图中我们没有画出真正发起http请求的过程,其默认是由simpleclienthttprequestfactory
创建,clienthttprequestfactory
的类图如下:
从调用时序图上我们看到,开始我们调用的是interceptingclienthttprequestfactory
来获取interceptingclienthttprequest
,它们通过组合的方式集成了clienthttprequestfactory
和拦截器,interceptingclienthttprequest
发起调用的时候委托了其内部类interceptingrequestexecution
去处理,核心逻辑:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
@override public clienthttpresponse execute(httprequest request, byte [] body) throws ioexception { if ( this .iterator.hasnext()) { clienthttprequestinterceptor nextinterceptor = this .iterator.next(); return nextinterceptor.intercept(request, body, this ); } else { clienthttprequest delegate = requestfactory.createrequest(request.geturi(), request.getmethod()); for (map.entry<string, list<string>> entry : request.getheaders().entryset()) { list<string> values = entry.getvalue(); for (string value : values) { delegate.getheaders().add(entry.getkey(), value); } } if (body.length > 0 ) { streamutils.copy(body, delegate.getbody()); } return delegate.execute(); } } |
首先会先取出拦截器集合的第一个执行,当拦截器执行完成后,会回调回来,执行else的代码,真正发起http请求,主要有两种方式实现clienthttprequestfactory
接口:
-
一种是
simpleclienthttprequestfactory
,使用j2se提供的方式(既java.net包提供的方式)创建底层的http请求连接 -
一种方式是使用
httpcomponentsclienthttprequestfactory
方式,底层使用httpclient访问远程的http服务,使用httpclient可以配置连接池和证书等信息。
resttemplate默认是使用simpleclienthttprequestfactory,内部是调用jdk的httpconnection,默认超时为-1,可以这样设置超时时间:
1
2
3
4
5
6
7
8
|
@bean @loadbalanced public resttemplate resttemplate() { simpleclienthttprequestfactory factory = new simpleclienthttprequestfactory(); factory.setconnecttimeout( 1000 * 2 ); //连接超时时间 factory.setreadtimeout( 1000 * 1 ); //读超时时间 return new resttemplate(factory); } |
使用httpcomponentsclienthttprequestfactory
方式可以使用连接池(推荐) ,还可以设置重试策略(具体没有研究过)
如果想开启重试机制,我们可以引入spring的retry组件
1
2
3
4
5
|
<dependency> <groupid>org.springframework.retry</groupid> <artifactid>spring-retry</artifactid> <version>版本号</version> </dependency> |
这样springcloud-ribbon就会加重如下配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
@configuration @conditionalonclass (retrytemplate. class ) public static class retryautoconfiguration { @bean public retrytemplate retrytemplate() { retrytemplate template = new retrytemplate(); template.setthrowlastexceptiononexhausted( true ); return template; } @bean @conditionalonmissingbean public loadbalancedretrypolicyfactory loadbalancedretrypolicyfactory() { return new loadbalancedretrypolicyfactory.neverretryfactory(); } } @configuration @conditionalonclass (retrytemplate. class ) public static class retryinterceptorautoconfiguration { @bean @conditionalonmissingbean public retryloadbalancerinterceptor ribboninterceptor( loadbalancerclient loadbalancerclient, loadbalancerretryproperties properties, loadbalancedretrypolicyfactory lbretrypolicyfactory, loadbalancerrequestfactory requestfactory) { return new retryloadbalancerinterceptor(loadbalancerclient, properties, lbretrypolicyfactory, requestfactory); } @bean @conditionalonmissingbean public resttemplatecustomizer resttemplatecustomizer( final retryloadbalancerinterceptor loadbalancerinterceptor) { return new resttemplatecustomizer() { @override public void customize(resttemplate resttemplate) { list<clienthttprequestinterceptor> list = new arraylist<>( resttemplate.getinterceptors()); list.add(loadbalancerinterceptor); resttemplate.setinterceptors(list); } }; } } |
1
2
3
4
5
6
|
@bean @conditionalonclass (name = "org.springframework.retry.support.retrytemplate" ) @conditionalonmissingbean public loadbalancedretrypolicyfactory loadbalancedretrypolicyfactory(springclientfactory clientfactory) { return new ribbonloadbalancedretrypolicyfactory(clientfactory); } |
拦截器替换成retryloadbalancerinterceptor
了,这里集成了retry组件retrytemplate。重试策略由retryhandler
接口来配置,默认实现类defaultloadbalancerretryhandler
,如下为默认的配置参数
1
2
3
4
5
6
7
8
|
#最大的重试次数 ribbon.maxautoretries= 0 #最大重试server的个数 ribbon.maxautoretriesnextserver= 1 #是否开启任何异常都重试(默认在get请求下会重试,其他情况不会重试,除非设置为 true ) ribbon.oktoretryonalloperations= false #指定重试的http状态码 ribbon.retryablestatuscodes= 500 , 501 |
以上是对全局生效,如果加上xxx.ribbon.maxautoretries=1
这样只会对某个ribbon客户端生效。maxautoretries和maxautoretriesnextserver是配合使用的,最大重试次数是针对每一个server的,如果设置maxautoretries=1,maxautoretriesnextserver=1这样触发最大重试次数就是4次。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://segmentfault.com/a/1190000015858648