【SpringCloud微服务实战学习系列】客户端负载均衡Spring Cloud Ribbon

时间:2022-08-01 21:12:08

Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现.通过Spring Cloud的封装,可以让我们轻松地将面向服务的RES模板请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在于每一个Spring Cloud构建的微服务和基础设施中。因为微服务间的调用,API网关的请求转发等内容,实际上是通过Ribbon来实现的,包括后续我们将要学习的Feign,它也是基于Ribbon实现的工具。

一、客户端负载均衡

负载均衡是对系统的高可用、网络压力的缓解和处理能力扩容的重要手段之一。我们常说的负载均衡是指服务端负载均衡,其中分为硬件负载均衡和软件负载均衡。硬件负载均衡主要通过在服务器节点中回见安装专门用于负载均衡的设备,比如F5等;而软件负载均衡则是通过在服务器上安装一些具有负载均衡功能或模块的软件来完成请求分发工作,比如Nginx等,不论是采用硬件负载均衡还是软件负载均衡,只要是服务端负载均衡都能以类似如下图的架构方式构建起来。

硬件负载均衡的设备或是软件负载均衡的软件模块都会维护一个下挂可用的服务端清单,通过心跳检测来剔除故障的服务端节点以保证清单中都是可以正常访问的服务端节点,当客户端发送请求道负载均衡设备的时候,该设备按某种算法(eg:线性轮询、按权重负载、按流量负载等)从维护的可用服务端清单中取出一台服务端的地址,然后进行转发。

客户端负载均衡和服务端负载均衡最大的不同点在于上面所提及的服务清单所存储的位置。在客户端负载均衡中,所有客户端节点都维护这自己要访问的服务端清单,而这些服务端的清单来自于服务注册中心。通服务端负载均衡的架构类似,在客户端负载均衡中也需要心跳去维护服务端清单的健康性,这个步骤要与服务注册中心配合完成、在Spring Cloud实现的服务治理框架中,默认会创建针对各个服务治理框架的Ribbon自动化整合配置,比如Euruka中的org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration,Consul中的org.apringframework.cloud.consul.discovery.RibbonCounsulAutoConn。在实际使用的时候,我们通过查看这两个类的实现,以找到他们的配置详情来帮助我们更好的使用它。

通过Spring Cloud Ribbon的封装,我们在微服务架构中使用客户端负载均衡调用非常简单,只需要如下两步:

  • 服务提供者只需要启动多个服务实例并注册到一个注册中心或是多个相关联的服务注册中心。
  • 服务消费者直接通过调用被@LocdBalanced注解修饰过的RestTemplate来实现面向服务的接口调用。

二、RestTemplate详解

RestTemplate针对不同请求类型和参数类型的服务调用实现。

1、GET请求

在RestTemplate中,对GET请求可以通过如下两个方法进行调用实现

第一种:getForEntity函数,该方法返回的是ResponseEntity,该对象时Spring对HTTP请求响应的封装,其中主要存储了HTTP的几个重要元素,比如HTTP请求状态码的枚举对象HttpStatus、在它的服了你HttpEntity中还存储着HTTP请求头信息对象HttpHeaders已经泛型类型的请求体对象。eg:访问USER-SERVER服务的/user请求,同时最后一个参数didi会替换url中的{1}占位符,而返回的ResponseEntity对象中的body内容类型会根据第二个参数转换为String类型。

 RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://USER-SERVICE/user?name={1}",String.class,"didi");
String body = responseEntity.getBody();

如果我们希望返回的body是一个User对象类型,也可以如下:

 RestTemplate restTemplate = new RestTemplate();
ResponseEntity<User> responseEntiry = restTemplate.getForEntity("http://USER-SERVICE/user?name={1}",User.class,"didi");
User body = responseEntity.getBody();

上述是比较常用的方法,getForEntity函数实际上提供了以下三种不同的重载实现:

getForEntity(String url,Class responseType,Object ... urlVariables):该方法提供;额三个参数,其中url为请求的地址,responseType为请求相应提body的包装类型,urlVariables为url中的参数绑定。GET请求的参数绑定通常使用url中拼接的方式,比如:http://USER-SERVICE/user?name=didi.我们可以像这样将自己的参数拼接到url中,但是更好的方式是在url中使用占位符并配合参数urlVariable参数实现GET请求的参数绑定,比如url定义为http://USER-SERVICE/user?name={1},调用的时候使用getForEntity{"http://USER-SERVICE/user?name={1}",String.class,"didi"}其中第三个参数didi会替换url中的{1}占位符。

getForEntity(String url,Class responseType,Map urlVariables):该方法中,只有urlVariables的参数类型与上述的方法不同。这里使用了Map类型,所以使用该方法进行参数绑定时需要在占位符中指定Map中参数的key值,比如url定义为http://USER-SERVICE/user?name=${name}在Map类型的urlVariables中,我们就需要put一个key为name的参数来绑定url中{name}占位符的值

eg:

 RestTemplate restTemplate = new RestTemplate();
Map<String,String> params = new HashMap<>();
params.put("name","data");
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://USER-SERVICE/user?name={name}",String.class ,params);

getForEntity(URI  url ,Class ResponseType):该方法使用URI对象来代替之前的url和urlVariables参数来指定访问地址和参数绑定。URI是JDK java.net包下的一个类,它表示一个统一资源标识符引用

 RestTemplate restTemplate = new RestTemplate();
UriComponents uriComponents = UriComponentsBuilder.fromriString("http://USER-SERVICE/user?name=${name}").build().expand("dodo").encode();
URI uri = uriComponents.toUri();
ResponseEntity<String> responseEntity = restTemplate.getForEntity(url,String.class).getBody();

第二种:getForObject函数

该方法可以理解为getForEntity的进一步封装,通过HttpMessageConvertExtractor对HTTP的请求响应体body内容进行转接,实现请求直接返回包装好的对象内容。eg:

 RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(uri,String.class)

当body是一个User对象时,可以直接这样实现eg:

 RestTemplate restTemplate = new RestTemplate();
User result = restTemplate.getForObject(uri,User.class);

当不需要关注请求响应除body外的其他内容时,该函数非常好用,可以少有从Response中获取body的步骤。它与getForEntity函数类似,也提供了三种不同的重载实现

getForObject(String url,Class responseType,Object...urlVariables):与getForEntity的方法类似,url参数指定访问的地址,responseType参数定义该方法的返回类型,url参数指定访问的地址,responseType参数定义该方法的返回类型,urlVariables参数为url中占位符对应的参数

getForObject(String url,Class responseType,Map urlVariables):在该甘薯中,使用Map类型的urlVariable替代上面数组形式的urlVariable,因此使用时在url中需要将占位符的名称与Map类型的key一一对应设置

getForObject(URI url,Class responseType):该方法使用URI对象来代替之前的url和urlVariable参数使用。

2、POST请求

在RestTemplate中,对POST请求时可以通过如下三个方法进行调用实现。

第一种:postForEntity函数。该方法同GET请求中的getForEntity类型,会在调用后返回ResponseEntity<T>对象,其中T为请求响应的body类型,eg:使用postForEntity提交POST请求道USER--SERVICE服务的/user接口,提交的body内容为user对象,请求响应返回的body类型为String.

 RestTemplate restTemplate = new RestTemplate();
User user = new User("didi",);
ResponseEntity<String> responseEntity = restTemplate.postForEntity("http://USER-SERVICE/user",user,String.class);
String body = responseEntity.getBody();

postForEntity函数也实现了三种不同的重载方法

postForEntity(String url,Object request,Class responseType,Object...urlVariables)

posrForEntity(String url,Object request,Class  responseType,Map uriVariables)

postForEntity(URI uri ,Object request,Class responseType)

第二种:postForObject函数,该方法也跟getForObject的类型类似,它的作用是简化postForEntity的后续处理。通过直接将请求响应的body内容包装成对象类返回使用,eg:

 RestTemplate restTemplate = new RestTemplate()l
User user = new User("didi",);
String postResult = restTemplate.postForObject("http://USER-SERVICE/user",user,String.class);

postForObject函数也实现了三种不同的重载方法

postForObject(String url,Object request,Class responseType,Object...urlVariables)

postForObject(String url,Object request,Class  responseType,Map uriVariables)

postForObject(URI uri ,Object request,Class responseType)

第三种:postForLocation函数,该方法实现了以POST请求提交资源,并返回新资源的URI,比如下面的例子

 User user = new User("didi",);
URI responseURI = restTemplate.postForLocation("http://USER-SERVICE/user",user);

postForLocation函数也实现了三种不同的重载方法

postForLocation(String url,Object request,bject...urlVariables)

postForLocation(String url,Object request,Class  responseType,Map uriVariables)

postForLocation(URI uri ,Object request,Class responseType)

3、PUT请求

在RestTemplate中,对PUT请求可以通过put方法进行调用实现,比如:

 RestTemplate restTemplate = new RestTemplate();
Long id = 1000L;
User user = new User("didi",);
restTemplate.put("http://USER-SERVICE/user/{1}",user,id);

put函数三种不同的重载方法:

put(String url,Object request,bject...urlVariables)

put(String url,Object request,Map uriVariables)

put(URI uri ,Object request)

put函数为void类型,所以没有返回内容,也就没有其他函数定义的responseType参数,除此之外的其他传入参数定义与用法与postForObject基本一致。

4、DELETE请求

在RestTemplate中,对DELETE请求可以通过delete请求方法进行调用实现,eg:

 RestTemplate restTemplate = new RestTemplate();
Long id = 1000L;
restTemplate.delete("http://USER-SERVICE/user/{1}",id);

delete函数三种不同的重载:

delete(String url,bject...urlVariables)

delete(String url,Map uriVariables)

delete(URI uri )

由于我们在进行REST请求时,通常将DELETE请求的唯一标识拼接在url中,所以DELETE请求也不需要request的body信息。url指定DELETE请求的位置,urlVariables绑定url中的参数即可。