Ribbon OpenFeign
OpenFeign 点击跳转
Ribbon 的负载均衡 和 Rest调用
Spring Cloud Ribbon 是 Netflix Ribbon 实现的一套客户端 负载均衡工具
简单的说,Ribbon 是 Netflix 发布的开源项目,主要功能是提供 客户端的复杂就哼算法和服务调用。 Ribbon
客户端组件提供一系列完善的配置项如超时、重试等。简单的说,就是配置文件中列出 load Balancer (简称
LB)后面所有的机器,Ribbon 会自动的帮助你基于某种规则(如简单轮询,随机链接等)去链接这些机器。我们很容易使用 Ribbon
自定义的负载均衡算法。
-
进程内 LB
将 LB 逻辑集成到消费方,消费方从服务注册中心获取有哪些地址可用,然后自己再从这些地址中选择一个适合的服务器。
Ribbon 就属于进程内 LB,它只是一个类库,集成于消费方进程,消费方通过它阿莱获取服务提供方的地址。
@Resource
private RestTemplate restTemplate;*
restTemplate.getForEntity(PAYMENT_URL + “/payment/get/” + id, CommonResult.class);
entity.toString():
<200,CommonResult(code=200, message=查询成功 ,serverPort:8001, data={id=41, serial=999}),
Content-Type:"application/json",
Transfer-Encoding:"chunked",
Date:"Sat, 04 Apr 2020 06:23:09 GMT",
Keep-Alive:"timeout=60",
Connection:"keep-alive"]>
换行
200
换行
Ribbon 负载规则的替换
IRule :
官方文档明确给出了警告:
这个自定义配置类不能放在 @CommpomentScan 所扫描的当前包下以及子包下,
否则我们自定义的这个规则类会被所有的 Ribbon 客户端共享,达不到特殊定制化的目的
2 主启动类添加 @RibbonClient
在启动该微服务的时候就能去加载我们的自定义 Ribbon 配置类,从而使配置生效
测试结果 : 随机
Ribbon 负载均衡算法的原理
**负载均衡算法:rest 接口第几次请求数 % 服务器集群总数量 = 时机调用服务器位置下标,每次服务重启后rest 接口技术求从1开始。
List instances = discoverClient.getInstances(“PAYMENT-SERVICE”);
如: List[0] instances = 127.0.0.1:8002
List[1] instances = 127.0.0.1:8001
8001 + 8002 组合为集群,他们共计2台服务器,集群总数为2 , 按照轮询算法原理:
当请求总数为1 时:1%2 = 1, 对应下标位置为1, 则获得服务地址为 127.0.0.1:8001
当请求总数为2 时:2%2 = 0, 对应下标位置为1, 则获得服务地址为 127.0.0.1:8002
当请求总数为3 时:2%2 = 1, 对应下标位置为1, 则获得服务地址为 127.0.0.1:8001
依次类推 。。。。**
手写一个 负载均衡算法
2 LoadBalancer 接口
3. MyLb实现类
@Slf4j
@Component
public class MyLB implements LoadBalancer {
private AtomicInteger atomicInteger = new AtomicInteger(0);
private final int getAndIncrement() {
int current;
int next;
do {
current = this.atomicInteger.get();
next = current >= Integer.MAX_VALUE ? 0 : current + 1;
} while (this.atomicInteger.compareAndSet(current, next));
log.info(" =====> 第几次访问 next: {}", next);
return next;
}
@Override
public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
int index = getAndIncrement() % serviceInstances.size();
return serviceInstances.get(index);
}
}
4.OrderController
@Resource
private DiscoveryClient discoveryClient;
@Resource
private LoadBalancer loadBalancer;
* 路由规则:轮询
* @param
* @return
*/
@GetMapping(value = "/cc/payment/lb")
public String getPaymentLB(){
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PROVIDER-SERVICE");
if(instances == null || instances.size() <= 0){
return null;
}
ServiceInstance serviceInstance = loadBalancer.instances(instances);
URI uri = serviceInstance.getUri();
return restTemplate.getForObject(uri+"/payment/lb",String.class);
}
5.测试
轮询出现,(没啥意义,默认也是这样的)
2. OpenFeign
标题0 //锚点设置
Feign 能干什么 ?
-
Feign 旨在使编写 Java HTTP 客户端变得更加容易。
前面在使用 Ribbon + RestTemplate 时, 利用 RestTemplate
请求的封装处理,形成了一套模板化的调用方法。但是在实际开发中,对于服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常会针对每个微服务自行封装一些客户端类来包装这些服务依赖的调用。所以,Feign 在此基础上除了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign
的实现下,我们只需要创建一个可口,并使用注解的方式来配置它(以前是Dao 接口上main标注 Mapper
注解,现在是一个微服务接口上面标注一个Feign 注解即可),即可完成对一个服务提供方的接口绑定,简化了使用 Spring cloud
Ribbon 时, 自动封装服务调用客户端的开发量。Reign 集成了 Ribbon 利用 Ribbon 维护了服务方的服务列表信息,并且通过伦序实现了客户端的负载均衡,而 Ribbon
不同的是,通过reign 只需要定义服务绑定接口且一声明式的方法,优雅而简单的实现了服务调用。
OpenFeign
openfeign 是 spring cloud 在 feign 的基础上支持 spring mvc 注解 ,如 @RequestMapping 等等,OpenFeign 的 @FeignClient 可以解析Spring MVC 的 @RequestMapping 注解下的接口,并通过动态代理的方式生产实现类 ,实现类中做负载均衡并且用其他服务。
依赖
1.0 OpenFegin 的服务调用
- 新建 cloud-consumer-feign-order80
1.2 pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.atguigu.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-consumer-feign-order80</artifactId>
<dependencies>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--引入自定义的api通用包,可以使用Payment支付Entity-->
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--一般基础通用配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
3. application.yml
server:
port: 81
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka
4. 主启动类
package com.atguigu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients //开启Feign
public class OrderFeignMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderFeignMain80.class,args);
}
}
5. 业务类
//业务逻辑接口 + @FeignClient 配置调用provider服务
package com.atguigu.springcloud.service;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE") //指定调用哪个微服务
public interface PaymentFeignService {
@GetMapping(value = "/payment/get/{id}") //哪个地址
CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
}
package com.atguigu.springcloud.controller;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.service.PaymentFeignService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@Slf4j
public class OrderFeignController {
@Resource
private PaymentFeignService paymentFeignService;
@GetMapping(value = "/consumer/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
return paymentFeignService.getPaymentById(id);
}
}
6. 测试
先启动2个eureka集群 7001/7002
在启动2个微服务 8001/8002
启动OpenFeign 80
轮询出现
**
OpenFeign 超时控制
1.1 8001,8002 PaymenController 中添加 一个 超时 的方法
@GetMapping(value = "/payment/feign/timeout")
public String paymentTimeout(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return serverPort;
}
1.2 在 PaymentFeignService 添加方法
@GetMapping(value = "/payment/feign/timeout")
public String paymentTimeout();
1.3 Controller
/**
* feign 超时控制
* @return
*/
@GetMapping(value = "/consumer/payment/feign/timeout")
public String paymentTimeout(){
return paymentFeignService.paymentTimeout();
}
整体图展示
1.4 测试
1.5 修改配置
# 设置 feign 客户端超时时间(OpenFeign 默认支持 ribbon)
ribbon:
# 指的是建立链接所用的时间,适用于网络状况正常的情况下, 两端链接所用的时间
ConectTimeout: 5000
# 指的是建立链接后从服务器读取可用资源所用的时间
ReadTimeout: 5000
再次 测试 ,即可以成功看到端口号
OpenFeign日志增强
openfeign提供了日志打印功能。
Logger有四种类型:NONE(默认)、BASIC、HEADERS、FULL,通过注册Bean来设置日志记录级别
1.0 配置 Bean
package com.atguigu.springcloud.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignConfig {
@Bean
public Logger.Level feignLoggerLevel(){
return Logger.Level.FULL; //全部日志信息
}
}
2.0 yml
logging:
level:
# feign日志以什么级别监控哪个接口
com.atguigu.springcloud.service.PaymentFeignService: debug
访问 : http://localhost:81/consumer/payment/get/31