负载均衡算法简单实现

接口

/**
 * 负载均衡器(消费端使用)
 */
public interface LoadBalancer {

    /**
     * 选择服务调用
     *
     * @param requestParams       请求参数(一致性Hash会用到)
     * @param serviceMetaInfoList 可用服务列表
     */
    ServiceMetaInfo select(Map<String, Object> requestParams, List<ServiceMetaInfo> serviceMetaInfoList);

}

抽象类

/**
 * 为了兼顾无需请求参数的负载均衡
 * 使用抽象类
 */
public abstract class AbstractLoadBalancer implements LoadBalancer {

    @Override
    public ServiceMetaInfo select(Map<String, Object> requestParams, List<ServiceMetaInfo> serviceMetaInfoList) {
        return select(serviceMetaInfoList);
    }

    protected abstract ServiceMetaInfo select(List<ServiceMetaInfo> serviceMetaInfoList);
}

具体实现类仅需继承抽象类即可。

实现类

轮询

AtomicInteger 原子计数器,防止并发冲突问题

/**
 * 轮询负载均衡器
 */
public class RoundRobinLoadBalancer extends AbstractLoadBalancer {

  /**
   * 当前轮询的下标(原子类防止并发问题)
   */
  private final AtomicInteger currentIndex = new AtomicInteger(0);

  @Override
  protected ServiceMetaInfo select(List<ServiceMetaInfo> serviceMetaInfoList) {
    if (serviceMetaInfoList.isEmpty()) {
      return null;
    }
    // 只有一个服务,无需轮询
    int size = serviceMetaInfoList.size();
    if (size == 1) {
      return serviceMetaInfoList.get(0);
    }
    // 取模算法轮询
    int index = currentIndex.getAndIncrement() % size;
    return serviceMetaInfoList.get(index);
  }
}

随机

Random 实现随机选取

/**
 * 随机负载均衡器
 */
public class RandomLoadBalancer extends AbstractLoadBalancer {

    private final Random random = new Random();

    @Override
    protected ServiceMetaInfo select(List<ServiceMetaInfo> serviceMetaInfoList) {
        int size = serviceMetaInfoList.size();
        if (size == 0) {
            return null;
        }
        // 只有 1 个服务,不用随机
        if (size == 1) {
            return serviceMetaInfoList.get(0);
        }
        return serviceMetaInfoList.get(random.nextInt(size));
    }
}

一致性 Hash

可以使用 TreeMap 实现一致性 Hash 环,该数据结构提供了 ceilingEntryfirstEntry 两个方法,便于获取符合算法要求的节点。

/**
 * 一致性哈希负载均衡器
 */
public class ConsistentHashLoadBalancer extends AbstractLoadBalancer {

    /**
     * 一致性 Hash 环,存放虚拟节点
     */
    private final TreeMap<Integer, ServiceMetaInfo> virtualNodes = new TreeMap<>();

    /**
     * 虚拟节点数
     */
    private static final int VIRTUAL_NODE_NUM = 100;

    @Override
    public ServiceMetaInfo select(Map<String, Object> requestParams, List<ServiceMetaInfo> serviceMetaInfoList) {
        if (serviceMetaInfoList.isEmpty()) {
            return null;
        }

        // 构建虚拟节点环
        for (ServiceMetaInfo serviceMetaInfo : serviceMetaInfoList) {
            for (int i = 0; i < VIRTUAL_NODE_NUM; i++) {
                int hash = getHash(serviceMetaInfo.getServiceAddress() + "#" + i);
                virtualNodes.put(hash, serviceMetaInfo);
            }
        }

        // 获取调用请求的 hash 值
        int hash = getHash(requestParams);

        // 选择最接近且大于等于调用请求 hash 值的虚拟节点 【ceilingEntry】
        Map.Entry<Integer, ServiceMetaInfo> entry = virtualNodes.ceilingEntry(hash);
        if (entry == null) {
            // 如果没有大于等于调用请求 hash 值的虚拟节点,则返回环首部的节点 【firstEntry】
            entry = virtualNodes.firstEntry();
        }
        return entry.getValue();
    }

    @Override
    protected ServiceMetaInfo select(List<ServiceMetaInfo> serviceMetaInfoList) {
        // do nothing
        return null;
    }

    /**
     * Hash 算法,可自行实现
     *
     * @param key
     * @return
     */
    private int getHash(Object key) {
        return key.hashCode();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不进大厂不改名1

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

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

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

打赏作者

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

抵扣说明:

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

余额充值