rrpc:实现最短响应时间、一致性hash、随机轮询的负载均衡算法

 代码在gitee: rengerpc: rrpc远程方法调用框架开发

负载均衡的前置代码和介绍在我的这篇博客

 ​​​​​​http://t.csdn.cn/NW3qq

其他的就不多介绍了,之前的文章都有写

以下直接进行代码实现

 1、最短响应时间的负载均衡策略

@Slf4j
public class MinimumResponseTimeLoadBalancer extends AbstractLoadBalancer {
    @Override
    protected Selector getSelector(List<InetSocketAddress> serviceList) {
        return new MinimumResponseTimeSelector(serviceList);
    }

    private static class MinimumResponseTimeSelector implements Selector {

        public MinimumResponseTimeSelector(List<InetSocketAddress> serviceList) {

        }

        @Override
        public InetSocketAddress getNext() {
            Map.Entry<Long, Channel> entry = RrpcBootstrap.ANSWER_TIME_CHANNEL_CACHE.firstEntry();
            if (entry != null) {
                if (log.isDebugEnabled()){
                    log.debug("选取了响应时间为【{}ms】的服务节点.",entry.getKey());
                }
                return (InetSocketAddress) entry.getValue().remoteAddress();
            }


            // 直接从缓存中获取一个可用的就行了
            System.out.println("----->"+Arrays.toString(RrpcBootstrap.CHANNEL_CACHE.values().toArray()));
            Channel channel = (Channel)RrpcBootstrap.CHANNEL_CACHE.values().toArray()[0];
            return (InetSocketAddress)channel.remoteAddress();
        }



    }
}

2、一致性hash 的负载均衡策略

@Slf4j
public class ConsistentHashBalancer extends AbstractLoadBalancer {


    @Override
    protected Selector getSelector(List<InetSocketAddress> serviceList) {
        return new ConsistentHashSelector(serviceList,128);
    }

    /**
     * 一致性hash的具体算法实现
     * 将服务列表缓存进去
     */
    private static class  ConsistentHashSelector implements Selector{

       //hash环存储服务器节点 SortedMap中增加了元素的排序,这意味着可以给SortedMap中的元素排序
        private SortedMap<Integer,InetSocketAddress> circle = new TreeMap<>();
        //虚拟节点的个数
        private int virtualNodes;

        public ConsistentHashSelector(List<InetSocketAddress> serviceList,int virtualNodes) {
            this.virtualNodes = virtualNodes;
            for (InetSocketAddress inetSocketAddress : serviceList) {
                //把每一个节点放入hash环中
                addNodeToCircle(inetSocketAddress);

            }
        }

        @Override
        public InetSocketAddress getNext() {
           //1.hash环已经建立,需要rrpcRequest
            //使用ThreadLocal获取请求内容
            RrpcRequest rrpcRequest = RrpcBootstrap.REQUEST_THREAD_LOCAL.get();

            //根据请求的一些特征来选择服务器
            String requestId = Long.toString(rrpcRequest.getRequestId());

            //请求的id做hash,String默认的hash算法不够散列,是连续的内存地址
            int hash = hash(requestId);

            //判断hash值是否能直接落在一个服务器上,和服务器的hash一样
            if (!circle.containsKey(hash)){
                //寻找最近的节点,返回一个tailmap 红黑树
                SortedMap<Integer, InetSocketAddress> tailMap = circle.tailMap(hash);
                //判断是否为空,为空则表示树内没有比它的hash值更大的了,因此返回最小节点
                hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
            }



            return circle.get(hash);
        }

        /**
         * 将虚拟节点进行挂载
         * @param inetSocketAddress
         */
        private void addNodeToCircle(InetSocketAddress inetSocketAddress) {
            //为每一个节点生成匹配的虚拟节点进行挂载
            for (int i = 0; i < virtualNodes; i++) {
                //计算hash值,原有的hash内存是连续的,而我们需要散列的地址
                int hash = hash(inetSocketAddress.toString() + "-"+i);
                //挂载到hash环上
                circle.put(hash,inetSocketAddress);
                if (log.isDebugEnabled()){
                    log.debug("hash为【{}】的节点,已经挂载到了哈希环上",hash);
                }
            }
        }

        /**
         * 删除节点
         * @param inetSocketAddress
         */
        private void removeNodeToCircle(InetSocketAddress inetSocketAddress) {
            for (int i = 0; i < virtualNodes; i++) {
                //计算hash值,原有的hash内存是连续的,而我们需要散列的地址
                int hash = hash(inetSocketAddress.toString() + "-"+i);
                //删除节点
                circle.remove(hash);
            }
        }

        /**
         * 具体的hash算法
         * @param s
         * @return
         */
        private int hash(String s) {
            //使用md5生成hash值
            MessageDigest md;
            try {
                md=MessageDigest.getInstance("MD5");
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException(e);
            }
            byte[] digest = md.digest(s.getBytes());
            //将字节数组转化为int
            int res = 0;
            for (int i = 0; i < 4; i++) {
                //4个字节组成int
                res = res << 8;
                if (digest[i] < 0){
                    res = res | (digest[i] & 255);
                }else {
                    res = res | digest[i];
                }
             /*   System.out.println("d--> " + toBinary(digest[i]));
                System.out.println("r--> " + toBinary(res));*/
            }


            return res;
        }

        /**
         * 二进制补零函数
         * @param i
         * @return
         */
        private String toBinary(int i){
            //如果二进制数不到32位 其余位置补零
            String s = Integer.toBinaryString(i);
            int index = 32 - s.length();
            StringBuilder stringBuilder = new StringBuilder();
            for (int j = 0; j < index; j++) {
                stringBuilder.append(0);
            }
            stringBuilder.append(s);
            return stringBuilder.toString();
        }



    }
}

3、随机轮询的负载均衡策略

@Slf4j
public class RandomRobinLoadBalancer extends AbstractLoadBalancer {


    @Override
    protected Selector getSelector(List<InetSocketAddress> serviceList) {
        return new RandomRobinSelector(serviceList);
    }

    /**
     * 选择器
     * 将服务列表缓存进去
     */
    private static class RandomRobinSelector implements Selector{

        private List<InetSocketAddress> serviceList;

        public RandomRobinSelector(List<InetSocketAddress> serviceList) {
            this.serviceList = serviceList;
        }

        @Override
        public InetSocketAddress getNext() {
            if (serviceList == null || serviceList.size() == 0){
                log.error("进行负载均衡选取节点时,发现服务列表为空");
                throw new LoadBalanceException();
            }
            //随机轮询
            Random random = new Random();
            int rand = random.nextInt(0,serviceList.size());

            InetSocketAddress address = serviceList.get(rand);

            return address;
        }


    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

北方569

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

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

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

打赏作者

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

抵扣说明:

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

余额充值