All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tencent.polaris.plugins.loadbalancer.ringhash.ConsistentHashLoadBalance Maven / Gradle / Ivy

/*
 * Tencent is pleased to support the open source community by making Polaris available.
 *
 * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
 *
 * Licensed under the BSD 3-Clause License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://opensource.org/licenses/BSD-3-Clause
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

package com.tencent.polaris.plugins.loadbalancer.ringhash;

import com.tencent.polaris.api.config.consumer.LoadBalanceConfig;
import com.tencent.polaris.api.control.Destroyable;
import com.tencent.polaris.api.exception.PolarisException;
import com.tencent.polaris.api.plugin.IdAwarePlugin;
import com.tencent.polaris.api.plugin.PluginType;
import com.tencent.polaris.api.plugin.cache.FlowCache;
import com.tencent.polaris.api.plugin.common.InitContext;
import com.tencent.polaris.api.plugin.common.PluginTypes;
import com.tencent.polaris.api.plugin.compose.Extensions;
import com.tencent.polaris.api.plugin.loadbalance.LoadBalancer;
import com.tencent.polaris.api.pojo.Instance;
import com.tencent.polaris.api.pojo.ServiceInstances;
import com.tencent.polaris.api.rpc.Criteria;
import com.tencent.polaris.api.utils.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.Function;

public class ConsistentHashLoadBalance extends Destroyable implements LoadBalancer, IdAwarePlugin {

    /**
     * 虚拟环倍数
     */
    private static final int VIRTUAL_NODE_SIZE = 5;
    private static final String VIRTUAL_NODE_SUFFIX = "&&";

    private int id;

    /**
     * 计算字符串hash的策略
     */
    private HashStrategy hashStrategy;

    private FlowCache flowCache;

    @Override
    public Instance chooseInstance(Criteria criteria, ServiceInstances instances) throws PolarisException {
        if (instances == null || CollectionUtils.isEmpty(instances.getInstances())) {
            return null;
        }
        TreeMap ring = flowCache.loadPluginCacheObject(getId(), instances,
                obj -> buildConsistentHashRing((ServiceInstances) obj));
        int invocationHashCode = hashStrategy.getHashCode(criteria.getHashKey());
        return lookup(ring, invocationHashCode);
    }

    @Override
    public String getName() {
        return LoadBalanceConfig.LOAD_BALANCE_RING_HASH;
    }

    @Override
    public PluginType getType() {
        return PluginTypes.LOAD_BALANCER.getBaseType();
    }

    @Override
    public void init(InitContext ctx) throws PolarisException {
        hashStrategy = new MurmurHash();
    }

    @Override
    public void postContextInit(Extensions ctx) throws PolarisException {
        this.flowCache = ctx.getFlowCache();
    }

    /**
     * 从环中获取一个实例
     *
     * @param ring 环
     * @param invocationHashCode hashcode
     * @return 实例
     */
    private Instance lookup(TreeMap ring, int invocationHashCode) {
        // 向右找到第一个 key
        Map.Entry locateEntry = ring.ceilingEntry(invocationHashCode);
        if (locateEntry == null) {
            // 超过尾部则取第一个 key
            locateEntry = ring.firstEntry();
        }
        return locateEntry.getValue();
    }

    /**
     * 构建一致性hash环
     *
     * @param serviceInstances 服务实例
     * @return 一致性hash环
     */
    private TreeMap buildConsistentHashRing(ServiceInstances serviceInstances) {
        List instances = serviceInstances.getInstances();
        TreeMap virtualNodeRing = new TreeMap<>();
        for (Instance instance : instances) {
            for (int i = 0; i < VIRTUAL_NODE_SIZE; i++) {
                // 新增虚拟节点
                virtualNodeRing.put(hashStrategy
                                .getHashCode(instance.getHost() + ":" + instance.getPort() + VIRTUAL_NODE_SUFFIX + i),
                        instance);
            }
        }
        return virtualNodeRing;
    }

    @Override
    public int getId() {
        return this.id;
    }

    @Override
    public void setId(int id) {
        this.id = id;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy