title: Controller services are daemons
date: 2020-06-30
categories:

  • Apache NIFI
    tags:
  • Apache NIFI
    author: 张诚
    location: BeiJing
    publish: true

今天只是扯扯淡,顺便记录一下我突然想到的一个low疑问。

有趣的对话

今天又某同事兴奋地跟我说,他的组件设置了100个并发。

我问他:“服务器什么配置,这么牛逼,能开到100个并发”

某:“没看”

我:“… 几核?”

某:“8核的吧”

我:“8核的。。。。就算这台服务器只跑了NIFI,那么NIFI的线程池数最多也就配置到32,刨去NIFI的主线程、守护线程不计,最多同一时刻也就一共16个线程在CPU里,并发开到100有啥意义?而且它开到100了,其他组件很容易拿不到资源的啊”

某:“这。。。32咋算的”

我:“一核最多2个线程,8核是16个,再来16个等待的,就是32了。”

。。。

所有官方推荐配置线程数为 核数 乘以 2到4倍

相关文章:了解Apache NiFi最大线程池和处理器并发任务设置 深入解析Apache NIFI的调度策略

疑问

然后不知怎的,我突然想到一个好玩的问题:

说一个Processor可以设置并发数,调度的时候是分配一个线程去执行任务,那么Controller Service呢?还有我们知道,Controller Service是可以多个组件公用的,Controller Service在哪里?怎么被调用的?Controller Service需要分配线程去执行嘛?

其实很简单的,没有那么复杂。

首先十分明确的一点是:Controller Service是无法设置并发数的,Controller Service是被调用的。

然后我们重点看一下Controller Service存到了哪里

create一个Controller Service的时候会调用StandardFlowManager.java的createControllerService

  1. public ControllerServiceNode createControllerService(final String type, final String id, final BundleCoordinate bundleCoordinate, final Set<URL> additionalUrls, final boolean firstTimeAdded,final boolean registerLogObserver) {
  2. ...
  3. final ControllerServiceProvider controllerServiceProvider = flowController.getControllerServiceProvider();
  4. final ControllerServiceNode serviceNode = new ExtensionBuilder()
  5. .identifier(id)
  6. .type(type)
  7. .bundleCoordinate(bundleCoordinate)
  8. .controllerServiceProvider(flowController.getControllerServiceProvider())
  9. .processScheduler(processScheduler)
  10. .nodeTypeProvider(flowController)
  11. .validationTrigger(flowController.getValidationTrigger())
  12. .reloadComponent(flowController.getReloadComponent())
  13. .variableRegistry(flowController.getVariableRegistry())
  14. .addClasspathUrls(additionalUrls)
  15. .kerberosConfig(flowController.createKerberosConfig(nifiProperties))
  16. .stateManagerProvider(flowController.getStateManagerProvider())
  17. .extensionManager(extensionManager)
  18. .buildControllerService();
  19. ...
  20. controllerServiceProvider.onControllerServiceAdded(serviceNode);
  21. return serviceNode;
  22. }

    private final ConcurrentMap<String, ControllerServiceNode> serviceCache = new ConcurrentHashMap<>();

    @Override
    public void onControllerServiceAdded(final ControllerServiceNode serviceNode) {
        serviceCache.putIfAbsent(serviceNode.getIdentifier(), serviceNode);
    }

看到没,Controller Service就是存到了一个Map里,任意一个线程执行Processor调度时,谁要用谁就去Map了查一下然后调用就行了。

所以说如果一个Processor支持并发,那么这个Processor用的Controller Service那得是线程安全的。

然后先前在Apache NIFI入门(读完即入门)一文中我们说过

Controller Services是守护进程(daemons)。它们在后台运行,并提供配置,资源和参数供处理器执行。

我们可以回顾一下daemon是什么意思

在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程) 用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守护线程的保姆.只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器),它就是一个很称职的守护者。User和Daemon两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果 User Thread已经全部退出运行了,只剩下Daemon Thread存在了,虚拟机也就退出了。 因为没有了被守护者,Daemon也就没有工作可做了,也就没有继续运行程序的必要了。

我们在Controller Service里用的最多的可能就是数据库连接池服务了吧,而数据库连接池本身就也包含着很多守护线程,监控连接个数、超时时间、状态等等。

如果Controller Service里有后台运行的线程,那么它(们)应该是守护线程(否则JVM怎么退出)(咱们这里不排除因为Processor等组件调用Controller Service而产生一些非守护线程,但如果有,那么这些非守护线程一定是会随着调度前后而退出的)。

到这里我们知道运行的NIFI里还有很多我们不易计数的守护线程,所以回到最开始的NIFI配置线程池线程数的问题,如果是8核服务器我们配置了8或者16,及时服务器只运行的NIFI,我们也千万不能天真的认为线程池里这8或者16个就可以肆意的遨游在CPU里了。