小言_互联网的博客

【微服务】2、一篇文章详解 Ribbon 负载均衡

351人阅读  评论(0)

📝 【上篇文章】
🔖 用 Eureka 做注册中心
🔖 user-service 的多个实例向 Eureka 注册中心注册自己的服务信息
🔖 order-service 通过服务名(如 userservice)获取 Eureka 注册中心里面指定服务名(如 userservice)的服务列表
🔖 通过一个服务名(如 userservice)可以获取到多个服务地址信息
❓ 究竟通过哪个地址信息发送请求呢 ❓
🔖 这通过【负载均衡】来决定

一、负载均衡原理(debug 源码)

(1) 基本介绍



📖 @LoadBalanced 注解表示:将来 RestTemplate 发起的请求要被 ribbon 拦截

📖 这个拦截操作是通过 LoadBalancerInterceptor 完成的
📖 它是 SpringCloud 中的拦截器org.springframework.cloud.client.loadbalancer
📖 LoadBalancerInterceptorClientHttpRequestInterceptor 接口的实现类



(2) 打断点

① LoadBalancerInterceptor.java - intercept()

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);
	// this.loadBalancer: RibbonLoadBalancerClient
	// 进入 this.loadBalancer.execute 方法
	return this.loadBalancer.execute(serviceName,
			this.requestFactory.createRequest(request, body, execution));
}

② RibbonLoadBalancerClient.java - execute()

public <T> T execute(String serviceId, LoadBalancerRequest<T> request)
		throws IOException {
   
	// serviceId: 服务名
	return execute(serviceId, request, null);
}

③ RibbonLoadBalancerClient.java - execute()

🔋 调用 RibbonLoadBalancerClient 重载的另一个 execute() 方法

public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
		throws IOException {
   
	// 通过服务名获取负载均衡器
	// loadBalancer 的 allServerList 中包含服务地址信息
	ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
	// 负载均衡, 获取某个服务地址信息
	Server server = getServer(loadBalancer, hint);
	if (server == null) {
   
		throw new IllegalStateException("No instances available for " + serviceId);
	}
	RibbonServer ribbonServer = new RibbonServer(serviceId, server,
			isSecure(server, serviceId),
			serverIntrospector(serviceId).getMetadata(server));

	return execute(serviceId, ribbonServer, request);
}

 


④ RibbonLoadBalancerClient.java - getServer()

protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
   
	if (loadBalancer == null) {
   
		return null;
	}
	// Use 'default' on a null hint, or just pass it on?
	return loadBalancer.chooseServer(hint != null ? hint : "default");
}

⑤ ZoneAwareLoadBalancer.java - chooseServer()

🔋 调用父类的 chooseServer()

⑥ BaseLoadBalancer.java - chooseServer()

public Server chooseServer(Object key) {
   
    if (counter == null) {
   
        counter = createCounter();
    }
    counter.increment();
    // rule 默认是 ZoneAvoidanceRule 类型, 不为 null
    if (rule == null) {
   
        return null;
    } else {
   
        try {
   
            // 根据 key 选择一个活着的的服务
            return rule.choose(key);
        } catch (Exception e) {
   
            logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
            return null;
        }
    }
}

 


⑦ RibbonLoadBalancerClient.java - execute()

🔋 拿真实的服务地址替换服务名

(3) 流程

二、负载均衡策略

(1) 负载均衡策略

📝 Ribbon 的负载均衡策略是通过一个叫做 IRule 的接口来定义的,每个实现类是一种策略

(2) 调整负载均衡策略

📝 有种方式指定 IRule 实现,进而修改负载均衡规则

① 注入(@Bean)自己需要的负载均衡策略(IRule)

全局:整个微服务

  @Bean
  public IRule iRule() {
   
      return new RandomRule();
  }

📝 整个微服务发送的请求都通过【随机】方式负载均衡

② yaml 文件指定对某个指定微服务发送请求的使用采用指定的负载均衡策略

局部:只对某个微服务有作用

userservice: # 该微服务向 userservice 发送的请求使用【随机】负载均衡
  ribbon:
   NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

(3) 饥饿加载

📝 Ribbon 默认是采用懒加载 【第一次访问时才会创建LoadBalanceClient,请求时间会很长】
📝 饥饿加载会让 LoadBalanceClient 在项目启动时被创建,进而降低第一次访问的耗时

ribbon:
  eager-load:
    enabled: true # 开启饥饿加载
    clients:
      - userservice # 指定对哪些微服务进行饥饿加载

📖 根据 Bilibili 黑马程序员进行学习
📖 如有错误,请不吝赐教哦


转载:https://blog.csdn.net/m0_54189068/article/details/128246604
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场