深入学习springCloud——Ribbon/OpenFeign负载均衡服务接口调用

1.Ribbon是什么
2.Ribbon负载均衡演示
3.Ribbon轮询算法原理和手写
4.OpenFeign是什么
5.OpenFeign使用步骤
6.OpenFeign超时控制
7.OpenFeign日志打印
8.OpenFeign和Feign的区别
1.Ribbon是什么
前面我们学过了深入学习springCloud——服务注册中心之eureka,但是服务和服务之间该如何通过注册中心进行调用,以及如果有多个服务提供者的话,如何提供负载均衡的策略,这都是我们要考虑的问题,此时就出现了Ribbon!
Ribbon是spring Cloud 的一套客户端负载均衡的工具!
Ribbon主要提供的功能是提供客户端软件的负载均衡算法和服务调用。简单地说,Ribbon能获取该服务的所有实例,他会自动地帮你基于某种规则(比如轮询,随机等)算法去调用这些机器。我们很容易地使用Ribbon实现自定义的的Load Balancer(简称LB)算法。
但是Ribbon目前也进入了维护模式,替代方案是OpenFeign,不过为了更好地学习OpenFeign,我们就从Ribbon开始讲解,慢慢过渡到OpenFeign.
深入学习springCloud——Ribbon/OpenFeign负载均衡服务接口调用
文章图片

为了更好地学习Ribbon,我们先来了解一下负载均衡(LB)是什么:
因为我们的服务都是集群部署的,所以负载均衡就是将客户端的请求,平均地分配到同一种集群服务的多个实例上面,从而让每一个服务接受到的请求数量差不多,达到HA(高可用)。
常见的负载均衡方式:
1)Ribbon本地负载均衡
在调用微服务接口的时候,调用者会从注册中心获取服务器注册表,然后通过算法进行平均调用,负载均衡是由客户端实现的(进程内LB)。
2)Nginx服务端负载均衡
客户端先把所有请求交给nginx,然后由nginx转发实现请求,负载均衡是由服务器端实现的(集中式LB)。
2.Ribbon负载均衡演示
用Ribbon调用服务的流程图:
深入学习springCloud——Ribbon/OpenFeign负载均衡服务接口调用
文章图片

Ribbon在工作的时候,有以下的步骤:
1)调用者先从Eureka中取出被调用者的服务列表
2)根据用户的策略(轮询,随机等),从server取到的服务注册表中选取一个地址,进行调用。
我们先准备两个服务提供者,以便更好地显示出负载均衡的效果。
深入学习springCloud——Ribbon/OpenFeign负载均衡服务接口调用
文章图片

pom:

4.0.0org.springframework.boot spring-boot-starter-parent 2.6.6 com.example service-provider-8501 0.0.1-SNAPSHOT service-provider-8501 Demo project for Spring Boot1.8 2021.0.1 org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import org.springframework.boot spring-boot-maven-plugin

yml:
server: port: 8501eureka: instance: hostname: service-provider #eureka服务端的实例名称 prefer-ip-address: true #访问路径可以显示IP地址 instance-id: ${spring.cloud.client.ip-address}:${server.port}-service-provider #访问路径的名称格式 client: register-with-eureka: true#false表示不向注册中心注册自己。 fetch-registry: true#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务 service-url: #集群指向其它eureka defaultZone: http://127.0.0.1:8001/eureka/,http://127.0.0.1:8002/eureka/ spring: application: name: service-provider

主启动:
@SpringBootApplication @EnableEurekaClient public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }

测试的controller:
@RestController public class ProviderController {@Value("${server.port}") private String serverPort; @GetMapping(value = "https://www.it610.com/payment/get/{id}") public String getPaymentById(@PathVariable("id") Long id) { return "调用成功,服务端口为"+serverPort; } }

我们复制一个一模一样的,改改端口号和名字就可以了。
接下来是服务消费者:
pom:
4.0.0org.springframework.boot spring-boot-starter-parent 2.6.6 com.example ribbon-cunsumer-8401 0.0.1-SNAPSHOT ribbon-cunsumer-8401 Demo project for Spring Boot1.8 2021.0.1 org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import org.springframework.boot spring-boot-maven-plugin

yml:
server: port: 8401eureka: instance: hostname: ribbon-cunsumer #eureka服务端的实例名称 prefer-ip-address: true #访问路径可以显示IP地址 instance-id: ${spring.cloud.client.ip-address}:${server.port}-ribbon-cunsumer #访问路径的名称格式 client: register-with-eureka: true#false表示不向注册中心注册自己。 fetch-registry: true#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务 service-url: #集群指向其它eureka defaultZone: http://127.0.0.1:8001/eureka/,http://127.0.0.1:8002/eureka/ spring: application: name: ribbon-cunsumer

主启动:
@SpringBootApplication @EnableEurekaClient public class DemoApplication {public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }}

测试类:
config:
//这是一个方便我们使用http调用的一个服务类 import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class ApplicationContextConfig { @Bean @LoadBalanced//负载均衡调用 public RestTemplate getRestTemplate() { return new RestTemplate(); } }

controller:
@RestController public class consumerController {//通过eureka的调用的服务地址 public static final String PAYMENT_URL = "http://SERVICE-PROVIDER"; @Resource private RestTemplate restTemplate; @GetMapping("/payment/get/{id}") public String create(@PathVariable("id") Long id) { return restTemplate.getForObject(PAYMENT_URL +"/payment/get/"+id,String.class); }}

我们启动服务后,调用http://localhost:8401/payment...,一直F5刷新,就会轮询两个服务,达到负载均衡的目的了。
深入学习springCloud——Ribbon/OpenFeign负载均衡服务接口调用
文章图片

深入学习springCloud——Ribbon/OpenFeign负载均衡服务接口调用
文章图片

3.Ribbon轮询算法的更换和原理
api的调用其实并不是难事,我们可以试着来配一下其它负载均衡的算法,以及更进一步地看看负载均衡算法的源码。
配置类:
@Configuration public class MyLoadBalanceConfig {@Bean public ReactorLoadBalancer randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory){ String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); //随机访问为RandomLoadBalancer,轮询访问为RoundRobinLoadBalancer return new RandomLoadBalancer(loadBalancerClientFactory .getLazyProvider(name, ServiceInstanceListSupplier.class),//RoundRobinLoadBalancer name); }}

主启动:
@SpringBootApplication @EnableEurekaClient @LoadBalancerClients(defaultConfiguration = {MyLoadBalanceConfig.class}) public class DemoApplication {public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }}

重启一下,看看访问的规则,是随机的。
深入学习springCloud——Ribbon/OpenFeign负载均衡服务接口调用
文章图片

轮询的源码分析:
private Response getInstanceResponse(List instances) { if (instances.isEmpty()) { if (log.isWarnEnabled()) { log.warn("No servers available for service: " + serviceId); } return new EmptyResponse(); } // TODO: enforce order? int pos = Math.abs(this.position.incrementAndGet()); //接口的请求次数%集群服务器的总数量=实际调用服务的下标,每次重启服务后,接口访问次数从0开始 ServiceInstance instance = instances.get(pos % instances.size()); return new DefaultResponse(instance); }

4.OpenFeign是什么
我们先上spring cloud官网看一下,openFeign是一个什么样的工具:https://cloud.spring.io/sprin...
深入学习springCloud——Ribbon/OpenFeign负载均衡服务接口调用
文章图片

Feign是一个声明式的WebService客户端,使用Feign能让编写Web Service客户端更加简单。它的使用方法是定义一个服务接口然后再上面增加注解。Spring cloud对feign进行了封装,使其支持Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。
说了这么多,我们可能还是一头雾水,简单地说:
Feign旨在使编写java Http客户端变得更容易。
前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的时候,形成了一套模板化的调用方法。但是在实际开发的过程中,对其它微服务的调用可能不止一处,一个接口会被多次使用,所以针对每一个微服务,都自行封装成一些客户端类来包装这些依赖服务的调用,就显得尤为重要。所以Feign根据这个理念进行了封装,它对每个微服务的调用都封装成了一个独立的类。在Feign的实现下,我们只需要创建一个接口并使用注解的方式来配置它,就可以完成对服务提供方的绑定,进行统一管理,简化了Ribbon的使用,降低了服务调用客户端的开发量。
OpenFeign同时也集成了Ribbon
利用Ribbon维护了服务提供者的列表,并且通过轮询的方式实现了客户端的负载均衡,而与Ribbon不同的是,feign只需要定义服务绑定接口且以声明式的方法,优雅而简单地实现了服务调用。
5.OpenFeign使用步骤
我们新建一个项目:
深入学习springCloud——Ribbon/OpenFeign负载均衡服务接口调用
文章图片

pom:
4.0.0org.springframework.boot spring-boot-starter-parent 2.6.6 com.example feign-cunsumer-8402 0.0.1-SNAPSHOT feign-cunsumer-8402 Demo project for Spring Boot1.8 2021.0.1 org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.cloud spring-cloud-starter-openfeign org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import org.springframework.boot spring-boot-maven-plugin

yml
server: port: 8402eureka: instance: hostname: feign-cunsumer #eureka服务端的实例名称 prefer-ip-address: true #访问路径可以显示IP地址 instance-id: ${spring.cloud.client.ip-address}:${server.port}-feign-cunsumer #访问路径的名称格式 client: register-with-eureka: true#false表示不向注册中心注册自己。 fetch-registry: true#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务 service-url: #集群指向其它eureka defaultZone: http://127.0.0.1:8001/eureka/,http://127.0.0.1:8002/eureka/ spring: application: name: feign-cunsumer

主启动
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableEurekaClient @EnableFeignClients public class DemoApplication {public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }}

测试类:
@Component @FeignClient(value="https://www.it610.com/article/SERVICE-PROVIDER")//要调用的服务名字 public interface ProviderFeignService { //要调用的接口 @GetMapping(value = "https://www.it610.com/payment/get/{id}") String getPaymentById(@PathVariable("id") Long id); }

@RestController public class ConsumerController {@Autowired private ProviderFeignService providerFeignService; @GetMapping("/payment/get/{id}") public String create(@PathVariable("id") Long id) { //直接使用 return providerFeignService.getPaymentById(id); }}

调用结果:
深入学习springCloud——Ribbon/OpenFeign负载均衡服务接口调用
文章图片

6.OpenFeign超时控制
在调用一个接口的时候,一定要有超时控制,这在工作中有两个好处:
1)防止一个接口拖垮整个系统,导致整个功能不可用。
2)可以保护自己,如果这是别人的接口,超时了,可以把问题丢给别人,防止自己背锅,因为这本来就是别人的锅,报的错不应该在你的接口上。
方法很简单,我们做一个配置就行了,
yml
ribbon: #建立连接所用时间 ReadTimeout: 60000 #建立连接后从服务端获取资源的最大时间 ConnectTimeout: 60000

7.OpenFeign日志打印
Feign提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解Feign中Http请求的详情,从而进行监控和输出。
yml
logging: level: # feign日志以什么级别监控哪个接口 com.example.demo.feign.*: debug

配置类:
@Configuration public class FeignConfig { @Bean Logger.Level feignLoggerLevel() { //NONE:默认的,不显示任何日志; //BASIC:仅记录请求方法、URL、响应状态码及执行时间; //HEADERS:除了 BASIC 中定义的信息之外,还有请求和响应的头信息; //FULL:除了 HEADERS 中定义的信息之外,还有请求和响应的正文及元数据。 return Logger.Level.FULL; } }

运行结果:
深入学习springCloud——Ribbon/OpenFeign负载均衡服务接口调用
文章图片

8.OpenFeign和Feign的区别
【深入学习springCloud——Ribbon/OpenFeign负载均衡服务接口调用】其实OpenFeign出来之前,还有一个Feign组件,不过Feign已经停止维护了,所以我们直接使用OpenFeign就好。
feign OpenFeign
内置了Ribbon,用来做客户端的负载均衡,去调用服务列表的服务 在Feign的基础上支持了Spring MVC的注解
使用Feign注解调用接口,调用这个接口,就可以调用服务注册中心的服务 @FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

    推荐阅读