Ribbon中自定义负载均衡策略一致性hash的实现

134 阅读1分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

自定义IRule

我们可以通过实现IRule接口,来实现一个自定义的负载均衡策略。接下来我们实现一个一致性哈希的负载均衡策略,一致性哈希的负载均衡策略不仅在负载均衡的领域,在很多其他的领域都有广泛的应用。我们废话不多说,直接开干!

/**
 * 一致性哈希的负载均衡策略
 *
 * @author liangks
 * @date 2022/2/23
 */
@NoArgsConstructor
public class MyRule extends AbstractLoadBalancerRule implements IRule {

    /**
     * 虚拟节点的数目,这里写死,一个真实结点对应10个虚拟节点
     */
    protected static final int VIRTUAL_NODES = 10;

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
    }

    @Override
    public Server choose(Object key) {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        // 使用uri作为摘要
        String uri = request.getServletPath() + "?" + request.getQueryString();
        return route(uri.hashCode(), getLoadBalancer().getAllServers());
    }

    /**
     * @param hashId     hash摘要
     * @param allServers 服务节点
     * @return Server
     */
    public Server route(int hashId, List<Server> allServers) {
        if (CollectionUtils.isEmpty(allServers)) {
            return null;
        }

        TreeMap<Long, Server> serverRing = new TreeMap<>();
        // 服务上环
        allServers.forEach(server -> {
            // 通过采取虚拟节点的方法,一个真实结点不再固定在Hash换上的某个点,而是大量地分布在整个Hash环上,这样即使上线、下线服务器,也不会造成整体的负载不均衡。
            for (int i = 0; i < VIRTUAL_NODES; i++) {
                long hash = hash(server.getId() + i);
                serverRing.put(hash, server);
            }
        });


        long hash = hash(String.valueOf(hashId));
        // 返回最近的服务组
        SortedMap<Long, Server> last = serverRing.tailMap(hash);

        // 为空代表到了末尾,取第一个即可
        if (last.isEmpty()) {
            return serverRing.firstEntry().getValue();
        }

        // 不为空则取最近的一个
        return last.get(last.firstKey());
    }

    /**
     * 使用FNV1_32_HASH算法计算Hash值
     */
    public int hash(String key) {

        final int p = 16777619;
        int hash = (int) 2166136261L;
        for (int i = 0; i < key.length(); i++) {
            hash = (hash ^ key.charAt(i)) * p;
        }
        hash += hash << 13;
        hash ^= hash >> 7;
        hash += hash << 3;
        hash ^= hash >> 17;
        hash += hash << 5;

        // 如果算出来的值为负数则取其绝对值
        if (hash < 0) {
            hash = Math.abs(hash);
        }
        return hash;
    }

项目中如何使用

在这里插入图片描述