Ribbon和Feign的原理

1.Ribbon

1.1 Ribbon的简介

Ribbon也是微服务治理框架中的一员,主要功能是负载均衡。负责从多个服务实例中根据某些规则选取一台进行调用,在跟nacos整合后,通过nacos获取服务列表,再根据负载均衡算法选取具体服务进行调用。

1.2 Ribbon整合原理

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

我们在使用Ribbon时会向上面代码声明一个RestTemplate添加@LoadBalanced,在Ribbon的依赖中有一个自动配置类,就会对这个这个RestTemplate增加添加一个拦截器LoadBalancerInterceptor,在使用RestTemplate访问具体服务时就会被拦截,将url中的服务名替换成真正的IP和Port。
流程图:
在这里插入图片描述

1.2 Ribbon整合关键代码

  • 1.自动配置类向restTemplate 添加拦截器
    LoadBalancerAutoConfiguration
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();

#LoadBalancerAutoConfiguration.LoadBalancerInterceptorConfig#restTemplateCustomizer
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
		restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
// 给restTemplate 添加拦截器
restTemplate.setInterceptors(list);
  • 2.执行请求进入拦截器
    LoadBalancerInterceptor#intercept
// 使用restTemplate发送请求就会被拦截
restTemplate.getForObject(url,String.class);

// 执行请求进入拦截器逻辑
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
		final ClientHttpRequestExecution execution) throws IOException {
	final URI originalUri = request.getURI();
	String serviceName = originalUri.getHost();
	Assert.state(serviceName != null,
			"Request URI does not contain a valid hostname: " + originalUri);
	// 使用服务名负载均衡
	return this.loadBalancer.execute(serviceName,
			this.requestFactory.createRequest(request, body, execution));
}
  • 3.执行负载均衡器的execute方法
    RibbonLoadBalancerClient#execute()
 public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
 	 // 初始化loadBalancer 
     ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
     // 根据负载均衡规则选择server 
     Server server = this.getServer(loadBalancer, hint);
     if (server == null) {
         throw new IllegalStateException("No instances available for " + serviceId);
     } else {
         RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
         return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);
     }
 }
  • 4.初始化loadBalancer相关逻辑
    this.getLoadBalancer(serviceId)
# DynamicServerListLoadBalancer#restOfInit
// 实例化默认ZoneAwareLoadBalancer
new ZoneAwareLoadBalancer
super(clientConfig, rule, ping, serverList, filter, serverListUpdater);
# 实例化父类
restOfInit(clientConfig);
# DynamicServerListLoadBalancer#updateListOfServers
// 更新服务列表
updateListOfServers()
servers = serverListImpl.getUpdatedListOfServers();
# NacosServerList#getServers
// nacos中的实现获取微服务列表
getServers()
discoveryProperties.namingServiceInstance().selectInstances(serviceId, group, true)

2.Feign

2.1 Feign简介

Feign主要是微服务之间的远程调用,传统服务间调用需要建立远程连接调用,使用Feign跨服务间接口调用就像是调用本地的方法一样。Feign整合了Ribbon,通过定义的feign接口使用Ribbon去选择具体服务调用,Ribbon又会将服务名替换成真正的IP和Port调用。

2.2 Feign原理

1.springboot的启动类上添加注解 @EnableFeignClients
2.定义接口如下OrderFeignService 
// 代理类中拼接方式:"http://mall-order/order/findOrderByUserId/"+id
@FeignClient(value = "mall-order",path = "/order")
public interface OrderFeignService {
    @RequestMapping("/findOrderByUserId/{userId}")
    R findOrderByUserId(@PathVariable("userId") Integer userId);
}

我们在使用Feign时需要在启动类上启用Feign,这个注解会引入一个类,这个类会专门扫描添加了@FeignClient注解的接口,通过spring的FactoryBean原理注入到spring容器中,同时生成代理。最后通过整合Ribbon替换路径中的服务名。
流程图:
在这里插入图片描述

2.3 Feign关键代码

  • 1.将添加了@FeignClient的类注册为FeignClientFactoryBean
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
this.validate(attributes);
definition.addPropertyValue("url", this.getUrl(attributes));
definition.addPropertyValue("path", this.getPath(attributes));
  • 2.bean实例化时执行的是FeignClientFactoryBean#getObject
if (!StringUtils.hasText(this.url)) {
    if (!this.name.startsWith("http")) {
        this.url = "http://" + this.name;
    } else {
        this.url = this.name;
    }

    this.url = this.url + this.cleanPath();
    // 负载均衡调用
    return this.loadBalance(builder, context, new HardCodedTarget(this.type, this.name, this.url));
}
  • 3.获取Client
protected <T> T loadBalance(Builder builder, FeignContext context, HardCodedTarget<T> target) {
    Client client = (Client)this.getOptional(context, Client.class);
    if (client != null) {
        builder.client(client);
        Targeter targeter = (Targeter)this.get(context, Targeter.class);
        return targeter.target(this, builder, context, target);
    } 
}
  • 4.执行Client.execute方法
    LoadBalancerFeignClient#execute
URI asUri = URI.create(request.url());
String clientName = asUri.getHost();
URI uriWithoutHost = cleanUrl(request.url(), clientName);
FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
	this.delegate, request, uriWithoutHost);

IClientConfig requestConfig = getClientConfig(options, clientName);
return lbClient(clientName)
	.executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
  • 5.执行Ribbon中的chooseServer
  • com.netflix.loadbalancer.ZoneAwareLoadBalancer#chooseServer
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值