网站建设分金手指排名十四,百度seo推广优化,网站网页设计公司有哪些,树莓派打开wordpress在本文中#xff0c;我们将扩展具有监视功能的ExecutorService实现。 这种监视功能将帮助我们在实时生产环境中测量多个池参数#xff0c;即活动线程#xff0c;工作队列大小等。 它还将使我们能够衡量任务执行时间#xff0c;成功任务计数和失败任务计数。 监控库 至于监… 在本文中我们将扩展具有监视功能的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,BlockingQueueRunnable workQueue,MetricRegistry metricRegistry) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);this.metricRegistry metricRegistry;}public MonitoredThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueueRunnable 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,BlockingQueueRunnable 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,BlockingQueueRunnable 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,BlockingQueueRunnable 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), (GaugeInteger) this::getCorePoolSize);metricRegistry.register(MetricRegistry.name(metricsPrefix, activeThreads), (GaugeInteger) this::getActiveCount);metricRegistry.register(MetricRegistry.name(metricsPrefix, maxPoolSize), (GaugeInteger) this::getMaximumPoolSize);metricRegistry.register(MetricRegistry.name(metricsPrefix, queueSize), (GaugeInteger) () - getQueue().size());
} 对于我们的示例我们正在测量核心池大小活动线程数最大池大小和任务队列大小。 根据监视要求我们可以注册更多/更少的量规来测量不同的属性。 现在所有构造函数都将调用此私有方法– public MonitoredThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueueRunnable workQueue,MetricRegistry metricRegistry,String poolName
) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);this.metricRegistry metricRegistry;this.metricsPrefix MetricRegistry.name(getClass(), poolName);registerGauges();
}测量任务执行时间 为了衡量任务执行时间我们将覆盖ThreadPoolExecutor提供的两个生命周期方法– beforeExecute和afterExecute 。 顾名思义执行任务之前将由执行任务的线程调用beforeExecute回调。 此回调的默认实现不执行任何操作。 同样在执行每个任务之后执行任务的线程将调用afterExecute回调。 此回调的默认实现也不执行任何操作。 即使任务抛出未捕获的RuntimeException或Error 也会调用此回调。 我们将在beforeExecute覆盖中启动一个Timer 然后将其用于afterExecute覆盖中以获取总的任务执行时间。 为了存储对Timer的引用我们将在类中引入一个新的ThreadLocal字段。 回调的实现如下 public class MonitoredThreadPoolExecutor extends ThreadPoolExecutor {private final MetricRegistry metricRegistry;private final String metricsPrefix;private ThreadLocalTimer.Context taskExecutionTimer new ThreadLocal();// ConstructorsOverrideprotected 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引用导致执行终止的未捕获RuntimeException或Error 。 我们可以使用此信息来部分计算由于未捕获的异常而突然终止的任务总数。 要获得失败任务的总数我们必须考虑另一种情况。 使用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