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.
为了更好地学习Ribbon,咱们先来理解一下负载平衡(LB)是什么:
因为咱们的服务都是集群部署的,所以负载平衡就是将客户端的申请,均匀地调配到同一种集群服务的多个实例下面,从而让每一个服务承受到的申请数量差不多,达到HA(高可用)。
常见的负载平衡形式:
1)Ribbon本地负载平衡
在调用微服务接口的时候,调用者会从注册核心获取服务器注册表,而后通过算法进行均匀调用,负载平衡是由客户端实现的(过程内LB)。
2)Nginx服务端负载平衡
客户端先把所有申请交给nginx,而后由nginx转发实现申请,负载平衡是由服务器端实现的(集中式LB)。
2.Ribbon负载平衡演示
用Ribbon调用服务的流程图:
Ribbon在工作的时候,有以下的步骤:
1)调用者先从Eureka中取出被调用者的服务列表
2)依据用户的策略(轮询,随机等),从server取到的服务注册表中选取一个地址,进行调用。
咱们先筹备两个服务提供者,以便更好地显示出负载平衡的成果。
pom:
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.6</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>service-provider-8501</artifactId> <version>0.0.1-SNAPSHOT</version> <name>service-provider-8501</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>2021.0.1</spring-cloud.version> </properties> <dependencies> <!-- 这里应用eureka-client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- 留神要援用spring-boot-starter-web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>
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@EnableEurekaClientpublic class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }}
测试的controller:
@RestControllerpublic class ProviderController { @Value("${server.port}") private String serverPort; @GetMapping(value = "/payment/get/{id}") public String getPaymentById(@PathVariable("id") Long id) { return "调用胜利,服务端口为"+serverPort; }}
咱们复制一个截然不同的,改改端口号和名字就能够了。
接下来是服务消费者:
pom:
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.6</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>ribbon-cunsumer-8401</artifactId> <version>0.0.1-SNAPSHOT</version> <name>ribbon-cunsumer-8401</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>2021.0.1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>
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@EnableEurekaClientpublic 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;@Configurationpublic class ApplicationContextConfig{ @Bean @LoadBalanced//负载平衡调用 public RestTemplate getRestTemplate() { return new RestTemplate(); }}
controller:
@RestControllerpublic 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刷新,就会轮询两个服务,达到负载平衡的目标了。
3.Ribbon轮询算法的更换和原理
api的调用其实并不是难事,咱们能够试着来配一下其它负载平衡的算法,以及更进一步地看看负载平衡算法的源码。
配置类:
@Configurationpublic class MyLoadBalanceConfig { @Bean public ReactorLoadBalancer<ServiceInstance> 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); }}
重启一下,看看拜访的规定,是随机的。
轮询的源码剖析:
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> 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...
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应用步骤
咱们新建一个我的项目:
pom:
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.6</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>feign-cunsumer-8402</artifactId> <version>0.0.1-SNAPSHOT</version> <name>feign-cunsumer-8402</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>2021.0.1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>
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@EnableFeignClientspublic class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }}
测试类:
@Component@FeignClient(value="SERVICE-PROVIDER")//要调用的服务名字public interface ProviderFeignService { //要调用的接口 @GetMapping(value = "/payment/get/{id}") String getPaymentById(@PathVariable("id") Long id);}
@RestControllerpublic class ConsumerController { @Autowired private ProviderFeignService providerFeignService; @GetMapping("/payment/get/{id}") public String create(@PathVariable("id") Long id) { //间接应用 return providerFeignService.getPaymentById(id); }}
调用后果:
6.OpenFeign超时管制
在调用一个接口的时候,肯定要有超时管制,这在工作中有两个益处:
1)避免一个接口拖垮整个零碎,导致整个性能不可用。
2)能够爱护本人,如果这是他人的接口,超时了,能够把问题丢给他人,避免本人背锅,因为这原本就是他人的锅,报的错不应该在你的接口上。
办法很简略,咱们做一个配置就行了,
yml
ribbon: #建设连贯所用工夫 ReadTimeout: 60000 #建设连贯后从服务端获取资源的最大工夫 ConnectTimeout: 60000
7.OpenFeign日志打印
Feign提供了日志打印性能,咱们能够通过配置来调整日志级别,从而理解Feign中Http申请的详情,从而进行监控和输入。
yml
logging: level: # feign日志以什么级别监控哪个接口 com.example.demo.feign.*: debug
配置类:
@Configurationpublic class FeignConfig{ @Bean Logger.Level feignLoggerLevel() { //NONE:默认的,不显示任何日志; //BASIC:仅记录申请办法、URL、响应状态码及执行工夫; //HEADERS:除了 BASIC 中定义的信息之外,还有申请和响应的头信息; //FULL:除了 HEADERS 中定义的信息之外,还有申请和响应的注释及元数据。 return Logger.Level.FULL; }}
运行后果:
8.OpenFeign和Feign的区别
其实OpenFeign进去之前,还有一个Feign组件,不过Feign曾经进行保护了,所以咱们间接应用OpenFeign就好。
feign | OpenFeign |
---|---|
内置了Ribbon,用来做客户端的负载平衡,去调用服务列表的服务 | 在Feign的根底上反对了Spring MVC的注解 |
应用Feign注解调用接口,调用这个接口,就能够调用服务注册核心的服务 | @FeignClient能够解析SpringMVC的@RequestMapping注解下的接口,并通过动静代理的形式产生实现类,实现类中做负载平衡并调用其余服务。 |