Dubbo负载均衡调优策略

前言

虽然目前Spring Cloud全家桶非常的火热,Dubbo依然在很多公司有着举足轻重的地位。我自己也经历了很久Dubbo的项目,今天突然想整理下Dubbo的优化策略。曾经有一段时间,我们项目底层的提供层集群经常发生卡顿,而且,一旦一个服务器开始卡顿,整个系统就会受到影响,甚至整个服务宕机。虽然重启所有集群服务器可以解决问题,但是,这样总归是治标不治本的。

Dubbo的负载策略 loadbalance

负载均衡策略,可选值:random,roundrobin,leastactive,ConsistentHash 分别表示:随机,轮询,最少活跃调用,一致性Hash。

思考

Dubbo的缺省值为random,也就意味着在大量的请求中,会呈现均匀分布。这样就会引发一个问题——当有一台机器出现卡顿的时候,每次消费端连接到这台机器都会卡顿,进而造成消费层的很多请求都在这台机器上卡顿很久,影响到整个系统。在轮询跟一致性Hash也会造成类似的现象。个人认为,最好的策略肯定是跳过这台卡顿的提供层服务或者尽量少去跟这台机器进行连接。所以,leastactive策略是最好的解决方案。

LeastActiveLoadBalance

解释:最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差,使慢的机器收到更少。

看下LeastActiveLoadBalance这个类的doSelect方法:

protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
		//总个数
        int length = invokers.size();
        int leastActive = -1;
        int leastCount = 0;
        int[] leastIndexs = new int[length];
        int totalWeight = 0;
        int firstWeight = 0;
        boolean sameWeight = true;

        int offsetWeight;
        int leastIndex;
        for(offsetWeight = 0; offsetWeight < length; ++offsetWeight) {
            Invoker<T> invoker = (Invoker)invokers.get(offsetWeight);
            leastIndex = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive();
            int weight = invoker.getUrl().getMethodParameter(invocation.getMethodName(), "weight", 100);
            if (leastActive != -1 && leastIndex >= leastActive) {
                if (leastIndex == leastActive) {
                    leastIndexs[leastCount++] = offsetWeight;
                    totalWeight += weight;
                    if (sameWeight && offsetWeight > 0 && weight != firstWeight) {
                        sameWeight = false;
                    }
                }
            } else {
                leastActive = leastIndex;
                leastCount = 1;
                leastIndexs[0] = offsetWeight;
                totalWeight = weight;
                firstWeight = weight;
                sameWeight = true;
            }
        }

        if (leastCount == 1) {
            return (Invoker)invokers.get(leastIndexs[0]);
        } else {
            if (!sameWeight && totalWeight > 0) {
                offsetWeight = this.random.nextInt(totalWeight);

                for(int i = 0; i < leastCount; ++i) {
                    leastIndex = leastIndexs[i];
                    offsetWeight -= this.getWeight((Invoker)invokers.get(leastIndex), invocation);
                    if (offsetWeight <= 0) {
                        return (Invoker)invokers.get(leastIndex);
                    }
                }
            }
            return (Invoker)invokers.get(leastIndexs[this.random.nextInt(leastCount)]);
        }
    }
}

在这个类中一直在计算活跃数和权重,然后去选择Invoker

配置

在消费层中,配置loadbalance=“leastactive” ,注意配置 filter=“activelimit”,这样才能统计到活跃数的变化

 <dubbo:reference  id="helloService" check="false" interface="top.yuyufeng.learn.dubbo.service.IHelloService" loadbalance="leastactive" filter="activelimit"/>

测试

搭建好一个简单的dubbo程序后,我们启动3个提供层服务,并且配置loadbalance=“leastactive”,响应时间分别为100ms、1000ms、5000ms
然后启动一个消费层,不断得去调用提供层。
代码如下:
提供层服务:

public class HelloServiceImpl implements IHelloService {
    private AtomicInteger ai = new AtomicInteger();
    private int timeout = 1000;

    public HelloServiceImpl(int timeout) {
        this.timeout = timeout;
        System.out.println("响应时间:" + timeout);
    }

    @Override
    public String saySomething(String words) {
        int i = ai.incrementAndGet();
        try {
            TimeUnit.MILLISECONDS.sleep(timeout);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("你好:" + words + " 累计收到:" + i);
        return "你好:" + words;
    }
}

消费层服务:

/**
 * @author yuyufeng
 * @date 2018/7/23.
 */
public class AppConsumerExcutor {
    private static int request = 10000;


    public static void main(String[] args) throws InterruptedException {
        long benginTime = System.currentTimeMillis();
        AtomicInteger ai = new AtomicInteger();
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"consumer.xml"});
        context.start();
        CountDownLatch countDownLatch = new CountDownLatch(request);

        ExecutorService executorService = Executors.newFixedThreadPool(5);

        for (int i = 0; i < request; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    // Obtaining a remote service proxy
                    IHelloService helloService = (IHelloService) context.getBean("helloService");
                    // Executing remote methods
                    int aiNow = ai.incrementAndGet();

                  String hello = helloService.saySomething("yuyufeng 累计收到:" + aiNow + "速度:" + (float) aiNow / (float) ((System.currentTimeMillis() - benginTime) / 1000));
                    // Display the call result
                    System.out.println(hello);
                    countDownLatch.countDown();
                }
            });
        }

        countDownLatch.await();
        executorService.shutdown();
    }
}

全部启动之后,服务正常运行:
在这里插入图片描述
此时的速度为13+次/秒,
接着我们配置loadbalance=“random”,看下相同的程序运行效果
在这里插入图片描述
此时的速度只有1-2次/秒,可见loadbalance=“leastactive” 的配置对系统得作用。

示例demo

https://github.com/yuyufeng1994/learn-dubbo

参考文档

http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-consumer.html

发布了154 篇原创文章 · 获赞 142 · 访问量 35万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览