Java技巧:创建监视友好的ExecutorService

在本文中,我们将扩展具有监视功能的ExecutorService实现。 这种监视功能将帮助我们在实时生产环境中测量多个池参数,即活动线程,工作队列大小等。 它还将使我们能够衡量任务执行时间,成功任务计数和失败任务计数。

监控库

至于监控库,我们将使用Metrics 。 为了简单起见,我们将使用ConsoleReporter ,它将向控制台报告指标。 对于生产级应用程序,我们应该使用高级报告器(即Graphite报告器)。 如果您不熟悉指标,那么建议您阅读入门指南 。

让我们开始吧。

扩展ThreadPoolExecutor

我们将使用ThreadPoolExecutor作为新类型的基类。 我们将其称为MonitoredThreadPoolExecutor 。 此类将接受MetricRegistry作为其构造函数参数之一–

public class MonitoredThreadPoolExecutor extends ThreadPoolExecutor {private final MetricRegistry metricRegistry;public MonitoredThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,MetricRegistry metricRegistry) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);this.metricRegistry = metricRegistry;}public MonitoredThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,MetricRegistry metricRegistry) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);this.metricRegistry = metricRegistry;}public MonitoredThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler,MetricRegistry metricRegistry) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);this.metricRegistry = metricRegistry;}public MonitoredThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler,MetricRegistry metricRegistry) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);this.metricRegistry = metricRegistry;}
}

注册仪表以测量特定于池的参数

量表是一个值的瞬时量度。 我们将使用它来测量不同的池参数,例如活动线程数,任务队列大小等。

在注册仪表之前,我们需要确定如何为线程池计算指标名称。 每个度量标准,无论是仪表,计时器还是仪表,都有一个唯一的名称。 此名称用于标识度量标准来源。 此处的约定是使用点分字符串,该点分字符串通常由要监视的类的完全限定名称构成。

对于我们的线程池,我们将使用其完全限定名称作为指标名称的前缀。 另外,我们将添加另一个名为
poolName,客户端将使用它来指定特定于实例的标识符。

实施这些更改后,该类如下所示–

public class MonitoredThreadPoolExecutor extends ThreadPoolExecutor {private final MetricRegistry metricRegistry;private final String metricsPrefix;public MonitoredThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,MetricRegistry metricRegistry,String poolName) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);this.metricRegistry = metricRegistry;this.metricsPrefix = MetricRegistry.name(getClass(), poolName);}// Rest of the constructors
}

现在我们准备注册我们的仪表。 为此,我们将定义一个私有方法–

private void registerGauges() {metricRegistry.register(MetricRegistry.name(metricsPrefix, "corePoolSize"), (Gauge<Integer>) this::getCorePoolSize);metricRegistry.register(MetricRegistry.name(metricsPrefix, "activeThreads"), (Gauge<Integer>) this::getActiveCount);metricRegistry.register(MetricRegistry.name(metricsPrefix, "maxPoolSize"), (Gauge<Integer>) this::getMaximumPoolSize);metricRegistry.register(MetricRegistry.name(metricsPrefix, "queueSize"), (Gauge<Integer>) () -> getQueue().size());
}

对于我们的示例,我们正在测量核心池大小,活动线程数,最大池大小和任务队列大小。 根据监视要求,我们可以注册更多/更少的量规来测量不同的属性。

现在,所有构造函数都将调用此私有方法–

public MonitoredThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,MetricRegistry metricRegistry,String poolName
) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);this.metricRegistry = metricRegistry;this.metricsPrefix = MetricRegistry.name(getClass(), poolName);registerGauges();
}

测量任务执行时间

为了衡量任务执行时间,我们将覆盖ThreadPoolExecutor提供的两个生命周期方法– beforeExecuteafterExecute

顾名思义,执行任务之前,将由执行任务的线程调用beforeExecute回调。 此回调的默认实现不执行任何操作。

同样,在执行每个任务之后,执行任务的线程将调用afterExecute回调。 此回调的默认实现也不执行任何操作。 即使任务抛出未捕获的RuntimeExceptionError ,也会调用此回调。

我们将在beforeExecute覆盖中启动一个Timer ,然后将其用于afterExecute覆盖中以获取总的任务执行时间。 为了存储对Timer的引用,我们将在类中引入一个新的ThreadLocal字段。

回调的实现如下:

public class MonitoredThreadPoolExecutor extends ThreadPoolExecutor {private final MetricRegistry metricRegistry;private final String metricsPrefix;private ThreadLocal<Timer.Context> taskExecutionTimer = new ThreadLocal<>();// Constructors@Overrideprotected void beforeExecute(Thread thread, Runnable task) {super.beforeExecute(thread, task);Timer timer = metricRegistry.timer(MetricRegistry.name(metricsPrefix, "task-execution"));taskExecutionTimer.set(timer.time());}@Overrideprotected void afterExecute(Runnable task, Throwable throwable) {Timer.Context context = taskExecutionTimer.get();context.stop();super.afterExecute(task, throwable);}
}

记录由于未捕获的异常而导致的失败任务数

afterExecute回调的第二个参数是Throwable 。 如果非null,则此Throwable引用导致执行终止的未捕获RuntimeExceptionError 。 我们可以使用此信息来部分计算由于未捕获的异常而突然终止的任务总数。

要获得失败任务的总数,我们必须考虑另一种情况。 使用execute方法提交的任务将抛出任何未捕获的异常,并且它将用作afterExecute回调的第二个参数。 但是,执行者服务会吞下使用Submit方法提交的任务。 JavaDoc (重点是我的话)中对此做了清楚的解释-


注意:如果将动作显式地或通过诸如Submit之类的方法包含在任务(例如FutureTask)中,则这些任务对象会捕获并维护计算异常,因此它们不会导致突然终止,并且内部异常不会传递给此方法。 如果您想使用此方法捕获两种类型的失败,则可以进一步探查此类情况,例如,在此示例子类中,如果任务被中止,则打印直接原因或潜在异常。 幸运的是,同一文档还为此提供了一种解决方案,即检查可运行对象以查看其是否为Future ,然后获取基础异常。

结合这些方法,我们可以如下修改afterExecute方法–

@Override
protected void afterExecute(Runnable runnable, Throwable throwable) {Timer.Context context = taskExecutionTimer.get();context.stop();super.afterExecute(runnable, throwable);if (throwable == null && runnable instanceof Future && ((Future) runnable).isDone()) {try {((Future) runnable).get();} catch (CancellationException ce) {throwable = ce;} catch (ExecutionException ee) {throwable = ee.getCause();} catch (InterruptedException ie) {Thread.currentThread().interrupt();}}if (throwable != null) {Counter failedTasksCounter = metricRegistry.counter(MetricRegistry.name(metricsPrefix, "failed-tasks"));failedTasksCounter.inc();}
}

计算成功任务的总数

先前的方法也可以用于计算成功任务的总数:完成的任务不会抛出任何异常或错误–

@Override
protected void afterExecute(Runnable runnable, Throwable throwable) {// Rest of the method body .....if (throwable != null) {Counter failedTasksCounter = metricRegistry.counter(MetricRegistry.name(metricsPrefix, "failed-tasks"));failedTasksCounter.inc();} else {Counter successfulTasksCounter = metricRegistry.counter(MetricRegistry.name(metricsPrefix, "successful-tasks"));successfulTasksCounter.inc();}
}

结论

在本文中,我们研究了对ExecutorService实现的一些监视友好的自定义。 像往常一样,任何建议/改进/错误修复将不胜感激。 至于示例源代码,它已上传到
Github 。

翻译自: https://www.javacodegeeks.com/2018/05/java-tips-creating-a-monitoring-friendly-executorservice.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/346779.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java如果把字符串转成对象_Java中的重复对象:不仅仅是字符串

当Java应用程序消耗大量内存时&#xff0c;它本身就会出现问题&#xff0c;并可能导致GC压力增加和GC暂停时间过长。在我之前的一篇文章中&#xff0c;我讨论了Java中常见的内存浪费源&#xff1a;重复字符串。两个 java.lang.String 对象&#xff0c; a 并 b 在重复时 a ! b &…

mysql五大子句_MySQL的查询语句中可以使用以下哪个子句来表示分组查询

【多选题】人类行为遗传学工作者倾向于把人的行为遗传分为哪几类【填空题】MySQL的连接操作包括内连接、( )和交叉连接。【判断题】社会生活类尤其是人文风光类纪录片的解说则多用文学、 散文手法, 既抒情又有解释说明, 语言华丽富于美感,情感真挚浓郁。【单选题】1 、作业区边…

批处理 设置电脑最佳性能_批处理最佳做法

批处理 设置电脑最佳性能大多数应用程序至少具有一个批处理任务&#xff0c;在后台执行特定的逻辑。 编写批处理作业并不复杂&#xff0c;但是您需要了解一些基本规则&#xff0c;我将列举一些我发现最重要的规则。 从输入类型的角度来看&#xff0c;处理项目可以通过轮询处理…

layui登录页面写入数据_layui基本使用(动态获取数据,并把需要的数据传到新打开的窗口)...

\n‘ ‘ \n‘ ‘ \n‘ ‘ \n‘ ‘ \n‘ ‘ \n‘ ‘ \n‘ ‘ ‘ item.TITLE ‘\n‘ ‘ ‘ item.ZDRQ ‘\n‘ ‘ \n‘ ‘ \n‘ ‘ \n‘ ‘ \n‘ ‘ ‘ item.CDATE ‘\n‘ ‘ ‘ item.TJR ‘\n‘ ‘ \n‘ ‘ \n‘ ‘ ‘‘//消息的跳转页面$(".xiaoxi").click(fu…

mysql 5.6自动任务_mysql定时执行某任务

查看event是否开启: show variables like %sche%;将事件计划开启: set global event_scheduler1;关闭事件任务: alter event e_test ON COMPLETION PRESERVE DISABLE;开户事件任务: alter event e_test ON COMPLETION PRESERVE ENABLE;简单实例.创建表 CREATE TABLE test(endti…

JVM体系结构:JVM类加载器和运行时数据区

各位读者好&#xff01; 在JVM系列的上一篇文章中&#xff0c;开发人员了解了Java虚拟机&#xff08;JVM&#xff09;及其体系结构。 本教程将帮助开发人员正确回答以下主题的问题&#xff1a; ClassLoader子系统 运行时数据区 1.简介 在继续之前&#xff0c;让我们看一下Ja…

mysql flush cache_mysql的SQL_NO_CACHE(在查询时不使用缓存)和sql_cache用法

转自&#xff1a;http://www.169it.com/article/5994930453423417575.html为了测试sql语句的效率&#xff0c;有时候要不用缓存来查询。使用SELECT SQL_NO_CACHE ...语法即可SQL_NO_CACHE的真正作用是禁止缓存查询结果&#xff0c;但并不意味着cache不作为结果返回给query。目前…

-%3erow mysql_MySQL查询优化

# MySQL查询优化* [请简述项目中优化MySQL语句执行效率的方法&#xff0c;从哪些方面入手&#xff0c;SQL语句性能如何分析&#xff1f;](https://www.kancloud.cn/ranjun940726/php_interview/596348#MySQLSQL_3)* [分析查询速度](https://www.kancloud.cn/ranjun940726/php_i…

apache karaf_Apache Karaf遇到Apache HBase

apache karaf介绍 Apache HBase是一个以Google Bigtable为蓝本的开源&#xff0c;分布式&#xff0c;版本化&#xff0c;面向列的商店。 如果您是普通读者&#xff0c;那么您可能已经知道Apache Karaf是什么&#xff0c;但是对于那些不是的读者&#xff1a;Apache Karaf是一个O…

mysql5.6特性_MySQL5.6新版本特性

MySQL已发布新的系列版本5.6.x&#xff0c;如果打算升级的朋友可以尝试&#xff0c;虽然目前没有收到新版本的使用反馈&#xff0c;但凭借MySQL占据市场份额来看&#xff0c;新版本的确值得期待。五大特性&#xff1a;优化器的改进MySQL Optimizer 团队做了大量的工作为了不断的…

实现threadlocal_ThreadLocal如何实现?

实现threadlocal这是我上周的帖子的后续文章&#xff0c;其中我解释了ThreadLocal用法背后的动机 。 从帖子中我们可以回忆起&#xff0c;如果您希望为每个线程拥有一个独立初始化的变量副本&#xff0c;则ThreadLocal确实是一个很酷的概念。 现在&#xff0c;好奇的人可能已经…

mysql timeout的单位_mysql的timeout-阿里云开发者社区

mysql的timeout很多时候我们连接mysql会在timeout这里跌倒&#xff0c;这里明确下mysql的timeout&#xff1a;下面是获取timeout的变量&#xff1a;mysql> show global variables like "%timeout%";---------------------------------------| Variable_name | Val…

带有Spring Boot 2.0的Spring Security:UserDetailsS​​ervice

正如我们在上一篇文章中所看到的&#xff0c;我们的spring应用程序的用户名和密码是通过环境变量配置的。 这对于原型目的是可以的&#xff0c;但是在现实生活中&#xff0c;我们必须提供另一种方式来使用户有资格登录到该应用程序。 为此&#xff0c;我们使用UserDetailsS​​…

java计算整数出现的次数_[剑指offer题解][Java]1到n整数中1出现的次数

前言众所周知&#xff0c;《剑指offer》是一本“好书”。如果你是个算法菜鸡&#xff08;和我一样&#xff09;&#xff0c;那么最推荐的是先把剑指offer的题目搞明白。对于剑指offer题解这个系列&#xff0c;我的写作思路是&#xff0c;对于看过文章的读者&#xff0c;能够做到…

mysql数据库+ssh框架_SSH框架+Mysql数据库开发java web会员积分消费管理系统

项目描述会员消费管理&#xff1a;会员信息调取查询、会员消费商品添加金额计算、兑换商品等会员管理&#xff1a;会员增删改查积分管理&#xff1a;积分设置、商品积分设置等积分兑换、数据库备份还原、折扣管理、商品管理等运行环境jdk7(8)tomcat7(8)mysql5.7myeclipes或ecli…

spring和spring_Spring交易可见性

spring和spring在初始化应用程序上下文时&#xff0c;Spring在遇到带有Transactional标记的类时会创建代理。 Transactional可以应用于类级别或方法级别。 在类级别应用它意味着该类中定义的所有公共方法都是事务性的。 Spring创建的代理类型&#xff0c;即Jdk代理或CGLIB代理&…

python上传文件到linux服务器_python上传大文件到服务器报错

项目需要&#xff0c;在mac上将打包好的文件(四五百兆)自动上传到web后台&#xff0c;用了两种方式上传&#xff0c;都报了类似的错误&#xff0c;在windows和linux上测试不会报错&#xff0c;但是到了mac打包机上就会报错&#xff1a;①第一种报错&#xff1a;Traceback (most…

mysql 更改root密码及 主机_设置更改root密码(远程,本地)、连接mysql、mysql常用命令...

设置更改root密码1、将mysql加入环境变量中[rootcentos7 ~]# grep mysql /etc/profileexport PATH/usr/local/mysql/bin/:$PATH2、直接登录&#xff0c;无密码[rootcentos7 ~]# mysql -uroot3、方式一&#xff1a;设置密码[rootcentos7 ~]# mysqladmin -uroot password 123456W…

从NetBeans运行和调试WildFly Swarm应用程序

使用NetBeans的Java EE开发人员习惯于直接在NetBeans所选择的应用程序服务器中运行和调试其瘦战应用程序。 在开发打包为ber或镂空jars的微服务时&#xff0c;您期望使用相同的轻松方式进行运行和调试。 好消息是您可以。 在本文中&#xff0c;我将逐步演示如何在NetBeans中运行…

mysql 事实表 维度表_数据库的事实表与维度表

维度表示你要对数据进行分析时所用的一个量&#xff0c;比如你要分析产品销售情况&#xff0c;你可以选择按类别来进行分析&#xff0c;或按区域来分析&#xff0c;这样的按照什么分析就构成一个维度。前面的实例就可以有两个维度&#xff1a;类型和区域。另外每个维度还可以有…