关于dubbo 自定义线程池的问题

 更新时间:2022年04月18日 10:37:37   作者:小码农叔叔  
在日常开发中,线程池几乎涉及到了所有的开发框架,像我们熟悉的JDK线程池,druid连接线程池等等,线程池的使用,大大降低了人工维护线程的成本,而且提升了线程资源在使用中的效率,本文给大家介绍dubbo 自定义线程池的相关知识,感兴趣的朋友一起看看吧

初识dubbo

一、什么是dubbo?

Dubbo是阿里巴巴开源的基于 Java 的高性能 RPC(一种远程调用) 分布式服务框架(SOA),致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案,其实就是一种远程服务调用的分布式框架

二、为什么要用dubbo

在互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进,所以就出现了dubbo

单一应用框架:当网站流量很小时,只需一个应用,将所有功能都部署在一起。

垂直应用框架:当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。

分布式服务架构:当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。

流动计算架构:当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。

前言

在日常开发中,线程池几乎涉及到了所有的开发框架,或者一些中间件,像我们熟悉的JDK线程池,druid连接线程池等等,线程池的使用,大大降低了人工维护线程的成本,而且提升了线程资源在使用中的效率;

dubbo线程池

dubbo也不例外,默认情况下,当我们在说到dubbo线程池的时候,通常是指服务提供者一端的线程池,其常用配置参数如下:

spring.dubbo.protocol.threads = 2000
spring.dubbo.protocol.threadpool = cached
spring.dubbo.protocol.dispatcher = message

对应到dubbo的配置文件中如下:

 <dubbo:protocol name="dubbo" threadpool="cached" dispatcher="message" threads="50" port="20880"/>

dubbo线程池说明

dubbo在使用的时候,都是通过创建真实的业务线程来进行操作的。已知的线程池模型主要有2个,固定大小线程池和带缓存的线程池;

  • fix线程池,即固定大小线程池。也是dubbo默认的使用方式,默认情况下,不做任何配置,线程池最大的线程个数为200个,并且是没有任何等待队列的。所以这种线程池,在极端情况下,可能会存在问题,比如某个应用执行某个大批量操作时,可能因为线程堵塞造成其他应用无法调用的情况;
  • cached线程池,非固定大小线程池,当线程不足的时候,会自动创建新线程。这种类型的线程池的问题在于,如果有较高的TPS过来的时候,如果请求的dubbo接口比较耗时,未能及时响应,则会连续不断的创建新线程,则对系统的CPU以及负载都是巨大的压力,甚至可能造成系统宕机的风险; dubbo 自定义线程池

在真实的使用过程中,大多数开发人员是忽略这个配置的,也就是说通常情况下默认是使用fix模式的,如果对于那种TPS比较高,或者dubbo接口中执行的核心业务逻辑比较耗时,并且系统要应对的并发也是居高不下的场景下,fix模式最终因为线程数创建不足而产生错误;

在这种情况下,出了错误之后,通常来说也是无感知的,怎么能快速定位因线程创建不足而导致的问题呢?这就需要一种机制,能够监控dubbo线程池,对运行过程中线程池的状况进行监控,当核心指标达到告警阈值时,及时给出预警,通知开发或运维人员快速做出响应和调整;

这就需要自定义线程池来解决这个问题

自定义线程池代码实现步骤

1、自定义一个maven模块并添加核心依赖

<dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.7.0</version>
        </dependency>

2、自定义线程池类

自定义一个类,继承FixedThreadPool 类,并实现Runnable接口,即该类本身作为一个线程;

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 WatchingPool extends FixedThreadPool implements Runnable {

    private static final Logger LOGGER = LoggerFactory.getLogger(WatchingPool.class);

    // 线程池预警值【可以根据实际情况动态调整大小】
    private static final double ALARM_PERCENT = 0.70;

    private final Map<URL, ThreadPoolExecutor> theadPoolMap = new ConcurrentHashMap<>();

    public WatchingPool() {
        // 创建一个定时任务,每3秒执行一次【可以根据实际情况动态调整参数】
        Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(this, 1, 3, TimeUnit.SECONDS);
    }

    @Override
    public Executor getExecutor(URL url) {
        // 重写父类getExecutor, 如果executor是ThreadPoolExecutor,则放入theadPoolMap中
        Executor executor = super.getExecutor(url);
        if (executor instanceof ThreadPoolExecutor) {
            theadPoolMap.put(url, (ThreadPoolExecutor) executor);
        }

        return executor;

    public void run() {
        for (Map.Entry<URL, ThreadPoolExecutor> entry : theadPoolMap.entrySet()) {
            URL url = entry.getKey();
            ThreadPoolExecutor threadPoolExecutor = entry.getValue();
            // 获取正在活动的线程数
            int activeCount = threadPoolExecutor.getActiveCount();
            // 获取总的线程数  (继承的FixedThreadPool , 所以这里获取核心的线程数就是总的线程数)
            int corePoolSize = threadPoolExecutor.getCorePoolSize();

            double percent = activeCount / (corePoolSize * 1.0);

            LOGGER.info("线程池状态:{}/{},: {}%", activeCount, corePoolSize, percent*100);
            if (percent > ALARM_PERCENT) {
                LOGGER.error("超出警戒线 : host:{}, 当前使用量 {}%, URL:{}", url.getHost(), percent*100, url);
            }

}

该类的逻辑,主要是设置了一个最大的阈值,当超出这个阈值时候,打印出相关的信息,实际应用中,可以集成发短信或邮件进行告警;

3、将上面的类配置到META-INF目录中

4、在生产端工程中引入上面的模块

<dependency>
            <groupId>com.congge</groupId>
            <artifactId>common-pool</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

5、在生产端配置文件中,指定自定义线程池

<dubbo:protocol name="dubbo" threadpool="watchingPool" threads="50" port="20880"/>

6、改造消费端的启动类,使用1000个线程不断调用接口

import com.congge.service.HelloService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.io.IOException;

public class ConsumerMain {
    public static void main(String[] args) throws Exception {

        ApplicationContext ac = new ClassPathXmlApplicationContext("spring-consumer.xml");
        HelloService service = (HelloService) ac.getBean("helloService");
        while (true){
            for(int i=0;i<1000;i++){
                Thread.sleep(6);
                new Thread( () ->{
                    String hello = service.hello("Hello Provider");
                    System.out.println(hello);
                }).start();
            }
        }
    }
}

为了看到效果,我们将配置文件这里的线程数调整到了50个,下面启动生产端和消费端的代码,观察控制台输出日志(先启动生产端,再启动消费端)

生产端已就绪

启动消费端,不断发起了调用

这时再次回到生产端控制台,通过输出日志可以看到整个线程池经历了线程数不断往上增长的过程,直到最后达到了警戒线;

到此这篇关于dubbo 自定义线程池的文章就介绍到这了,更多相关dubbo 自定义线程池内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Mybatis的update更新批量与普通解决方式对比

    Mybatis的update更新批量与普通解决方式对比

    这篇文章主要为大家介绍了Mybatis的update更新批量与普通解决方式对比,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-04-04
  • java中读写Properties属性文件公用方法详解

    java中读写Properties属性文件公用方法详解

    在项目开发中我们会将很多环境特定的变量定义到一个配置文件中,比如properties文件,把数据库的用户名和密码存放到此属性文件中。下面这篇文章就主要介绍了java中读写Properties属性文件公用方法,需要的朋友可以参考借鉴。
    2017-01-01
  • java+SpringBoot设计实现评教系统

    java+SpringBoot设计实现评教系统

    这篇文章主要介绍了用过Java SpringBoot实现一个简单的评价系统,可以做到学生管理、教师管理、评教管理、指标管理、课程管理等。感兴趣的可以了解一下
    2021-12-12
  • SpringBoot中处理的转发与重定向方式

    SpringBoot中处理的转发与重定向方式

    这篇文章主要介绍了SpringBoot中处理的转发与重定向方式,分别就转发和重定向做了概念解说,结合示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-11-11
  • Java 房屋租赁系统的实现流程

    Java 房屋租赁系统的实现流程

    读万卷书不如行万里路,只学书上的理论是远远不够的,只有在实战中才能获得能力的提升,本篇文章手把手带你用java+SSM+jsp+mysql+maven实现一个房屋租赁系统,大家可以在过程中查缺补漏,提升水平
    2021-11-11
  • 如何修改maven默认的JDK版本

    如何修改maven默认的JDK版本

    这篇文章主要介绍了如何修改maven默认的JDK版本,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • SpringBoot封装响应处理超详细讲解

    SpringBoot封装响应处理超详细讲解

    这篇文章主要介绍了SpringBoot封装响应处理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-12-12
  • 浅谈java实现mongoDB的多条件查询

    浅谈java实现mongoDB的多条件查询

    这篇文章主要介绍了java实现mongoDB的多条件查询,具有一定参考价值,需要的朋友可以参考下。
    2017-09-09
  • 带你快速搞定java IO

    带你快速搞定java IO

    这篇文章主要介绍了Java IO流 文件传输基础的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下,希望能给你带来帮助
    2021-07-07
  • plsql实现DES对称加密 Java解密

    plsql实现DES对称加密 Java解密

    这篇文章主要介绍了plsql实现DES对称加密 Java解密的方法,帮助大家更好的理解和学习使用Oracle与Java,感兴趣的朋友可以了解下
    2021-02-02

最新评论