RestTemplate集成Ribbbon的示例代码

时间:2022-08-26 21:10:46

上一篇文章我们分析了ribbon的核心原理,接下来我们来看看springcloud是如何集成ribbon的,不同的springcloud的组件(feign,zuul,resttemplate)集成ribbon有所不同,这篇文章先来看看resttemplate。

resttemplate的类图如下

RestTemplate集成Ribbbon的示例代码

  • 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();

只要加入注解@loadbalancedresttemplate会被注入,在没有引入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,下面来看看整个调用过程

RestTemplate集成Ribbbon的示例代码

整个过程有点复杂,核心就是经过拦截器loadbalancerinterceptor,通过ribbonloadbalancerclient发起负载均衡调用。ribbonloadbalancerclienti组合了loadbalancer,所以具备了负载均衡的能力,也就是我们在上一篇文章解读的ribbon原理。

图中我们没有画出真正发起http请求的过程,其默认是由simpleclienthttprequestfactory创建,clienthttprequestfactory的类图如下:

RestTemplate集成Ribbbon的示例代码

从调用时序图上我们看到,开始我们调用的是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