Robbin 提供的客户端软负载均衡,是SpringCloud微服务的重要特征之一,同时,Fegin 提供了Robbin软负载的声明式调用。
一、Robbin
Robbin是springcloud的LB调用组件,提供客户端的软件负载均衡。Ribbon客户端组件提供一系列完善的配置项,如连接超时,重试等。使用 @LoadBalanced 注解和 RestTemplate 注入。
1、Robbin 的 LoadBalance 分类
目前主流的LB方案可分成两类,
- 集中式LB, 即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方。
- 进程内LB,将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。
Ribbon 属于进程内LB,即客户端的软件负载均衡。
2、Robbin 的负载均衡策略
- 轮询(RoundRobin),以轮询的方式依次将请求调度不同的服务器,即每次调度执行i = (i + 1) mod n,并选出第i台服务器。
- 随机(Random),随机选择状态为UP的Server。
- 加权响应时间(WeightedResponseTime),根据相应时间分配一个weight,相应时间越长,weight越小,被选中的可能性越低。
- 区域感知轮询(ZoneAvoidanceRule),复合判断server所在区域的性能和server的可用性选择server。
3、Robbin 的核心组件和工作流程
Ribbon的核心组件(均为接口类型)有以下几个,
- ServerList,用于获取地址列表。它既可以是静态的(提供一组固定的地址),也可以是动态的(从注册中心中定期查询地址列表)。
- ServerListFilter,仅当使用动态ServerList时使用,用于在原始的服务列表中使用一定策略过虑掉一部分地址。
- IRule,选择一个最终的服务地址作为LB结果。选择策略有轮询、根据响应时间加权、断路器(当Hystrix可用时)等。
Ribbon在工作时首选会通过ServerList来获取所有可用的服务列表,然后通过ServerListFilter过虑掉一部分地址,最后在剩下的地址中通过IRule选择出一台服务器作为最终结果。
4、Robbin 的使用示例
使用 Robbin 做服务调用的时候,我们用 @LoadBalanced 注解和 RestTemplate 注入。
a、启动类里,可以引入 RestTemplate,并使用 @LoadBalance 注解,
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class RibbonConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(RibbonConsumerApplication.class, args);
}
@Bean
@LoadBalanced // robbin 注解
public RestTemplate restTemplate(){
return new RestTemplate();
}
/*
//这里也可以自己定义Robbin的负载策略
@Bean
public IRule ribbonRule(){
return new RandomRule();
}*/
}
b、使用 RestTemplate 调用
@Service
public class ConsumerService {
@Autowired
private RestTemplate restTemplate; // 注入 restTemplate
public String helloService() {
return restTemplate.getForEntity("http://service-1/sayHi", String.class).getBody();
}
}
二、Fegin
Fegin 对 Robbin 进行了封装,是一个声明式的Http端调用。Feign整合了Ribbon和Hystrix,使服务端的调用编程更容易(使用@EnableFeignClients 和 @FeignClient 注解)。Feign具有如下特性:
- 可插拔的注解支持,包括Feign注解和JAX-RS注解;
- 支持可插拔的HTTP编码器和解码器;
- 支持Hystrix和它的Fallback;
- 支持Ribbon的负载均衡;
- 支持HTTP请求和响应的压缩。
1、Fegin 解决的问题
Fegin 封装了Http调用流程,更适合面向接口化的变成习惯。在服务调用的场景中,经常调用基于Http协议的服务,使用到的框架有HttpURLConnection、OkHttp3 、Netty等,这些框架在基于自身的专注点提供了自身特性。而从角色划分上来看,他们的职能是一致的提供Http调用服务。具体流程如下:
在springcloud中使用Robbin和RestTemplate的时候,也要走一下上面的流程,还是相对比较复杂的。而Fegin的出现,对HTTP请求调用的流程进行了封装,使服务端的调用编程更容易。
2、Fegin 的类加载流程
- 通过主类上的EnableFeignClients 注解开启FeignClient;
- 根据Feign 的规则实现接口,并加上FeignClient注解,供调用的地方注入调用;
- 程序启动后,会扫描所有FeignClient 注解的类,并将这些信息注入到IOC 容器中;
- 当b中接口被调用时,通过jdk代理,以及反射(Spring处理注解的方式),来生成具体的RequestTemplate
- RequestTemplate 生成Reqest
- Request 交给httpclient处理,这里的httpclient 可以是OkHttp,也可以是HttpUrlConnection 或者HttpClient
- 最后Client被封装到LoadBalanceClient类,这个类结合Ribbon 实现负载均衡。
3、Fegin 的运行原理
4、Fegin 使用示例
Fegin 的使用,主要注意两个注解,
- @EnableFeignClients:加在应用的启动类上。
- @FeignClient:加在定义的服务接口上,表示该接口支持Feign调用。
a、启动类 @EnableFeignClients 注解
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients // 启用fegin声明式调用
public class FeginComsumerApplication {
public static void main(String[] args) {
SpringApplication.run(FeginComsumerApplication.class, args);
}
}
b、声明一个调用的Feign接口,
@Service
@FeignClient(name = "name-service")
public interface NameService {
@RequestMapping(value = "/getName", method = RequestMethod.GET)
public String getName();
}
c、服务端提供接口实现
@RequestMapping(value = "/getName", method = RequestMethod.GET)
public String getName(){
return "hello world";
}
d、fegin接口调用
@Autowired
private NameService feginNameServiceClient;
@RequestMapping(value = "/getName", method= RequestMethod.GET)
public String getName(){
return feginNameServiceClient.getName();
}