【Pigeon源码阅读】客户端负载均衡实现原理(十一)

客户端负载均衡入口

先回顾下DefaultRouteManager#route的实现,在pigeon中,会通过调用这个方法,获取唯一的服务提供方连接实例,来完成RPC远程调用:

public Client route(List<Client> clientList, InvokerConfig<?> invokerConfig, InvocationRequest request) {
   
    if (logger.isDebugEnabled()) {
   
        for (Client client : clientList) {
   
            if (client != null) {
   
                logger.debug("available service provider:\t" + client.getAddress());
            }
        }
    }
    // 获取所有可能的连接
    List<Client> availableClients = getAvailableClients(clientList, invokerConfig, request);
    // 通过客户端负载均衡策略选取合适的唯一客户端连接
    Client selectedClient = select(availableClients, invokerConfig, request);

    // 如果不是活跃的连接,重新选取
    while (!selectedClient.isActive()) {
   
        logger.info("[route] remove client:" + selectedClient);
        // 去除不活跃的
        availableClients.remove(selectedClient);
        if (availableClients.isEmpty()) {
   
            // 为空退出,避免死循环
            break;
        }
        selectedClient = select(availableClients, invokerConfig, request);
    }

    // 没有获取到满足要求的活跃的连接
    if (!selectedClient.isActive()) {
   
        throw new ServiceUnavailableException("no available server exists for service[" + invokerConfig + "], env:"
                + ConfigManagerLoader.getConfigManager().getEnv());
    }
    return selectedClient;
}

在经过路由策略获取到所有可能的连接,下一步就是通过客户端负载均衡策略选取合适的唯一客户端这一步在DefaultRouteManager#select中完成。

负载均衡策略总体实现

在DefaultRouteManager#select应用真正的负载均衡策略之前,会先做两步配置:

  1. 获取合适的负载均衡策略器,这里按优先级有3层负载均衡配置:
  1. 方法级别配置,如配置:
    pigeon.loadbalance.dynamictype={"com.dianping.arch.test.benchmark.service.EchoService#echo" : "random"}
    则会对特定服务方法优先使用random策略
  2. 服务级别配置,通过xml、api等方式配置invokerConfig的loadbalance属性
  3. 应用默认配置,如指定pigeon.loadbalance.defaulttype 来配置默认负载均衡策略
  1. 尝试过滤出优先地址配置的服务提供方连接实例。

下面来看代码的具体实现:

private Client select(List<Client> availableClients, InvokerConfig<?> invokerConfig, InvocationRequest request) {
   
    LoadBalance loadBalance = null;
    if (loadBalance == null) {
   
        // 获取具体的负载均衡实例
        loadBalance = LoadBalanceManager.getLoadBalance(invokerConfig, request.getCallType());
    }
    if (loadBalance == null) {
   
        // 如果没有配置,默认为WeightedAutoawareLoadBalance
        loadBalance = WeightedAutoawareLoadBalance.instance;
        if (request.getCallType() == Constants.CALLTYPE_NOREPLY) {
   
            // 如果调用无需返回,则用随机负载均衡策略
            loadBalance = RandomLoadBalance.instance;
        }
    }

    // 判断是否有针对特定服务方法级别动态配置的负载均衡策略配置,看下一个函数代码实现
    LoadBalance dynamicLoadBalance = getDynamicLoadBalance(request);
    if (dynamicLoadBalance != null) {
   
        loadBalance = dynamicLoadBalance;
    }

    // 判断是否有优先的服务地址配置,如果有,先过滤指定地址的服务提供方连接实例
    List<Client> preferClients = null;
    if (enablePreferAddresses) {
   
        if (availableClients != null && availableClients.size() > 1 && !CollectionUtils.isEmpty(preferAddresses)) {
   
            preferClients = new ArrayList<Client>();
            for (String addr : preferAddresses) {
   
                for (Client client : availableClients) {
   
                    if (client.getHost().startsWith(addr)) {
   
                        preferClients.add(client);
                    }
                }
                // 找到优先的立即结束
                if (preferClients.size() > 0) {
   
                    break;
                }
            }
        }
    }
    if (preferClients == null || preferClients.size() == 0) {
   
        preferClients = availableClients;
    }
    // 应用具体的负载均衡策略
    Client selectedClient = loadBalance.select(preferClients, invokerConfig, request);
    checkClientNotNull(selectedClient, invokerConfig);

    return selectedClient;
}

在最后,通过调用LoadBalance#select方法,来应用负载均衡策略找出唯一合适的服务提供方连接。
在pigeon中,提供了4种负载均衡策略,如果用户有自定义的负载均衡策略,可以实现LoadBalance接口,并实现内部的select方法,在入参的连接实例列表种选择返回唯一的连接实例,而后调用LoadBalanceManager#register方法将自定义负载均衡策略注册到负载均衡管理器中。

负载均衡策略实现

在pigeon默认实现中,有4种负载均衡策略,包括:

  1. RandomLoadBalance:基于不同的权重,随机从多个连接实例种选择一个,权重越大,被选中的可能性也越大
  2. AutoawareLoadBalance:实时获取当前客户端对每个服务端连接的请求数,获取请求数最小的进行请求
  3. RoundRobinLoadBalance:基于不同的权重,从大到小轮询所有的连接实例进行请求
  4. WeightedAutoawareLoadBalance:基于不同的权重,在实时获取每个连接实例的请求量基础上,除以权重计算一个相对的负载量,取最小的连接实例完成请求。

AbstractLoadBalance

AbstractLoadBalance是LoadBanlance接口的抽象实现,定义了所有负载均衡器的公共逻辑部分。具体实现包括两个方法:

  1. select先尝试应用客户端强制路由配置,再调用模版方法doSelect应用子类的负载均衡策略。
  2. getWeight: 计算每个连接实例的最大权重索引。

先跳过权重的计算原理,看看在获取到每个示例连接的权重后,具体的负载均衡策略如何实现doSelect完成负载均衡调用

RandomLoadBalance 基于权重随机策略

对所有连接实例的权重进行累加,生成区间在0到权重和的随机数,按权重对连接实例分段,看随机数落在哪个连接实例权重区间,返回对应的连接实例。如有client1=2,client2=3,client3=3,则权重和为8,且按权重分段有client1=[0,2),client2=[2,5),cleint3=[5,8)。生成区间[0,8)的随机数n,看随机数落在哪个连接实例权重区间。
具体源码实现:

public Client doSelect(List<Client> clients, InvokerConfig<?> invokerConfig, InvocationRequest request, int[] weights) {
   
    assert (clients != null && clients.size() >= 1);
    // 连接实例唯一直接返回
    if (clients.size() == 1) {
   
        return clients.get(0);
    }
    int clientSize = clients.size();
    // 权重和
    int totalWeight = 0;
    // 是否全部权重相同
    boolean weightAllSame = true;
    for (int i = 0; i < clientSize; i++) {
   
        totalWeight += weights[i];
        if (weightAllSame && i > 0 && weights[i] != weights[i - 1]) {
   
            // 存在权重不一样的情况
            weightAllSame = false;
        }
    }
    if (!weightAllSame) {
   
        // 生成区间在0到权重和的随机数,按权重对连接实例分段,看随机数落在哪个连接实例权重区间,返回对应的连接实例
        int weightPoint = random.nextInt(totalWeight);
        for (int i = 0; i < clientSize; i++) {
   
            Client client = clients.get(i);
            weightPoint -= weights[i];
            if (weightPoint < 0) {
   
                return client;
            }
        }
    }
    // 权重都相同,随机返回实例列表的一个
    Client client = clients.get(random.nextInt(clientSize));
    if (logger.isDebugEnabled()) {
   
        logger.debug("select address:" + client.getAddress());
    }
    return client;
}

AutoawareLoadBalance 最小请求数策略

感知服务端负载情况, 将请求路由到负载较低的服务端。具体实现原理是获取客户端视图对每个服务端连接的负载量,正在处理的请求数。获取所有最少负载量的连接实例,如果

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值