1、Spring Cloud Ribbon概述
Spring Cloud Ribbon
是基于Netflix Ribbon
实现的一套客户端负载均衡工具
。主要功能是提供客户端的软件负载均衡算法和服务调用
。Ribbon客户端
组件提供了一系列完善的配置项,如连接超时,重试等。简单地说,就是在配置文件中列出Load Balancer
(简称LB
)后面所有的机器,Ribbon
会自动帮我们基于某种规则(如轮询
,随机连接
等)去连接这些机器,我们很容易使用Ribbon
实现自定义的负载均衡算法。
Ribbon
官网:https://github.com/Netflix/ribbon,根据官网信息,可以看到Ribbon现在进入了维护模式,替换方案是Loadbalancer
。
负载均衡的表现就是,将用户的请求分摊到多个服务器上,从而达到高可用的目的。常见的负载均衡软件有:Nginx
、LVS
、硬件F5等。
LoadBalancer
负载均衡分了两类:
集中式LoadBalancer
:在服务的消费方和提供方之间使用的独立LoadBalancer设备(可以是硬件,如F5
,也可以是软件,如Nginx
),由该设施负责把请求通过某种策略转发至服务的提供方。
进程内LoadBalancer
:将LoadBalancer集成到消费方,消费者从服务注册中心获取到可用地址,自己再从这些地址中,选择一个作为要访问的服务器。Ribbon就属于进程内LoadBalancer
,它只是一个类库,集成消费者进程,消费者通过它获取服务提供方的地址。
Nginx是服务器端负载均衡
,客户端的请求都发给Nginx,Nginx实现分发,将请求发送到不同的服务器上。
Ribbon是客户端负载均衡
,在调用微服务接口的时候,会在注册中心拿到注册信息服务列表缓存到本地JVM,在客户端通过某种规则,确定请求的链接,发送请求进行调用(从而在本地实现RPC远程服务调用技术)。
Ribbon
工作时分成两步:
第一步先选择Eureka Server
,它优先选择在同一个区域内负载均衡较少的server
。
第二步再根据用户指定的策略,在从server
取到的服务注册列表中选择一个地址。
其中Ribbon
提供了多种策略:比如轮询、随机和根据响应时间加权。
总结:Ribbon
其实就是一个软负载均衡的客户端组件(Ribbon
其实就是负载均衡 + RestTemplate
调用),它可以和其他所需请求的客户端结合使用,和eureka
结合只是其中的一个实例。
2、Ribbon负载均衡演示
之前,我们好像并没有加入Ribbon
的依赖,也实现了负载均衡,其实在spring-cloud-starter-netflix-eureka-client
依赖下,是带了spring-cloud-starter-netflix-ribbon
依赖包的,所以,我们仅仅只需要添加一个@LoadBalanced
就可以实现负载均衡。
RestTemplate
常见的方法有getForObject()
、getForEntity()
、postForObject()
、postForEntity()
方法。其中*ForObject()
方法返回对象为响应体中数据转换成的对象,基本理解为返回JSON
数据;*ForEntity()
方法返回对象是ResponseEntity
对象,包含了响应中的信息,比如响应头,响应状态码,响应体等。
将cloud-eureka-server7001
、cloud-eureka-server7002
的配置文件改成集群模式,让7001
注册中心和7002
注册中心互相注册,将cloud-provider-payment8001
服务提供者、cloud-provider-payment8002
服务提供者的配置文件改成集群模式,将cloud-consumer-order80
消费者的配置文件改成集群模式。在cloud-consumer-order80
模块中的OrderController
控制层里,添加两个方法,分别调用 getForEntity()
和postForEntity()
方法。
package com.king.springcloud.controller;
import com.king.springcloud.entities.CommonResult;
import com.king.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@Slf4j
@RestController
public class OrderController {
/**
* Eureka注册中心的提供服务模块的应用名称
*/
public static final String PAYMENT_URL = "http://CLOUD-PROVIDER-PAYMENT";
@Autowired
private RestTemplate restTemplate;
/**
* 客户端调用服务端创建方法
* @param payment
* @return
*/
@GetMapping("/consumer/payment/create")
public CommonResult create(Payment payment) {
// postForObject写操作,按照JSON数据格式
return restTemplate.postForObject(PAYMENT_URL + "/payment/createPayment", payment, CommonResult.class);
}
/**
* 客户端调用服务端创建方法
* @param payment
* @return
*/
@GetMapping("/consumer/payment/create2")
public CommonResult create2(Payment payment) {
// postForEntity写操作,按照ResponseEntity数据格式
return restTemplate.postForEntity(PAYMENT_URL + "/payment/create", payment, CommonResult.class).getBody();
}
/**
* 客户端调用服务端查询方法
* @param id
* @return
*/
@GetMapping("/consumer/payment/get/{id}")
public CommonResult getPaymentById(@PathVariable("id") Long id) {
// getForObject读操作,返回JSON对象
return restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);
}
/**
* 客户端调用服务端查询方法
* @param id
* @return
*/
@GetMapping("/consumer/payment/getForEntity/{id}")
public CommonResult getPaymentById2(@PathVariable("id") Long id) {
// getForObject读操作,返回ResponseEntity对象包含了响应中的信息,比如响应头,响应状态码,响应体等
ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);
System.out.println("status code="