微服务系列(3)- Ribbon负载均衡

1. Ribbon负载均衡

1.1 负载均衡原理

SpringCloud底层其实是利用了一个名为Ribbon的组件,来实现负载均衡功能的。

image-20220416111833049

1.2 负载均衡源码

原理 : LoadBalancerInterceptor 类会对RestTemplate的请求进行拦截. 然后从Eureka根据服务id获取服务列表,随后利用负载均衡算法得到真实的服务地址信息,替换服务id。

浏览器发出请求后, 先被 LoadBalancerInterceptorintercept 所拦截

	
	// LoadBalancerInterceptor 类
	@Override
	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));
	}

然后通过 getHost()getURI(); 获取服务名和请求链接

final URI originalUri = request.getURI();
String serviceName = originalUri.getHost();

接下来执行 execute() 方法, 先从 Eureka 服务端根据服务名拉取微服务实例

RibbonLoadBalancerClientexecute() 方法如下

	// RibbonLoadBalancerClient 类
	public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
			throws IOException {
		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);
	}

具体步骤是 : 先通过 getLoadBalancer(serviceId); 方法根据微服务名称 order-service 获取所有的微服务实例 :[localhost:8081, localhost:8082] 然后再根据一定的负载均衡策略选取微服务

	// RibbonLoadBalancerClient 类
	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");
	}

通过 loadBalancer.chooseServer选取具体的微服务策略

    // ZoneAwareLoadBalancer 类
	@Override
    public Server chooseServer(Object key) {
        if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {
            logger.debug("Zone aware logic disabled or there is only one zone");
            return super.chooseServer(key);
        }
    	// .... 省略
    }

第一次执行先调用父类的 super.chooseServer(key) 方法

    // BaseLoadBalancer 类
	public Server chooseServer(Object key) {
        if (counter == null) {
            counter = createCounter();
        }
        counter.increment();
        if (rule == null) {
            return null;
        } else {
            try {
                return rule.choose(key);
            } catch (Exception e) {
                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
                return null;
            }
        }
    }

然后根据相应的负载均衡策略 rule.choose(key) 去选择服务 , 规则接口是 IRule

protected IRule rule = DEFAULT_RULE;

public interface IRule{
    /*
     * choose one alive server from lb.allServers or
     * lb.upServers according to key
     * 
     * @return choosen Server object. NULL is returned if none
     *  server is available 
     */

    public Server choose(Object key);
    
    public void setLoadBalancer(ILoadBalancer lb);
    
    public ILoadBalancer getLoadBalancer();    
}

我们使用 ctrl + h 快捷键即可查看 IRule 接口的所有的实现类, 或者选中接口, 单击右键找到Diagrams 功能展示该接口所有的实现类

image-20220418143136431

如上图, 该接口有多重实现策略, 比如 RoundRobinRule 就是轮寻策略

最后来个总结, 负载均衡的具体流程 :

image-20220418145003072

1.3 负载均衡策略

1.3.1 基本策略

负载均衡的规则都定义在IRule接口中,而IRule接口有很多不同的实现类:

image-20220418150246715

具体的策略如下

image-20220418145809585

默认的实现就是ZoneAvoidanceRule,是一种轮询方案 , 轮询方案就是挨个调用所有的服务, 比如第一次请求调用微服务01, 第二次调用微服务02, 依次类推…

1.3.2 自定义负载均衡策略

我们通过定义IRule实现可以修改负载均衡规则,有两种方式:

  • 方式一 : 在order-service中的OrderApplication类中,定义一个新的IRule:

        @Bean
        public IRule randomRule(){
            return new RandomRule();
        }
    
  • 方式二 : 在order-service的application.yml文件中,添加新的配置也可以修改规则:

    user-service: # 微服务名称
      ribbon: # 负载均衡规则
        NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
    

注意 : 一般用默认的负载均衡规则,不做修改

1.4 饥饿加载

Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。

而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:

下面是在 order-service 微服务上的配置

user-service: # 微服务名称
  ribbon: # 负载均衡规则
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
# 开启饥饿加载
ribbon:
  eager-load:
    clients: user-service # 指定对 user-service 这个服务端进行饥饿加载
    enabled: false # 开启饥饿加载

开启配置后我们可以通过观察user-service 日志

image-20220420143033846

从初始化到拉取微服务实例这一段是需要消耗一定的时间的, 如下图所示第一次访问因为要初始化负载均衡器

image-20220420143651652

所以第一次访问请求响应时间是 816ms , 而再次访问时由于服务实例已经拉取完毕, 所以时间缩短很多

image-20220420143819362

而当我们开启饥饿加载则会在项目启动时创建,降低第一次访问的耗时

image-20220420144156393

如上图可以看到, 项目在启动的时候就会自动创建 DynamicServerListLoadBalancer 实例, 然后从注册中心拉取微服务实例

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

兀坐晴窗独饮茶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值