Elasticsearch源码分析—线程池(十一)

转自:https://www.felayman.com/articles/2017/11/10/1510291570687.html

线程池

每个节点都有一些线程池来优化线程内存的消耗,按节点来配置管理。有些线程池还拥有与之关联的队列配置,用来允许挂住一些未处理的请求,而不是丢弃它。

Elasticsearch对线程池的处理的源码在org.elasticsearch.node.Node中,核心代码为:

final ThreadPool threadPool = new ThreadPool(settings, executorBuilders.toArray(new ExecutorBuilder[0]));

其具体实现为:

super(settings);

    assert Node.NODE_NAME_SETTING.exists(settings);

    final Map<String, ExecutorBuilder> builders = new HashMap<>();
final int availableProcessors = EsExecutors.boundedNumberOfProcessors(settings);
final int halfProcMaxAt5 = halfNumberOfProcessorsMaxFive(availableProcessors);
final int halfProcMaxAt10 = halfNumberOfProcessorsMaxTen(availableProcessors);
final int genericThreadPoolMax = boundedBy(4 * availableProcessors, 128, 512);
builders.put(Names.GENERIC, new ScalingExecutorBuilder(Names.GENERIC, 4, genericThreadPoolMax, TimeValue.timeValueSeconds(30)));
builders.put(Names.INDEX, new FixedExecutorBuilder(settings, Names.INDEX, availableProcessors, 200));
builders.put(Names.BULK, new FixedExecutorBuilder(settings, Names.BULK, availableProcessors, 200)); // now that we reuse bulk for index/delete ops
builders.put(Names.GET, new FixedExecutorBuilder(settings, Names.GET, availableProcessors, 1000));
builders.put(Names.SEARCH, new FixedExecutorBuilder(settings, Names.SEARCH, searchThreadPoolSize(availableProcessors), 1000));
builders.put(Names.MANAGEMENT, new ScalingExecutorBuilder(Names.MANAGEMENT, 1, 5, TimeValue.timeValueMinutes(5)));
// no queue as this means clients will need to handle rejections on listener queue even if the operation succeeded
// the assumption here is that the listeners should be very lightweight on the listeners side
builders.put(Names.LISTENER, new FixedExecutorBuilder(settings, Names.LISTENER, halfProcMaxAt10, -1));
builders.put(Names.FLUSH, new ScalingExecutorBuilder(Names.FLUSH, 1, halfProcMaxAt5, TimeValue.timeValueMinutes(5)));
builders.put(Names.REFRESH, new ScalingExecutorBuilder(Names.REFRESH, 1, halfProcMaxAt10, TimeValue.timeValueMinutes(5)));
builders.put(Names.WARMER, new ScalingExecutorBuilder(Names.WARMER, 1, halfProcMaxAt5, TimeValue.timeValueMinutes(5)));
builders.put(Names.SNAPSHOT, new ScalingExecutorBuilder(Names.SNAPSHOT, 1, halfProcMaxAt5, TimeValue.timeValueMinutes(5)));
builders.put(Names.FETCH_SHARD_STARTED, new ScalingExecutorBuilder(Names.FETCH_SHARD_STARTED, 1, 2 * availableProcessors, TimeValue.timeValueMinutes(5)));
builders.put(Names.FORCE_MERGE, new FixedExecutorBuilder(settings, Names.FORCE_MERGE, 1, -1));
builders.put(Names.FETCH_SHARD_STORE, new ScalingExecutorBuilder(Names.FETCH_SHARD_STORE, 1, 2 * availableProcessors, TimeValue.timeValueMinutes(5)));
for (final ExecutorBuilder<?> builder : customBuilders) {
if (builders.containsKey(builder.name())) {
throw new IllegalArgumentException("builder with name [" + builder.name() + "] already exists");
}
builders.put(builder.name(), builder);
}
this.builders = Collections.unmodifiableMap(builders); threadContext = new ThreadContext(settings); final Map<String, ExecutorHolder> executors = new HashMap<>();
for (@SuppressWarnings("unchecked") final Map.Entry<String, ExecutorBuilder> entry : builders.entrySet()) {
final ExecutorBuilder.ExecutorSettings executorSettings = entry.getValue().getSettings(settings);
final ExecutorHolder executorHolder = entry.getValue().build(executorSettings, threadContext);
if (executors.containsKey(executorHolder.info.getName())) {
throw new IllegalStateException("duplicate executors with name [" + executorHolder.info.getName() + "] registered");
}
logger.debug("created thread pool: {}", entry.getValue().formatInfo(executorHolder.info));
executors.put(entry.getKey(), executorHolder);
} executors.put(Names.SAME, new ExecutorHolder(DIRECT_EXECUTOR, new Info(Names.SAME, ThreadPoolType.DIRECT)));
this.executors = unmodifiableMap(executors); this.scheduler = new ScheduledThreadPoolExecutor(1, EsExecutors.daemonThreadFactory(settings, "scheduler"), new EsAbortPolicy());
this.scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
this.scheduler.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
this.scheduler.setRemoveOnCancelPolicy(true); TimeValue estimatedTimeInterval = ESTIMATED_TIME_INTERVAL_SETTING.get(settings);
this.cachedTimeThread = new CachedTimeThread(EsExecutors.threadName(settings, "[timer]"), estimatedTimeInterval.millis());
this.cachedTimeThread.start();

从源码中可以看到,Elasticsearch的线程池基本有许多不同名称的线程池,这些线程池的命名都缓存在一个常量静态内部类ThreadPool.Names中,源码如下: ~~~java
public static class Names {
public static final String SAME = "same";
public static final String GENERIC = "generic";
public static final String LISTENER = "listener";
public static final String GET = "get";
public static final String INDEX = "index";
public static final String BULK = "bulk";
public static final String SEARCH = "search";
public static final String MANAGEMENT = "management";
public static final String FLUSH = "flush";
public static final String REFRESH = "refresh";
public static final String WARMER = "warmer";
public static final String SNAPSHOT = "snapshot";
public static final String FORCE_MERGE = "force_merge";
public static final String FETCH_SHARD_STARTED = "fetch_shard_started";
public static final String FETCH_SHARD_STORE = "fetch_shard_store";
}

而且Elasticsearch还将这些线程池分成了三个类型,分别为direct,fixed,scaling,这些类别也缓存在改常量类中,源码为:

public enum ThreadPoolType {
DIRECT("direct"),
FIXED("fixed"),
SCALING("scaling");
private final String type;
//省略getter/setter

默认地,Elasticsearch将上述的各个线程池采用不同的类型,源码如下:

static {
HashMap<String, ThreadPoolType> map = new HashMap<>();
map.put(Names.SAME, ThreadPoolType.DIRECT);
map.put(Names.GENERIC, ThreadPoolType.SCALING);
map.put(Names.LISTENER, ThreadPoolType.FIXED);
map.put(Names.GET, ThreadPoolType.FIXED);
map.put(Names.INDEX, ThreadPoolType.FIXED);
map.put(Names.BULK, ThreadPoolType.FIXED);
map.put(Names.SEARCH, ThreadPoolType.FIXED);
map.put(Names.MANAGEMENT, ThreadPoolType.SCALING);
map.put(Names.FLUSH, ThreadPoolType.SCALING);
map.put(Names.REFRESH, ThreadPoolType.SCALING);
map.put(Names.WARMER, ThreadPoolType.SCALING);
map.put(Names.SNAPSHOT, ThreadPoolType.SCALING);
map.put(Names.FORCE_MERGE, ThreadPoolType.FIXED);
map.put(Names.FETCH_SHARD_STARTED, ThreadPoolType.SCALING);
map.put(Names.FETCH_SHARD_STORE, ThreadPoolType.SCALING);
THREAD_POOL_TYPES = Collections.unmodifiableMap(map);
}

各线程池功能说明

  • GENERIC

    用于通用的操作(例如:后台节点发现),线程池类型为 scaling

  • INDEX

    用于index/delete操作,线程池类型为 fixed, 大小的为处理器数量,队列大小为200,最大线程数为 1 + 处理器数量

  • BULK

    用于bulk操作,线程池类型为 fixed, 大小的为处理器数量,队列大小为200,该池的最大线程数为 1 + 处理器数量

  • GET

    用于get操作。线程池类型为 fixed,大小的为处理器数量,队列大小为1000。

  • SEARCH

    用于count/search/suggest操作。线程池类型为 fixed, 大小的为 int((处理器数量 3) / 2) +1,队列大小为1000

  • MANAGEMENT

    官方暂未说明(新版本才有)

  • LISTENER

    主要用于Java客户端线程监听器被设置为true时执行动作。线程池类型为 scaling,最大线程数为min(10, (处理器数量)/2)

  • FLUSH

    用于flush操作。线程池类型为 scaling,线程空闲保持存活时间为5分钟,最大线程数为min(10, (处理器数量)/2)

  • REFRESH

    用于refresh操作。线程池类型为 scaling,线程空闲保持存活时间为5分钟,最大线程数为min(10, (处理器数量)/2)

  • WARMER

    用于segment warm-up操作。线程池类型为 scaling,线程保持存活时间为5分钟,最大线程数为min(5, (处理器数量)/2)

  • SNAPSHOT

    用于snaphost/restore操作。线程池类型为 scaling,线程保持存活时间为5分钟,最大线程数为min(5, (处理器数量)/2)

  • FETCH_SHARD_STARTED

    官方暂未说明(新版本才有)

  • FORCE_MERGE

    官方暂未说明(新版本才有)

  • FETCH_SHARD_STORE

    官方暂未说明(新版本才有)

  • SAME

    官方暂未说明(新版本才有)

各线程类型说明

  • direct

    此类线程是一种不支持关闭的线程,就意味着一旦使用,则会一直存活下去.

  • fixed

    此类线程池拥有固定数量的线程来处理请求,在没有空闲线程时请求将被挂在队列中(可选配)

  • scaling

    此类线程池拥有的线程数量是动态的。这个数字介于core和max参数的配置之间变化

这些线程池的创建如果在调试源码的时候日志级别更改为DEBUG,也是可以看出的,如下:

[2017-09-27T14:31:47,558][DEBUG][o.e.t.ThreadPool         ] [x2LMQHg] created thread pool: name [force_merge], size [1], queue size [unbounded]
[2017-09-27T14:31:47,560][DEBUG][o.e.t.ThreadPool ] [x2LMQHg] created thread pool: name [fetch_shard_started], core [1], max [16], keep alive [5m]
[2017-09-27T14:31:47,561][DEBUG][o.e.t.ThreadPool ] [x2LMQHg] created thread pool: name [listener], size [4], queue size [unbounded]
[2017-09-27T14:31:47,565][DEBUG][o.e.t.ThreadPool ] [x2LMQHg] created thread pool: name [index], size [8], queue size [200]
[2017-09-27T14:31:47,565][DEBUG][o.e.t.ThreadPool ] [x2LMQHg] created thread pool: name [refresh], core [1], max [4], keep alive [5m]
[2017-09-27T14:31:47,566][DEBUG][o.e.t.ThreadPool ] [x2LMQHg] created thread pool: name [generic], core [4], max [128], keep alive [30s]
[2017-09-27T14:31:47,566][DEBUG][o.e.t.ThreadPool ] [x2LMQHg] created thread pool: name [warmer], core [1], max [4], keep alive [5m]
[2017-09-27T14:31:47,566][DEBUG][o.e.t.ThreadPool ] [x2LMQHg] created thread pool: name [search], size [13], queue size [1k]
[2017-09-27T14:31:47,567][DEBUG][o.e.t.ThreadPool ] [x2LMQHg] created thread pool: name [flush], core [1], max [4], keep alive [5m]
[2017-09-27T14:31:47,567][DEBUG][o.e.t.ThreadPool ] [x2LMQHg] created thread pool: name [fetch_shard_store], core [1], max [16], keep alive [5m]
[2017-09-27T14:31:47,567][DEBUG][o.e.t.ThreadPool ] [x2LMQHg] created thread pool: name [management], core [1], max [5], keep alive [5m]
[2017-09-27T14:31:47,568][DEBUG][o.e.t.ThreadPool ] [x2LMQHg] created thread pool: name [get], size [8], queue size [1k]
[2017-09-27T14:31:47,568][DEBUG][o.e.t.ThreadPool ] [x2LMQHg] created thread pool: name [bulk], size [8], queue size [200]
[2017-09-27T14:31:47,568][DEBUG][o.e.t.ThreadPool ] [x2LMQHg] created thread pool: name [snapshot], core [1], max [4], keep alive [5m]

参考

Elasticsearch源码分析—线程池(十一) ——就是从队列里处理请求的更多相关文章

  1. JUC源码分析-线程池篇(三)ScheduledThreadPoolExecutor

    JUC源码分析-线程池篇(三)ScheduledThreadPoolExecutor ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor.它主要用来在 ...

  2. JUC源码分析-线程池篇(二)FutureTask

    JUC源码分析-线程池篇(二)FutureTask JDK5 之后提供了 Callable 和 Future 接口,通过它们就可以在任务执行完毕之后得到任务的执行结果.本文从源代码角度分析下具体的实现 ...

  3. JUC源码分析-线程池篇(三)Timer

    JUC源码分析-线程池篇(三)Timer Timer 是 java.util 包提供的一个定时任务调度器,在主线程之外起一个单独的线程执行指定的计划任务,可以指定执行一次或者反复执行多次. 1. Ti ...

  4. JUC源码分析-线程池篇(一):ThreadPoolExecutor

    JUC源码分析-线程池篇(一):ThreadPoolExecutor Java 中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池.在开发过程中,合理地使用线程池 ...

  5. nginx源码分析线程池详解

    nginx源码分析线程池详解 一.前言     nginx是采用多进程模型,master和worker之间主要通过pipe管道的方式进行通信,多进程的优势就在于各个进程互不影响.但是经常会有人问道,n ...

  6. nginx源码分析——线程池

    源码: nginx 1.13.0-release   一.前言      nginx是采用多进程模型,master和worker之间主要通过pipe管道的方式进行通信,多进程的优势就在于各个进程互不影 ...

  7. 手机自动化测试:appium源码分析之bootstrap十一

    手机自动化测试:appium源码分析之bootstrap十一   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣 ...

  8. Elasticsearch源码分析 - 源码构建

    原文地址:https://mp.weixin.qq.com/s?__biz=MzU2Njg5Nzk0NQ==&mid=2247483694&idx=1&sn=bd03afe5a ...

  9. elasticsearch源码分析之search模块(server端)

    elasticsearch源码分析之search模块(server端) 继续接着上一篇的来说啊,当client端将search的请求发送到某一个node之后,剩下的事情就是server端来处理了,具体 ...

随机推荐

  1. JDBC链接数据库步骤

    java中定义链接数据库的标准:JDBC 1.导包:不同数据库有不同的jdbc驱动包,而且jdbc驱动包和数据库版本必须对应 2.测试 3.写代码 try { 1.//加载JDBC驱动    Clas ...

  2. 10、scala面向对象编程之Trait

    1.  将trait作为接口使用 2.trait中定义具体方法 3.trait定义具体字段 4.trait中定义抽象字段 5.为实例对象混入trait 6.trait调用链 7.在trait中覆盖抽象 ...

  3. java_遍历文件目录

    package util; import java.io.File; import java.io.IOException; //列出File的一些常用操作 public class util { / ...

  4. ionic错误

    1. 问题:Error: read ECONNRESET 启动使用ionic serve启动服务器之后只要一刷新界面就会导致服务器关闭,报的错误如下: events.js:136 throw er; ...

  5. centOS Linux下用yum安装mysql

    centOS Linux下用yum安装mysql      第一篇:安装和配置MySQL   第一步:安装MySQL   [root@192 local]# yum -y install mysql- ...

  6. 面试bb

    1.面试者进行自我介绍 2.学校/学历/大学生活及课程的心得总结(人品,性格评估) 3.技能介绍(是否对应聘工作有帮助,评估学习能力):编程/操作系统/测试工具/测试方法/脚本 4.离职原因/项目经验 ...

  7. How To:python pip install

    官方网站 https://pypi.python.org/pypi/pip/   下载需要的版本   wget https://pypi.python.org/packages/source/p/pi ...

  8. c/c++排坑(2) -- c语言中的符号重载

    所谓的符号重载就是在不同的上下文环境里有不同的意义.甚至有些关键字也被重载而具有好几种意义,这也是C语言的作用域规则对程序员不那么清晰的主要原因. 本章内容摘自<c专家编程>P37. 大家 ...

  9. HDU-4055 Number String 动态规划 巧妙的转移

    题目链接:https://cn.vjudge.net/problem/HDU-4055 题意 给一个序列相邻元素各个上升下降情况('I'上升'D'下降'?'随便),问有几种满足的排列. 例:ID 答: ...

  10. JS练习:商品的左右选择

    代码: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title ...