客户端负载均衡:Spring Cloud Ribbon
简介
- Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。
- 通过Spring Cloud的封装,可以让我们轻松地将面对服务的REST模块请求自动转换成客户端负载均衡的服务调用。
- 虽然Ribbon只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是Spring Cloud Ribbon几乎存在于每一个Spring Cloud构建的微服务和基础设施中。
客户端负载均衡
我们通常所说的负载均衡都是指服务端负载均衡,其中分为硬件负载均衡和软件负载均衡。硬件负载均衡主要在服务节点之间安装专门的负载均衡设备,比如F5等;而软件负载均衡则是通过在服务器上安装一些具有负载功能或模块的软件来完成请求分发工作,比如Ngnix等。不论采用硬件负载均衡还是软件负载均衡,只要是服务端负载均衡都能以类似下图的架构方式工作:
硬件负载均衡的设备或是软件负载均衡的软件模块都会维护一个下挂可用的服务端清单,通过心跳检测来剔除故障的服务端节点以保证清单中都是可以正常访问的服务端节点。当客户端发送请求到负载均衡设备的时候,该设备按照某种算法(比如线性轮询、按权重负载、按流量负载)从维护的可用服务端清单中取出一台服务端的地址,然后进行转发。
而客户端负载均衡和客户端负载均衡最大的不同点在于上面所提到的服务清单所存储的位置。在客户端负载均衡中,所有客户端节点都维护着自己要访问的服务端清单,而这些服务端的清单来自于服务注册中心,如果Eureka 服务端。同服务端负载均衡的架构类似,在客户端负载均衡也需要心跳去维护服务端清单的健康性,只是这个步骤需要与服务注册中配合完成。在Spring Cloud实现的服务治理框架中,默认会创建针对各个服务治理框架的Ribbon自动化整合配置,比如Eureka中的org.springframework.cloud.netflix.ribbon.eurek.RibbonEurekaAutoConfiguration,Consul中的org.springframework.cloud.consul.discovery.RibbonConsulAutoConfiguration。在实际使用的时候,我们可以通过查看这两个类的实现,以找到它们的配置详情来帮助我们更好地使用它。
通过Spring Cloud Ribbon的封装,我们在微服务架构中使用客户端负载均衡调用非常简单,只需要两步:
- 服务提供者只需要启动多个服务实例并注册到一个注册中心或多个相关联的服务注册中心。
- 服务消费者直接通过调用被@LoadBalanced注解修饰过的RestTemplate来是实现面向服务的接口调用。
这样,我们就可以将服务提供者的高可用以及服务消费者的负载均衡调用一起实现了。
RestTemplate详解
RestTemplate是非常有用的服务访问对象,该对象会使用Ribbon的自动化配置,同时通过配置@LoadBalanced还能够开启客户端负载均衡。
GET请求
getForEntity系列函数
该方法返回的是responseEntity对象,该对象是spring对HTTP请求响应的封装。
1 getForEntity(String url,Class responseType,Object ... urlVariables)
2 getForEntity(String url,Class responseType,Map urlVariables)
3 getForEntity(URI url,Class responseType)
// GET
// getForEntity(URI url,Class responseType)
result.append(restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class).getBody()).append("<br>");
//getForEntity(String url,Class responseType,Object ... urlVariables)
result.append(restTemplate.getForEntity("http://HELLO-SERVICE/hello1?name={1}", String.class, "didi").getBody()).append("<br>");
//getForEntity(String url,Class responseType,Map urlVariables)
Map<String, String> params = new HashMap<>();
params.put("name", "dada");
result.append(restTemplate.getForEntity("http://HELLO-SERVICE/hello1?name={name}", String.class, params).getBody()).append("<br>");
// getForEntity(URI url,Class responseType)
UriComponents uriComponents = UriComponentsBuilder.fromUriString(
"http://HELLO-SERVICE/hello1?name={name}")
.build()
.expand("dodo")
.encode();
//生成uri
URI uri = uriComponents.toUri();
result.append(restTemplate.getForEntity(uri, String.class).getBody()).append("<br>");
getForObject系列函数
该方法可以理解成getForEntity的进一步封装,实现直接返回包装好的内容对象,无需getBody()一下。
1 getForObject(String url,Class responseType,Object ... urlVariables)
2 getForObject(String url,Class responseType,Map urlVariables)
3 getForObject(URI url,Class responseType)
String result = restTemplate.getForObject(url, String.class);
POST请求
postForEntity函数
- 方法的第一参数表示要调用的服务的地址
- 方法的第二个参数表示上传的参数
- 方法的第三个参数表示返回的消息体的数据类型
postForEntity(String url,Object request,Class responseType,Object ... urlVariables)
postForEntity(String url,Object request,Class responseType,Map urlVariables)
postForEntity(String url,Object request,Class responseType)
user = new User("didi", 30);
ResponseEntity<String> responseEntity = restTemplate.postForEntity("http://HELLO-SERVICE/hello3", user, String.class);
result.append(responseEntity.getBody()).append("<br>");
postForObject函数
如果你只关注,返回的消息体,可以直接使用postForObject。用法和getForObject一致。
- 方法的第一参数表示要调用的服务的地址
- 方法的第二个参数表示上传的参数
- 方法的第三个参数表示返回的消息体的数据类型
postForObject(String url,Object request,Class responseType,Object ... urlVariables)
postForObject(String url,Object request,Class responseType,Map urlVariables)
postForObject(String url,Object request,Class responseType)
postForLocation函数
PUT请求
在RestTemplate中,PUT请求可以通过put方法调用,put方法的参数和前面介绍的postForEntity方法的参数基本一致,只是put方法没有返回值而已。
DELETE请求
对delete请求我们可以通过delete方法调用来实现。
Long id = 10001L;
restTemplate.delete("http://USER-SERVICE/user/{1}", id);
delete函数也实现了三种不同的重载方法: