Bootstrap

dubbo-9:线程池

一、Dubbo已有线程池

官网说明
dubbo在使用时,都是通过创建真实的业务线程池进行操作的。目前已知的线程池模型有两个和java中的相互对应:

  • fix: 表示创建固定大小的线程池。也是Dubbo默认的使用方式,默认创建的执行线程数为200,并且是没有任何等待队列的。所以再极端的情况下可能会存在问题,比如某个操作大量执行时,可能存在堵塞的情况。后面也会讲相关的处理办法。
  • cache: 创建非固定大小的线程池,当线程不足时,会自动创建新的线程。但是使用这种的时候需要注意,如果突然有高TPS的请求过来,方法没有及时完成,则会造成大量的线程创建,对系统的CPU和负载都是压力,执行越多反而会拖慢整个系统。

二、自定义线程池

在这里插入图片描述

在真实的使用过程中可能会因为使用fix模式的线程池,导致具体某些业务场景因为线程池中的线程数量不足而产生错误,而很多业务研发是对这些无感知的,只有当出现错误的时候才会去查看告警或者通过客户反馈出现严重的问题才去查看,结果发现是线程池满了。所以可以在创建线程池的时,通过某些手段对这个线程池进行监控,这样就可以进行及时的扩缩容机器或者告警。

1、自定义类并继承FixedThreadPool

①引入pom

    <dependencies>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-common</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
        </dependency>
    </dependencies>

②编写线程池类

package com.lagou.threadpool;

import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.threadpool.support.fixed.FixedThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.concurrent.*;


public class WatchingThreadPool extends FixedThreadPool implements Runnable {
    private Logger logger = LoggerFactory.getLogger(WatchingThreadPool.class);

    // 定义线程池使用的阀值
    private static final double alarm_percent = 0.90;
    // URL与线程池的对应关系
    private final Map<URL, ThreadPoolExecutor> thread_pools = new ConcurrentHashMap<>();


    public WatchingThreadPool() {
        // 每隔3秒打印线程使用情况
        Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(this, 1, 3, TimeUnit.SECONDS);
    }

    // 通过父类创建线程池
    @Override
    public Executor getExecutor(URL url) {
        Executor executor = super.getExecutor(url);
        if (executor instanceof ThreadPoolExecutor) {
            thread_pools.put(url, (ThreadPoolExecutor) executor);
        }

        return executor;
    }

    @Override
    public void run() {
        // 遍历线程池
        for (URL url : thread_pools.keySet()) {
            ThreadPoolExecutor executor = thread_pools.get(url);

            // 计算相关指标
            // 当前执行中的线程数
            int activeCount = executor.getActiveCount();
            // 总计线程数
            int corePoolSize = executor.getCorePoolSize();

            double usedPercent = activeCount / (corePoolSize * 1.0);
            logger.info("线程池执行状态:[{}/{}:{}%]", activeCount, corePoolSize, usedPercent * 100);
//            System.out.println("线程池执行状态:[" + activeCount + "/" + corePoolSize + ":" + usedPercent * 100 + "%]");
            if (usedPercent > alarm_percent) {
                logger.error("超出警戒线! host:{} 当前使用率是:{},URL:{}", url.getIp(), usedPercent * 100, url);
//                System.out.println("超出警戒线! host:" + url.getIp() + " 当前使用率是:" + usedPercent * 100 + ",URL:" + url + "");
            }
        }

    }
}

2、SPI声明,创建文件 META-INF/dubbo/org.apache.dubbo.common.threadpool.ThreadPool

# watching=包名.线程池名
watching=com.lagou.threadpool.WatchingThreadPool

3、服务方

①引入该依赖

 <dependency>
       <groupId>com.lagou</groupId>
       <artifactId>spi-threadpool</artifactId>
       <version>1.0-SNAPSHOT</version>
</dependency>

②设置使用该线程池生成器

dubbo.provider.threadpool=watching

③service方法设置休眠

public class HelloServiceImpl   implements HelloService {
    @Override
    public String sayHello(String name, int timeToWait) {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "hello:"+name;
    }
}

4、消费方

package com.lagou;

import com.lagou.bean.ComsumerComponet;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import java.io.IOException;


public class AnnotationConsumerMain {
    public static void main(String[] args) throws IOException, InterruptedException {
        System.out.println("-------------");
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class);
        context.start();
        // 获取消费者组件
        ComsumerComponet service = context.getBean(ComsumerComponet.class);
        while (true) {
            for (int i = 0; i < 1000; i++) {
                Thread.sleep(4);
                Thread thread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        String msg = service.sayHello("哈哈哈", 0);
                        System.out.println("result:" + msg);
                    }
                });

                thread.start();
            }
        }
    }

    @Configuration
    @PropertySource("classpath:/dubbo-comsumer.properties")
    @ComponentScan(basePackages = "com.lagou.bean")
    @EnableDubbo
    static class ConsumerConfiguration {

    }
}

在这里插入图片描述

;