From ea51810f79f62eb15c3a8a21bf7fc5974b11f78e Mon Sep 17 00:00:00 2001 From: blinkfox Date: Wed, 20 May 2020 23:42:01 +0800 Subject: [PATCH 01/37] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E4=B9=8B?= =?UTF-8?q?=E5=89=8D=E7=89=88=E6=9C=AC=E4=B8=8D=E6=AD=A3=E7=A1=AE=E7=9A=84?= =?UTF-8?q?=20CHANGELOG=20=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- README_CN.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 990d9fe..4fc75c9 100644 --- a/README.md +++ b/README.md @@ -209,7 +209,7 @@ This [stalker](https://github.com/blinkfox/stalker) library is open sourced unde - v1.1.1 New statistical index of throughput (2020-05-20) - New statistical index of throughput; -- v1.1.0 fixes the limitation when creating too many threads (2020-05-14) +- v1.1.0 Added the function of getting return results after running (2020-05-14) - Added the ability to output results in `MeasureOutput`, and the default run method will also return its results;   - Added `runStatis` method, you can get the original statistical result data; - v1.0.1 Fix the limitation problem when too many threads created (2019-09-14) diff --git a/README_CN.md b/README_CN.md index 479d896..9254e27 100644 --- a/README_CN.md +++ b/README_CN.md @@ -207,7 +207,7 @@ Assert.assertFaster(Options.of(), - v1.1.1 新增了吞吐量的统计指标 (2020-05-20) - 新增了吞吐量的统计指标; -- v1.1.0 修复线程创建过多时的限制问题 (2020-05-14) +- v1.1.0 新增了运行后可以获取返回结果的功能 (2020-05-14) - 新增了 `MeasureOutput` 中可输出结果的功能,且默认的 run 方法,也会返回其结果; - 新增了 `runStatis` 方法,可以拿到原始的统计结果数据; - v1.0.1 修复线程创建过多时的限制问题 (2019-09-14) From 774af98fa50df6c4d5bf68adfd3e7291d52e57e3 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Sat, 23 May 2020 17:04:09 +0800 Subject: [PATCH 02/37] =?UTF-8?q?=E5=BC=80=E5=90=AF=E4=BA=86=20v1.2.0=20?= =?UTF-8?q?=E7=89=88=E6=9C=AC=EF=BC=8C=E6=96=B0=E5=A2=9E=E4=BA=86=E9=83=A8?= =?UTF-8?q?=E5=88=86=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 7 +- .../java/com/blinkfox/stalker/Stalker.java | 31 +++++++- .../runner/ConcurrentMeasureRunner.java | 17 ++++- .../stalker/runner/MeasureRunnerContext.java | 75 ++++++++++++++++++- 4 files changed, 124 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 3a63d8b..5d90333 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.blinkfox stalker - 1.1.1 + 1.2.0-SNAPSHOT jar stalker @@ -46,6 +46,11 @@ mini-table 1.0.0 + + com.blinkfox + id-worker + 1.1.0 + org.slf4j slf4j-api diff --git a/src/main/java/com/blinkfox/stalker/Stalker.java b/src/main/java/com/blinkfox/stalker/Stalker.java index b00bf70..6cc8364 100644 --- a/src/main/java/com/blinkfox/stalker/Stalker.java +++ b/src/main/java/com/blinkfox/stalker/Stalker.java @@ -2,7 +2,6 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.output.MeasureOutputContext; -import com.blinkfox.stalker.result.MeasurementCollector; import com.blinkfox.stalker.result.bean.Measurement; import com.blinkfox.stalker.runner.MeasureRunnerContext; import java.util.List; @@ -17,6 +16,34 @@ @UtilityClass public class Stalker { + /** + * 使用默认选项参数来提交可运行的测量任务,并立即返回此次会话的 ID. + * + * @param task 任务 + * @return 会话 ID + * @author blinkfox on 2020-05-23 + * @since v1.2.0 + */ + public String submit(Runnable task) { + return submit(Options.of(), task); + } + + /** + * 提交可运行的测量任务,并立即返回此次会话的 ID. + * + * @param task 任务 + * @return 会话 ID + * @author blinkfox on 2020-05-23 + * @since v1.2.0 + */ + public String submit(Options options, Runnable task) { + if (options == null || task == null) { + throw new IllegalArgumentException("options or runnables is null (or empty)!"); + } + options.valid(); + return MeasureRunnerContext.submit(options, task); + } + /** * 测量要执行的代码的性能评估. * @@ -56,7 +83,7 @@ public Measurement[] runStatis(Options options, Runnable... runnables) { // 循环遍历测量各个 Runnable 实例的性能结果,然后将各个结果存放到数组中,最后统一输出出来. Measurement[] measurements = new Measurement[len]; for (int i = 0; i < len; i++) { - measurements[i] = new MeasurementCollector().collect(new MeasureRunnerContext(options).run(runnables[i])); + measurements[i] = new MeasureRunnerContext(options).runAndCollect(runnables[i]); } return measurements; } diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java index 9b5bad5..48cbc2a 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java @@ -9,7 +9,9 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; /** @@ -26,23 +28,34 @@ public class ConcurrentMeasureRunner implements MeasureRunner { /** * 每次'成功'测量出的待测量方法的耗时时间,单位为纳秒(ns). */ + @Getter private final Queue eachMeasures; /** * 测量过程中执行的总次数. */ + @Getter private final AtomicLong total; /** * 测量过程中执行成功的次数. */ + @Getter private final AtomicLong success; /** * 测量过程中执行失败的次数. */ + @Getter private final AtomicLong failure; + /** + * 是否已经运行完成. + * + * @since v1.2.0 + */ + private final AtomicBoolean complete; + /** * 构造方法. * @@ -53,6 +66,7 @@ public ConcurrentMeasureRunner() { this.total = new AtomicLong(0); this.success = new AtomicLong(0); this.failure = new AtomicLong(0); + this.complete = new AtomicBoolean(false); } /** @@ -93,6 +107,7 @@ public OverallResult run(Options options, Runnable runnable) { // 等待所有线程执行完毕,并关闭线程池,最后将结果封装成实体信息. this.awaitAndShutdown(countDownLatch, executorService); + this.complete.compareAndSet(false, true); return this.buildMeasurement(System.nanoTime() - start); } @@ -105,7 +120,6 @@ public OverallResult run(Options options, Runnable runnable) { */ private void loopMeasure(int runs, boolean printErrorLog, final Runnable runnable) { for (int j = 0; j < runs; j++) { - this.total.incrementAndGet(); try { long eachStart = System.nanoTime(); runnable.run(); @@ -118,6 +132,7 @@ private void loopMeasure(int runs, boolean printErrorLog, final Runnable runnabl log.error("测量方法耗时信息在多线程下出错!", e); } } + this.total.incrementAndGet(); } } diff --git a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java index 3d3ef40..e6efe3b 100644 --- a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java +++ b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java @@ -1,8 +1,16 @@ package com.blinkfox.stalker.runner; +import com.blinkfox.IdWorker; import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.kit.StrKit; +import com.blinkfox.stalker.result.MeasurementCollector; +import com.blinkfox.stalker.result.bean.Measurement; import com.blinkfox.stalker.result.bean.OverallResult; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; /** @@ -14,6 +22,22 @@ @Slf4j public final class MeasureRunnerContext { + /** + * 用于异步提交任务的线程池. + */ + private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(1, + 3, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<>(5)); + + /** + * 用来存储 {@link MeasureRunner} 的容器,Key 是运行时的 sessionId, value 是 {@link MeasureRunner} 的实例. + */ + private static final Map measureMap = new ConcurrentHashMap<>(); + + /** + * 使用雪花算法生成短 ID 的生成器. + */ + public static final IdWorker idWorker = new IdWorker(); + /** * 运行测量的性能参数配置选项. */ @@ -35,7 +59,7 @@ public MeasureRunnerContext(Options options) { * @param options 参数选项 * @param runnable runnable */ - private void warmup(Options options, Runnable runnable) { + private static void warmup(Options options, Runnable runnable) { final boolean printErrorLog = options.isPrintErrorLog(); log.debug("【stalker 提示】预热开始..."); long start = System.nanoTime(); @@ -64,10 +88,57 @@ private void warmup(Options options, Runnable runnable) { * @return 运行的测量结果 */ public OverallResult run(Runnable runnable) { - this.warmup(options, runnable); + warmup(options, runnable); return options.getThreads() > 1 && options.getConcurrens() > 1 ? new ConcurrentMeasureRunner().run(options, runnable) : new SimpleMeasureRunner().run(options, runnable); } + /** + * 检查Options参数是否合法,并进行预热准备,然后执行 runnable 方法,并将执行结果的耗时纳秒(ns)值存入到集合中. + * + * @param runnable 可运行实例 + * @return 运行的经过测量结果 + * @author blinkfox on 2020-05-23. + * @since v1.2.0 + */ + public Measurement runAndCollect(Runnable runnable) { + return new MeasurementCollector().collect(this.run(runnable)); + } + + /** + * 检查Options参数是否合法,并进行预热准备,然后执行 runnable 方法,并将执行结果的耗时纳秒(ns)值存入到集合中. + * + * @param options 运行的选项参数 + * @param runnable 可运行实例 + * @return 此次运行的会话 ID + * @author blinkfox on 2020-05-23. + * @since v1.2.0 + */ + public static String submit(final Options options, final Runnable runnable) { + // 预热运行. + warmup(options, runnable); + + // 将 measureRunner 存储到 map 中,并异步执行任务. + MeasureRunner measureRunner = options.getThreads() > 1 && options.getConcurrens() > 1 + ? new ConcurrentMeasureRunner() + : new SimpleMeasureRunner(); + String sessionId = idWorker.get62RadixId(); + measureMap.put(sessionId, measureRunner); + executor.execute(() -> measureRunner.run(options, runnable)); + return sessionId; + } + + /** + * 根据运行的测量会话 ID,来查询该会话所对应的测量结果. + * + * @param sessionId 会话 ID + * @return 测量结果 + * @author blinkfox on 2020-05-23. + * @since v1.2.0 + */ + public static Measurement submit(String sessionId) { + return null; + } + } From 54539589237b6704c6a80b93cfb8b54a05df1a0c Mon Sep 17 00:00:00 2001 From: blinkfox Date: Sun, 24 May 2020 15:21:22 +0800 Subject: [PATCH 03/37] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=BA=86=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E7=9A=84=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/blinkfox/stalker/Stalker.java | 26 +++ .../stalker/exception/StalkerException.java | 22 +++ .../java/com/blinkfox/stalker/kit/StrKit.java | 15 +- .../stalker/result/bean/RunnerInfo.java | 28 ++++ .../stalker/runner/AbstractMeasureRunner.java | 156 ++++++++++++++++++ .../runner/ConcurrentMeasureRunner.java | 84 ++-------- .../stalker/runner/MeasureRunner.java | 88 ++++++++++ .../stalker/runner/MeasureRunnerContext.java | 37 ++++- .../stalker/runner/SimpleMeasureRunner.java | 55 ++---- 9 files changed, 389 insertions(+), 122 deletions(-) create mode 100644 src/main/java/com/blinkfox/stalker/exception/StalkerException.java create mode 100644 src/main/java/com/blinkfox/stalker/result/bean/RunnerInfo.java create mode 100644 src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java diff --git a/src/main/java/com/blinkfox/stalker/Stalker.java b/src/main/java/com/blinkfox/stalker/Stalker.java index 6cc8364..870b642 100644 --- a/src/main/java/com/blinkfox/stalker/Stalker.java +++ b/src/main/java/com/blinkfox/stalker/Stalker.java @@ -31,6 +31,7 @@ public String submit(Runnable task) { /** * 提交可运行的测量任务,并立即返回此次会话的 ID. * + * @param options 选项参数 * @param task 任务 * @return 会话 ID * @author blinkfox on 2020-05-23 @@ -44,6 +45,31 @@ public String submit(Options options, Runnable task) { return MeasureRunnerContext.submit(options, task); } + /** + * 根据会话的 ID 查询其对应的运行任务的测量结果数据,本结果是在 {@link Options#getOutputs()} 的属性中 + * {@link com.blinkfox.stalker.output.MeasureOutput} 接口定义的输出结果. + * + * @param sessionId 会话 ID + * @return 运行的最终输出结果 + * @author blinkfox on 2020-05-23 + * @since v1.2.0 + */ + public Object query(String sessionId) { + return MeasureRunnerContext.query(sessionId); + } + + /** + * 根据会话的 ID 查询其对应的运行任务的测量结果数据. + * + * @param sessionId 会话 ID + * @return 运行的测量结果 + * @author blinkfox on 2020-05-23 + * @since v1.2.0 + */ + public Measurement queryMeasurement(String sessionId) { + return MeasureRunnerContext.queryMeasurement(sessionId); + } + /** * 测量要执行的代码的性能评估. * diff --git a/src/main/java/com/blinkfox/stalker/exception/StalkerException.java b/src/main/java/com/blinkfox/stalker/exception/StalkerException.java new file mode 100644 index 0000000..2b58400 --- /dev/null +++ b/src/main/java/com/blinkfox/stalker/exception/StalkerException.java @@ -0,0 +1,22 @@ +package com.blinkfox.stalker.exception; + +/** + * Stalker 运行过程中相关的运行时异常. + * + * @author blinkfox on 2020-05-23. + * @since v1.2.0 + */ +public class StalkerException extends RuntimeException { + + private static final long serialVersionUID = 8452522047738865868L; + + /** + * 异常实例的构造方法. + * + * @param s 异常描述信息 + */ + public StalkerException(String s) { + super(s); + } + +} diff --git a/src/main/java/com/blinkfox/stalker/kit/StrKit.java b/src/main/java/com/blinkfox/stalker/kit/StrKit.java index 9058ac9..b2a1c4d 100644 --- a/src/main/java/com/blinkfox/stalker/kit/StrKit.java +++ b/src/main/java/com/blinkfox/stalker/kit/StrKit.java @@ -3,6 +3,7 @@ import java.math.BigDecimal; import java.math.RoundingMode; import lombok.experimental.UtilityClass; +import org.slf4j.helpers.MessageFormatter; /** * 字符串操作工具类. @@ -28,6 +29,8 @@ public boolean isEmpty(String s) { * * @param objects 不定参数对象 * @return 字符串 + * @author blinkfox on 2020-05-23. + * @since v1.2.0 */ public String join(Object... objects) { if (objects != null && objects.length > 0) { @@ -37,10 +40,20 @@ public String join(Object... objects) { } return sb.toString(); } - return ""; } + /** + * 使用 Slf4j 中的字符串格式化方式来格式化字符串. + * + * @param pattern 待格式化的字符串 + * @param args 参数 + * @return 格式化后的字符串 + */ + public static String format(String pattern, Object... args) { + return pattern == null ? "" : MessageFormatter.arrayFormat(pattern, args).getMessage(); + } + /** * 将纳秒的时间转为与其最贴近的时间单位. * diff --git a/src/main/java/com/blinkfox/stalker/result/bean/RunnerInfo.java b/src/main/java/com/blinkfox/stalker/result/bean/RunnerInfo.java new file mode 100644 index 0000000..a89f374 --- /dev/null +++ b/src/main/java/com/blinkfox/stalker/result/bean/RunnerInfo.java @@ -0,0 +1,28 @@ +package com.blinkfox.stalker.result.bean; + +import com.blinkfox.stalker.config.Options; +import com.blinkfox.stalker.runner.MeasureRunner; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 可运行任务的相关的信息实体. + * + * @author blinkfox on 2020-05-24. + * @since v1.2.0 + */ +@Getter +@AllArgsConstructor +public class RunnerInfo { + + /** + * 可运行任务的选项参数信息. + */ + private final Options options; + + /** + * 任务运行的 {@link MeasureRunner} 实例. + */ + private final MeasureRunner measureRunner; + +} diff --git a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java new file mode 100644 index 0000000..5777180 --- /dev/null +++ b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java @@ -0,0 +1,156 @@ +package com.blinkfox.stalker.runner; + +import com.blinkfox.stalker.kit.MathKit; +import com.blinkfox.stalker.result.bean.OverallResult; +import java.util.ArrayDeque; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import lombok.Getter; + +/** + * 用于测量待执行方法耗时情况等信息的抽象运行器,实现了 {@link MeasureRunner} 接口. + * + * @author blinkfox on 2020-05-24. + * @see MeasureRunner + * @see SimpleMeasureRunner + * @see ConcurrentMeasureRunner + * @since v1.2.0 + */ +public abstract class AbstractMeasureRunner implements MeasureRunner { + + /** + * 每次'成功'测量出的待测量方法的耗时时间,单位为纳秒({@code ns}). + */ + protected Queue eachMeasures; + + /** + * 测量过程中执行的总次数. + */ + protected AtomicLong total; + + /** + * 测量过程中执行成功的次数. + */ + protected AtomicLong success; + + /** + * 测量过程中执行失败的次数. + */ + protected AtomicLong failure; + + /** + * 是否已经运行完成. + */ + protected AtomicBoolean complete; + + /** + * 运行开始时的纳秒时间戳,单位为纳秒({@code ns}). + */ + @Getter + protected long startNanoTime; + + /** + * 运行结束时的纳秒时间戳,单位为纳秒({@code ns}). + */ + @Getter + protected long endNanoTime; + + /** + * 公共的抽象父构造方法. + */ + public AbstractMeasureRunner() { + this.eachMeasures = new ConcurrentLinkedQueue<>(); + this.total = new AtomicLong(0); + this.success = new AtomicLong(0); + this.failure = new AtomicLong(0); + this.complete = new AtomicBoolean(false); + } + + @Override + public long[] getEachMeasures() { + // 为了不影响正在运行中的数据及当前或以后数据统计的"准确性",这里复制一份队列中的数据来单独计算和返回. + Queue queue = new ArrayDeque<>(this.eachMeasures); + int len = queue.size(); + long[] measures = new long[len]; + for (int i = 0; i < len; i++) { + Long cost = queue.poll(); + if (cost != null) { + measures[i] = cost; + } + } + return measures; + } + + @Override + public long getTotal() { + return this.total.get(); + } + + @Override + public long getSuccess() { + return this.success.get(); + } + + @Override + public long getFailure() { + return this.failure.get(); + } + + @Override + public boolean isComplete() { + return this.complete.get(); + } + + @Override + public long getCosts() { + return this.endNanoTime - this.startNanoTime; + } + + /** + * 构造正在运行中的任务的测量结果信息的 {@link OverallResult} 对象. + * + * @return 总体测量结果信息 + */ + @Override + public OverallResult buildRunningMeasurement() { + // 如果任务已经完成,就直接返回最终的测试结果数据即可. + if (this.complete.get()) { + return this.buildFinalMeasurement(); + } + + // 如果任务仍然在运行中,由于各个计数器是独立的,对于整体上的各个统计数据的结果来说,并不能保证"线程安全". + // 为了减小仍在运行中时的任务,获取各个统计数据时线程安全所导致的误差. + // 这里只获取 eachMeasures 和 failure 两个值,基于这两个值来计算其他值,消耗的时间使用当前时间来计算. + long failure = this.getFailure(); + long[] eachCosts = this.getEachMeasures(); + long totalCount = eachCosts.length; + long costs = System.currentTimeMillis() - this.startNanoTime; + return new OverallResult() + .setEachMeasures(eachCosts) + .setCosts(costs) + .setTotal(totalCount) + .setSuccess(totalCount - failure) + .setFailure(failure) + .setThroughput(MathKit.calcThroughput(totalCount, costs)); + } + + /** + * 构造最终运行完时的测量的结果信息的 {@link OverallResult} 对象. + * + * @return 总体测量结果信息 + */ + @Override + public OverallResult buildFinalMeasurement() { + long totalCount = this.getTotal(); + return new OverallResult() + .setEachMeasures(this.getEachMeasures()) + .setCosts(this.getCosts()) + .setTotal(totalCount) + .setSuccess(this.getSuccess()) + .setFailure(this.getFailure()) + .setThroughput(MathKit.calcThroughput(totalCount, this.getCosts())); + } + +} diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java index 48cbc2a..461eb93 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java @@ -1,17 +1,11 @@ package com.blinkfox.stalker.runner; import com.blinkfox.stalker.config.Options; -import com.blinkfox.stalker.kit.MathKit; import com.blinkfox.stalker.result.bean.OverallResult; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; -import lombok.Getter; import lombok.extern.slf4j.Slf4j; /** @@ -21,52 +15,17 @@ * @since v1.0.0 */ @Slf4j -public class ConcurrentMeasureRunner implements MeasureRunner { +public class ConcurrentMeasureRunner extends AbstractMeasureRunner { private static final int N_1024 = 1024; - /** - * 每次'成功'测量出的待测量方法的耗时时间,单位为纳秒(ns). - */ - @Getter - private final Queue eachMeasures; - - /** - * 测量过程中执行的总次数. - */ - @Getter - private final AtomicLong total; - - /** - * 测量过程中执行成功的次数. - */ - @Getter - private final AtomicLong success; - - /** - * 测量过程中执行失败的次数. - */ - @Getter - private final AtomicLong failure; - - /** - * 是否已经运行完成. - * - * @since v1.2.0 - */ - private final AtomicBoolean complete; - /** * 构造方法. * *

这个类中的属性,需要支持高并发写入.

*/ public ConcurrentMeasureRunner() { - this.eachMeasures = new ConcurrentLinkedQueue<>(); - this.total = new AtomicLong(0); - this.success = new AtomicLong(0); - this.failure = new AtomicLong(0); - this.complete = new AtomicBoolean(false); + super(); } /** @@ -87,7 +46,7 @@ public OverallResult run(Options options, Runnable runnable) { Semaphore semaphore = new Semaphore(Math.min(concurrens, threads)); CountDownLatch countDownLatch = new CountDownLatch(threads); ExecutorService executorService = Executors.newFixedThreadPool(Math.min(threads, N_1024)); - final long start = System.nanoTime(); + super.startNanoTime = System.nanoTime(); // 在多线程下控制线程并发量,与循环搭配来一起执行和测量. for (int i = 0; i < threads; i++) { @@ -107,8 +66,9 @@ public OverallResult run(Options options, Runnable runnable) { // 等待所有线程执行完毕,并关闭线程池,最后将结果封装成实体信息. this.awaitAndShutdown(countDownLatch, executorService); - this.complete.compareAndSet(false, true); - return this.buildMeasurement(System.nanoTime() - start); + super.endNanoTime = System.nanoTime(); + super.complete.compareAndSet(false, true); + return super.buildFinalMeasurement(); } /** @@ -123,16 +83,16 @@ private void loopMeasure(int runs, boolean printErrorLog, final Runnable runnabl try { long eachStart = System.nanoTime(); runnable.run(); - this.eachMeasures.add(System.nanoTime() - eachStart); - this.success.incrementAndGet(); + super.eachMeasures.add(System.nanoTime() - eachStart); + super.success.incrementAndGet(); } catch (Exception e) { // 如果待测量的方法,执行错误则失败数 +1,且根据选项参数来判断是否打印异常错误日志. - this.failure.incrementAndGet(); + super.failure.incrementAndGet(); if (printErrorLog) { log.error("测量方法耗时信息在多线程下出错!", e); } } - this.total.incrementAndGet(); + super.total.incrementAndGet(); } } @@ -153,28 +113,4 @@ private void awaitAndShutdown(CountDownLatch countDownLatch, ExecutorService exe } } - /** - * 构造测量的结果信息的 Measurement 对象. - * - * @param costs 消耗的总耗时,单位是纳秒 - * @return Measurement对象 - */ - private OverallResult buildMeasurement(long costs) { - // 将队列转数组. - int len = this.eachMeasures.size(); - long[] measures = new long[len]; - for (int i = 0; i < len; i++) { - measures[i] = eachMeasures.remove(); - } - - long totalCount = this.total.get(); - return new OverallResult() - .setEachMeasures(measures) - .setCosts(costs) - .setTotal(totalCount) - .setSuccess(this.success.get()) - .setFailure(this.failure.get()) - .setThroughput(MathKit.calcThroughput(totalCount, costs)); - } - } diff --git a/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java index 78f3171..07a720d 100644 --- a/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java @@ -20,4 +20,92 @@ public interface MeasureRunner { */ OverallResult run(Options options, Runnable runnable); + /** + * 获取当前运行任务中,每次执行的耗时情况的长整形数组. + * + * @return 每次执行耗时情况的长整形数组 + * @author blinkfox on 2020-05-23. + * @since v1.2.0 + */ + long[] getEachMeasures(); + + /** + * 获取当前总共的运行次数. + * + * @return 运行总次数 + * @author blinkfox on 2020-05-23. + * @since v1.2.0 + */ + long getTotal(); + + /** + * 获取当前运行成功的次数. + * + * @return 运行成功的次数 + * @author blinkfox on 2020-05-23. + * @since v1.2.0 + */ + long getSuccess(); + + /** + * 获取当前运行失败的次数. + * + * @return 运行失败的次数 + * @author blinkfox on 2020-05-23. + * @since v1.2.0 + */ + long getFailure(); + + /** + * 判断当前任务是否已经执行完成. + * + * @return 是否执行完成的布尔值 + * @author blinkfox on 2020-05-23. + * @since v1.2.0 + */ + boolean isComplete(); + + /** + * 获取任务开始运行时的纳秒时间戳. + * + * @return 开始运行时间 + * @author blinkfox on 2020-05-23. + * @since v1.2.0 + */ + long getStartNanoTime(); + + /** + * 获取任务结束运行时的纳秒时间戳. + * + * @return 结束运行时间 + * @author blinkfox on 2020-05-23. + * @since v1.2.0 + */ + long getEndNanoTime(); + + /** + * 获取任务最终完成时实际所消耗的总时间. + * + * @return 实际所消耗的总时间 + * @author blinkfox on 2020-05-23. + * @since v1.2.0 + */ + long getCosts(); + + /** + * 构建运行中的任务的总体测量结果信息. + * + * @return 总体测量结果信息 + */ + OverallResult buildRunningMeasurement(); + + /** + * 构建运行任务完成后的最终总体测量结果信息. + * + * @return 总体测量结果信息 + * @author blinkfox on 2020-05-23. + * @since v1.2.0 + */ + OverallResult buildFinalMeasurement(); + } diff --git a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java index e6efe3b..f55bd19 100644 --- a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java +++ b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java @@ -2,10 +2,14 @@ import com.blinkfox.IdWorker; import com.blinkfox.stalker.config.Options; +import com.blinkfox.stalker.exception.StalkerException; import com.blinkfox.stalker.kit.StrKit; +import com.blinkfox.stalker.output.MeasureOutputContext; import com.blinkfox.stalker.result.MeasurementCollector; import com.blinkfox.stalker.result.bean.Measurement; import com.blinkfox.stalker.result.bean.OverallResult; +import com.blinkfox.stalker.result.bean.RunnerInfo; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; @@ -29,9 +33,9 @@ public final class MeasureRunnerContext { 3, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<>(5)); /** - * 用来存储 {@link MeasureRunner} 的容器,Key 是运行时的 sessionId, value 是 {@link MeasureRunner} 的实例. + * 用来存储 {@link MeasureRunner} 的容器,Key 是运行时的 sessionId, value 是 {@link RunnerInfo} 的实例. */ - private static final Map measureMap = new ConcurrentHashMap<>(); + private static final Map measureMap = new ConcurrentHashMap<>(); /** * 使用雪花算法生成短 ID 的生成器. @@ -124,7 +128,7 @@ public static String submit(final Options options, final Runnable runnable) { ? new ConcurrentMeasureRunner() : new SimpleMeasureRunner(); String sessionId = idWorker.get62RadixId(); - measureMap.put(sessionId, measureRunner); + measureMap.put(sessionId, new RunnerInfo(options, measureRunner)); executor.execute(() -> measureRunner.run(options, runnable)); return sessionId; } @@ -137,8 +141,31 @@ public static String submit(final Options options, final Runnable runnable) { * @author blinkfox on 2020-05-23. * @since v1.2.0 */ - public static Measurement submit(String sessionId) { - return null; + public static Measurement queryMeasurement(String sessionId) { + RunnerInfo runnerInfo = measureMap.get(sessionId); + if (runnerInfo == null) { + throw new StalkerException(StrKit.format("根据当前 sessionId【{}】无法找到对应的运行任务," + + "或者该任务已过期,请重新开始执行。", sessionId)); + } + return new MeasurementCollector().collect(runnerInfo.getMeasureRunner().buildRunningMeasurement()); + } + + /** + * 根据运行的测量会话 ID,来查询该会话所对应的测量结果. + * + * @param sessionId 会话 ID + * @return 最终的输出结果 + * @author blinkfox on 2020-05-23. + * @since v1.2.0 + */ + public static List query(String sessionId) { + RunnerInfo runnerInfo = measureMap.get(sessionId); + if (runnerInfo == null) { + throw new StalkerException(StrKit.format("根据当前 sessionId【{}】无法找到对应的运行任务," + + "或者该任务已过期,请重新开始执行。", sessionId)); + } + return new MeasureOutputContext().output(runnerInfo.getOptions(), + new MeasurementCollector().collect(runnerInfo.getMeasureRunner().buildRunningMeasurement())); } } diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java index f6f7572..47745c2 100644 --- a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java @@ -1,7 +1,6 @@ package com.blinkfox.stalker.runner; import com.blinkfox.stalker.config.Options; -import com.blinkfox.stalker.kit.MathKit; import com.blinkfox.stalker.result.bean.OverallResult; import lombok.extern.slf4j.Slf4j; @@ -12,27 +11,14 @@ * @since v1.0.0 */ @Slf4j -public class SimpleMeasureRunner implements MeasureRunner { +public class SimpleMeasureRunner extends AbstractMeasureRunner { /** - * 每次'成功'测量出的待测量方法的耗时时间,单位为纳秒(ns). + * 构造方法. */ - private long[] eachMeasures; - - /** - * 测量过程中执行的总次数. - */ - private long total; - - /** - * 测量过程中执行成功的次数. - */ - private long success; - - /** - * 测量过程中执行失败的次数. - */ - private long failure; + public SimpleMeasureRunner() { + super(); + } /** * 执行 runnable 方法,并将执行成功与否、耗时结果等信息存入到 OverallResult 实体对象中. @@ -46,42 +32,27 @@ public class SimpleMeasureRunner implements MeasureRunner { public OverallResult run(Options options, Runnable runnable) { boolean printErrorLog = options.isPrintErrorLog(); int totalCount = options.getThreads() * options.getRuns(); - this.eachMeasures = new long[totalCount]; - final long start = System.nanoTime(); + super.startNanoTime = System.nanoTime(); // 单线程循环执行 runs 次. for (int i = 0; i < totalCount; ++i) { - this.total++; try { long eachStart = System.nanoTime(); runnable.run(); - this.eachMeasures[i] = System.nanoTime() - eachStart; - this.success++; + super.eachMeasures.add(System.nanoTime() - eachStart); + super.success.incrementAndGet(); } catch (RuntimeException e) { - this.failure++; + super.failure.incrementAndGet(); if (printErrorLog) { log.error("【stalker 错误】测量方法耗时信息出错!", e); } } + super.total.incrementAndGet(); } - return this.buildMeasurement(System.nanoTime() - start); - } - - /** - * 构造测量的结果信息的 Measurement 对象. - * - * @param costs 消耗的总耗时,单位是纳秒 - * @return Measurement对象 - */ - private OverallResult buildMeasurement(long costs) { - return new OverallResult() - .setEachMeasures(this.eachMeasures) - .setCosts(costs) - .setTotal(this.total) - .setSuccess(this.success) - .setFailure(this.failure) - .setThroughput(MathKit.calcThroughput(this.total, costs)); + super.endNanoTime = System.nanoTime(); + super.complete.compareAndSet(false, true); + return super.buildFinalMeasurement(); } } From 51a9d7f72b56f47f9bb96d5be53d2626116ff229 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Sun, 24 May 2020 16:16:54 +0800 Subject: [PATCH 04/37] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BA=86=E6=96=B0?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E7=9A=84=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/blinkfox/stalker/Stalker.java | 25 +++++++- .../stalker/runner/AbstractMeasureRunner.java | 2 +- .../stalker/runner/MeasureRunnerContext.java | 24 ++++++++ .../blinkfox/stalker/test/StalkerTest.java | 59 +++++++++++++++++++ .../stalker/{ => test}/kit/MathKitTest.java | 3 +- .../stalker/test/prepare/MyTestService.java | 15 ++++- 6 files changed, 124 insertions(+), 4 deletions(-) rename src/test/java/com/blinkfox/stalker/{ => test}/kit/MathKitTest.java (83%) diff --git a/src/main/java/com/blinkfox/stalker/Stalker.java b/src/main/java/com/blinkfox/stalker/Stalker.java index 870b642..fd84458 100644 --- a/src/main/java/com/blinkfox/stalker/Stalker.java +++ b/src/main/java/com/blinkfox/stalker/Stalker.java @@ -54,10 +54,33 @@ public String submit(Options options, Runnable task) { * @author blinkfox on 2020-05-23 * @since v1.2.0 */ - public Object query(String sessionId) { + public List query(String sessionId) { return MeasureRunnerContext.query(sessionId); } + /** + * 根据运行的测量会话 ID,判断任务是否在运行中,即时该任务也许不存在,查找不到时,也会认为是 {@code true}. + * + * @param sessionId 会话 ID + * @return 布尔值 + * @author blinkfox on 2020-05-23. + * @since v1.2.0 + */ + public boolean isRunning(String sessionId) { + return MeasureRunnerContext.isRunning(sessionId); + } + + /** + * 根据运行的测量会话 ID,移除相关的运行任务记录.目前不会停止任务,只是从缓存中移除任务记录. + * + * @param sessionId 会话 ID + * @author blinkfox on 2020-05-23 + * @since v1.2.0 + */ + public void remove(String sessionId) { + MeasureRunnerContext.remove(sessionId); + } + /** * 根据会话的 ID 查询其对应的运行任务的测量结果数据. * diff --git a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java index 5777180..3b6f2db 100644 --- a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java @@ -126,7 +126,7 @@ public OverallResult buildRunningMeasurement() { long failure = this.getFailure(); long[] eachCosts = this.getEachMeasures(); long totalCount = eachCosts.length; - long costs = System.currentTimeMillis() - this.startNanoTime; + long costs = System.nanoTime() - this.startNanoTime; return new OverallResult() .setEachMeasures(eachCosts) .setCosts(costs) diff --git a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java index f55bd19..c90cbc2 100644 --- a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java +++ b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java @@ -168,4 +168,28 @@ public static List query(String sessionId) { new MeasurementCollector().collect(runnerInfo.getMeasureRunner().buildRunningMeasurement())); } + /** + * 根据运行的测量会话 ID,移除相关的运行任务记录.目前不会停止任务,只是从缓存中移除任务记录. + * + * @param sessionId 会话 ID + * @author blinkfox on 2020-05-23. + * @since v1.2.0 + */ + public static void remove(String sessionId) { + measureMap.remove(sessionId); + } + + /** + * 根据运行的测量会话 ID,判断任务是否在运行中,即时该任务也许不存在,查找不到时,也会认为是 {@code true}. + * + * @param sessionId 会话 ID + * @return 布尔值 + * @author blinkfox on 2020-05-23. + * @since v1.2.0 + */ + public static boolean isRunning(String sessionId) { + RunnerInfo runnerInfo = measureMap.get(sessionId); + return runnerInfo != null && !runnerInfo.getMeasureRunner().isComplete(); + } + } diff --git a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java index 2a1f3d2..5426f00 100644 --- a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java +++ b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java @@ -3,6 +3,8 @@ import com.blinkfox.stalker.Stalker; import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.test.prepare.MyTestService; +import java.util.List; +import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Test; @@ -12,6 +14,7 @@ * @author blinkfox on 2019-02-03. * @since v1.0.0 */ +@Slf4j public class StalkerTest { /** @@ -92,4 +95,60 @@ public void runWithConcurrentException() { () -> new MyTestService().helloException()); } + /** + * 测试没有Options选项参数时的执行情况. + */ + @Test + public void submit() throws InterruptedException { + String sessionId = Stalker.submit(() -> new MyTestService().hello()); + Assert.assertNotNull(sessionId); + + while (Stalker.isRunning(sessionId)) { + List results = Stalker.query(sessionId); + Assert.assertNotNull(results.get(0)); + Thread.sleep(1L); + } + + log.info("任务已完成,获取最后的执行结果,并移除任务记录."); + Stalker.query(sessionId); + // 执行完成之后移除 sessionId. + Stalker.remove(sessionId); + } + + /** + * 测试慢方法的执行情况. + */ + @Test + public void submitWithSlowMethod() throws InterruptedException { + String sessionId = Stalker.submit(Options.of("SlowTest", 20, 5, 1), () -> new MyTestService().slowHello()); + Assert.assertNotNull(sessionId); + + while (Stalker.isRunning(sessionId)) { + List results = Stalker.query(sessionId); + Assert.assertNotNull(results.get(0)); + Thread.sleep(50L); + } + + log.info("任务已完成,获取最后的执行结果,并移除任务记录."); + Stalker.query(sessionId); + + // 执行完成之后移除 sessionId. + Stalker.remove(sessionId); + } + + /** + * 测试 queryMeasurement 方法. + */ + @Test + public void queryMeasurement() throws InterruptedException { + String sessionId = Stalker.submit(() -> new MyTestService().hello()); + Assert.assertNotNull(sessionId); + + while (Stalker.isRunning(sessionId)) { + Stalker.queryMeasurement(sessionId); + Thread.sleep(5L); + } + // 执行完成之后移除 sessionId. + Stalker.remove(sessionId); + } } diff --git a/src/test/java/com/blinkfox/stalker/kit/MathKitTest.java b/src/test/java/com/blinkfox/stalker/test/kit/MathKitTest.java similarity index 83% rename from src/test/java/com/blinkfox/stalker/kit/MathKitTest.java rename to src/test/java/com/blinkfox/stalker/test/kit/MathKitTest.java index f848f18..9d9842c 100644 --- a/src/test/java/com/blinkfox/stalker/kit/MathKitTest.java +++ b/src/test/java/com/blinkfox/stalker/test/kit/MathKitTest.java @@ -1,5 +1,6 @@ -package com.blinkfox.stalker.kit; +package com.blinkfox.stalker.test.kit; +import com.blinkfox.stalker.kit.MathKit; import org.junit.Assert; import org.junit.Test; diff --git a/src/test/java/com/blinkfox/stalker/test/prepare/MyTestService.java b/src/test/java/com/blinkfox/stalker/test/prepare/MyTestService.java index 72611a5..472e251 100644 --- a/src/test/java/com/blinkfox/stalker/test/prepare/MyTestService.java +++ b/src/test/java/com/blinkfox/stalker/test/prepare/MyTestService.java @@ -13,7 +13,7 @@ public class MyTestService { /** - * 测试方法1,模拟业务代码耗时 2~5 ms,且会有约 5% 的几率执行异常. + * 测试方法1,模拟业务代码耗时 2~5 ms,且会有约 1% 的几率执行异常. */ public void hello() { // 模拟运行抛出异常. @@ -32,6 +32,19 @@ public void fastHello() { this.sleep(2L); } + /** + * 测试方法3,模拟业务代码耗时 20~100 ms,且会有约 3% 的几率执行异常. + */ + public void slowHello() { + // 模拟运行抛出异常. + if (new Random().nextInt(100) < 3) { + throw new MyServiceException("My Service Exception."); + } + + // 模拟运行占用约 20~100 ms 的时间. + this.sleep(20L + new Random().nextInt(80)); + } + /** * 本线程调用该方法时,睡眠指定时间,用来模拟业务耗时. * From 9fbb51f41a381925a73612705ace9abad19952d8 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Sun, 24 May 2020 23:39:26 +0800 Subject: [PATCH 05/37] =?UTF-8?q?=E6=94=B9=E7=94=A8=E4=BA=86=E6=80=A7?= =?UTF-8?q?=E8=83=BD=E6=9B=B4=E9=AB=98=E7=9A=84=20LongAdder=20=E6=9D=A5?= =?UTF-8?q?=E4=BD=9C=E8=AE=A1=E6=95=B0=EF=BC=8C=E4=BF=AE=E5=A4=8D=E4=BA=86?= =?UTF-8?q?=E5=8F=AF=E8=83=BD=E5=87=BA=E7=8E=B0=E5=BC=82=E5=B8=B8=E6=83=85?= =?UTF-8?q?=E5=86=B5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/blinkfox/stalker/kit/MathKit.java | 2 +- .../stalker/runner/AbstractMeasureRunner.java | 22 +++++++++---------- .../runner/ConcurrentMeasureRunner.java | 6 ++--- .../stalker/runner/SimpleMeasureRunner.java | 6 ++--- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/blinkfox/stalker/kit/MathKit.java b/src/main/java/com/blinkfox/stalker/kit/MathKit.java index c6ce8dc..1827c86 100644 --- a/src/main/java/com/blinkfox/stalker/kit/MathKit.java +++ b/src/main/java/com/blinkfox/stalker/kit/MathKit.java @@ -21,7 +21,7 @@ public class MathKit { * @return 吞吐率 */ public double calcThroughput(long count, long costs) { - return count / ((double) costs / 1e9); + return costs == 0 ? 0.0d : count / ((double) costs / 1e9); } } diff --git a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java index 3b6f2db..108a941 100644 --- a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java @@ -6,7 +6,7 @@ import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.LongAdder; import lombok.Getter; /** @@ -28,17 +28,17 @@ public abstract class AbstractMeasureRunner implements MeasureRunner { /** * 测量过程中执行的总次数. */ - protected AtomicLong total; + protected LongAdder total; /** * 测量过程中执行成功的次数. */ - protected AtomicLong success; + protected LongAdder success; /** * 测量过程中执行失败的次数. */ - protected AtomicLong failure; + protected LongAdder failure; /** * 是否已经运行完成. @@ -62,9 +62,9 @@ public abstract class AbstractMeasureRunner implements MeasureRunner { */ public AbstractMeasureRunner() { this.eachMeasures = new ConcurrentLinkedQueue<>(); - this.total = new AtomicLong(0); - this.success = new AtomicLong(0); - this.failure = new AtomicLong(0); + this.total = new LongAdder(); + this.success = new LongAdder(); + this.failure = new LongAdder(); this.complete = new AtomicBoolean(false); } @@ -85,17 +85,17 @@ public long[] getEachMeasures() { @Override public long getTotal() { - return this.total.get(); + return this.total.longValue(); } @Override public long getSuccess() { - return this.success.get(); + return this.success.longValue(); } @Override public long getFailure() { - return this.failure.get(); + return this.failure.longValue(); } @Override @@ -126,7 +126,7 @@ public OverallResult buildRunningMeasurement() { long failure = this.getFailure(); long[] eachCosts = this.getEachMeasures(); long totalCount = eachCosts.length; - long costs = System.nanoTime() - this.startNanoTime; + long costs = this.startNanoTime == 0 ? 0 : System.nanoTime() - this.startNanoTime; return new OverallResult() .setEachMeasures(eachCosts) .setCosts(costs) diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java index 461eb93..c566a0e 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java @@ -84,15 +84,15 @@ private void loopMeasure(int runs, boolean printErrorLog, final Runnable runnabl long eachStart = System.nanoTime(); runnable.run(); super.eachMeasures.add(System.nanoTime() - eachStart); - super.success.incrementAndGet(); + super.success.increment(); } catch (Exception e) { // 如果待测量的方法,执行错误则失败数 +1,且根据选项参数来判断是否打印异常错误日志. - super.failure.incrementAndGet(); + super.failure.increment(); if (printErrorLog) { log.error("测量方法耗时信息在多线程下出错!", e); } } - super.total.incrementAndGet(); + super.total.increment(); } } diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java index 47745c2..eeecc7a 100644 --- a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java @@ -40,14 +40,14 @@ public OverallResult run(Options options, Runnable runnable) { long eachStart = System.nanoTime(); runnable.run(); super.eachMeasures.add(System.nanoTime() - eachStart); - super.success.incrementAndGet(); + super.success.increment(); } catch (RuntimeException e) { - super.failure.incrementAndGet(); + super.failure.increment(); if (printErrorLog) { log.error("【stalker 错误】测量方法耗时信息出错!", e); } } - super.total.incrementAndGet(); + super.total.increment(); } super.endNanoTime = System.nanoTime(); From a89cf275996b6b35150e4288cde6e86b8ed6f2fe Mon Sep 17 00:00:00 2001 From: blinkfox Date: Mon, 25 May 2020 00:05:38 +0800 Subject: [PATCH 06/37] =?UTF-8?q?=E5=B0=86=E7=BA=BF=E7=A8=8B=E6=B1=A0?= =?UTF-8?q?=E5=92=8C=E8=AE=A1=E6=95=B0=E5=99=A8=E9=94=81=E7=A7=BB=E5=88=B0?= =?UTF-8?q?=E4=BA=86=E6=8A=BD=E8=B1=A1=E7=B1=BB=E4=B8=AD=EF=BC=8C=E4=BE=BF?= =?UTF-8?q?=E4=BA=8E=E4=BD=9C=E7=BB=9F=E4=B8=80=E4=BD=9C=E5=B9=B6=E5=8F=91?= =?UTF-8?q?=E6=8E=A7=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stalker/runner/AbstractMeasureRunner.java | 32 +++++++++++++++++ .../runner/ConcurrentMeasureRunner.java | 26 +++----------- .../stalker/runner/SimpleMeasureRunner.java | 36 ++++++++++++------- 3 files changed, 59 insertions(+), 35 deletions(-) diff --git a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java index 108a941..3ba121e 100644 --- a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java @@ -5,9 +5,12 @@ import java.util.ArrayDeque; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.LongAdder; import lombok.Getter; +import lombok.extern.slf4j.Slf4j; /** * 用于测量待执行方法耗时情况等信息的抽象运行器,实现了 {@link MeasureRunner} 接口. @@ -18,8 +21,19 @@ * @see ConcurrentMeasureRunner * @since v1.2.0 */ +@Slf4j public abstract class AbstractMeasureRunner implements MeasureRunner { + /** + * 线程池. + */ + protected ExecutorService executorService; + + /** + * 线程执行计数器锁. + */ + protected CountDownLatch countLatch; + /** * 每次'成功'测量出的待测量方法的耗时时间,单位为纳秒({@code ns}). */ @@ -153,4 +167,22 @@ public OverallResult buildFinalMeasurement() { .setThroughput(MathKit.calcThroughput(totalCount, this.getCosts())); } + /** + * 等待所有线程执行完毕,并最终关闭线程池. + */ + protected void awaitAndShutdown() { + try { + if (this.countLatch != null) { + this.countLatch.await(); + } + } catch (InterruptedException e) { + log.error("在多线程下等待测量结果结束时出错!", e); + Thread.currentThread().interrupt(); + } finally { + if (this.executorService != null) { + this.executorService.shutdown(); + } + } + } + } diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java index c566a0e..6a12178 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java @@ -3,7 +3,6 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.result.bean.OverallResult; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import lombok.extern.slf4j.Slf4j; @@ -44,8 +43,8 @@ public OverallResult run(Options options, Runnable runnable) { // 初始化存储的集合、线程池、并发工具类中的对象实例等. Semaphore semaphore = new Semaphore(Math.min(concurrens, threads)); - CountDownLatch countDownLatch = new CountDownLatch(threads); - ExecutorService executorService = Executors.newFixedThreadPool(Math.min(threads, N_1024)); + super.countLatch = new CountDownLatch(threads); + super.executorService = Executors.newFixedThreadPool(Math.min(threads, N_1024)); super.startNanoTime = System.nanoTime(); // 在多线程下控制线程并发量,与循环搭配来一起执行和测量. @@ -59,13 +58,13 @@ public OverallResult run(Options options, Runnable runnable) { log.error("测量方法耗时信息在多线程下出错!", e); Thread.currentThread().interrupt(); } finally { - countDownLatch.countDown(); + super.countLatch.countDown(); } }); } // 等待所有线程执行完毕,并关闭线程池,最后将结果封装成实体信息. - this.awaitAndShutdown(countDownLatch, executorService); + this.awaitAndShutdown(); super.endNanoTime = System.nanoTime(); super.complete.compareAndSet(false, true); return super.buildFinalMeasurement(); @@ -96,21 +95,4 @@ private void loopMeasure(int runs, boolean printErrorLog, final Runnable runnabl } } - /** - * 等待所有线程执行完毕,并最终关闭线程池. - * - * @param countDownLatch countDownLatch实例 - * @param executorService 线程池 - */ - private void awaitAndShutdown(CountDownLatch countDownLatch, ExecutorService executorService) { - try { - countDownLatch.await(); - } catch (InterruptedException e) { - log.error("在多线程下等待测量结果结束时出错!", e); - Thread.currentThread().interrupt(); - } finally { - executorService.shutdown(); - } - } - } diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java index eeecc7a..df7a436 100644 --- a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java @@ -2,6 +2,8 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.result.bean.OverallResult; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; import lombok.extern.slf4j.Slf4j; /** @@ -32,24 +34,32 @@ public SimpleMeasureRunner() { public OverallResult run(Options options, Runnable runnable) { boolean printErrorLog = options.isPrintErrorLog(); int totalCount = options.getThreads() * options.getRuns(); + super.countLatch = new CountDownLatch(1); + super.executorService = Executors.newSingleThreadExecutor(); super.startNanoTime = System.nanoTime(); - // 单线程循环执行 runs 次. - for (int i = 0; i < totalCount; ++i) { - try { - long eachStart = System.nanoTime(); - runnable.run(); - super.eachMeasures.add(System.nanoTime() - eachStart); - super.success.increment(); - } catch (RuntimeException e) { - super.failure.increment(); - if (printErrorLog) { - log.error("【stalker 错误】测量方法耗时信息出错!", e); + // 由于并发数是 1,直接单线程循环执行 (runs * threads) 次即可. + executorService.submit(() -> { + for (int i = 0; i < totalCount; ++i) { + try { + long eachStart = System.nanoTime(); + runnable.run(); + super.eachMeasures.add(System.nanoTime() - eachStart); + super.success.increment(); + } catch (Exception e) { + super.failure.increment(); + if (printErrorLog) { + log.error("【stalker 错误】测量方法耗时信息出错!", e); + } + } finally { + super.total.increment(); } } - super.total.increment(); - } + super.countLatch.countDown(); + }); + // 等待所有线程执行完毕,并关闭线程池,最后将结果封装成实体信息. + this.awaitAndShutdown(); super.endNanoTime = System.nanoTime(); super.complete.compareAndSet(false, true); return super.buildFinalMeasurement(); From 2ded0ab8fdc23a080c68f852fa05ab8aab34c1ba Mon Sep 17 00:00:00 2001 From: blinkfox Date: Mon, 25 May 2020 00:10:47 +0800 Subject: [PATCH 07/37] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BA=86=E5=81=9C?= =?UTF-8?q?=E6=AD=A2=E7=9A=84=E6=8E=A5=E5=8F=A3=E5=92=8C=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stalker/runner/ConcurrentMeasureRunner.java | 12 ++++++++++++ .../com/blinkfox/stalker/runner/MeasureRunner.java | 7 +++++++ .../blinkfox/stalker/runner/SimpleMeasureRunner.java | 12 ++++++++++++ 3 files changed, 31 insertions(+) diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java index 6a12178..c5221c6 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java @@ -95,4 +95,16 @@ private void loopMeasure(int runs, boolean printErrorLog, final Runnable runnabl } } + /** + * 停止相关的运行测量任务. + * + * @return 是否成功的布尔值 + * @author blinkfox on 2020-05-25. + * @since v1.2.0 + */ + public boolean stop() { + // TODO + return false; + } + } diff --git a/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java index 07a720d..fedc11d 100644 --- a/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java @@ -92,6 +92,13 @@ public interface MeasureRunner { */ long getCosts(); + /** + * 停止相关的运行测量任务. + * + * @return 是否成功的布尔值 + */ + boolean stop(); + /** * 构建运行中的任务的总体测量结果信息. * diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java index df7a436..05f26a9 100644 --- a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java @@ -65,4 +65,16 @@ public OverallResult run(Options options, Runnable runnable) { return super.buildFinalMeasurement(); } + /** + * 停止相关的运行测量任务. + * + * @return 是否成功的布尔值 + * @author blinkfox on 2020-05-25. + * @since v1.2.0 + */ + public boolean stop() { + // TODO + return false; + } + } From 93c99a6f5f2bd352bd24dd204c594101a919bf54 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Mon, 25 May 2020 01:14:46 +0800 Subject: [PATCH 08/37] =?UTF-8?q?SimpleMeasureRunner=20=E4=B8=AD=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E4=BA=86=20Stop=20=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stalker/exception/StalkerException.java | 10 +++ .../stalker/runner/AbstractMeasureRunner.java | 26 ++++++++ .../runner/ConcurrentMeasureRunner.java | 2 +- .../stalker/runner/SimpleMeasureRunner.java | 62 ++++++++++++++----- 4 files changed, 83 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/blinkfox/stalker/exception/StalkerException.java b/src/main/java/com/blinkfox/stalker/exception/StalkerException.java index 2b58400..494f2c4 100644 --- a/src/main/java/com/blinkfox/stalker/exception/StalkerException.java +++ b/src/main/java/com/blinkfox/stalker/exception/StalkerException.java @@ -19,4 +19,14 @@ public StalkerException(String s) { super(s); } + /** + * 附带异常实例的构造方法. + * + * @param s 异常描述信息 + * @param e 异常实例 + */ + public StalkerException(String s, Throwable e) { + super(s, e); + } + } diff --git a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java index 3ba121e..d5ed9f6 100644 --- a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java @@ -7,6 +7,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; +import java.util.concurrent.FutureTask; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.LongAdder; import lombok.Getter; @@ -34,6 +35,11 @@ public abstract class AbstractMeasureRunner implements MeasureRunner { */ protected CountDownLatch countLatch; + /** + * 用于存储正在执行中的任务队列,记录下来便于全局控制任务的取消暂停等. + */ + protected Queue> measureTaskQueue; + /** * 每次'成功'测量出的待测量方法的耗时时间,单位为纳秒({@code ns}). */ @@ -59,6 +65,11 @@ public abstract class AbstractMeasureRunner implements MeasureRunner { */ protected AtomicBoolean complete; + /** + * 是否发起了停止执行任务的信号,如果为 true,需要停止掉当前正在执行或未来待执行的所有任务,并关闭线程池. + */ + protected AtomicBoolean stopSignal; + /** * 运行开始时的纳秒时间戳,单位为纳秒({@code ns}). */ @@ -75,11 +86,13 @@ public abstract class AbstractMeasureRunner implements MeasureRunner { * 公共的抽象父构造方法. */ public AbstractMeasureRunner() { + this.measureTaskQueue = new ConcurrentLinkedQueue<>(); this.eachMeasures = new ConcurrentLinkedQueue<>(); this.total = new LongAdder(); this.success = new LongAdder(); this.failure = new LongAdder(); this.complete = new AtomicBoolean(false); + this.stopSignal = new AtomicBoolean(false); } @Override @@ -185,4 +198,17 @@ protected void awaitAndShutdown() { } } + /** + * 立即安静的关闭线程池. + */ + protected void shutdownNowQuietly() { + if (this.executorService != null) { + try { + this.executorService.shutdownNow(); + } catch (Exception e) { + log.error("【Stalker 错误】关闭测量任务的线程池失败,失败原因:【{}】.", e.getMessage()); + } + } + } + } diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java index c5221c6..960837c 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java @@ -64,7 +64,7 @@ public OverallResult run(Options options, Runnable runnable) { } // 等待所有线程执行完毕,并关闭线程池,最后将结果封装成实体信息. - this.awaitAndShutdown(); + super.awaitAndShutdown(); super.endNanoTime = System.nanoTime(); super.complete.compareAndSet(false, true); return super.buildFinalMeasurement(); diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java index 05f26a9..0954723 100644 --- a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java @@ -1,9 +1,11 @@ package com.blinkfox.stalker.runner; import com.blinkfox.stalker.config.Options; +import com.blinkfox.stalker.exception.StalkerException; import com.blinkfox.stalker.result.bean.OverallResult; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; import lombok.extern.slf4j.Slf4j; /** @@ -15,6 +17,11 @@ @Slf4j public class SimpleMeasureRunner extends AbstractMeasureRunner { + /** + * 测量任务的 {@link FutureTask} 实例. + */ + private FutureTask measureTask; + /** * 构造方法. */ @@ -38,28 +45,41 @@ public OverallResult run(Options options, Runnable runnable) { super.executorService = Executors.newSingleThreadExecutor(); super.startNanoTime = System.nanoTime(); - // 由于并发数是 1,直接单线程循环执行 (runs * threads) 次即可. + // 由于并发数是 1,直接单线程循环执行 (runs * threads) 次即可, + // 将执行的相关任务以 FutureTask 的形式来执行,便于程序动态取消任务. executorService.submit(() -> { - for (int i = 0; i < totalCount; ++i) { - try { - long eachStart = System.nanoTime(); - runnable.run(); - super.eachMeasures.add(System.nanoTime() - eachStart); - super.success.increment(); - } catch (Exception e) { - super.failure.increment(); - if (printErrorLog) { - log.error("【stalker 错误】测量方法耗时信息出错!", e); + this.measureTask = new FutureTask<>(() -> { + for (int i = 0; i < totalCount; ++i) { + try { + // 开始执行测量任务,记录开始时间、执行次数等. + long eachStart = System.nanoTime(); + measureTask.get(); + super.eachMeasures.add(System.nanoTime() - eachStart); + super.success.increment(); + } catch (Exception e) { + super.failure.increment(); + if (printErrorLog) { + log.error("【stalker 错误】测量方法耗时信息出错!", e); + } + } finally { + super.total.increment(); } - } finally { - super.total.increment(); } + }, null); + + // 阻塞调用执行测量任务. + try { + this.measureTask.get(); + } catch (Exception e) { + throw new StalkerException("【Stalker 异常】执行测量任务发生异常!", e); } + super.countLatch.countDown(); + measureTaskQueue.remove(null); }); // 等待所有线程执行完毕,并关闭线程池,最后将结果封装成实体信息. - this.awaitAndShutdown(); + super.awaitAndShutdown(); super.endNanoTime = System.nanoTime(); super.complete.compareAndSet(false, true); return super.buildFinalMeasurement(); @@ -73,8 +93,18 @@ public OverallResult run(Options options, Runnable runnable) { * @since v1.2.0 */ public boolean stop() { - // TODO - return false; + if (isComplete()) { + log.info("【Stalker 提示】任务已完成,将不再暂停测量任务."); + return true; + } + + // 立即关闭线程池. + super.shutdownNowQuietly(); + // 取消正在执行中的任务. + if (this.measureTask != null && !this.measureTask.isDone()) { + return this.measureTask.cancel(true); + } + return true; } } From 2e57fbff7b25c58d8591ef959def434c2507e7b7 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Tue, 26 May 2020 00:10:30 +0800 Subject: [PATCH 09/37] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=20Simple=20?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E9=83=A8=E5=88=86=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stalker/runner/AbstractMeasureRunner.java | 6 -- .../runner/ConcurrentMeasureRunner.java | 11 +++- .../stalker/runner/SimpleMeasureRunner.java | 62 +++++++++---------- .../blinkfox/stalker/test/StalkerTest.java | 2 +- 4 files changed, 37 insertions(+), 44 deletions(-) diff --git a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java index d5ed9f6..f104f9f 100644 --- a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java @@ -35,11 +35,6 @@ public abstract class AbstractMeasureRunner implements MeasureRunner { */ protected CountDownLatch countLatch; - /** - * 用于存储正在执行中的任务队列,记录下来便于全局控制任务的取消暂停等. - */ - protected Queue> measureTaskQueue; - /** * 每次'成功'测量出的待测量方法的耗时时间,单位为纳秒({@code ns}). */ @@ -86,7 +81,6 @@ public abstract class AbstractMeasureRunner implements MeasureRunner { * 公共的抽象父构造方法. */ public AbstractMeasureRunner() { - this.measureTaskQueue = new ConcurrentLinkedQueue<>(); this.eachMeasures = new ConcurrentLinkedQueue<>(); this.total = new LongAdder(); this.success = new LongAdder(); diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java index 960837c..16b5b81 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java @@ -64,9 +64,9 @@ public OverallResult run(Options options, Runnable runnable) { } // 等待所有线程执行完毕,并关闭线程池,最后将结果封装成实体信息. - super.awaitAndShutdown(); super.endNanoTime = System.nanoTime(); super.complete.compareAndSet(false, true); + super.awaitAndShutdown(); return super.buildFinalMeasurement(); } @@ -98,13 +98,18 @@ private void loopMeasure(int runs, boolean printErrorLog, final Runnable runnabl /** * 停止相关的运行测量任务. * + *

注意:如果任务未完成,则立即停止线程池,但是还不能停止正在运行中的若干任务线程, + * 暂时还没想到一个更好的、高性能的停止所有运行中的任务的方法.

+ * * @return 是否成功的布尔值 * @author blinkfox on 2020-05-25. * @since v1.2.0 */ public boolean stop() { - // TODO - return false; + if (!isComplete()) { + super.shutdownNowQuietly(); + } + return true; } } diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java index 0954723..f841c27 100644 --- a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java @@ -1,11 +1,9 @@ package com.blinkfox.stalker.runner; import com.blinkfox.stalker.config.Options; -import com.blinkfox.stalker.exception.StalkerException; import com.blinkfox.stalker.result.bean.OverallResult; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; -import java.util.concurrent.FutureTask; +import java.util.concurrent.Future; import lombok.extern.slf4j.Slf4j; /** @@ -18,9 +16,11 @@ public class SimpleMeasureRunner extends AbstractMeasureRunner { /** - * 测量任务的 {@link FutureTask} 实例. + * 执行中的测量任务的 {@link Future} 实例. + * + * @since v1.2.0 */ - private FutureTask measureTask; + private Future measureTask; /** * 构造方法. @@ -41,47 +41,41 @@ public SimpleMeasureRunner() { public OverallResult run(Options options, Runnable runnable) { boolean printErrorLog = options.isPrintErrorLog(); int totalCount = options.getThreads() * options.getRuns(); - super.countLatch = new CountDownLatch(1); super.executorService = Executors.newSingleThreadExecutor(); super.startNanoTime = System.nanoTime(); // 由于并发数是 1,直接单线程循环执行 (runs * threads) 次即可, - // 将执行的相关任务以 FutureTask 的形式来执行,便于程序动态取消任务. - executorService.submit(() -> { - this.measureTask = new FutureTask<>(() -> { - for (int i = 0; i < totalCount; ++i) { - try { - // 开始执行测量任务,记录开始时间、执行次数等. - long eachStart = System.nanoTime(); - measureTask.get(); - super.eachMeasures.add(System.nanoTime() - eachStart); - super.success.increment(); - } catch (Exception e) { - super.failure.increment(); - if (printErrorLog) { - log.error("【stalker 错误】测量方法耗时信息出错!", e); - } - } finally { - super.total.increment(); + // 将执行的相关任务以 Future 的形式来执行,便于程序动态取消任务或判断任务执行情况等. + this.measureTask = executorService.submit(() -> { + for (int i = 0; i < totalCount; ++i) { + try { + // 开始执行测量任务,记录开始时间、执行次数等. + long eachStart = System.nanoTime(); + runnable.run(); + super.eachMeasures.add(System.nanoTime() - eachStart); + super.success.increment(); + } catch (Exception e) { + super.failure.increment(); + if (printErrorLog) { + log.error("【stalker 错误】测量方法耗时信息出错!", e); } + } finally { + super.total.increment(); } - }, null); - - // 阻塞调用执行测量任务. - try { - this.measureTask.get(); - } catch (Exception e) { - throw new StalkerException("【Stalker 异常】执行测量任务发生异常!", e); } - - super.countLatch.countDown(); - measureTaskQueue.remove(null); }); + // 阻塞调用要执行的测量任务,达到等待任务结束的目的. + try { + this.measureTask.get(); + } catch (Exception e) { + log.error("【Stalker 错误】执行测量任务发生错误!", e); + } + // 等待所有线程执行完毕,并关闭线程池,最后将结果封装成实体信息. - super.awaitAndShutdown(); super.endNanoTime = System.nanoTime(); super.complete.compareAndSet(false, true); + super.awaitAndShutdown(); return super.buildFinalMeasurement(); } diff --git a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java index 5426f00..8fa7fb4 100644 --- a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java +++ b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java @@ -106,7 +106,7 @@ public void submit() throws InterruptedException { while (Stalker.isRunning(sessionId)) { List results = Stalker.query(sessionId); Assert.assertNotNull(results.get(0)); - Thread.sleep(1L); + Thread.sleep(3L); } log.info("任务已完成,获取最后的执行结果,并移除任务记录."); From 8308cdb761e3452b3114e7e2c9ce485840e8ae39 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Tue, 26 May 2020 00:56:25 +0800 Subject: [PATCH 10/37] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20Stop=20=E7=AD=89?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/blinkfox/stalker/Stalker.java | 33 +++++++++++++++ .../stalker/runner/AbstractMeasureRunner.java | 7 ---- .../runner/ConcurrentMeasureRunner.java | 2 + .../stalker/runner/MeasureRunnerContext.java | 37 ++++++++++++++++ .../stalker/runner/SimpleMeasureRunner.java | 3 ++ .../blinkfox/stalker/test/StalkerTest.java | 42 ++++++++++++++++++- 6 files changed, 115 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/blinkfox/stalker/Stalker.java b/src/main/java/com/blinkfox/stalker/Stalker.java index fd84458..8260d3f 100644 --- a/src/main/java/com/blinkfox/stalker/Stalker.java +++ b/src/main/java/com/blinkfox/stalker/Stalker.java @@ -5,6 +5,7 @@ import com.blinkfox.stalker.result.bean.Measurement; import com.blinkfox.stalker.runner.MeasureRunnerContext; import java.util.List; +import java.util.Set; import lombok.experimental.UtilityClass; /** @@ -81,6 +82,17 @@ public void remove(String sessionId) { MeasureRunnerContext.remove(sessionId); } + /** + * 根据运行的测量会话 ID,停止相关的测量任务. + * + * @param sessionId 会话 ID + * @author blinkfox on 2020-05-23 + * @since v1.2.0 + */ + public void stop(String sessionId) { + MeasureRunnerContext.stop(sessionId); + } + /** * 根据会话的 ID 查询其对应的运行任务的测量结果数据. * @@ -93,6 +105,27 @@ public Measurement queryMeasurement(String sessionId) { return MeasureRunnerContext.queryMeasurement(sessionId); } + /** + * 清除所有测量任务记录. + * + * @author blinkfox on 2020-05-26. + * @since v1.2.0 + */ + public void clear() { + MeasureRunnerContext.clear(); + } + + /** + * 获取所有测量任务记录的 Session ID 集合. + * + * @return Session ID 集合 + * @author blinkfox on 2020-05-26. + * @since v1.2.0 + */ + public Set getAllSessions() { + return MeasureRunnerContext.getAllSessions(); + } + /** * 测量要执行的代码的性能评估. * diff --git a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java index f104f9f..77b6204 100644 --- a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java @@ -7,7 +7,6 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; -import java.util.concurrent.FutureTask; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.LongAdder; import lombok.Getter; @@ -60,11 +59,6 @@ public abstract class AbstractMeasureRunner implements MeasureRunner { */ protected AtomicBoolean complete; - /** - * 是否发起了停止执行任务的信号,如果为 true,需要停止掉当前正在执行或未来待执行的所有任务,并关闭线程池. - */ - protected AtomicBoolean stopSignal; - /** * 运行开始时的纳秒时间戳,单位为纳秒({@code ns}). */ @@ -86,7 +80,6 @@ public AbstractMeasureRunner() { this.success = new LongAdder(); this.failure = new LongAdder(); this.complete = new AtomicBoolean(false); - this.stopSignal = new AtomicBoolean(false); } @Override diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java index 16b5b81..3177d0e 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java @@ -107,6 +107,8 @@ private void loopMeasure(int runs, boolean printErrorLog, final Runnable runnabl */ public boolean stop() { if (!isComplete()) { + super.endNanoTime = System.nanoTime(); + super.complete.compareAndSet(false, true); super.shutdownNowQuietly(); } return true; diff --git a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java index c90cbc2..49b1af4 100644 --- a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java +++ b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java @@ -11,6 +11,7 @@ import com.blinkfox.stalker.result.bean.RunnerInfo; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; @@ -192,4 +193,40 @@ public static boolean isRunning(String sessionId) { return runnerInfo != null && !runnerInfo.getMeasureRunner().isComplete(); } + /** + * 根据运行的测量会话 ID,停止相关的测量任务. + * + * @param sessionId 会话 ID + * @return 布尔值 + * @author blinkfox on 2020-05-26. + * @since v1.2.0 + */ + public static void stop(String sessionId) { + RunnerInfo runnerInfo = measureMap.get(sessionId); + if (runnerInfo != null) { + runnerInfo.getMeasureRunner().stop(); + } + } + + /** + * 清除所有测量任务记录. + * + * @author blinkfox on 2020-05-26. + * @since v1.2.0 + */ + public static void clear() { + measureMap.clear(); + } + + /** + * 获取所有测量任务记录的 Session ID 集合. + * + * @return Session ID 集合 + * @author blinkfox on 2020-05-26. + * @since v1.2.0 + */ + public static Set getAllSessions() { + return measureMap.keySet(); + } + } diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java index f841c27..74d1854 100644 --- a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java @@ -92,6 +92,9 @@ public boolean stop() { return true; } + super.endNanoTime = System.nanoTime(); + super.complete.compareAndSet(false, true); + // 立即关闭线程池. super.shutdownNowQuietly(); // 取消正在执行中的任务. diff --git a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java index 8fa7fb4..b62d69e 100644 --- a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java +++ b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java @@ -2,9 +2,11 @@ import com.blinkfox.stalker.Stalker; import com.blinkfox.stalker.config.Options; +import com.blinkfox.stalker.result.bean.Measurement; import com.blinkfox.stalker.test.prepare.MyTestService; import java.util.List; import lombok.extern.slf4j.Slf4j; +import org.junit.AfterClass; import org.junit.Assert; import org.junit.Test; @@ -120,7 +122,8 @@ public void submit() throws InterruptedException { */ @Test public void submitWithSlowMethod() throws InterruptedException { - String sessionId = Stalker.submit(Options.of("SlowTest", 20, 5, 1), () -> new MyTestService().slowHello()); + String sessionId = Stalker.submit(Options.of("SlowTest", 20, 5, 1), + () -> new MyTestService().slowHello()); Assert.assertNotNull(sessionId); while (Stalker.isRunning(sessionId)) { @@ -145,10 +148,45 @@ public void queryMeasurement() throws InterruptedException { Assert.assertNotNull(sessionId); while (Stalker.isRunning(sessionId)) { - Stalker.queryMeasurement(sessionId); + Measurement measurement = Stalker.queryMeasurement(sessionId); + Assert.assertNotNull(measurement); Thread.sleep(5L); } // 执行完成之后移除 sessionId. Stalker.remove(sessionId); } + + /** + * 测试慢方法的执行情况. + */ + @Test + public void submitWithStop() throws InterruptedException { + String sessionId = Stalker.submit(Options.of("StopTest", 20, 5, 1), + () -> new MyTestService().slowHello()); + Assert.assertNotNull(sessionId); + + Thread.sleep(50L); + List results = Stalker.query(sessionId); + Assert.assertNotNull(results.get(0)); + Stalker.stop(sessionId); + log.info("任务已停止,获取停止前的执行结果."); + Stalker.query(sessionId); + Thread.sleep(100L); + + log.info("任务已停止,获取最后的执行结果."); + Stalker.query(sessionId); + + // 执行完成之后移除 sessionId. + Stalker.remove(sessionId); + } + + /** + * 单测结束后执行的方法. + */ + @AfterClass + public static void destroy() { + Assert.assertNotNull(Stalker.getAllSessions()); + Stalker.clear(); + } + } From 7323e8a8ab369b4003854fc3a86eb600fa33cc05 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Tue, 26 May 2020 00:57:33 +0800 Subject: [PATCH 11/37] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E5=A4=9A=E4=BD=99=E7=9A=84=20Javadoc=20=E6=A0=87=E7=AD=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/blinkfox/stalker/runner/MeasureRunnerContext.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java index 49b1af4..0e0c644 100644 --- a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java +++ b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java @@ -197,7 +197,6 @@ public static boolean isRunning(String sessionId) { * 根据运行的测量会话 ID,停止相关的测量任务. * * @param sessionId 会话 ID - * @return 布尔值 * @author blinkfox on 2020-05-26. * @since v1.2.0 */ From 921feebf691c1d15099d85d1430b217e18183f06 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Tue, 26 May 2020 01:09:15 +0800 Subject: [PATCH 12/37] =?UTF-8?q?=E5=8E=BB=E6=8E=89=E4=BA=86=E6=80=BB?= =?UTF-8?q?=E6=95=B0=E7=9A=84=E5=8D=95=E7=8B=AC=E8=AE=A1=E6=95=B0=EF=BC=8C?= =?UTF-8?q?=E8=80=8C=E6=98=AF=E4=BD=BF=E7=94=A8=E6=88=90=E5=8A=9F=E6=95=B0?= =?UTF-8?q?+=E5=A4=B1=E8=B4=A5=E6=95=B0=E6=9D=A5=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E6=80=BB=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stalker/runner/AbstractMeasureRunner.java | 16 ++++++---------- .../stalker/runner/ConcurrentMeasureRunner.java | 1 - .../stalker/runner/SimpleMeasureRunner.java | 2 -- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java index 77b6204..2589d35 100644 --- a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java @@ -39,11 +39,6 @@ public abstract class AbstractMeasureRunner implements MeasureRunner { */ protected Queue eachMeasures; - /** - * 测量过程中执行的总次数. - */ - protected LongAdder total; - /** * 测量过程中执行成功的次数. */ @@ -76,7 +71,6 @@ public abstract class AbstractMeasureRunner implements MeasureRunner { */ public AbstractMeasureRunner() { this.eachMeasures = new ConcurrentLinkedQueue<>(); - this.total = new LongAdder(); this.success = new LongAdder(); this.failure = new LongAdder(); this.complete = new AtomicBoolean(false); @@ -99,7 +93,7 @@ public long[] getEachMeasures() { @Override public long getTotal() { - return this.total.longValue(); + return this.success.longValue() + this.failure.longValue(); } @Override @@ -157,13 +151,15 @@ public OverallResult buildRunningMeasurement() { */ @Override public OverallResult buildFinalMeasurement() { - long totalCount = this.getTotal(); + long successCount = this.success.longValue(); + long failureCount = this.failure.longValue(); + long totalCount = successCount + failureCount; return new OverallResult() .setEachMeasures(this.getEachMeasures()) .setCosts(this.getCosts()) .setTotal(totalCount) - .setSuccess(this.getSuccess()) - .setFailure(this.getFailure()) + .setSuccess(successCount) + .setFailure(failureCount) .setThroughput(MathKit.calcThroughput(totalCount, this.getCosts())); } diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java index 3177d0e..ec412b1 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java @@ -91,7 +91,6 @@ private void loopMeasure(int runs, boolean printErrorLog, final Runnable runnabl log.error("测量方法耗时信息在多线程下出错!", e); } } - super.total.increment(); } } diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java index 74d1854..156661b 100644 --- a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java @@ -59,8 +59,6 @@ public OverallResult run(Options options, Runnable runnable) { if (printErrorLog) { log.error("【stalker 错误】测量方法耗时信息出错!", e); } - } finally { - super.total.increment(); } } }); From 7beb58b45712621cfe537ad2552d54b04dbd2e3b Mon Sep 17 00:00:00 2001 From: blinkfox Date: Tue, 26 May 2020 01:12:34 +0800 Subject: [PATCH 13/37] =?UTF-8?q?=E5=B0=8F=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/com/blinkfox/stalker/test/StalkerTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java index b62d69e..ee22334 100644 --- a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java +++ b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java @@ -97,6 +97,15 @@ public void runWithConcurrentException() { () -> new MyTestService().helloException()); } + /** + * 测试慢方法的执行情况. + */ + @Test + public void runWithSlowMethod() { + Stalker.run(Options.of("SlowTest", 20, 5, 1), + () -> new MyTestService().slowHello()); + } + /** * 测试没有Options选项参数时的执行情况. */ From f08a25278c7ea27c961af08bb5b9af90fd444958 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Tue, 26 May 2020 23:58:12 +0800 Subject: [PATCH 14/37] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=89=8D=E4=B8=A4?= =?UTF-8?q?=E6=AC=A1=E4=BB=A3=E7=A0=81=E6=94=B9=E5=8A=A8=E5=A4=9A=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=B8=8B=E5=B9=B6=E5=8F=91=E6=89=A7=E8=A1=8C=E7=9A=84?= =?UTF-8?q?=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stalker/runner/AbstractMeasureRunner.java | 21 +++------------- .../runner/ConcurrentMeasureRunner.java | 25 +++++++++++++++---- .../stalker/runner/SimpleMeasureRunner.java | 2 +- .../blinkfox/stalker/test/StalkerTest.java | 2 +- .../stalker/test/prepare/MyTestService.java | 3 +-- 5 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java index 2589d35..7ac5242 100644 --- a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java @@ -5,7 +5,6 @@ import java.util.ArrayDeque; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.LongAdder; @@ -29,11 +28,6 @@ public abstract class AbstractMeasureRunner implements MeasureRunner { */ protected ExecutorService executorService; - /** - * 线程执行计数器锁. - */ - protected CountDownLatch countLatch; - /** * 每次'成功'测量出的待测量方法的耗时时间,单位为纳秒({@code ns}). */ @@ -166,18 +160,9 @@ public OverallResult buildFinalMeasurement() { /** * 等待所有线程执行完毕,并最终关闭线程池. */ - protected void awaitAndShutdown() { - try { - if (this.countLatch != null) { - this.countLatch.await(); - } - } catch (InterruptedException e) { - log.error("在多线程下等待测量结果结束时出错!", e); - Thread.currentThread().interrupt(); - } finally { - if (this.executorService != null) { - this.executorService.shutdown(); - } + protected void shutdown() { + if (this.executorService != null) { + this.executorService.shutdown(); } } diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java index ec412b1..45afd10 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java @@ -43,7 +43,7 @@ public OverallResult run(Options options, Runnable runnable) { // 初始化存储的集合、线程池、并发工具类中的对象实例等. Semaphore semaphore = new Semaphore(Math.min(concurrens, threads)); - super.countLatch = new CountDownLatch(threads); + CountDownLatch countLatch = new CountDownLatch(threads); super.executorService = Executors.newFixedThreadPool(Math.min(threads, N_1024)); super.startNanoTime = System.nanoTime(); @@ -55,18 +55,19 @@ public OverallResult run(Options options, Runnable runnable) { this.loopMeasure(runs, printErrorLog, runnable); semaphore.release(); } catch (InterruptedException e) { - log.error("测量方法耗时信息在多线程下出错!", e); + log.error("【Stalker 错误提示】在多线程并发情况下测量任务执行的耗时信息出错!", e); Thread.currentThread().interrupt(); } finally { - super.countLatch.countDown(); + countLatch.countDown(); } }); } - // 等待所有线程执行完毕,并关闭线程池,最后将结果封装成实体信息. + // 等待所有线程执行完毕,记录是否完成和完成时间,并关闭线程池,最后将结果封装成实体信息返回. + this.await(countLatch); super.endNanoTime = System.nanoTime(); super.complete.compareAndSet(false, true); - super.awaitAndShutdown(); + super.shutdown(); return super.buildFinalMeasurement(); } @@ -113,4 +114,18 @@ public boolean stop() { return true; } + /** + * 等待所有线程执行完毕,并最终关闭线程池. + */ + private void await(CountDownLatch countLatch) { + try { + if (countLatch != null) { + countLatch.await(); + } + } catch (InterruptedException e) { + log.error("【Stalker 错误提示】在并发执行下等待任务执行结束时出错!", e); + Thread.currentThread().interrupt(); + } + } + } diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java index 156661b..28a25da 100644 --- a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java @@ -73,7 +73,7 @@ public OverallResult run(Options options, Runnable runnable) { // 等待所有线程执行完毕,并关闭线程池,最后将结果封装成实体信息. super.endNanoTime = System.nanoTime(); super.complete.compareAndSet(false, true); - super.awaitAndShutdown(); + super.shutdown(); return super.buildFinalMeasurement(); } diff --git a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java index ee22334..6c812a9 100644 --- a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java +++ b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java @@ -117,7 +117,7 @@ public void submit() throws InterruptedException { while (Stalker.isRunning(sessionId)) { List results = Stalker.query(sessionId); Assert.assertNotNull(results.get(0)); - Thread.sleep(3L); + Thread.sleep(2L); } log.info("任务已完成,获取最后的执行结果,并移除任务记录."); diff --git a/src/test/java/com/blinkfox/stalker/test/prepare/MyTestService.java b/src/test/java/com/blinkfox/stalker/test/prepare/MyTestService.java index 472e251..c268633 100644 --- a/src/test/java/com/blinkfox/stalker/test/prepare/MyTestService.java +++ b/src/test/java/com/blinkfox/stalker/test/prepare/MyTestService.java @@ -54,8 +54,7 @@ private void sleep(long time) { try { Thread.sleep(time); } catch (InterruptedException e) { - log.info("InterruptedException", e); - Thread.currentThread().interrupt(); + log.error("【Stalker 提示】本方法的执行已中断,异常简述为:【{}】.", e.getMessage()); } } From 80f801d904b269e8747a1667ad39d5189c11b423 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Wed, 27 May 2020 00:11:34 +0800 Subject: [PATCH 15/37] =?UTF-8?q?=E6=97=A5=E5=BF=97=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/blinkfox/stalker/Stalker.java | 2 +- .../com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java | 4 ++++ .../com/blinkfox/stalker/runner/MeasureRunnerContext.java | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/blinkfox/stalker/Stalker.java b/src/main/java/com/blinkfox/stalker/Stalker.java index 8260d3f..63067d8 100644 --- a/src/main/java/com/blinkfox/stalker/Stalker.java +++ b/src/main/java/com/blinkfox/stalker/Stalker.java @@ -159,7 +159,7 @@ public List run(Options options, Runnable... runnables) { public Measurement[] runStatis(Options options, Runnable... runnables) { int len; if (options == null || runnables == null || (len = runnables.length) == 0) { - throw new IllegalArgumentException("options or runnables is null (or empty)!"); + throw new IllegalArgumentException("【Stalker 参数异常】options or runnables is null (or empty)!"); } // 循环遍历测量各个 Runnable 实例的性能结果,然后将各个结果存放到数组中,最后统一输出出来. diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java index 45afd10..0861bf7 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java @@ -116,6 +116,10 @@ public boolean stop() { /** * 等待所有线程执行完毕,并最终关闭线程池. + * + * @param countLatch 计数锁 + * @author blinkfox on 2020-05-25. + * @since v1.2.0 */ private void await(CountDownLatch countLatch) { try { diff --git a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java index 0e0c644..315cfba 100644 --- a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java +++ b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java @@ -145,7 +145,7 @@ public static String submit(final Options options, final Runnable runnable) { public static Measurement queryMeasurement(String sessionId) { RunnerInfo runnerInfo = measureMap.get(sessionId); if (runnerInfo == null) { - throw new StalkerException(StrKit.format("根据当前 sessionId【{}】无法找到对应的运行任务," + throw new StalkerException(StrKit.format("【Stalker 异常】根据当前 sessionId【{}】无法找到对应的运行任务," + "或者该任务已过期,请重新开始执行。", sessionId)); } return new MeasurementCollector().collect(runnerInfo.getMeasureRunner().buildRunningMeasurement()); @@ -162,7 +162,7 @@ public static Measurement queryMeasurement(String sessionId) { public static List query(String sessionId) { RunnerInfo runnerInfo = measureMap.get(sessionId); if (runnerInfo == null) { - throw new StalkerException(StrKit.format("根据当前 sessionId【{}】无法找到对应的运行任务," + throw new StalkerException(StrKit.format("【Stalker 异常】根据当前 sessionId【{}】无法找到对应的运行任务," + "或者该任务已过期,请重新开始执行。", sessionId)); } return new MeasureOutputContext().output(runnerInfo.getOptions(), From 936dc42d3c58c90276d48c725df189d8aff57bf4 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Wed, 27 May 2020 00:29:26 +0800 Subject: [PATCH 16/37] =?UTF-8?q?=E4=BD=BF=E7=94=A8=2062=20=E8=BF=9B?= =?UTF-8?q?=E5=88=B6=E7=9A=84=20UUID=20=E4=BD=9C=E4=B8=BA=20sessionId?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 5 -- .../com/blinkfox/stalker/kit/RadixKit.java | 86 +++++++++++++++++++ .../java/com/blinkfox/stalker/kit/StrKit.java | 17 +++- .../stalker/runner/MeasureRunnerContext.java | 8 +- .../blinkfox/stalker/test/kit/StrKitTest.java | 8 ++ 5 files changed, 111 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/blinkfox/stalker/kit/RadixKit.java diff --git a/pom.xml b/pom.xml index 5d90333..7e2bc20 100644 --- a/pom.xml +++ b/pom.xml @@ -46,11 +46,6 @@ mini-table 1.0.0 - - com.blinkfox - id-worker - 1.1.0 - org.slf4j slf4j-api diff --git a/src/main/java/com/blinkfox/stalker/kit/RadixKit.java b/src/main/java/com/blinkfox/stalker/kit/RadixKit.java new file mode 100644 index 0000000..39704a9 --- /dev/null +++ b/src/main/java/com/blinkfox/stalker/kit/RadixKit.java @@ -0,0 +1,86 @@ +package com.blinkfox.stalker.kit; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +/** + * 进制处理工具类. + * + * @author blinkfox on 2020-05-27. + * @since v1.2.0 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class RadixKit { + + /** + * 62 进制数需要的 char 数组字符表, + * 为了保证生成的各个进制数的字符串保证 ASCII 的大小顺序,Radix 类生成的进制顺序是先数字,大写字母,再小写字母的顺序. + */ + private static final char[] DIGITS = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; + + /** + * 支持的最大进制数. + */ + static final int RADIX_62 = DIGITS.length; + + /** + * 默认的 10 进制常量. + */ + private static final int RADIX_10 = 10; + + /** + * 将长整型数值转换为指定的进制数(最大支持 62 进制,字母数字已经用尽). + * + * @param i 待转换数字 + * @param radix 进制数 + * @return 转换后的字符串 + */ + public static String toString(long i, int radix) { + if (radix < Character.MIN_RADIX || radix > RADIX_62) { + radix = RADIX_10; + } + + if (radix == RADIX_10) { + return Long.toString(i); + } + + boolean negative = (i < 0); + if (!negative) { + i = -i; + } + + final int size = 65; + int charPos = 64; + char[] buf = new char[size]; + + while (i <= -radix) { + buf[charPos--] = DIGITS[(int) (-(i % radix))]; + i = i / radix; + } + buf[charPos] = DIGITS[(int) (-i)]; + + if (negative) { + buf[--charPos] = '-'; + } + + return new String(buf, charPos, (size - charPos)); + } + + /** + * 根据对应的整数和移位数求得对应的字符串值. + * + * @param val 整数 + * @param digits 移位数 + * @return 字符串值 + */ + public static String digits(long val, int digits) { + long hi = 1L << (digits * 4); + return toString(hi | (val & (hi - 1)), RADIX_62).substring(1); + } + +} diff --git a/src/main/java/com/blinkfox/stalker/kit/StrKit.java b/src/main/java/com/blinkfox/stalker/kit/StrKit.java index b2a1c4d..c4637b6 100644 --- a/src/main/java/com/blinkfox/stalker/kit/StrKit.java +++ b/src/main/java/com/blinkfox/stalker/kit/StrKit.java @@ -2,6 +2,7 @@ import java.math.BigDecimal; import java.math.RoundingMode; +import java.util.UUID; import lombok.experimental.UtilityClass; import org.slf4j.helpers.MessageFormatter; @@ -50,7 +51,7 @@ public String join(Object... objects) { * @param args 参数 * @return 格式化后的字符串 */ - public static String format(String pattern, Object... args) { + public String format(String pattern, Object... args) { return pattern == null ? "" : MessageFormatter.arrayFormat(pattern, args).getMessage(); } @@ -98,4 +99,18 @@ public String roundToString(double d) { return BigDecimal.valueOf(d).setScale(2, RoundingMode.HALF_UP).toString(); } + /** + * 获取 {@code 62} 进制的长度为 19 位长度的 {@code UUID} 字符串. + * + * @return {@code 62} 进制位的 {@code UUID} + */ + public String get62RadixUuid() { + UUID uuid = UUID.randomUUID(); + return join(RadixKit.digits(uuid.getMostSignificantBits() >> 32, 8), + RadixKit.digits(uuid.getMostSignificantBits() >> 16, 4), + RadixKit.digits(uuid.getMostSignificantBits(), 4), + RadixKit.digits(uuid.getLeastSignificantBits() >> 48, 4), + RadixKit.digits(uuid.getLeastSignificantBits(), 12)); + } + } \ No newline at end of file diff --git a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java index 315cfba..09b7563 100644 --- a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java +++ b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java @@ -1,6 +1,5 @@ package com.blinkfox.stalker.runner; -import com.blinkfox.IdWorker; import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.exception.StalkerException; import com.blinkfox.stalker.kit.StrKit; @@ -38,11 +37,6 @@ public final class MeasureRunnerContext { */ private static final Map measureMap = new ConcurrentHashMap<>(); - /** - * 使用雪花算法生成短 ID 的生成器. - */ - public static final IdWorker idWorker = new IdWorker(); - /** * 运行测量的性能参数配置选项. */ @@ -128,7 +122,7 @@ public static String submit(final Options options, final Runnable runnable) { MeasureRunner measureRunner = options.getThreads() > 1 && options.getConcurrens() > 1 ? new ConcurrentMeasureRunner() : new SimpleMeasureRunner(); - String sessionId = idWorker.get62RadixId(); + String sessionId = StrKit.get62RadixUuid(); measureMap.put(sessionId, new RunnerInfo(options, measureRunner)); executor.execute(() -> measureRunner.run(options, runnable)); return sessionId; diff --git a/src/test/java/com/blinkfox/stalker/test/kit/StrKitTest.java b/src/test/java/com/blinkfox/stalker/test/kit/StrKitTest.java index 3d0f357..28659cd 100644 --- a/src/test/java/com/blinkfox/stalker/test/kit/StrKitTest.java +++ b/src/test/java/com/blinkfox/stalker/test/kit/StrKitTest.java @@ -60,4 +60,12 @@ public void getRoundString() { Assert.assertEquals("27.00", StrKit.roundToString(26.998)); } + /** + * 测试获取 UUID. + */ + @Test + public void get62RadixUuid() { + Assert.assertEquals(19, StrKit.get62RadixUuid().length()); + } + } \ No newline at end of file From c6da19c176011cb2809da7c0c6da23ec2aab63f1 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Wed, 27 May 2020 00:31:26 +0800 Subject: [PATCH 17/37] =?UTF-8?q?=E5=AE=8C=E5=96=84=20Javadoc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/blinkfox/stalker/kit/MathKit.java | 2 +- src/main/java/com/blinkfox/stalker/kit/StrKit.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/blinkfox/stalker/kit/MathKit.java b/src/main/java/com/blinkfox/stalker/kit/MathKit.java index 1827c86..74503de 100644 --- a/src/main/java/com/blinkfox/stalker/kit/MathKit.java +++ b/src/main/java/com/blinkfox/stalker/kit/MathKit.java @@ -6,7 +6,7 @@ * 数学计算的相关工具类. * * @author blinkfox on 2020-05-15. - * @since v1.0.0 + * @since v1.1.1 */ @UtilityClass public class MathKit { diff --git a/src/main/java/com/blinkfox/stalker/kit/StrKit.java b/src/main/java/com/blinkfox/stalker/kit/StrKit.java index c4637b6..f0063e6 100644 --- a/src/main/java/com/blinkfox/stalker/kit/StrKit.java +++ b/src/main/java/com/blinkfox/stalker/kit/StrKit.java @@ -103,6 +103,8 @@ public String roundToString(double d) { * 获取 {@code 62} 进制的长度为 19 位长度的 {@code UUID} 字符串. * * @return {@code 62} 进制位的 {@code UUID} + * @author blinkfox on 2020-05-27. + * @since v1.2.0 */ public String get62RadixUuid() { UUID uuid = UUID.randomUUID(); From c626550f446ed0c8d95f56363b4fab42d0b08232 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Sun, 31 May 2020 23:04:07 +0800 Subject: [PATCH 18/37] =?UTF-8?q?=E9=87=8D=E6=9E=84=E4=BA=86=20ConcurrentM?= =?UTF-8?q?easureRunner=20=E7=9A=84=E5=AE=9E=E7=8E=B0=EF=BC=8C=E8=83=BD?= =?UTF-8?q?=E6=9B=B4=E5=A5=BD=E7=9A=84=E5=81=9C=E6=AD=A2=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stalker/kit/ConcurrentHashSet.java | 114 ++++++++++++++++++ .../runner/ConcurrentMeasureRunner.java | 54 +++++++-- 2 files changed, 157 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/blinkfox/stalker/kit/ConcurrentHashSet.java diff --git a/src/main/java/com/blinkfox/stalker/kit/ConcurrentHashSet.java b/src/main/java/com/blinkfox/stalker/kit/ConcurrentHashSet.java new file mode 100644 index 0000000..d0577f3 --- /dev/null +++ b/src/main/java/com/blinkfox/stalker/kit/ConcurrentHashSet.java @@ -0,0 +1,114 @@ +package com.blinkfox.stalker.kit; + +import java.util.AbstractSet; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * 支持高并发操作的 HashSet 集合,内部使用 {@link ConcurrentHashMap} 实现. + * + * @author blinkfox on 2020-05-30. + * @since v1.2.0 + */ +public class ConcurrentHashSet extends AbstractSet implements Set, java.io.Serializable { + + private static final long serialVersionUID = 8473385025738451022L; + + /** + * 恒定的不可变对象,每个 key 都引用此对象,以节省空间. + */ + private static final Object PRESENT = new Object(); + + private final ConcurrentMap map; + + /** + * 默认构造方法. + */ + public ConcurrentHashSet() { + this.map = new ConcurrentHashMap<>(); + } + + /** + * 基于初始容量的构造方法. + * + * @param capacity 初始容量 + */ + public ConcurrentHashSet(int capacity) { + this.map = new ConcurrentHashMap<>(capacity); + } + + /** + * 返回此集合中元素的迭代器. + * + * @return 迭代器 + * @see java.util.ConcurrentModificationException + */ + @Override + public Iterator iterator() { + return this.map.keySet().iterator(); + } + + /** + * 返回此集合中的元素个数. + * + * @return 元素个数 + */ + @Override + public int size() { + return this.map.size(); + } + + /** + * 判断此集合是否为空. + * + * @return 如果为空就返回 {@code true} + */ + @Override + public boolean isEmpty() { + return this.map.isEmpty(); + } + + /** + * 判断此集合中是否包含指定的元素. + * + * @param o 元素 + * @return 布尔值 + */ + @Override + public boolean contains(Object o) { + return this.map.containsKey(o); + } + + /** + * 如果此元素在集合中不存在,就向此集合中添加元素. + * + * @param e 要添加的元素 + * @return 如果此集合中还没有包含此元素就返回 {@code true} + */ + @Override + public boolean add(E e) { + return this.map.put(e, PRESENT) == null; + } + + /** + * 如果此集合中包含某个元素,就将该元素从集合中移除. + * + * @param o 要移除的对象 + * @return 如果包含了该元素,就返回 {@code true}. + */ + @Override + public boolean remove(Object o) { + return this.map.remove(o) == PRESENT; + } + + /** + * 删除此集合中的所有元素,之后集合将为空. + */ + @Override + public void clear() { + this.map.clear(); + } + +} diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java index 0861bf7..696016b 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java @@ -1,8 +1,13 @@ package com.blinkfox.stalker.runner; import com.blinkfox.stalker.config.Options; +import com.blinkfox.stalker.kit.ConcurrentHashSet; import com.blinkfox.stalker.result.bean.OverallResult; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import lombok.extern.slf4j.Slf4j; @@ -18,6 +23,16 @@ public class ConcurrentMeasureRunner extends AbstractMeasureRunner { private static final int N_1024 = 1024; + /** + * 用于异步移除已经执行完成的线程的后台任务线程池. + */ + private final ExecutorService backExecutorService; + + /** + * 用于存放正在运行中的 Future 线程,便于在手动"停止"运行时,能取消正在执行中的任务. + */ + private final Set> runningFutures; + /** * 构造方法. * @@ -25,6 +40,8 @@ public class ConcurrentMeasureRunner extends AbstractMeasureRunner { */ public ConcurrentMeasureRunner() { super(); + this.backExecutorService = Executors.newSingleThreadExecutor(); + this.runningFutures = new ConcurrentHashSet<>(); } /** @@ -49,25 +66,28 @@ public OverallResult run(Options options, Runnable runnable) { // 在多线程下控制线程并发量,与循环搭配来一起执行和测量. for (int i = 0; i < threads; i++) { - executorService.submit(() -> { - try { - semaphore.acquire(); + try { + semaphore.acquire(); + final CompletableFuture future = CompletableFuture.runAsync(() -> { this.loopMeasure(runs, printErrorLog, runnable); semaphore.release(); - } catch (InterruptedException e) { - log.error("【Stalker 错误提示】在多线程并发情况下测量任务执行的耗时信息出错!", e); - Thread.currentThread().interrupt(); - } finally { countLatch.countDown(); - } - }); + }, super.executorService); + + // 将 future 添加到正在运行的 Future 信息集合中,并在 future 完成时,异步移除已经完成了的 future. + runningFutures.add(future); + future.whenCompleteAsync((a, b) -> runningFutures.remove(future), backExecutorService); + } catch (InterruptedException e) { + log.error("【Stalker 错误提示】在多线程并发情况下测量任务执行的耗时信息的线程已被中断!", e); + } } - // 等待所有线程执行完毕,记录是否完成和完成时间,并关闭线程池,最后将结果封装成实体信息返回. + // 等待所有线程执行完毕,记录是否完成和完成时间,并关闭线程池等资源,最后将结果封装成实体信息返回. this.await(countLatch); super.endNanoTime = System.nanoTime(); super.complete.compareAndSet(false, true); super.shutdown(); + this.backExecutorService.shutdown(); return super.buildFinalMeasurement(); } @@ -109,7 +129,20 @@ public boolean stop() { if (!isComplete()) { super.endNanoTime = System.nanoTime(); super.complete.compareAndSet(false, true); + + // 停止时直接关闭线程池. super.shutdownNowQuietly(); + this.backExecutorService.shutdownNow(); + + // 迭代删除正在运行中的 Future,并取消正在运行中的任务. + Iterator> futureIterator = this.runningFutures.iterator(); + while (futureIterator.hasNext()) { + CompletableFuture future = futureIterator.next(); + this.runningFutures.remove(future); + if (!future.isDone()) { + future.cancel(true); + } + } } return true; } @@ -128,7 +161,6 @@ private void await(CountDownLatch countLatch) { } } catch (InterruptedException e) { log.error("【Stalker 错误提示】在并发执行下等待任务执行结束时出错!", e); - Thread.currentThread().interrupt(); } } From 23cc2ff98b067d2c5ab176d7b906c3f0452f806e Mon Sep 17 00:00:00 2001 From: blinkfox Date: Mon, 1 Jun 2020 01:04:22 +0800 Subject: [PATCH 19/37] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=8C=81=E7=BB=AD?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E4=BB=BB=E5=8A=A1=E7=9A=84=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/blinkfox/stalker/config/Options.java | 110 ++++++++++++++- .../blinkfox/stalker/config/RunDuration.java | 127 ++++++++++++++++++ .../runner/ConcurrentMeasureRunner.java | 4 +- .../ConcurrentScheduledMeasureRunner.java | 43 ++++++ .../stalker/runner/SimpleMeasureRunner.java | 5 +- .../runner/SimpleScheduledMeasureRunner.java | 103 ++++++++++++++ 6 files changed, 386 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/blinkfox/stalker/config/RunDuration.java create mode 100644 src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java create mode 100644 src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java diff --git a/src/main/java/com/blinkfox/stalker/config/Options.java b/src/main/java/com/blinkfox/stalker/config/Options.java index b2c49a5..fe47abb 100644 --- a/src/main/java/com/blinkfox/stalker/config/Options.java +++ b/src/main/java/com/blinkfox/stalker/config/Options.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.TimeUnit; import lombok.Getter; /** @@ -40,6 +41,13 @@ public class Options { */ private int runs; + /** + * 运行的持续时间. + * + * @since v1.2.0 + */ + private RunDuration duration; + /** * 是否打印出执行错误(异常运行)的日志,默认是false. */ @@ -152,6 +160,92 @@ public static Options of(String name, int threads, int concurrens, int runs) { return options; } + /** + * 根据'持续时间的量'、'持续时间的单位'来构建 Options 实例. + * + * @param amount 运行持续时间的量 + * @param timeUnit 运行持续时间的单位 + * @return Options实例 + * @author blinkfox on 2020-06-01. + * @since v1.2.0 + */ + public static Options ofDuration(long amount, TimeUnit timeUnit) { + Options options = of(1, 1); + options.runs = 1; + options.duration = RunDuration.of(amount, timeUnit); + return options; + } + + /** + * 根据'持续时间的量'、'持续时间的单位'和'并发数'来构建 Options 实例. + * + * @param amount 运行持续时间的量 + * @param timeUnit 运行持续时间的单位 + * @param concurrens 并发数 + * @return Options实例 + * @author blinkfox on 2020-06-01. + * @since v1.2.0 + */ + public static Options ofDuration(long amount, TimeUnit timeUnit, int concurrens) { + Options options = of(1, 1); + options.concurrens = concurrens; + options.runs = 1; + options.duration = RunDuration.of(amount, timeUnit); + return options; + } + + /** + * 根据'持续秒数的量'和'并发数'来构建 Options 实例. + * + * @param amount 运行持续秒数的量 + * @param concurrens 并发数 + * @return Options实例 + * @author blinkfox on 2020-06-01. + * @since v1.2.0 + */ + public static Options ofDurationSeconds(long amount, int concurrens) { + return ofDuration(amount, TimeUnit.SECONDS, concurrens); + } + + /** + * 根据'持续分钟数的量'和'并发数'来构建 Options 实例. + * + * @param amount 运行持续分钟数的量 + * @param concurrens 并发数 + * @return Options实例 + * @author blinkfox on 2020-06-01. + * @since v1.2.0 + */ + public static Options ofDurationMinutes(long amount, int concurrens) { + return ofDuration(amount, TimeUnit.MINUTES, concurrens); + } + + /** + * 根据'持续小时数的量'和'并发数'来构建 Options 实例. + * + * @param amount 运行持续小时数的量 + * @param concurrens 并发数 + * @return Options实例 + * @author blinkfox on 2020-06-01. + * @since v1.2.0 + */ + public static Options ofDurationHours(long amount, int concurrens) { + return ofDuration(amount, TimeUnit.HOURS, concurrens); + } + + /** + * 根据'持续天数的量'和'并发数'来构建 Options 实例. + * + * @param amount 运行持续小时数的量 + * @param concurrens 并发数 + * @return Options实例 + * @author blinkfox on 2020-06-01. + * @since v1.2.0 + */ + public static Options ofDurationDays(long amount, int concurrens) { + return ofDuration(amount, TimeUnit.DAYS, concurrens); + } + /** * 校验需要进行测量的 Options 选项参数是否合法,如果不合法,则抛出异常. */ @@ -227,13 +321,27 @@ public Options warmups(int warmups) { * 设置执行次数的 runs 的属性值. * * @param runs 运行次数 - * @return Options实例 + * @return Options 实例 */ public Options runs(int runs) { this.runs = runs; return this; } + /** + * 设置运行的持续时间. + * + * @param amount 持续时间的量 + * @param timeUnit 持续时间的单位 + * @return Options 实例 + * @author blinkfox on 2020-06-01. + * @since v1.2.0 + */ + public Options duration(long amount, TimeUnit timeUnit) { + this.duration = RunDuration.of(amount, timeUnit); + return this; + } + /** * 设置是否打印运行错误的日志的 printErrorLog 的属性值. * diff --git a/src/main/java/com/blinkfox/stalker/config/RunDuration.java b/src/main/java/com/blinkfox/stalker/config/RunDuration.java new file mode 100644 index 0000000..f245b12 --- /dev/null +++ b/src/main/java/com/blinkfox/stalker/config/RunDuration.java @@ -0,0 +1,127 @@ +package com.blinkfox.stalker.config; + +import java.util.concurrent.TimeUnit; +import lombok.Getter; +import lombok.Setter; + +/** + * 程序运行的持续时间实体类. + * + * @author blinkfox on 2020-05-31. + * @since v1.2.0 + */ +public class RunDuration { + + /** + * 运行的持续时间量. + */ + @Getter + private final long amount; + + /** + * 运行的持续时间单位. + */ + @Getter + private final TimeUnit timeUnit; + + /** + * 开始执行时的纳秒时间戳. + */ + @Getter + @Setter + private long startNanoTime; + + /** + * 结束执行时的纳秒时间戳. + */ + @Getter + @Setter + private long endNanoTime; + + /** + * 构造方法. + * + * @param amount 持续时间的量 + * @param timeUnit 持续时间的单位 + */ + private RunDuration(long amount, TimeUnit timeUnit) { + this.amount = amount; + this.timeUnit = timeUnit; + } + + /** + * 构造运行持续时间的 {@link RunDuration} 实例. + * + * @param amount 持续时间的量 + * @param timeUnit 持续时间的单位 + * @return {@link RunDuration} 实例 + */ + public static RunDuration of(long amount, TimeUnit timeUnit) { + checkParams(amount, timeUnit); + return new RunDuration(amount, timeUnit); + } + + /** + * 构造运行持续指定【秒数】的 {@link RunDuration} 实例. + * + * @param amount 持续时间的量 + * @return {@link RunDuration} 实例 + */ + public static RunDuration ofSeconds(long amount) { + checkAmount(amount); + return new RunDuration(amount, TimeUnit.SECONDS); + } + + /** + * 构造运行持续指定【分钟数】的 {@link RunDuration} 实例. + * + * @param amount 持续时间的量 + * @return {@link RunDuration} 实例 + */ + public static RunDuration ofMinutes(long amount) { + checkAmount(amount); + return new RunDuration(amount, TimeUnit.MINUTES); + } + + /** + * 构造运行持续指定【小时数】的 {@link RunDuration} 实例. + * + * @param amount 持续时间的量 + * @return {@link RunDuration} 实例 + */ + public static RunDuration ofHours(long amount) { + checkAmount(amount); + return new RunDuration(amount, TimeUnit.HOURS); + } + + /** + * 构造运行持续指定【天数】的 {@link RunDuration} 实例. + * + * @param amount 持续时间的量 + * @return {@link RunDuration} 实例 + */ + public static RunDuration ofDays(long amount) { + checkAmount(amount); + return new RunDuration(amount, TimeUnit.DAYS); + } + + private static void checkParams(long amount, TimeUnit timeUnit) { + checkAmount(amount); + if (timeUnit == null) { + throw new IllegalArgumentException("【Stalker 无效参数异常】运行的最小持续时间单位不能为空【null】."); + } + if (timeUnit == TimeUnit.NANOSECONDS + || timeUnit == TimeUnit.MICROSECONDS + || timeUnit == TimeUnit.MILLISECONDS) { + throw new IllegalArgumentException("【Stalker 无效参数异常】运行的最小持续时间单位至少是【秒】," + + "不能是【纳秒】、【微秒】或者【毫秒】,获取到的值是:【" + timeUnit.name() + "】."); + } + } + + private static void checkAmount(long amount) { + if (amount <= 0) { + throw new IllegalArgumentException("【Stalker 无效参数异常】运行的续时间必须是正整数,获取到的值是:【" + amount + "】."); + } + } + +} diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java index 696016b..da33141 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java @@ -26,12 +26,12 @@ public class ConcurrentMeasureRunner extends AbstractMeasureRunner { /** * 用于异步移除已经执行完成的线程的后台任务线程池. */ - private final ExecutorService backExecutorService; + protected final ExecutorService backExecutorService; /** * 用于存放正在运行中的 Future 线程,便于在手动"停止"运行时,能取消正在执行中的任务. */ - private final Set> runningFutures; + protected final Set> runningFutures; /** * 构造方法. diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java new file mode 100644 index 0000000..6f3cf60 --- /dev/null +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java @@ -0,0 +1,43 @@ +package com.blinkfox.stalker.runner; + +import com.blinkfox.stalker.config.Options; +import com.blinkfox.stalker.result.bean.OverallResult; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +/** + * ConcurrentScheduledMeasureRunner. + * + * @author blinkfox on 2020-06-01. + * @since v1.0.0 + */ +public class ConcurrentScheduledMeasureRunner extends ConcurrentMeasureRunner { + + /** + * 用于异步定时调度任务的线程池. + */ + private final ScheduledExecutorService scheduledExecutorService; + + /** + * 构造方法. + * + *

这个类中的属性,需要支持高并发写入.

+ */ + public ConcurrentScheduledMeasureRunner() { + super(); + this.scheduledExecutorService = Executors.newScheduledThreadPool(1); + } + + /** + * 持续并发的执行指定时间的 runnable 方法,并将执行成功与否、耗时结果等信息存入到 OverallResult 实体对象中. + * + * @param options 运行的配置选项实例 + * @param runnable 可运行实例 + * @return 测量结果 + */ + @Override + public OverallResult run(Options options, Runnable runnable) { + return null; + } + +} diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java index 28a25da..df9504c 100644 --- a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java @@ -20,18 +20,18 @@ public class SimpleMeasureRunner extends AbstractMeasureRunner { * * @since v1.2.0 */ - private Future measureTask; + protected Future measureTask; /** * 构造方法. */ public SimpleMeasureRunner() { super(); + super.executorService = Executors.newSingleThreadExecutor(); } /** * 执行 runnable 方法,并将执行成功与否、耗时结果等信息存入到 OverallResult 实体对象中. - *

这里由于是单线程的重写方法,不再需要`new Thread`了,直接调用`runnable.run()`即可.

* * @param options 运行的配置选项实例 * @param runnable 可运行实例 @@ -41,7 +41,6 @@ public SimpleMeasureRunner() { public OverallResult run(Options options, Runnable runnable) { boolean printErrorLog = options.isPrintErrorLog(); int totalCount = options.getThreads() * options.getRuns(); - super.executorService = Executors.newSingleThreadExecutor(); super.startNanoTime = System.nanoTime(); // 由于并发数是 1,直接单线程循环执行 (runs * threads) 次即可, diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java new file mode 100644 index 0000000..e599c2b --- /dev/null +++ b/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java @@ -0,0 +1,103 @@ +package com.blinkfox.stalker.runner; + +import com.blinkfox.stalker.config.Options; +import com.blinkfox.stalker.config.RunDuration; +import com.blinkfox.stalker.result.bean.OverallResult; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import lombok.extern.slf4j.Slf4j; + +/** + * 继承自 {@link SimpleMeasureRunner},在单线程情况下的运行指定的持续时间的测量运行器. + * + * @author blinkfox on 2020-06-01. + * @since v1.2.0 + */ +@Slf4j +public class SimpleScheduledMeasureRunner extends SimpleMeasureRunner { + + /** + * 用于异步定时调度任务的线程池. + */ + private final ScheduledExecutorService scheduledExecutorService; + + /** + * 执行中的测量任务的 {@link Future} 实例. + * + * @since v1.2.0 + */ + protected Future scheduledFuture; + + /** + * 构造方法. + */ + public SimpleScheduledMeasureRunner() { + super(); + this.scheduledExecutorService = Executors.newScheduledThreadPool(1); + } + + /** + * 持续执行指定时间的 runnable 方法,并将执行成功与否、耗时结果等信息存入到 OverallResult 实体对象中. + * + * @param options 运行的配置选项实例 + * @param runnable 可运行实例 + * @return 测量结果 + */ + @Override + public OverallResult run(Options options, Runnable runnable) { + boolean printErrorLog = options.isPrintErrorLog(); + super.executorService = Executors.newSingleThreadExecutor(); + super.startNanoTime = System.nanoTime(); + + // 将单线程中执行的任务放在 while 循环中,一直执行下去. + super.measureTask = executorService.submit(() -> { + while (true) { + try { + // 开始执行测量任务,记录开始时间、执行次数等. + long eachStart = System.nanoTime(); + runnable.run(); + super.eachMeasures.add(System.nanoTime() - eachStart); + super.success.increment(); + } catch (Exception e) { + super.failure.increment(); + if (printErrorLog) { + log.error("【stalker 错误】测量方法耗时信息出错!", e); + } + } + } + }); + + // 到指定的持续时间之后,就取消执行中的任务,并关闭线程池. + final RunDuration duration = options.getDuration(); + this.scheduledFuture = this.scheduledExecutorService.schedule((Runnable) this::stop, + duration.getAmount(), duration.getTimeUnit()); + + // 阻塞调用要执行的测量任务,达到阻塞等待任务结束的目的. + try { + this.measureTask.get(); + } catch (Exception e) { + log.error("【Stalker 错误】执行测量任务发生错误!", e); + } + return super.buildFinalMeasurement(); + } + + /** + * 停止相关的运行测量任务. + * + * @return 是否成功的布尔值 + * @author blinkfox on 2020-05-25. + * @since v1.2.0 + */ + public boolean stop() { + super.stop(); + + // 关闭定时任务线程池和取消对应的定时任务. + this.scheduledExecutorService.shutdown(); + if (this.scheduledFuture != null && !this.scheduledFuture.isDone()) { + return this.scheduledFuture.cancel(true); + } + return true; + } + +} From a49d93075c269063e5ea3bfd8212df5d1f3d4163 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Mon, 1 Jun 2020 23:19:18 +0800 Subject: [PATCH 20/37] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=B9=B6=E5=8F=91?= =?UTF-8?q?=E6=83=85=E5=86=B5=E4=B8=8B=E7=9A=84=E6=8C=81=E7=BB=AD=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E7=9B=B8=E5=85=B3=E7=9A=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blinkfox/stalker/config/RunDuration.java | 42 ++++++---- .../runner/ConcurrentMeasureRunner.java | 7 +- .../ConcurrentScheduledMeasureRunner.java | 84 ++++++++++++++++++- .../stalker/runner/MeasureRunnerContext.java | 28 +++++-- 4 files changed, 133 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/blinkfox/stalker/config/RunDuration.java b/src/main/java/com/blinkfox/stalker/config/RunDuration.java index f245b12..6f217e2 100644 --- a/src/main/java/com/blinkfox/stalker/config/RunDuration.java +++ b/src/main/java/com/blinkfox/stalker/config/RunDuration.java @@ -2,7 +2,6 @@ import java.util.concurrent.TimeUnit; import lombok.Getter; -import lombok.Setter; /** * 程序运行的持续时间实体类. @@ -24,20 +23,6 @@ public class RunDuration { @Getter private final TimeUnit timeUnit; - /** - * 开始执行时的纳秒时间戳. - */ - @Getter - @Setter - private long startNanoTime; - - /** - * 结束执行时的纳秒时间戳. - */ - @Getter - @Setter - private long endNanoTime; - /** * 构造方法. * @@ -105,6 +90,33 @@ public static RunDuration ofDays(long amount) { return new RunDuration(amount, TimeUnit.DAYS); } + /** + * 根据开始纳秒时间和持续时间计算出期望的结束纳秒时间. + * + * @param startNanoTime 开始纳秒时间 + * @return 结束纳秒时间 + */ + public long getEndNanoTime(long startNanoTime) { + switch(this.timeUnit) { + case NANOSECONDS : + return startNanoTime + amount; + case MICROSECONDS : + return startNanoTime + amount * 1000L; + case MILLISECONDS : + return startNanoTime + amount * 1000_000L; + case SECONDS: + return startNanoTime + amount * 1000_000_000L; + case MINUTES: + return startNanoTime + amount * 60_000_000_000L; + case HOURS: + return startNanoTime + amount * 3600_000_000_000L; + case DAYS: + return startNanoTime + amount * 86400_000_000_000L; + default : + return startNanoTime; + } + } + private static void checkParams(long amount, TimeUnit timeUnit) { checkAmount(amount); if (timeUnit == null) { diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java index da33141..b969685 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java @@ -21,7 +21,7 @@ @Slf4j public class ConcurrentMeasureRunner extends AbstractMeasureRunner { - private static final int N_1024 = 1024; + protected static final int N_1024 = 1024; /** * 用于异步移除已经执行完成的线程的后台任务线程池. @@ -76,7 +76,7 @@ public OverallResult run(Options options, Runnable runnable) { // 将 future 添加到正在运行的 Future 信息集合中,并在 future 完成时,异步移除已经完成了的 future. runningFutures.add(future); - future.whenCompleteAsync((a, b) -> runningFutures.remove(future), backExecutorService); + future.whenCompleteAsync((a, b) -> runningFutures.remove(future), this.backExecutorService); } catch (InterruptedException e) { log.error("【Stalker 错误提示】在多线程并发情况下测量任务执行的耗时信息的线程已被中断!", e); } @@ -98,7 +98,7 @@ public OverallResult run(Options options, Runnable runnable) { * @param printErrorLog 是否打印输出错误日志 * @param runnable 可执行实例 */ - private void loopMeasure(int runs, boolean printErrorLog, final Runnable runnable) { + protected void loopMeasure(int runs, boolean printErrorLog, final Runnable runnable) { for (int j = 0; j < runs; j++) { try { long eachStart = System.nanoTime(); @@ -125,6 +125,7 @@ private void loopMeasure(int runs, boolean printErrorLog, final Runnable runnabl * @author blinkfox on 2020-05-25. * @since v1.2.0 */ + @Override public boolean stop() { if (!isComplete()) { super.endNanoTime = System.nanoTime(); diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java index 6f3cf60..e0d7ee9 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java @@ -1,16 +1,22 @@ package com.blinkfox.stalker.runner; import com.blinkfox.stalker.config.Options; +import com.blinkfox.stalker.config.RunDuration; import com.blinkfox.stalker.result.bean.OverallResult; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.Semaphore; +import lombok.extern.slf4j.Slf4j; /** - * ConcurrentScheduledMeasureRunner. + * 继承自 {@link ConcurrentMeasureRunner},在多线程并发情况下的运行指定的持续时间的测量运行器. * * @author blinkfox on 2020-06-01. - * @since v1.0.0 + * @since v1.2.0 */ +@Slf4j public class ConcurrentScheduledMeasureRunner extends ConcurrentMeasureRunner { /** @@ -18,6 +24,13 @@ public class ConcurrentScheduledMeasureRunner extends ConcurrentMeasureRunner { */ private final ScheduledExecutorService scheduledExecutorService; + /** + * 执行中的测量任务的 {@link Future} 实例. + * + * @since v1.2.0 + */ + protected Future scheduledFuture; + /** * 构造方法. * @@ -26,6 +39,7 @@ public class ConcurrentScheduledMeasureRunner extends ConcurrentMeasureRunner { public ConcurrentScheduledMeasureRunner() { super(); this.scheduledExecutorService = Executors.newScheduledThreadPool(1); + super.executorService = Executors.newFixedThreadPool(N_1024); } /** @@ -37,7 +51,71 @@ public ConcurrentScheduledMeasureRunner() { */ @Override public OverallResult run(Options options, Runnable runnable) { - return null; + int concurrens = options.getConcurrens(); + int runs = options.getRuns(); + boolean printErrorLog = options.isPrintErrorLog(); + + // 初始化存储的集合、线程池、并发工具类中的对象实例等. + final Semaphore semaphore = new Semaphore(concurrens); + + // 到指定的持续时间之后,就取消执行中的任务,并关闭线程池. + final RunDuration duration = options.getDuration(); + this.scheduledFuture = this.scheduledExecutorService.schedule((Runnable) this::stop, + duration.getAmount(), duration.getTimeUnit()); + + super.startNanoTime = System.nanoTime(); + long expectEndNanoTime = duration.getEndNanoTime(super.startNanoTime); + + while (true) { + try { + semaphore.acquire(); + // 如果当前时间大于了期望的结束时间,就跳出 while 循环. + if (System.nanoTime() > expectEndNanoTime) { + break; + } + final CompletableFuture future = CompletableFuture.runAsync(() -> { + this.loopMeasure(runs, printErrorLog, runnable); + semaphore.release(); + }, super.executorService); + + // 将 future 添加到正在运行的 Future 信息集合中,并在 future 完成时,异步移除已经完成了的 future. + runningFutures.add(future); + future.whenCompleteAsync((a, b) -> runningFutures.remove(future), super.backExecutorService); + } catch (InterruptedException e) { + log.error("【Stalker 错误提示】在多线程并发情况下测量任务执行的耗时信息的线程已被中断!", e); + } + } + + // 等待所有线程执行完毕,记录是否完成和完成时间,并关闭线程池等资源,最后将结果封装成实体信息返回. + super.endNanoTime = System.nanoTime(); + super.complete.compareAndSet(false, true); + super.shutdown(); + super.backExecutorService.shutdown(); + this.scheduledExecutorService.shutdown(); + if (!this.scheduledFuture.isDone()) { + this.scheduledFuture.cancel(true); + } + return super.buildFinalMeasurement(); + } + + /** + * 停止相关的运行测量任务. + * + *

注意:如果任务未完成,则立即停止线程池,但是还不能停止正在运行中的若干任务线程, + * 暂时还没想到一个更好的、高性能的停止所有运行中的任务的方法.

+ * + * @return 是否成功的布尔值 + */ + @Override + public boolean stop() { + super.stop(); + + // 关闭定时任务线程池和取消对应的定时任务. + this.scheduledExecutorService.shutdown(); + if (this.scheduledFuture != null && !this.scheduledFuture.isDone()) { + return this.scheduledFuture.cancel(true); + } + return true; } } diff --git a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java index 09b7563..3a17b84 100644 --- a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java +++ b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java @@ -88,9 +88,15 @@ private static void warmup(Options options, Runnable runnable) { */ public OverallResult run(Runnable runnable) { warmup(options, runnable); - return options.getThreads() > 1 && options.getConcurrens() > 1 - ? new ConcurrentMeasureRunner().run(options, runnable) - : new SimpleMeasureRunner().run(options, runnable); + if (options.getDuration() != null) { + return options.getConcurrens() > 1 + ? new ConcurrentScheduledMeasureRunner().run(options, runnable) + : new SimpleScheduledMeasureRunner().run(options, runnable); + } else { + return options.getConcurrens() > 1 + ? new ConcurrentMeasureRunner().run(options, runnable) + : new SimpleMeasureRunner().run(options, runnable); + } } /** @@ -118,10 +124,18 @@ public static String submit(final Options options, final Runnable runnable) { // 预热运行. warmup(options, runnable); - // 将 measureRunner 存储到 map 中,并异步执行任务. - MeasureRunner measureRunner = options.getThreads() > 1 && options.getConcurrens() > 1 - ? new ConcurrentMeasureRunner() - : new SimpleMeasureRunner(); + // 获取对应的 measureRunner,并将 measureRunner 存储到 map 中,并异步执行任务. + MeasureRunner measureRunner; + if (options.getDuration() != null) { + measureRunner = options.getConcurrens() > 1 + ? new ConcurrentScheduledMeasureRunner() + : new SimpleScheduledMeasureRunner(); + } else { + measureRunner = options.getConcurrens() > 1 + ? new ConcurrentMeasureRunner() + : new SimpleMeasureRunner(); + } + String sessionId = StrKit.get62RadixUuid(); measureMap.put(sessionId, new RunnerInfo(options, measureRunner)); executor.execute(() -> measureRunner.run(options, runnable)); From c5e8cee2830dc135d0a15b9be5ad88564943f25d Mon Sep 17 00:00:00 2001 From: blinkfox Date: Mon, 1 Jun 2020 23:41:53 +0800 Subject: [PATCH 21/37] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BA=86=E5=AF=B9?= =?UTF-8?q?=E5=BA=94=E7=9A=84=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ConcurrentScheduledMeasureRunner.java | 8 +++- .../blinkfox/stalker/test/StalkerTest.java | 47 +++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java index e0d7ee9..8c6d623 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java @@ -87,8 +87,12 @@ public OverallResult run(Options options, Runnable runnable) { } // 等待所有线程执行完毕,记录是否完成和完成时间,并关闭线程池等资源,最后将结果封装成实体信息返回. - super.endNanoTime = System.nanoTime(); - super.complete.compareAndSet(false, true); + if (super.endNanoTime == 0) { + super.endNanoTime = System.nanoTime(); + } + if (!super.complete.get()) { + super.complete.compareAndSet(false, true); + } super.shutdown(); super.backExecutorService.shutdown(); this.scheduledExecutorService.shutdown(); diff --git a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java index 6c812a9..b25979a 100644 --- a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java +++ b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java @@ -5,6 +5,7 @@ import com.blinkfox.stalker.result.bean.Measurement; import com.blinkfox.stalker.test.prepare.MyTestService; import java.util.List; +import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; import org.junit.AfterClass; import org.junit.Assert; @@ -35,6 +36,30 @@ public void run() { Stalker.run(Options.of(100, 20), () -> new MyTestService().hello()); } + /** + * 测试有 duration 选项参数时的执行情况. + */ + @Test + public void runWithDuration() { + Stalker.run(Options.ofDuration(1, TimeUnit.SECONDS), () -> new MyTestService().hello()); + } + + /** + * 测试有 duration 选项参数时的执行情况. + */ + @Test + public void runWithDurationConcurrent() { + Stalker.run(Options.ofDurationSeconds(1, 3), () -> new MyTestService().hello()); + } + + /** + * 测试有 duration 选项参数时的执行情况. + */ + @Test(expected = IllegalArgumentException.class) + public void runWithDurationException() { + Stalker.run(Options.ofDuration(2, TimeUnit.MILLISECONDS), () -> new MyTestService().hello()); + } + /** * 测试简单无并发的执行情况. */ @@ -148,6 +173,28 @@ public void submitWithSlowMethod() throws InterruptedException { Stalker.remove(sessionId); } + /** + * 测试慢方法的执行情况. + */ + @Test + public void submitWithSlowMethodDuration() throws InterruptedException { + String sessionId = Stalker.submit(Options.ofDurationSeconds(2, 4), + () -> new MyTestService().slowHello()); + Assert.assertNotNull(sessionId); + + while (Stalker.isRunning(sessionId)) { + List results = Stalker.query(sessionId); + Assert.assertNotNull(results.get(0)); + Thread.sleep(500L); + } + + log.info("任务已完成,获取最后的执行结果,并移除任务记录."); + Stalker.query(sessionId); + + // 执行完成之后移除 sessionId. + Stalker.remove(sessionId); + } + /** * 测试 queryMeasurement 方法. */ From 97262ad6554691db9d77c78e5d3c26e1dcc2be45 Mon Sep 17 00:00:00 2001 From: chenjiayin Date: Tue, 2 Jun 2020 15:24:39 +0800 Subject: [PATCH 22/37] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E6=B5=8B=E9=87=8F=E7=9B=B8=E5=85=B3=E7=9A=84=E9=83=A8?= =?UTF-8?q?=E5=88=86=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stalker/runner/AbstractMeasureRunner.java | 55 ++++------ .../runner/ConcurrentMeasureRunner.java | 30 +++--- .../ConcurrentScheduledMeasureRunner.java | 36 +++---- .../stalker/runner/MeasureRunner.java | 4 +- .../stalker/runner/MeasureRunnerContext.java | 12 ++- .../stalker/runner/SimpleMeasureRunner.java | 39 ++++--- .../runner/SimpleScheduledMeasureRunner.java | 26 +++-- .../runner/executor/StalkerExecutors.java | 102 ++++++++++++++++++ .../executor/StalkerRejectedHandler.java | 27 +++++ 9 files changed, 219 insertions(+), 112 deletions(-) create mode 100644 src/main/java/com/blinkfox/stalker/runner/executor/StalkerExecutors.java create mode 100644 src/main/java/com/blinkfox/stalker/runner/executor/StalkerRejectedHandler.java diff --git a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java index 7ac5242..dca3222 100644 --- a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java @@ -17,7 +17,9 @@ * @author blinkfox on 2020-05-24. * @see MeasureRunner * @see SimpleMeasureRunner + * @see SimpleScheduledMeasureRunner * @see ConcurrentMeasureRunner + * @see ConcurrentScheduledMeasureRunner * @since v1.2.0 */ @Slf4j @@ -110,6 +112,17 @@ public long getCosts() { return this.endNanoTime - this.startNanoTime; } + /** + * 如果结束时间的值是 0,那么就设置结束时的纳秒时间. + * + * @param endNanoTime 结束纳秒时间. + */ + public void setEndNanoTimeIfEmpty(long endNanoTime) { + if (this.endNanoTime == 0) { + this.endNanoTime = endNanoTime; + } + } + /** * 构造正在运行中的任务的测量结果信息的 {@link OverallResult} 对象. * @@ -125,17 +138,17 @@ public OverallResult buildRunningMeasurement() { // 如果任务仍然在运行中,由于各个计数器是独立的,对于整体上的各个统计数据的结果来说,并不能保证"线程安全". // 为了减小仍在运行中时的任务,获取各个统计数据时线程安全所导致的误差. // 这里只获取 eachMeasures 和 failure 两个值,基于这两个值来计算其他值,消耗的时间使用当前时间来计算. - long failure = this.getFailure(); - long[] eachCosts = this.getEachMeasures(); - long totalCount = eachCosts.length; - long costs = this.startNanoTime == 0 ? 0 : System.nanoTime() - this.startNanoTime; + final long currFailure = this.getFailure(); + final long[] currEachCosts = this.getEachMeasures(); + long currTotalCount = currEachCosts.length; + long currCosts = this.startNanoTime == 0 ? 0 : System.nanoTime() - this.startNanoTime; return new OverallResult() - .setEachMeasures(eachCosts) - .setCosts(costs) - .setTotal(totalCount) - .setSuccess(totalCount - failure) - .setFailure(failure) - .setThroughput(MathKit.calcThroughput(totalCount, costs)); + .setEachMeasures(currEachCosts) + .setCosts(currCosts) + .setTotal(currTotalCount) + .setSuccess(currTotalCount - currFailure) + .setFailure(currFailure) + .setThroughput(MathKit.calcThroughput(currTotalCount, currCosts)); } /** @@ -157,26 +170,4 @@ public OverallResult buildFinalMeasurement() { .setThroughput(MathKit.calcThroughput(totalCount, this.getCosts())); } - /** - * 等待所有线程执行完毕,并最终关闭线程池. - */ - protected void shutdown() { - if (this.executorService != null) { - this.executorService.shutdown(); - } - } - - /** - * 立即安静的关闭线程池. - */ - protected void shutdownNowQuietly() { - if (this.executorService != null) { - try { - this.executorService.shutdownNow(); - } catch (Exception e) { - log.error("【Stalker 错误】关闭测量任务的线程池失败,失败原因:【{}】.", e.getMessage()); - } - } - } - } diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java index b969685..3818615 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java @@ -3,12 +3,12 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.kit.ConcurrentHashSet; import com.blinkfox.stalker.result.bean.OverallResult; +import com.blinkfox.stalker.runner.executor.StalkerExecutors; import java.util.Iterator; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import lombok.extern.slf4j.Slf4j; @@ -21,12 +21,10 @@ @Slf4j public class ConcurrentMeasureRunner extends AbstractMeasureRunner { - protected static final int N_1024 = 1024; - /** * 用于异步移除已经执行完成的线程的后台任务线程池. */ - protected final ExecutorService backExecutorService; + protected final ExecutorService recordExecutorService; /** * 用于存放正在运行中的 Future 线程,便于在手动"停止"运行时,能取消正在执行中的任务. @@ -40,7 +38,7 @@ public class ConcurrentMeasureRunner extends AbstractMeasureRunner { */ public ConcurrentMeasureRunner() { super(); - this.backExecutorService = Executors.newSingleThreadExecutor(); + this.recordExecutorService = StalkerExecutors.newSingleThreadExecutor("concurrent-record-thread"); this.runningFutures = new ConcurrentHashSet<>(); } @@ -59,9 +57,9 @@ public OverallResult run(Options options, Runnable runnable) { boolean printErrorLog = options.isPrintErrorLog(); // 初始化存储的集合、线程池、并发工具类中的对象实例等. - Semaphore semaphore = new Semaphore(Math.min(concurrens, threads)); + Semaphore semaphore = new Semaphore(concurrens); CountDownLatch countLatch = new CountDownLatch(threads); - super.executorService = Executors.newFixedThreadPool(Math.min(threads, N_1024)); + super.executorService = StalkerExecutors.newFixedThreadExecutor(threads, "concurrent-measure-thread"); super.startNanoTime = System.nanoTime(); // 在多线程下控制线程并发量,与循环搭配来一起执行和测量. @@ -76,18 +74,18 @@ public OverallResult run(Options options, Runnable runnable) { // 将 future 添加到正在运行的 Future 信息集合中,并在 future 完成时,异步移除已经完成了的 future. runningFutures.add(future); - future.whenCompleteAsync((a, b) -> runningFutures.remove(future), this.backExecutorService); + future.whenCompleteAsync((a, b) -> runningFutures.remove(future), this.recordExecutorService); } catch (InterruptedException e) { log.error("【Stalker 错误提示】在多线程并发情况下测量任务执行的耗时信息的线程已被中断!", e); + Thread.currentThread().interrupt(); } } // 等待所有线程执行完毕,记录是否完成和完成时间,并关闭线程池等资源,最后将结果封装成实体信息返回. this.await(countLatch); - super.endNanoTime = System.nanoTime(); + super.setEndNanoTimeIfEmpty(System.nanoTime()); super.complete.compareAndSet(false, true); - super.shutdown(); - this.backExecutorService.shutdown(); + StalkerExecutors.shutdown(this.executorService, this.recordExecutorService); return super.buildFinalMeasurement(); } @@ -121,19 +119,17 @@ protected void loopMeasure(int runs, boolean printErrorLog, final Runnable runna *

注意:如果任务未完成,则立即停止线程池,但是还不能停止正在运行中的若干任务线程, * 暂时还没想到一个更好的、高性能的停止所有运行中的任务的方法.

* - * @return 是否成功的布尔值 * @author blinkfox on 2020-05-25. * @since v1.2.0 */ @Override - public boolean stop() { + public void stop() { if (!isComplete()) { - super.endNanoTime = System.nanoTime(); + super.setEndNanoTimeIfEmpty(System.nanoTime()); super.complete.compareAndSet(false, true); // 停止时直接关闭线程池. - super.shutdownNowQuietly(); - this.backExecutorService.shutdownNow(); + StalkerExecutors.shutdownNow(this.executorService, this.recordExecutorService); // 迭代删除正在运行中的 Future,并取消正在运行中的任务. Iterator> futureIterator = this.runningFutures.iterator(); @@ -145,7 +141,6 @@ public boolean stop() { } } } - return true; } /** @@ -162,6 +157,7 @@ private void await(CountDownLatch countLatch) { } } catch (InterruptedException e) { log.error("【Stalker 错误提示】在并发执行下等待任务执行结束时出错!", e); + Thread.currentThread().interrupt(); } } diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java index 8c6d623..4a5203d 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java @@ -3,8 +3,8 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.config.RunDuration; import com.blinkfox.stalker.result.bean.OverallResult; +import com.blinkfox.stalker.runner.executor.StalkerExecutors; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.Semaphore; @@ -38,8 +38,9 @@ public class ConcurrentScheduledMeasureRunner extends ConcurrentMeasureRunner { */ public ConcurrentScheduledMeasureRunner() { super(); - this.scheduledExecutorService = Executors.newScheduledThreadPool(1); - super.executorService = Executors.newFixedThreadPool(N_1024); + this.scheduledExecutorService = StalkerExecutors.newScheduledThreadPool(1, "concurrent-scheduled-thread"); + super.executorService = StalkerExecutors.newFixedThreadExecutor( + StalkerExecutors.MAX_POOL_SIZE, "concurrent-measure-thread"); } /** @@ -60,7 +61,7 @@ public OverallResult run(Options options, Runnable runnable) { // 到指定的持续时间之后,就取消执行中的任务,并关闭线程池. final RunDuration duration = options.getDuration(); - this.scheduledFuture = this.scheduledExecutorService.schedule((Runnable) this::stop, + this.scheduledFuture = this.scheduledExecutorService.schedule(this::stop, duration.getAmount(), duration.getTimeUnit()); super.startNanoTime = System.nanoTime(); @@ -80,22 +81,17 @@ public OverallResult run(Options options, Runnable runnable) { // 将 future 添加到正在运行的 Future 信息集合中,并在 future 完成时,异步移除已经完成了的 future. runningFutures.add(future); - future.whenCompleteAsync((a, b) -> runningFutures.remove(future), super.backExecutorService); + future.whenCompleteAsync((a, b) -> runningFutures.remove(future), super.recordExecutorService); } catch (InterruptedException e) { log.error("【Stalker 错误提示】在多线程并发情况下测量任务执行的耗时信息的线程已被中断!", e); + Thread.currentThread().interrupt(); } } // 等待所有线程执行完毕,记录是否完成和完成时间,并关闭线程池等资源,最后将结果封装成实体信息返回. - if (super.endNanoTime == 0) { - super.endNanoTime = System.nanoTime(); - } - if (!super.complete.get()) { - super.complete.compareAndSet(false, true); - } - super.shutdown(); - super.backExecutorService.shutdown(); - this.scheduledExecutorService.shutdown(); + super.setEndNanoTimeIfEmpty(System.nanoTime()); + super.complete.compareAndSet(false, true); + StalkerExecutors.shutdown(this.executorService, this.recordExecutorService, this.scheduledExecutorService); if (!this.scheduledFuture.isDone()) { this.scheduledFuture.cancel(true); } @@ -104,22 +100,16 @@ public OverallResult run(Options options, Runnable runnable) { /** * 停止相关的运行测量任务. - * - *

注意:如果任务未完成,则立即停止线程池,但是还不能停止正在运行中的若干任务线程, - * 暂时还没想到一个更好的、高性能的停止所有运行中的任务的方法.

- * - * @return 是否成功的布尔值 */ @Override - public boolean stop() { + public void stop() { super.stop(); // 关闭定时任务线程池和取消对应的定时任务. - this.scheduledExecutorService.shutdown(); + StalkerExecutors.shutdown(this.scheduledExecutorService); if (this.scheduledFuture != null && !this.scheduledFuture.isDone()) { - return this.scheduledFuture.cancel(true); + this.scheduledFuture.cancel(true); } - return true; } } diff --git a/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java index fedc11d..456e89b 100644 --- a/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java @@ -94,10 +94,8 @@ public interface MeasureRunner { /** * 停止相关的运行测量任务. - * - * @return 是否成功的布尔值 */ - boolean stop(); + void stop(); /** * 构建运行中的任务的总体测量结果信息. diff --git a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java index 3a17b84..784a0b4 100644 --- a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java +++ b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java @@ -8,13 +8,12 @@ import com.blinkfox.stalker.result.bean.Measurement; import com.blinkfox.stalker.result.bean.OverallResult; import com.blinkfox.stalker.result.bean.RunnerInfo; +import com.blinkfox.stalker.runner.executor.StalkerExecutors; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; /** @@ -29,8 +28,8 @@ public final class MeasureRunnerContext { /** * 用于异步提交任务的线程池. */ - private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(1, - 3, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<>(5)); + private static final ThreadPoolExecutor executor = + StalkerExecutors.newThreadExecutor(4, 16, "stalker-measure-thread"); /** * 用来存储 {@link MeasureRunner} 的容器,Key 是运行时的 sessionId, value 是 {@link RunnerInfo} 的实例. @@ -138,7 +137,10 @@ public static String submit(final Options options, final Runnable runnable) { String sessionId = StrKit.get62RadixUuid(); measureMap.put(sessionId, new RunnerInfo(options, measureRunner)); - executor.execute(() -> measureRunner.run(options, runnable)); + executor.execute(() -> { + log.info("已经添加了新的执行任务."); + measureRunner.run(options, runnable); + }); return sessionId; } diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java index df9504c..155d1fe 100644 --- a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java @@ -2,6 +2,7 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.result.bean.OverallResult; +import com.blinkfox.stalker.runner.executor.StalkerExecutors; import java.util.concurrent.Executors; import java.util.concurrent.Future; import lombok.extern.slf4j.Slf4j; @@ -20,14 +21,13 @@ public class SimpleMeasureRunner extends AbstractMeasureRunner { * * @since v1.2.0 */ - protected Future measureTask; + protected Future measureFuture; /** * 构造方法. */ public SimpleMeasureRunner() { super(); - super.executorService = Executors.newSingleThreadExecutor(); } /** @@ -41,11 +41,12 @@ public SimpleMeasureRunner() { public OverallResult run(Options options, Runnable runnable) { boolean printErrorLog = options.isPrintErrorLog(); int totalCount = options.getThreads() * options.getRuns(); + super.executorService = StalkerExecutors.newSingleThreadExecutor("simple-measure-thread"); super.startNanoTime = System.nanoTime(); // 由于并发数是 1,直接单线程循环执行 (runs * threads) 次即可, // 将执行的相关任务以 Future 的形式来执行,便于程序动态取消任务或判断任务执行情况等. - this.measureTask = executorService.submit(() -> { + this.measureFuture = executorService.submit(() -> { for (int i = 0; i < totalCount; ++i) { try { // 开始执行测量任务,记录开始时间、执行次数等. @@ -64,41 +65,37 @@ public OverallResult run(Options options, Runnable runnable) { // 阻塞调用要执行的测量任务,达到等待任务结束的目的. try { - this.measureTask.get(); + this.measureFuture.get(); } catch (Exception e) { log.error("【Stalker 错误】执行测量任务发生错误!", e); } // 等待所有线程执行完毕,并关闭线程池,最后将结果封装成实体信息. - super.endNanoTime = System.nanoTime(); + super.setEndNanoTimeIfEmpty(System.nanoTime()); super.complete.compareAndSet(false, true); - super.shutdown(); + StalkerExecutors.shutdown(super.executorService); return super.buildFinalMeasurement(); } /** * 停止相关的运行测量任务. * - * @return 是否成功的布尔值 * @author blinkfox on 2020-05-25. * @since v1.2.0 */ - public boolean stop() { - if (isComplete()) { - log.info("【Stalker 提示】任务已完成,将不再暂停测量任务."); - return true; - } - - super.endNanoTime = System.nanoTime(); - super.complete.compareAndSet(false, true); + @Override + public void stop() { + if (!isComplete()) { + super.setEndNanoTimeIfEmpty(System.nanoTime()); + super.complete.compareAndSet(false, true); - // 立即关闭线程池. - super.shutdownNowQuietly(); - // 取消正在执行中的任务. - if (this.measureTask != null && !this.measureTask.isDone()) { - return this.measureTask.cancel(true); + // 立即关闭线程池. + StalkerExecutors.shutdownNow(super.executorService); + // 取消正在执行中的任务. + if (this.measureFuture != null && !this.measureFuture.isDone()) { + this.measureFuture.cancel(true); + } } - return true; } } diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java index e599c2b..064ec25 100644 --- a/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java @@ -3,7 +3,7 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.config.RunDuration; import com.blinkfox.stalker.result.bean.OverallResult; -import java.util.concurrent.Executors; +import com.blinkfox.stalker.runner.executor.StalkerExecutors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import lombok.extern.slf4j.Slf4j; @@ -34,7 +34,7 @@ public class SimpleScheduledMeasureRunner extends SimpleMeasureRunner { */ public SimpleScheduledMeasureRunner() { super(); - this.scheduledExecutorService = Executors.newScheduledThreadPool(1); + this.scheduledExecutorService = StalkerExecutors.newScheduledThreadPool(1, "simple-scheduled-thread"); } /** @@ -47,11 +47,11 @@ public SimpleScheduledMeasureRunner() { @Override public OverallResult run(Options options, Runnable runnable) { boolean printErrorLog = options.isPrintErrorLog(); - super.executorService = Executors.newSingleThreadExecutor(); + super.executorService = StalkerExecutors.newSingleThreadExecutor("simple-scheduled-measure-thread"); super.startNanoTime = System.nanoTime(); // 将单线程中执行的任务放在 while 循环中,一直执行下去. - super.measureTask = executorService.submit(() -> { + super.measureFuture = executorService.submit(() -> { while (true) { try { // 开始执行测量任务,记录开始时间、执行次数等. @@ -70,34 +70,38 @@ public OverallResult run(Options options, Runnable runnable) { // 到指定的持续时间之后,就取消执行中的任务,并关闭线程池. final RunDuration duration = options.getDuration(); - this.scheduledFuture = this.scheduledExecutorService.schedule((Runnable) this::stop, + this.scheduledFuture = this.scheduledExecutorService.schedule(this::stop, duration.getAmount(), duration.getTimeUnit()); // 阻塞调用要执行的测量任务,达到阻塞等待任务结束的目的. try { - this.measureTask.get(); + this.measureFuture.get(); } catch (Exception e) { log.error("【Stalker 错误】执行测量任务发生错误!", e); } + + // 如果没有设置相关的结束信息资源,就设置,没有关闭相关的资源就进行关闭. + super.setEndNanoTimeIfEmpty(System.nanoTime()); + super.complete.compareAndSet(false, true); + StalkerExecutors.shutdown(super.executorService, this.scheduledExecutorService); return super.buildFinalMeasurement(); } /** * 停止相关的运行测量任务. * - * @return 是否成功的布尔值 * @author blinkfox on 2020-05-25. * @since v1.2.0 */ - public boolean stop() { + @Override + public void stop() { super.stop(); // 关闭定时任务线程池和取消对应的定时任务. - this.scheduledExecutorService.shutdown(); + StalkerExecutors.shutdown(this.scheduledExecutorService); if (this.scheduledFuture != null && !this.scheduledFuture.isDone()) { - return this.scheduledFuture.cancel(true); + this.scheduledFuture.cancel(true); } - return true; } } diff --git a/src/main/java/com/blinkfox/stalker/runner/executor/StalkerExecutors.java b/src/main/java/com/blinkfox/stalker/runner/executor/StalkerExecutors.java new file mode 100644 index 0000000..a3aa656 --- /dev/null +++ b/src/main/java/com/blinkfox/stalker/runner/executor/StalkerExecutors.java @@ -0,0 +1,102 @@ +package com.blinkfox.stalker.runner.executor; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +/** + * Stalker 中使用到的线程池执行器工具类. + * + * @author blinkfox on 2020-06-02. + * @since v1.2.0 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class StalkerExecutors { + + private static final int MAX_QUEUE_SIZE = 65535; + + public static final int MAX_POOL_SIZE = 1024; + + /** + * 根据线程名称创建新的单线程线程池. + * + * @param threadName 线程名称 + * @return 线程池 + */ + public static ThreadPoolExecutor newSingleThreadExecutor(String threadName) { + return new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, + new LinkedBlockingQueue<>(2), r -> new Thread(r, threadName), new StalkerRejectedHandler()); + } + + /** + * 根据线程名称创建固定数量线程的线程池,最大线程数最多 1024 个. + * + * @param corePoolSize 核心线程数 + * @param threadName 线程名称 + * @return 线程池 + */ + public static ThreadPoolExecutor newFixedThreadExecutor(int corePoolSize, String threadName) { + int fixedPoolSize = Math.min(corePoolSize, MAX_POOL_SIZE); + return new ThreadPoolExecutor(fixedPoolSize, fixedPoolSize, 0L, TimeUnit.SECONDS, + new LinkedBlockingQueue<>(MAX_QUEUE_SIZE), r -> new Thread(r, threadName), + new StalkerRejectedHandler()); + } + + /** + * 根据线程名称创建固定数量线程的线程池,最大线程数最多 1024 个. + * + * @param corePoolSize 核心线程数 + * @param maxPoolSize 最大线程数 + * @param threadName 线程名称 + * @return 线程池 + */ + public static ThreadPoolExecutor newThreadExecutor(int corePoolSize, int maxPoolSize, String threadName) { + return new ThreadPoolExecutor(corePoolSize, maxPoolSize, 30L, TimeUnit.SECONDS, + new LinkedBlockingQueue<>(MAX_QUEUE_SIZE), r -> new Thread(r, threadName), + new StalkerRejectedHandler()); + } + + /** + * 根据线程名称创建新的可调度定时任务的程线程池. + * + * @param corePoolSize 核心线程数 + * @param threadName 线程名称 + * @return 线程池 + */ + public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, String threadName) { + return new ScheduledThreadPoolExecutor(corePoolSize, + r -> new Thread(r, threadName), new StalkerRejectedHandler()); + } + + /** + * 等待所有线程执行完毕,并最终关闭这若干个线程池集合. + * + * @param executorServices 若干个待关闭的线程池执行器集合 + */ + public static void shutdown(ExecutorService... executorServices) { + for (ExecutorService executorService : executorServices) { + if (executorService != null && !executorService.isShutdown()) { + executorService.shutdown(); + } + } + } + + /** + * 立即安静的关闭若干个线程池集合. + * + * @param executorServices 若干个待立即关闭的线程池执行器集合 + */ + public static void shutdownNow(ExecutorService... executorServices) { + for (ExecutorService executorService : executorServices) { + if (executorService != null && !executorService.isShutdown()) { + executorService.shutdownNow(); + } + } + } + +} diff --git a/src/main/java/com/blinkfox/stalker/runner/executor/StalkerRejectedHandler.java b/src/main/java/com/blinkfox/stalker/runner/executor/StalkerRejectedHandler.java new file mode 100644 index 0000000..22a1f21 --- /dev/null +++ b/src/main/java/com/blinkfox/stalker/runner/executor/StalkerRejectedHandler.java @@ -0,0 +1,27 @@ +package com.blinkfox.stalker.runner.executor; + +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadPoolExecutor; +import lombok.extern.slf4j.Slf4j; + +/** + * Stalker 中线程池队列满了之后的拒绝执行策略. + * + * @author blinkfox on 2020-06-02. + * @since v1.2.0 + */ +@Slf4j +public class StalkerRejectedHandler implements RejectedExecutionHandler { + + /** + * 当线程池队列满了之后,将拒绝接收新的任务,即放弃任务并打印出 {@code warn} 级别的日志基于警示. + * + * @param r 可运行任务 + * @param executor 线程池执行器 + */ + @Override + public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { + log.warn("【Stalker 警示】线程池队列任务已满,将拒绝接收新的执行任务,建议你调低运行的【并发数】。"); + } + +} From c6cef32f6d510210d3d312e882a056406fb2bd4c Mon Sep 17 00:00:00 2001 From: chenjiayin Date: Wed, 3 Jun 2020 10:10:33 +0800 Subject: [PATCH 23/37] =?UTF-8?q?=E5=AE=8C=E5=96=84=E9=83=A8=E5=88=86?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stalker/result/StalkerFuture.java | 130 ++++++++++++++++++ .../stalker/runner/AbstractMeasureRunner.java | 21 ++- .../runner/ConcurrentMeasureRunner.java | 7 +- .../ConcurrentScheduledMeasureRunner.java | 2 +- .../stalker/runner/MeasureRunner.java | 11 +- .../stalker/runner/SimpleMeasureRunner.java | 8 +- .../runner/SimpleScheduledMeasureRunner.java | 2 +- 7 files changed, 166 insertions(+), 15 deletions(-) create mode 100644 src/main/java/com/blinkfox/stalker/result/StalkerFuture.java diff --git a/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java b/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java new file mode 100644 index 0000000..9f0d978 --- /dev/null +++ b/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java @@ -0,0 +1,130 @@ +package com.blinkfox.stalker.result; + +import com.blinkfox.stalker.config.Options; +import com.blinkfox.stalker.result.bean.Measurement; +import com.blinkfox.stalker.runner.MeasureRunner; +import java.util.concurrent.RunnableFuture; +import java.util.concurrent.TimeUnit; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * Stalker 中异步任务的 Future 结果对象. + * + * @author blinkfox on 2020-06-02. + * @since v1.2.0 + */ +@Slf4j +@AllArgsConstructor +public class StalkerFuture implements RunnableFuture { + + /** + * 可运行任务的选项参数信息. + */ + private final Options options; + + /** + * 可运行的任务. + */ + private final Runnable runnable; + + /** + * 任务运行的 {@link MeasureRunner} 实例. + */ + private final MeasureRunner measureRunner; + + /** + * 执行此可运行的方法. + */ + @Override + public void run() { + this.measureRunner.run(this.options, this.runnable); + } + + /** + * 立即取消正在执行的测量任务,并立即关闭运行中的任务线程池. + * + * @return 正常情况下返回 {@code true},如果期间发生异常将返回 {@code false} + */ + public boolean cancel() { + return this.cancel(true); + } + + /** + * 立即取消正在执行的测量任务,并关闭运行中的任务线程池. + * + * @param mayInterruptIfRunning 该参数将始终是 {@code true},即取消任务时不管是否执行完了相关的任务都会立即取消任务. + * @return 正常情况下返回 {@code true},如果期间发生异常将返回 {@code false} + */ + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + try { + this.measureRunner.stop(); + return true; + } catch (Exception e) { + log.error("【Stalker 错误提示】取消正在执行中的测量任务时发生异常!", e); + return false; + } + } + + /** + * 获取测量任务在完成之前是否已经被取消. + * 如果任务是正常完成的,将返回 {@code false},如果任务是在完成之前手动取消的,将返回 {@code true}. + * + * @return 布尔值 + */ + @Override + public boolean isCancelled() { + return this.measureRunner.isCancelled(); + } + + /** + * 获取测量任务是否已经执行结束. + * 不管任务是正常结束还是手动取消,只要任务结束了,都将返回 {@code true},如果任务还在执行中,将返回 {@code false}. + * + * @return 布尔值 + */ + @Override + public boolean isDone() { + return this.measureRunner.isCompleted(); + } + + /** + * 获取测量任务是否已经正常执行完毕. + * 只有当任务正常执行完毕时才返回 {@code true},如果任务还在执行中或者被手动取消将返回 {@code false}. + * + * @return 布尔值 + */ + public boolean isDoneSuccessfully() { + return this.measureRunner.isCompleted() && !this.measureRunner.isCancelled(); + } + + /** + * 获取任务的执行结果. + * 请注意,该方法获取结果时是“非阻塞的”,每次都能获取到正在执行中的任务进度结果,即时任务被取消也能获取到取消时的最终结果信息。 + * 所以,你不应该调用此方法来阻塞等待执行结果. + * + * @return Measurement 实体对象 + */ + @Override + public Measurement get() { + return new MeasurementCollector().collect(this.measureRunner.buildRunningMeasurement()); + } + + /** + * 获取任务的执行结果,本方法. + * 请注意,该方法同 {@link #get()} 语义相同,获取结果时是“非阻塞的”,每次都能获取到正在执行中的任务进度结果, + * 即时任务被取消也能获取到取消时的最终结果信息。 + * + *

所以,你不应该调用此方法来阻塞等待执行结果.

+ * + * @param timeout 超时时间 + * @param unit 超时时间单位 + * @return {@link Measurement} 结果 + */ + @Override + public Measurement get(long timeout, TimeUnit unit) { + return this.get(); + } + +} diff --git a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java index dca3222..e5c1974 100644 --- a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java @@ -48,7 +48,12 @@ public abstract class AbstractMeasureRunner implements MeasureRunner { /** * 是否已经运行完成. */ - protected AtomicBoolean complete; + protected AtomicBoolean completed; + + /** + * 是否已经被取消. + */ + protected AtomicBoolean canceled; /** * 运行开始时的纳秒时间戳,单位为纳秒({@code ns}). @@ -69,7 +74,8 @@ public AbstractMeasureRunner() { this.eachMeasures = new ConcurrentLinkedQueue<>(); this.success = new LongAdder(); this.failure = new LongAdder(); - this.complete = new AtomicBoolean(false); + this.completed = new AtomicBoolean(false); + this.canceled = new AtomicBoolean(false); } @Override @@ -103,8 +109,13 @@ public long getFailure() { } @Override - public boolean isComplete() { - return this.complete.get(); + public boolean isCompleted() { + return this.completed.get(); + } + + @Override + public boolean isCancelled() { + return this.canceled.get(); } @Override @@ -131,7 +142,7 @@ public void setEndNanoTimeIfEmpty(long endNanoTime) { @Override public OverallResult buildRunningMeasurement() { // 如果任务已经完成,就直接返回最终的测试结果数据即可. - if (this.complete.get()) { + if (this.completed.get()) { return this.buildFinalMeasurement(); } diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java index 3818615..25ff900 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java @@ -84,7 +84,7 @@ public OverallResult run(Options options, Runnable runnable) { // 等待所有线程执行完毕,记录是否完成和完成时间,并关闭线程池等资源,最后将结果封装成实体信息返回. this.await(countLatch); super.setEndNanoTimeIfEmpty(System.nanoTime()); - super.complete.compareAndSet(false, true); + super.completed.compareAndSet(false, true); StalkerExecutors.shutdown(this.executorService, this.recordExecutorService); return super.buildFinalMeasurement(); } @@ -124,9 +124,10 @@ protected void loopMeasure(int runs, boolean printErrorLog, final Runnable runna */ @Override public void stop() { - if (!isComplete()) { + if (!isCompleted()) { super.setEndNanoTimeIfEmpty(System.nanoTime()); - super.complete.compareAndSet(false, true); + super.completed.compareAndSet(false, true); + super.canceled.compareAndSet(false, true); // 停止时直接关闭线程池. StalkerExecutors.shutdownNow(this.executorService, this.recordExecutorService); diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java index 4a5203d..ffd667a 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java @@ -90,7 +90,7 @@ public OverallResult run(Options options, Runnable runnable) { // 等待所有线程执行完毕,记录是否完成和完成时间,并关闭线程池等资源,最后将结果封装成实体信息返回. super.setEndNanoTimeIfEmpty(System.nanoTime()); - super.complete.compareAndSet(false, true); + super.completed.compareAndSet(false, true); StalkerExecutors.shutdown(this.executorService, this.recordExecutorService, this.scheduledExecutorService); if (!this.scheduledFuture.isDone()) { this.scheduledFuture.cancel(true); diff --git a/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java index 456e89b..74463d0 100644 --- a/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java @@ -63,7 +63,16 @@ public interface MeasureRunner { * @author blinkfox on 2020-05-23. * @since v1.2.0 */ - boolean isComplete(); + boolean isCompleted(); + + /** + * 判断当前任务是否已经被取消. + * + * @return 是否被取消的布尔值 + * @author blinkfox on 2020-06-02. + * @since v1.2.0 + */ + boolean isCancelled(); /** * 获取任务开始运行时的纳秒时间戳. diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java index 155d1fe..c7f89c4 100644 --- a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java @@ -3,7 +3,6 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.result.bean.OverallResult; import com.blinkfox.stalker.runner.executor.StalkerExecutors; -import java.util.concurrent.Executors; import java.util.concurrent.Future; import lombok.extern.slf4j.Slf4j; @@ -72,7 +71,7 @@ public OverallResult run(Options options, Runnable runnable) { // 等待所有线程执行完毕,并关闭线程池,最后将结果封装成实体信息. super.setEndNanoTimeIfEmpty(System.nanoTime()); - super.complete.compareAndSet(false, true); + super.completed.compareAndSet(false, true); StalkerExecutors.shutdown(super.executorService); return super.buildFinalMeasurement(); } @@ -85,9 +84,10 @@ public OverallResult run(Options options, Runnable runnable) { */ @Override public void stop() { - if (!isComplete()) { + if (!isCompleted()) { super.setEndNanoTimeIfEmpty(System.nanoTime()); - super.complete.compareAndSet(false, true); + super.completed.compareAndSet(false, true); + super.canceled.compareAndSet(false, true); // 立即关闭线程池. StalkerExecutors.shutdownNow(super.executorService); diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java index 064ec25..472e648 100644 --- a/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java @@ -82,7 +82,7 @@ public OverallResult run(Options options, Runnable runnable) { // 如果没有设置相关的结束信息资源,就设置,没有关闭相关的资源就进行关闭. super.setEndNanoTimeIfEmpty(System.nanoTime()); - super.complete.compareAndSet(false, true); + super.completed.compareAndSet(false, true); StalkerExecutors.shutdown(super.executorService, this.scheduledExecutorService); return super.buildFinalMeasurement(); } From 150a5accef1130baf13c83e957ace9ea0bfac99d Mon Sep 17 00:00:00 2001 From: blinkfox Date: Wed, 3 Jun 2020 14:30:06 +0800 Subject: [PATCH 24/37] =?UTF-8?q?submit=20=E7=9A=84=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E5=80=BC=E6=94=B9=E4=B8=BA=E4=BA=86=E4=BD=BF=E7=94=A8=20Stalke?= =?UTF-8?q?rFuture=20=E6=9D=A5=E4=BB=A3=E6=9B=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/blinkfox/stalker/Stalker.java | 94 +----------- .../stalker/result/StalkerFuture.java | 145 ++++++++++++++++-- .../ConcurrentScheduledMeasureRunner.java | 9 +- .../stalker/runner/MeasureRunnerContext.java | 128 +--------------- .../runner/SimpleScheduledMeasureRunner.java | 7 +- .../blinkfox/stalker/test/StalkerTest.java | 84 +++++----- 6 files changed, 191 insertions(+), 276 deletions(-) diff --git a/src/main/java/com/blinkfox/stalker/Stalker.java b/src/main/java/com/blinkfox/stalker/Stalker.java index 63067d8..3ba2196 100644 --- a/src/main/java/com/blinkfox/stalker/Stalker.java +++ b/src/main/java/com/blinkfox/stalker/Stalker.java @@ -2,10 +2,10 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.output.MeasureOutputContext; +import com.blinkfox.stalker.result.StalkerFuture; import com.blinkfox.stalker.result.bean.Measurement; import com.blinkfox.stalker.runner.MeasureRunnerContext; import java.util.List; -import java.util.Set; import lombok.experimental.UtilityClass; /** @@ -21,11 +21,11 @@ public class Stalker { * 使用默认选项参数来提交可运行的测量任务,并立即返回此次会话的 ID. * * @param task 任务 - * @return 会话 ID - * @author blinkfox on 2020-05-23 + * @return {@link StalkerFuture} 对象实例 + * @author blinkfox on 2020-06-03 * @since v1.2.0 */ - public String submit(Runnable task) { + public StalkerFuture submit(Runnable task) { return submit(Options.of(), task); } @@ -34,11 +34,11 @@ public String submit(Runnable task) { * * @param options 选项参数 * @param task 任务 - * @return 会话 ID - * @author blinkfox on 2020-05-23 + * @return {@link StalkerFuture} 对象实例 + * @author blinkfox on 2020-06-03 * @since v1.2.0 */ - public String submit(Options options, Runnable task) { + public StalkerFuture submit(Options options, Runnable task) { if (options == null || task == null) { throw new IllegalArgumentException("options or runnables is null (or empty)!"); } @@ -46,86 +46,6 @@ public String submit(Options options, Runnable task) { return MeasureRunnerContext.submit(options, task); } - /** - * 根据会话的 ID 查询其对应的运行任务的测量结果数据,本结果是在 {@link Options#getOutputs()} 的属性中 - * {@link com.blinkfox.stalker.output.MeasureOutput} 接口定义的输出结果. - * - * @param sessionId 会话 ID - * @return 运行的最终输出结果 - * @author blinkfox on 2020-05-23 - * @since v1.2.0 - */ - public List query(String sessionId) { - return MeasureRunnerContext.query(sessionId); - } - - /** - * 根据运行的测量会话 ID,判断任务是否在运行中,即时该任务也许不存在,查找不到时,也会认为是 {@code true}. - * - * @param sessionId 会话 ID - * @return 布尔值 - * @author blinkfox on 2020-05-23. - * @since v1.2.0 - */ - public boolean isRunning(String sessionId) { - return MeasureRunnerContext.isRunning(sessionId); - } - - /** - * 根据运行的测量会话 ID,移除相关的运行任务记录.目前不会停止任务,只是从缓存中移除任务记录. - * - * @param sessionId 会话 ID - * @author blinkfox on 2020-05-23 - * @since v1.2.0 - */ - public void remove(String sessionId) { - MeasureRunnerContext.remove(sessionId); - } - - /** - * 根据运行的测量会话 ID,停止相关的测量任务. - * - * @param sessionId 会话 ID - * @author blinkfox on 2020-05-23 - * @since v1.2.0 - */ - public void stop(String sessionId) { - MeasureRunnerContext.stop(sessionId); - } - - /** - * 根据会话的 ID 查询其对应的运行任务的测量结果数据. - * - * @param sessionId 会话 ID - * @return 运行的测量结果 - * @author blinkfox on 2020-05-23 - * @since v1.2.0 - */ - public Measurement queryMeasurement(String sessionId) { - return MeasureRunnerContext.queryMeasurement(sessionId); - } - - /** - * 清除所有测量任务记录. - * - * @author blinkfox on 2020-05-26. - * @since v1.2.0 - */ - public void clear() { - MeasureRunnerContext.clear(); - } - - /** - * 获取所有测量任务记录的 Session ID 集合. - * - * @return Session ID 集合 - * @author blinkfox on 2020-05-26. - * @since v1.2.0 - */ - public Set getAllSessions() { - return MeasureRunnerContext.getAllSessions(); - } - /** * 测量要执行的代码的性能评估. * diff --git a/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java b/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java index 9f0d978..1b1951f 100644 --- a/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java +++ b/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java @@ -1,11 +1,17 @@ package com.blinkfox.stalker.result; import com.blinkfox.stalker.config.Options; +import com.blinkfox.stalker.output.MeasureOutputContext; import com.blinkfox.stalker.result.bean.Measurement; +import com.blinkfox.stalker.result.bean.OverallResult; import com.blinkfox.stalker.runner.MeasureRunner; +import com.blinkfox.stalker.runner.executor.StalkerExecutors; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; import java.util.concurrent.RunnableFuture; import java.util.concurrent.TimeUnit; -import lombok.AllArgsConstructor; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; /** @@ -15,8 +21,13 @@ * @since v1.2.0 */ @Slf4j -@AllArgsConstructor -public class StalkerFuture implements RunnableFuture { +public class StalkerFuture implements RunnableFuture> { + + /** + * 用于异步提交任务的线程池. + */ + private static final ExecutorService executor = + StalkerExecutors.newThreadExecutor(4, 16, "stalker-future-thread"); /** * 可运行任务的选项参数信息. @@ -31,14 +42,43 @@ public class StalkerFuture implements RunnableFuture { /** * 任务运行的 {@link MeasureRunner} 实例. */ + @Getter private final MeasureRunner measureRunner; + /** + * 用于 StalkerFuture 内部识别和控制任务运行状态的 {@link CompletableFuture} 对象. + */ + private CompletableFuture runFuture; + + /** + * 构造方法. + * + * @param options 运行任务的选项参数 + * @param runnable 可运行实例 + * @param measureRunner 运行测量器 + */ + public StalkerFuture(Options options, Runnable runnable, MeasureRunner measureRunner) { + this.options = options; + this.runnable = runnable; + this.measureRunner = measureRunner; + } + /** * 执行此可运行的方法. + *

注意,此次使用双重检查锁机制,使得该对象的任务只会被运行一次.

*/ @Override public void run() { - this.measureRunner.run(this.options, this.runnable); + if (this.runFuture != null) { + return; + } + + synchronized (this) { + if (this.runFuture == null) { + this.runFuture = CompletableFuture.runAsync( + () -> this.measureRunner.run(this.options, this.runnable), executor); + } + } } /** @@ -58,13 +98,20 @@ public boolean cancel() { */ @Override public boolean cancel(boolean mayInterruptIfRunning) { + // 使用布尔值记录,核心任务是否运行完成. + boolean flag = true; try { this.measureRunner.stop(); - return true; } catch (Exception e) { log.error("【Stalker 错误提示】取消正在执行中的测量任务时发生异常!", e); - return false; + flag = false; + } + + // 需要将本 Future 中的任务也停止. + if (this.runFuture != null && !this.runFuture.isDone()) { + this.runFuture.cancel(true); } + return flag; } /** @@ -100,19 +147,19 @@ public boolean isDoneSuccessfully() { } /** - * 获取任务的执行结果. + * 实时获取任务的执行结果. * 请注意,该方法获取结果时是“非阻塞的”,每次都能获取到正在执行中的任务进度结果,即时任务被取消也能获取到取消时的最终结果信息。 * 所以,你不应该调用此方法来阻塞等待执行结果. * - * @return Measurement 实体对象 + * @return {@link Options#getOutputs()} 中定义多种的输出通道结果 */ @Override - public Measurement get() { - return new MeasurementCollector().collect(this.measureRunner.buildRunningMeasurement()); + public List get() { + return new MeasureOutputContext().output(this.options, this.getMeasurement()); } /** - * 获取任务的执行结果,本方法. + * 实时获取任务的执行结果. * 请注意,该方法同 {@link #get()} 语义相同,获取结果时是“非阻塞的”,每次都能获取到正在执行中的任务进度结果, * 即时任务被取消也能获取到取消时的最终结果信息。 * @@ -120,11 +167,83 @@ public Measurement get() { * * @param timeout 超时时间 * @param unit 超时时间单位 - * @return {@link Measurement} 结果 + * @return {@link Options#getOutputs()} 中定义多种的输出通道结果 */ @Override - public Measurement get(long timeout, TimeUnit unit) { + public List get(long timeout, TimeUnit unit) { return this.get(); } + /** + * 实时获取运行中的任务的总体测量结果信息. + * + * @return 总体测量结果 {@link OverallResult} 信息 + */ + public OverallResult getOverallResult() { + return this.measureRunner.buildRunningMeasurement(); + } + + /** + * 实时获取任务的执行结果. + * + * @return {@link Measurement} 结果 + */ + public Measurement getMeasurement() { + return new MeasurementCollector().collect(this.measureRunner.buildRunningMeasurement()); + } + + /** + * 获取当前已经运行的总次数. + * + * @return 运行总次数 + */ + public long getTotal() { + return this.measureRunner.getTotal(); + } + + /** + * 获取到当前时的运行成功的次数. + * + * @return 运行成功的次数 + */ + public long getSuccess() { + return this.measureRunner.getSuccess(); + } + + /** + * 获取当前运行失败的次数. + * + * @return 运行失败的次数 + */ + public long getFailure() { + return this.measureRunner.getFailure(); + } + + /** + * 获取任务开始运行时的纳秒时间戳. + * + * @return 开始运行时的纳秒时间戳 + */ + public long getStartNanoTime() { + return this.measureRunner.getStartNanoTime(); + } + + /** + * 获取任务结束运行时的纳秒时间戳,如果任务还未结束,该值将是 {@code 0}. + * + * @return 结束时纳秒时间戳 + */ + public long getEndNanoTime() { + return this.measureRunner.getEndNanoTime(); + } + + /** + * 获取任务最终完成时实际所消耗的总的纳秒时间数. + * + * @return 实际所消耗的总的纳秒时间数 + */ + public long getCosts() { + return this.measureRunner.getCosts(); + } + } diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java index ffd667a..ec5a470 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java @@ -59,10 +59,13 @@ public OverallResult run(Options options, Runnable runnable) { // 初始化存储的集合、线程池、并发工具类中的对象实例等. final Semaphore semaphore = new Semaphore(concurrens); - // 到指定的持续时间之后,就取消执行中的任务,并关闭线程池. + // 到指定的持续时间之后,就取消执行中的任务,并关闭线程池, + // 注意,由于是定时任务,所以“是否取消”也设置为 false,用于区分是否是人为取消了任务,只有人为取消的才是 true. final RunDuration duration = options.getDuration(); - this.scheduledFuture = this.scheduledExecutorService.schedule(this::stop, - duration.getAmount(), duration.getTimeUnit()); + this.scheduledFuture = this.scheduledExecutorService.schedule(() -> { + this.stop(); + super.canceled.compareAndSet(true, false); + }, duration.getAmount(), duration.getTimeUnit()); super.startNanoTime = System.nanoTime(); long expectEndNanoTime = duration.getEndNanoTime(super.startNanoTime); diff --git a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java index 784a0b4..7aa47cb 100644 --- a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java +++ b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java @@ -1,19 +1,11 @@ package com.blinkfox.stalker.runner; import com.blinkfox.stalker.config.Options; -import com.blinkfox.stalker.exception.StalkerException; import com.blinkfox.stalker.kit.StrKit; -import com.blinkfox.stalker.output.MeasureOutputContext; import com.blinkfox.stalker.result.MeasurementCollector; +import com.blinkfox.stalker.result.StalkerFuture; import com.blinkfox.stalker.result.bean.Measurement; import com.blinkfox.stalker.result.bean.OverallResult; -import com.blinkfox.stalker.result.bean.RunnerInfo; -import com.blinkfox.stalker.runner.executor.StalkerExecutors; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ThreadPoolExecutor; import lombok.extern.slf4j.Slf4j; /** @@ -25,17 +17,6 @@ @Slf4j public final class MeasureRunnerContext { - /** - * 用于异步提交任务的线程池. - */ - private static final ThreadPoolExecutor executor = - StalkerExecutors.newThreadExecutor(4, 16, "stalker-measure-thread"); - - /** - * 用来存储 {@link MeasureRunner} 的容器,Key 是运行时的 sessionId, value 是 {@link RunnerInfo} 的实例. - */ - private static final Map measureMap = new ConcurrentHashMap<>(); - /** * 运行测量的性能参数配置选项. */ @@ -119,7 +100,7 @@ public Measurement runAndCollect(Runnable runnable) { * @author blinkfox on 2020-05-23. * @since v1.2.0 */ - public static String submit(final Options options, final Runnable runnable) { + public static StalkerFuture submit(final Options options, final Runnable runnable) { // 预热运行. warmup(options, runnable); @@ -135,107 +116,10 @@ public static String submit(final Options options, final Runnable runnable) { : new SimpleMeasureRunner(); } - String sessionId = StrKit.get62RadixUuid(); - measureMap.put(sessionId, new RunnerInfo(options, measureRunner)); - executor.execute(() -> { - log.info("已经添加了新的执行任务."); - measureRunner.run(options, runnable); - }); - return sessionId; - } - - /** - * 根据运行的测量会话 ID,来查询该会话所对应的测量结果. - * - * @param sessionId 会话 ID - * @return 测量结果 - * @author blinkfox on 2020-05-23. - * @since v1.2.0 - */ - public static Measurement queryMeasurement(String sessionId) { - RunnerInfo runnerInfo = measureMap.get(sessionId); - if (runnerInfo == null) { - throw new StalkerException(StrKit.format("【Stalker 异常】根据当前 sessionId【{}】无法找到对应的运行任务," - + "或者该任务已过期,请重新开始执行。", sessionId)); - } - return new MeasurementCollector().collect(runnerInfo.getMeasureRunner().buildRunningMeasurement()); - } - - /** - * 根据运行的测量会话 ID,来查询该会话所对应的测量结果. - * - * @param sessionId 会话 ID - * @return 最终的输出结果 - * @author blinkfox on 2020-05-23. - * @since v1.2.0 - */ - public static List query(String sessionId) { - RunnerInfo runnerInfo = measureMap.get(sessionId); - if (runnerInfo == null) { - throw new StalkerException(StrKit.format("【Stalker 异常】根据当前 sessionId【{}】无法找到对应的运行任务," - + "或者该任务已过期,请重新开始执行。", sessionId)); - } - return new MeasureOutputContext().output(runnerInfo.getOptions(), - new MeasurementCollector().collect(runnerInfo.getMeasureRunner().buildRunningMeasurement())); - } - - /** - * 根据运行的测量会话 ID,移除相关的运行任务记录.目前不会停止任务,只是从缓存中移除任务记录. - * - * @param sessionId 会话 ID - * @author blinkfox on 2020-05-23. - * @since v1.2.0 - */ - public static void remove(String sessionId) { - measureMap.remove(sessionId); - } - - /** - * 根据运行的测量会话 ID,判断任务是否在运行中,即时该任务也许不存在,查找不到时,也会认为是 {@code true}. - * - * @param sessionId 会话 ID - * @return 布尔值 - * @author blinkfox on 2020-05-23. - * @since v1.2.0 - */ - public static boolean isRunning(String sessionId) { - RunnerInfo runnerInfo = measureMap.get(sessionId); - return runnerInfo != null && !runnerInfo.getMeasureRunner().isComplete(); - } - - /** - * 根据运行的测量会话 ID,停止相关的测量任务. - * - * @param sessionId 会话 ID - * @author blinkfox on 2020-05-26. - * @since v1.2.0 - */ - public static void stop(String sessionId) { - RunnerInfo runnerInfo = measureMap.get(sessionId); - if (runnerInfo != null) { - runnerInfo.getMeasureRunner().stop(); - } - } - - /** - * 清除所有测量任务记录. - * - * @author blinkfox on 2020-05-26. - * @since v1.2.0 - */ - public static void clear() { - measureMap.clear(); - } - - /** - * 获取所有测量任务记录的 Session ID 集合. - * - * @return Session ID 集合 - * @author blinkfox on 2020-05-26. - * @since v1.2.0 - */ - public static Set getAllSessions() { - return measureMap.keySet(); + // 构造 StalkerFuture 对象,并开始运行任务. + StalkerFuture stalkerFuture = new StalkerFuture(options, runnable, measureRunner); + stalkerFuture.run(); + return stalkerFuture; } } diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java index 472e648..52c3f29 100644 --- a/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java @@ -69,9 +69,12 @@ public OverallResult run(Options options, Runnable runnable) { }); // 到指定的持续时间之后,就取消执行中的任务,并关闭线程池. + // 注意,由于是定时任务,所以“是否取消”也设置为 false,用于区分是否是人为取消了任务,只有人为取消的才是 true. final RunDuration duration = options.getDuration(); - this.scheduledFuture = this.scheduledExecutorService.schedule(this::stop, - duration.getAmount(), duration.getTimeUnit()); + this.scheduledFuture = this.scheduledExecutorService.schedule(() -> { + this.stop(); + super.canceled.compareAndSet(true, false); + }, duration.getAmount(), duration.getTimeUnit()); // 阻塞调用要执行的测量任务,达到阻塞等待任务结束的目的. try { diff --git a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java index b25979a..b5bcdb7 100644 --- a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java +++ b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java @@ -2,12 +2,12 @@ import com.blinkfox.stalker.Stalker; import com.blinkfox.stalker.config.Options; +import com.blinkfox.stalker.result.StalkerFuture; import com.blinkfox.stalker.result.bean.Measurement; import com.blinkfox.stalker.test.prepare.MyTestService; import java.util.List; import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; -import org.junit.AfterClass; import org.junit.Assert; import org.junit.Test; @@ -136,19 +136,17 @@ public void runWithSlowMethod() { */ @Test public void submit() throws InterruptedException { - String sessionId = Stalker.submit(() -> new MyTestService().hello()); - Assert.assertNotNull(sessionId); + StalkerFuture stalkerFuture = Stalker.submit(() -> new MyTestService().hello()); + Assert.assertNotNull(stalkerFuture); - while (Stalker.isRunning(sessionId)) { - List results = Stalker.query(sessionId); + while (!stalkerFuture.isDone()) { + List results = stalkerFuture.get(); Assert.assertNotNull(results.get(0)); Thread.sleep(2L); } log.info("任务已完成,获取最后的执行结果,并移除任务记录."); - Stalker.query(sessionId); - // 执行完成之后移除 sessionId. - Stalker.remove(sessionId); + stalkerFuture.get(); } /** @@ -156,21 +154,18 @@ public void submit() throws InterruptedException { */ @Test public void submitWithSlowMethod() throws InterruptedException { - String sessionId = Stalker.submit(Options.of("SlowTest", 20, 5, 1), + StalkerFuture stalkerFuture = Stalker.submit(Options.of("SlowTest", 20, 5, 1), () -> new MyTestService().slowHello()); - Assert.assertNotNull(sessionId); + Assert.assertNotNull(stalkerFuture); - while (Stalker.isRunning(sessionId)) { - List results = Stalker.query(sessionId); + while (!stalkerFuture.isDone()) { + List results = stalkerFuture.get(); Assert.assertNotNull(results.get(0)); Thread.sleep(50L); } log.info("任务已完成,获取最后的执行结果,并移除任务记录."); - Stalker.query(sessionId); - - // 执行完成之后移除 sessionId. - Stalker.remove(sessionId); + stalkerFuture.get(); } /** @@ -178,21 +173,23 @@ public void submitWithSlowMethod() throws InterruptedException { */ @Test public void submitWithSlowMethodDuration() throws InterruptedException { - String sessionId = Stalker.submit(Options.ofDurationSeconds(2, 4), + StalkerFuture stalkerFuture = Stalker.submit(Options.ofDurationSeconds(2, 4), () -> new MyTestService().slowHello()); - Assert.assertNotNull(sessionId); + Assert.assertNotNull(stalkerFuture); + Assert.assertEquals(0, stalkerFuture.getEndNanoTime()); - while (Stalker.isRunning(sessionId)) { - List results = Stalker.query(sessionId); + while (!stalkerFuture.isDone()) { + List results = stalkerFuture.get(); Assert.assertNotNull(results.get(0)); Thread.sleep(500L); } log.info("任务已完成,获取最后的执行结果,并移除任务记录."); - Stalker.query(sessionId); - - // 执行完成之后移除 sessionId. - Stalker.remove(sessionId); + stalkerFuture.get(); + Assert.assertTrue(stalkerFuture.getStartNanoTime() > 0); + Assert.assertTrue(stalkerFuture.isDoneSuccessfully()); + Assert.assertEquals(stalkerFuture.getTotal(), stalkerFuture.getSuccess() + stalkerFuture.getFailure()); + Assert.assertTrue(stalkerFuture.getCosts() > 0); } /** @@ -200,16 +197,17 @@ public void submitWithSlowMethodDuration() throws InterruptedException { */ @Test public void queryMeasurement() throws InterruptedException { - String sessionId = Stalker.submit(() -> new MyTestService().hello()); - Assert.assertNotNull(sessionId); + StalkerFuture stalkerFuture = Stalker.submit(() -> new MyTestService().hello()); + Assert.assertNotNull(stalkerFuture); - while (Stalker.isRunning(sessionId)) { - Measurement measurement = Stalker.queryMeasurement(sessionId); + while (!stalkerFuture.isDone()) { + Measurement measurement = stalkerFuture.getMeasurement(); Assert.assertNotNull(measurement); Thread.sleep(5L); } - // 执行完成之后移除 sessionId. - Stalker.remove(sessionId); + + Assert.assertFalse(stalkerFuture.isCancelled()); + Assert.assertNotNull(stalkerFuture.getOverallResult()); } /** @@ -217,32 +215,20 @@ public void queryMeasurement() throws InterruptedException { */ @Test public void submitWithStop() throws InterruptedException { - String sessionId = Stalker.submit(Options.of("StopTest", 20, 5, 1), + StalkerFuture stalkerFuture = Stalker.submit(Options.of("StopTest", 20, 5, 1), () -> new MyTestService().slowHello()); - Assert.assertNotNull(sessionId); + Assert.assertNotNull(stalkerFuture); Thread.sleep(50L); - List results = Stalker.query(sessionId); + List results = stalkerFuture.get(); Assert.assertNotNull(results.get(0)); - Stalker.stop(sessionId); + stalkerFuture.cancel(); log.info("任务已停止,获取停止前的执行结果."); - Stalker.query(sessionId); + stalkerFuture.get(); Thread.sleep(100L); - log.info("任务已停止,获取最后的执行结果."); - Stalker.query(sessionId); - - // 执行完成之后移除 sessionId. - Stalker.remove(sessionId); - } - - /** - * 单测结束后执行的方法. - */ - @AfterClass - public static void destroy() { - Assert.assertNotNull(Stalker.getAllSessions()); - Stalker.clear(); + log.info("任务已停止,再次获取最后的执行结果,判断内容是否一致."); + stalkerFuture.get(); } } From 87fd60c58dbd69826c4faaa732175068dd07d031 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Wed, 3 Jun 2020 15:23:34 +0800 Subject: [PATCH 25/37] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=BA=86=20Stalker=20?= =?UTF-8?q?=E7=9A=84=E7=9B=B8=E5=85=B3=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blinkfox/stalker/config/RunDuration.java | 2 +- .../stalker/result/bean/RunnerInfo.java | 28 ------------------- .../runner/ConcurrentMeasureRunner.java | 13 ++------- .../ConcurrentScheduledMeasureRunner.java | 4 +-- .../runner/executor/StalkerExecutors.java | 2 +- 5 files changed, 7 insertions(+), 42 deletions(-) delete mode 100644 src/main/java/com/blinkfox/stalker/result/bean/RunnerInfo.java diff --git a/src/main/java/com/blinkfox/stalker/config/RunDuration.java b/src/main/java/com/blinkfox/stalker/config/RunDuration.java index 6f217e2..a6fbaa4 100644 --- a/src/main/java/com/blinkfox/stalker/config/RunDuration.java +++ b/src/main/java/com/blinkfox/stalker/config/RunDuration.java @@ -97,7 +97,7 @@ public static RunDuration ofDays(long amount) { * @return 结束纳秒时间 */ public long getEndNanoTime(long startNanoTime) { - switch(this.timeUnit) { + switch (this.timeUnit) { case NANOSECONDS : return startNanoTime + amount; case MICROSECONDS : diff --git a/src/main/java/com/blinkfox/stalker/result/bean/RunnerInfo.java b/src/main/java/com/blinkfox/stalker/result/bean/RunnerInfo.java deleted file mode 100644 index a89f374..0000000 --- a/src/main/java/com/blinkfox/stalker/result/bean/RunnerInfo.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.blinkfox.stalker.result.bean; - -import com.blinkfox.stalker.config.Options; -import com.blinkfox.stalker.runner.MeasureRunner; -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 可运行任务的相关的信息实体. - * - * @author blinkfox on 2020-05-24. - * @since v1.2.0 - */ -@Getter -@AllArgsConstructor -public class RunnerInfo { - - /** - * 可运行任务的选项参数信息. - */ - private final Options options; - - /** - * 任务运行的 {@link MeasureRunner} 实例. - */ - private final MeasureRunner measureRunner; - -} diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java index 25ff900..ab11fe3 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java @@ -8,7 +8,6 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Semaphore; import lombok.extern.slf4j.Slf4j; @@ -21,11 +20,6 @@ @Slf4j public class ConcurrentMeasureRunner extends AbstractMeasureRunner { - /** - * 用于异步移除已经执行完成的线程的后台任务线程池. - */ - protected final ExecutorService recordExecutorService; - /** * 用于存放正在运行中的 Future 线程,便于在手动"停止"运行时,能取消正在执行中的任务. */ @@ -38,7 +32,6 @@ public class ConcurrentMeasureRunner extends AbstractMeasureRunner { */ public ConcurrentMeasureRunner() { super(); - this.recordExecutorService = StalkerExecutors.newSingleThreadExecutor("concurrent-record-thread"); this.runningFutures = new ConcurrentHashSet<>(); } @@ -74,7 +67,7 @@ public OverallResult run(Options options, Runnable runnable) { // 将 future 添加到正在运行的 Future 信息集合中,并在 future 完成时,异步移除已经完成了的 future. runningFutures.add(future); - future.whenCompleteAsync((a, b) -> runningFutures.remove(future), this.recordExecutorService); + future.whenCompleteAsync((a, b) -> runningFutures.remove(future)); } catch (InterruptedException e) { log.error("【Stalker 错误提示】在多线程并发情况下测量任务执行的耗时信息的线程已被中断!", e); Thread.currentThread().interrupt(); @@ -85,7 +78,7 @@ public OverallResult run(Options options, Runnable runnable) { this.await(countLatch); super.setEndNanoTimeIfEmpty(System.nanoTime()); super.completed.compareAndSet(false, true); - StalkerExecutors.shutdown(this.executorService, this.recordExecutorService); + StalkerExecutors.shutdown(this.executorService); return super.buildFinalMeasurement(); } @@ -130,7 +123,7 @@ public void stop() { super.canceled.compareAndSet(false, true); // 停止时直接关闭线程池. - StalkerExecutors.shutdownNow(this.executorService, this.recordExecutorService); + StalkerExecutors.shutdownNow(this.executorService); // 迭代删除正在运行中的 Future,并取消正在运行中的任务. Iterator> futureIterator = this.runningFutures.iterator(); diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java index ec5a470..b845e75 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java @@ -84,7 +84,7 @@ public OverallResult run(Options options, Runnable runnable) { // 将 future 添加到正在运行的 Future 信息集合中,并在 future 完成时,异步移除已经完成了的 future. runningFutures.add(future); - future.whenCompleteAsync((a, b) -> runningFutures.remove(future), super.recordExecutorService); + future.whenCompleteAsync((a, b) -> runningFutures.remove(future)); } catch (InterruptedException e) { log.error("【Stalker 错误提示】在多线程并发情况下测量任务执行的耗时信息的线程已被中断!", e); Thread.currentThread().interrupt(); @@ -94,7 +94,7 @@ public OverallResult run(Options options, Runnable runnable) { // 等待所有线程执行完毕,记录是否完成和完成时间,并关闭线程池等资源,最后将结果封装成实体信息返回. super.setEndNanoTimeIfEmpty(System.nanoTime()); super.completed.compareAndSet(false, true); - StalkerExecutors.shutdown(this.executorService, this.recordExecutorService, this.scheduledExecutorService); + StalkerExecutors.shutdown(this.executorService, this.scheduledExecutorService); if (!this.scheduledFuture.isDone()) { this.scheduledFuture.cancel(true); } diff --git a/src/main/java/com/blinkfox/stalker/runner/executor/StalkerExecutors.java b/src/main/java/com/blinkfox/stalker/runner/executor/StalkerExecutors.java index a3aa656..0013b72 100644 --- a/src/main/java/com/blinkfox/stalker/runner/executor/StalkerExecutors.java +++ b/src/main/java/com/blinkfox/stalker/runner/executor/StalkerExecutors.java @@ -18,7 +18,7 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class StalkerExecutors { - private static final int MAX_QUEUE_SIZE = 65535; + private static final int MAX_QUEUE_SIZE = 524280; public static final int MAX_POOL_SIZE = 1024; From 40a67625936a1811ca3e4d20bdffb60cb4cbe734 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Thu, 4 Jun 2020 01:09:03 +0800 Subject: [PATCH 26/37] =?UTF-8?q?=E5=B0=8F=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stalker/result/bean/StatisResult.java | 14 ++++++++++++++ .../stalker/runner/AbstractMeasureRunner.java | 12 ++++++++++++ .../blinkfox/stalker/runner/MeasureRunner.java | 15 +++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/src/main/java/com/blinkfox/stalker/result/bean/StatisResult.java b/src/main/java/com/blinkfox/stalker/result/bean/StatisResult.java index 85eccbd..7609032 100644 --- a/src/main/java/com/blinkfox/stalker/result/bean/StatisResult.java +++ b/src/main/java/com/blinkfox/stalker/result/bean/StatisResult.java @@ -19,6 +19,13 @@ public class StatisResult { */ private long sum; + /** + * 总次数. + * + * @since v1.2.0 + */ + private long total; + /** * 平均耗时. */ @@ -34,6 +41,13 @@ public class StatisResult { */ private long max; + /** + * 方差和,该值表示各个值与平均值的差的平方和. + * + * @since v1.2.0 + */ + private double varSum; + /** * 标准差. */ diff --git a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java index e5c1974..1097b3d 100644 --- a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java @@ -2,6 +2,7 @@ import com.blinkfox.stalker.kit.MathKit; import com.blinkfox.stalker.result.bean.OverallResult; +import com.blinkfox.stalker.result.bean.StatisResult; import java.util.ArrayDeque; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; @@ -25,11 +26,22 @@ @Slf4j public abstract class AbstractMeasureRunner implements MeasureRunner { + /** + * 每隔 10 万,累计一次统计计数,并清空 {@code eachMeasures} 队列中的数据,防止内存溢出. + */ + protected static final int MAX_PERIOD_COUNT = 100000; + /** * 线程池. */ protected ExecutorService executorService; + /** + * 直到最后一次累计构建的统计结果信息. + */ + @Getter + protected StatisResult lastStatisResult; + /** * 每次'成功'测量出的待测量方法的耗时时间,单位为纳秒({@code ns}). */ diff --git a/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java index 74463d0..5d6e013 100644 --- a/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java @@ -2,6 +2,7 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.result.bean.OverallResult; +import com.blinkfox.stalker.result.bean.StatisResult; /** * 用于测量待执行方法耗时情况等信息的运行器接口. @@ -103,6 +104,9 @@ public interface MeasureRunner { /** * 停止相关的运行测量任务. + * + * @author blinkfox on 2020-06-03. + * @since v1.2.0 */ void stop(); @@ -110,6 +114,8 @@ public interface MeasureRunner { * 构建运行中的任务的总体测量结果信息. * * @return 总体测量结果信息 + * @author blinkfox on 2020-05-23. + * @since v1.2.0 */ OverallResult buildRunningMeasurement(); @@ -122,4 +128,13 @@ public interface MeasureRunner { */ OverallResult buildFinalMeasurement(); + /** + * 获取直到最后一次累计构建的统计结果信息. + * + * @return 直到最后一次累计构建的统计结果信息 + * @author blinkfox on 2020-06-04. + * @since v1.2.0 + */ + StatisResult getLastStatisResult(); + } From 389bdd3211ce8d7309c2596b1f63817dd9e5b1d1 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Fri, 5 Jun 2020 01:16:49 +0800 Subject: [PATCH 27/37] =?UTF-8?q?=E9=83=A8=E5=88=86=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stalker/result/MeasureStatistician.java | 206 ++++++++++++++++++ .../stalker/result/bean/StatisResult.java | 35 ++- .../result/statis/DefaultMeasureStatis.java | 3 +- .../stalker/result/statis/MeasureStatis.java | 22 -- .../stalker/runner/AbstractMeasureRunner.java | 96 +++----- .../stalker/runner/MeasureRunner.java | 71 +----- .../stalker/runner/SimpleMeasureRunner.java | 6 +- 7 files changed, 270 insertions(+), 169 deletions(-) create mode 100644 src/main/java/com/blinkfox/stalker/result/MeasureStatistician.java delete mode 100644 src/main/java/com/blinkfox/stalker/result/statis/MeasureStatis.java diff --git a/src/main/java/com/blinkfox/stalker/result/MeasureStatistician.java b/src/main/java/com/blinkfox/stalker/result/MeasureStatistician.java new file mode 100644 index 0000000..d52c5f0 --- /dev/null +++ b/src/main/java/com/blinkfox/stalker/result/MeasureStatistician.java @@ -0,0 +1,206 @@ +package com.blinkfox.stalker.result; + +import com.blinkfox.stalker.kit.MathKit; +import com.blinkfox.stalker.result.bean.StatisResult; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.locks.StampedLock; + +/** + * 针对测量出的消耗时间数据进行统计的统计器类. + * + * @author blinkfox on 2020-06-05. + * @since v1.2.0 + */ +public class MeasureStatistician { + + /** + * 95% 置信区间的 Z 值. + */ + private static final double Z = 1.96; + + /** + * 用来表示存放每次花费时间的数组容量的阈值常量,默认 10 万. + */ + private static final int THRESHOLD = 100_000; + + /** + * 用来高效读写数据的锁. + */ + private final StampedLock stampedLock = new StampedLock(); + + /** + * 用来记录每次测量出的待测量方法的消耗时间的集合,单位为纳秒(ns), + * 当集合中的数量超过设定的阈值时,就会清空本集合中的数据,防止内存移除. + */ + private final List eachCosts = new ArrayList<>(3325); + + /** + * 测量代码在执行过程中所消耗的总耗时,单位为纳秒(ns). + */ + private long costs; + + /** + * 总次数. + */ + private long total; + + /** + * 测量过程中执行成功的次数. + */ + private long success; + + /** + * 测量过程中执行失败的次数. + */ + private long failure; + + /** + * 吞吐率,指单位时间内(每秒)的执行总次数,即:{@code throughput = total / (costs / 10^9)}. + */ + private double throughput; + + /** + * 总耗时. + */ + private long sum; + + /** + * 平均耗时. + */ + private long avg; + + /** + * 最小耗时. + */ + private long min; + + /** + * 最大耗时. + */ + private long max; + + /** + * 方差和,该值表示各个值与平均值的差的平方和. + * + * @since v1.2.0 + */ + private double varSum; + + /** + * 标准差. + */ + private double stdDev; + + /** + * 95%置信区间下限. + */ + private double lowerConfidence; + + /** + * 95%置信区间上限. + */ + private double upperConfidence; + + /** + * 更新最新的统计数据. + * + *

为了防止多个线程更新数据时的线程安全问题,这里加了写锁.

+ * + * @param currSuccess 当前累计的成功运行次数 + * @param currFailure 当前累计的失败运行次数 + * @param currCosts 当前累计的总的运行时间 + * @param currEachCosts 从上次更新到本次更新期间的每次运行次数的花费时间 + */ + public StatisResult updateAndGet(long currSuccess, long currFailure, long currCosts, Long[] currEachCosts) { + long stamp = stampedLock.writeLock(); + try { + // 对基础统计数据进行赋值. + this.success = currSuccess; + this.failure = currFailure; + this.total = this.success + this.failure; + this.costs = currCosts; + this.throughput = MathKit.calcThroughput(this.total, this.costs); + + // 遍历求得所有测量值的和,最大值,最小值和平均值. + for (Long cost : currEachCosts) { + eachCosts.add(cost); + this.sum += cost; + if (this.min > cost) { + this.min = cost; + } + if (this.max < cost) { + this.max = cost; + } + } + this.avg = this.sum / this.total; + + // 计算方差所需的当前所有数据的平方差之和. + double currVarSum = this.varSum; + for (long measure : eachCosts) { + currVarSum += Math.pow(measure - (double) avg, 2); + } + + // 分别计算出标准差和95%的置信区间半径, + // 由于数据超过阈值之后,之前的每次花费时间的值会清空,而新的花费时间的平均值发生了变化, + // 因此,计算的总的平法差之和并不准确,从而导致标准差也不准确,但是在大数据情况时,这些误差可以容忍. + this.stdDev = Math.sqrt(currVarSum / this.total); + double radius = (Z * this.stdDev) / Math.sqrt(this.total); + this.lowerConfidence = this.avg - radius; + this.upperConfidence = this.avg + radius; + + // 当数据量超过阈值之后,为了防止后续程序继续运行时内存溢出,就清空 eachCosts 集合中的数据. + // 并记录当前累计的平法差之和,便于以后再累计,计算方差时使用. + if (this.eachCosts.size() > THRESHOLD) { + this.eachCosts.clear(); + this.varSum = currVarSum; + } + } finally { + stampedLock.unlockWrite(stamp); + } + return this.get(); + } + + /** + * 读取最新的统计结果信息. + * + *

这里先使用乐观读的方式读取数据,如果读取期间数据发生了变化,就采用悲观读的方式来再次读取最新的数据到新的结果变量中.

+ * + * @return 统计结果信息 + */ + public StatisResult get() { + StatisResult statisResult = new StatisResult(); + long stamp = stampedLock.tryOptimisticRead(); + this.copyStatisResultData(statisResult); + if (!stampedLock.validate(stamp)) { + stamp = stampedLock.readLock(); + try { + this.copyStatisResultData(statisResult); + } finally { + stampedLock.unlockRead(stamp); + } + } + return statisResult; + } + + /** + * 复制当前对象中的最新数据到新创建的 StatisResult 对象中. + * + * @param statisResult 新创建的 StatisResult 对象 + */ + private void copyStatisResultData(StatisResult statisResult) { + statisResult.setCosts(this.costs); + statisResult.setTotal(this.total); + statisResult.setSuccess(this.success); + statisResult.setFailure(this.failure); + statisResult.setThroughput(this.throughput); + statisResult.setSum(this.sum); + statisResult.setAvg(this.avg); + statisResult.setMin(this.min); + statisResult.setMax(this.max); + statisResult.setStdDev(this.stdDev); + statisResult.setLowerConfidence(this.lowerConfidence); + statisResult.setUpperConfidence(this.upperConfidence); + } + +} diff --git a/src/main/java/com/blinkfox/stalker/result/bean/StatisResult.java b/src/main/java/com/blinkfox/stalker/result/bean/StatisResult.java index 7609032..5bcee58 100644 --- a/src/main/java/com/blinkfox/stalker/result/bean/StatisResult.java +++ b/src/main/java/com/blinkfox/stalker/result/bean/StatisResult.java @@ -15,17 +15,37 @@ public class StatisResult { /** - * 总耗时. + * 测量代码在执行过程中所消耗的总耗时,单位为纳秒(ns). */ - private long sum; + private long costs; /** * 总次数. - * - * @since v1.2.0 */ private long total; + /** + * 测量过程中执行成功的次数. + */ + private long success; + + /** + * 测量过程中执行失败的次数. + */ + private long failure; + + /** + * 吞吐率,指单位时间内(每秒)的执行总次数,即:{@code throughput = total / (costs / 10^9)}. + * + * @since v1.1.1 + */ + private double throughput; + + /** + * 总耗时. + */ + private long sum; + /** * 平均耗时. */ @@ -41,13 +61,6 @@ public class StatisResult { */ private long max; - /** - * 方差和,该值表示各个值与平均值的差的平方和. - * - * @since v1.2.0 - */ - private double varSum; - /** * 标准差. */ diff --git a/src/main/java/com/blinkfox/stalker/result/statis/DefaultMeasureStatis.java b/src/main/java/com/blinkfox/stalker/result/statis/DefaultMeasureStatis.java index eba2dab..8aba4c2 100644 --- a/src/main/java/com/blinkfox/stalker/result/statis/DefaultMeasureStatis.java +++ b/src/main/java/com/blinkfox/stalker/result/statis/DefaultMeasureStatis.java @@ -9,7 +9,7 @@ * @author blinkfox on 2019-01-10. * @since v1.0.0 */ -public class DefaultMeasureStatis implements MeasureStatis { +public class DefaultMeasureStatis { /** * 95%置信区间的 Z 值. @@ -22,7 +22,6 @@ public class DefaultMeasureStatis implements MeasureStatis { * @param overallResult 测量结果数据 * @return 性能结果数据 */ - @Override public StatisResult statis(OverallResult overallResult) { StatisResult statisResult = new StatisResult(); long[] eachMeasures = overallResult.getEachMeasures(); diff --git a/src/main/java/com/blinkfox/stalker/result/statis/MeasureStatis.java b/src/main/java/com/blinkfox/stalker/result/statis/MeasureStatis.java deleted file mode 100644 index d828c54..0000000 --- a/src/main/java/com/blinkfox/stalker/result/statis/MeasureStatis.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.blinkfox.stalker.result.statis; - -import com.blinkfox.stalker.result.bean.OverallResult; -import com.blinkfox.stalker.result.bean.StatisResult; - -/** - * 测量结果统计接口. - * - * @author blinkfox on 2019-01-10. - * @since v1.0.0 - */ -public interface MeasureStatis { - - /** - * 将测量结果数据做统计分析,得出性能结果数据. - * - * @param overallResult 测量结果数据 - * @return 性能结果数据 - */ - StatisResult statis(OverallResult overallResult); - -} \ No newline at end of file diff --git a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java index 1097b3d..bf128f1 100644 --- a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java @@ -1,9 +1,7 @@ package com.blinkfox.stalker.runner; -import com.blinkfox.stalker.kit.MathKit; -import com.blinkfox.stalker.result.bean.OverallResult; +import com.blinkfox.stalker.result.MeasureStatistician; import com.blinkfox.stalker.result.bean.StatisResult; -import java.util.ArrayDeque; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; @@ -27,14 +25,14 @@ public abstract class AbstractMeasureRunner implements MeasureRunner { /** - * 每隔 10 万,累计一次统计计数,并清空 {@code eachMeasures} 队列中的数据,防止内存溢出. + * 线程池. */ - protected static final int MAX_PERIOD_COUNT = 100000; + protected ExecutorService executorService; /** - * 线程池. + * 测量统计器的实例对象. */ - protected ExecutorService executorService; + private final MeasureStatistician measureStatistician; /** * 直到最后一次累计构建的统计结果信息. @@ -79,10 +77,16 @@ public abstract class AbstractMeasureRunner implements MeasureRunner { @Getter protected long endNanoTime; + /** + * 用于记录以前总共读取了 eachMeasures 中的数据总量,即总的偏移量. + */ + private long beforeCount; + /** * 公共的抽象父构造方法. */ public AbstractMeasureRunner() { + this.measureStatistician = new MeasureStatistician(); this.eachMeasures = new ConcurrentLinkedQueue<>(); this.success = new LongAdder(); this.failure = new LongAdder(); @@ -90,32 +94,14 @@ public AbstractMeasureRunner() { this.canceled = new AtomicBoolean(false); } - @Override - public long[] getEachMeasures() { - // 为了不影响正在运行中的数据及当前或以后数据统计的"准确性",这里复制一份队列中的数据来单独计算和返回. - Queue queue = new ArrayDeque<>(this.eachMeasures); - int len = queue.size(); - long[] measures = new long[len]; - for (int i = 0; i < len; i++) { - Long cost = queue.poll(); - if (cost != null) { - measures[i] = cost; - } - } - return measures; - } - - @Override public long getTotal() { return this.success.longValue() + this.failure.longValue(); } - @Override public long getSuccess() { return this.success.longValue(); } - @Override public long getFailure() { return this.failure.longValue(); } @@ -130,11 +116,6 @@ public boolean isCancelled() { return this.canceled.get(); } - @Override - public long getCosts() { - return this.endNanoTime - this.startNanoTime; - } - /** * 如果结束时间的值是 0,那么就设置结束时的纳秒时间. * @@ -147,50 +128,35 @@ public void setEndNanoTimeIfEmpty(long endNanoTime) { } /** - * 构造正在运行中的任务的测量结果信息的 {@link OverallResult} 对象. + * 更新并获取统计结果信息数据,由于可能会有两个或多个线程去更新和获取统计数据,这里使用 {@code synchronized} 来同步. * - * @return 总体测量结果信息 + * @return 统计结果信息 */ @Override - public OverallResult buildRunningMeasurement() { + public synchronized StatisResult getStatisResult() { // 如果任务已经完成,就直接返回最终的测试结果数据即可. if (this.completed.get()) { - return this.buildFinalMeasurement(); + return measureStatistician.get(); } - // 如果任务仍然在运行中,由于各个计数器是独立的,对于整体上的各个统计数据的结果来说,并不能保证"线程安全". - // 为了减小仍在运行中时的任务,获取各个统计数据时线程安全所导致的误差. - // 这里只获取 eachMeasures 和 failure 两个值,基于这两个值来计算其他值,消耗的时间使用当前时间来计算. - final long currFailure = this.getFailure(); - final long[] currEachCosts = this.getEachMeasures(); - long currTotalCount = currEachCosts.length; + // 获取到截至到当前时间的正确运行次数数、错误运行次数,消耗的时间和每次的运行时间等数据. + long currFailure = this.getFailure(); + long currSuccess = this.getSuccess(); long currCosts = this.startNanoTime == 0 ? 0 : System.nanoTime() - this.startNanoTime; - return new OverallResult() - .setEachMeasures(currEachCosts) - .setCosts(currCosts) - .setTotal(currTotalCount) - .setSuccess(currTotalCount - currFailure) - .setFailure(currFailure) - .setThroughput(MathKit.calcThroughput(currTotalCount, currCosts)); - } + int len = (int) (currSuccess + currFailure - beforeCount); - /** - * 构造最终运行完时的测量的结果信息的 {@link OverallResult} 对象. - * - * @return 总体测量结果信息 - */ - @Override - public OverallResult buildFinalMeasurement() { - long successCount = this.success.longValue(); - long failureCount = this.failure.longValue(); - long totalCount = successCount + failureCount; - return new OverallResult() - .setEachMeasures(this.getEachMeasures()) - .setCosts(this.getCosts()) - .setTotal(totalCount) - .setSuccess(successCount) - .setFailure(failureCount) - .setThroughput(MathKit.calcThroughput(totalCount, this.getCosts())); + // 截取复制出最新的 + Long[] currMeasures = new Long[len]; + for (int i = 0; i < len; ++i) { + Long cost = this.eachMeasures.poll(); + if (cost != null) { + currMeasures[i] = cost; + } + } + beforeCount = currSuccess + currFailure; + + // 更新并获取最新的数据. + return measureStatistician.updateAndGet(currSuccess, currFailure, currCosts, currMeasures); } } diff --git a/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java index 5d6e013..ad0dba0 100644 --- a/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java @@ -21,42 +21,6 @@ public interface MeasureRunner { */ OverallResult run(Options options, Runnable runnable); - /** - * 获取当前运行任务中,每次执行的耗时情况的长整形数组. - * - * @return 每次执行耗时情况的长整形数组 - * @author blinkfox on 2020-05-23. - * @since v1.2.0 - */ - long[] getEachMeasures(); - - /** - * 获取当前总共的运行次数. - * - * @return 运行总次数 - * @author blinkfox on 2020-05-23. - * @since v1.2.0 - */ - long getTotal(); - - /** - * 获取当前运行成功的次数. - * - * @return 运行成功的次数 - * @author blinkfox on 2020-05-23. - * @since v1.2.0 - */ - long getSuccess(); - - /** - * 获取当前运行失败的次数. - * - * @return 运行失败的次数 - * @author blinkfox on 2020-05-23. - * @since v1.2.0 - */ - long getFailure(); - /** * 判断当前任务是否已经执行完成. * @@ -93,15 +57,6 @@ public interface MeasureRunner { */ long getEndNanoTime(); - /** - * 获取任务最终完成时实际所消耗的总时间. - * - * @return 实际所消耗的总时间 - * @author blinkfox on 2020-05-23. - * @since v1.2.0 - */ - long getCosts(); - /** * 停止相关的运行测量任务. * @@ -111,30 +66,12 @@ public interface MeasureRunner { void stop(); /** - * 构建运行中的任务的总体测量结果信息. - * - * @return 总体测量结果信息 - * @author blinkfox on 2020-05-23. - * @since v1.2.0 - */ - OverallResult buildRunningMeasurement(); - - /** - * 构建运行任务完成后的最终总体测量结果信息. - * - * @return 总体测量结果信息 - * @author blinkfox on 2020-05-23. - * @since v1.2.0 - */ - OverallResult buildFinalMeasurement(); - - /** - * 获取直到最后一次累计构建的统计结果信息. + * 获取运行中的任务的统计结果信息. * - * @return 直到最后一次累计构建的统计结果信息 - * @author blinkfox on 2020-06-04. + * @return 统计结果信息 + * @author blinkfox on 2020-06-05. * @since v1.2.0 */ - StatisResult getLastStatisResult(); + StatisResult getStatisResult(); } diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java index c7f89c4..52f74af 100644 --- a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java @@ -51,7 +51,7 @@ public OverallResult run(Options options, Runnable runnable) { // 开始执行测量任务,记录开始时间、执行次数等. long eachStart = System.nanoTime(); runnable.run(); - super.eachMeasures.add(System.nanoTime() - eachStart); + super.eachMeasures.offer(System.nanoTime() - eachStart); super.success.increment(); } catch (Exception e) { super.failure.increment(); @@ -73,7 +73,9 @@ public OverallResult run(Options options, Runnable runnable) { super.setEndNanoTimeIfEmpty(System.nanoTime()); super.completed.compareAndSet(false, true); StalkerExecutors.shutdown(super.executorService); - return super.buildFinalMeasurement(); + // TODO 待完成. + // return super.buildFinalMeasurement(); + return null; } /** From 6773637421f645151d3f0f41d2adf35b772473f3 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Sat, 6 Jun 2020 18:10:56 +0800 Subject: [PATCH 28/37] =?UTF-8?q?=E5=A4=A7=E9=87=8F=E7=9A=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/blinkfox/stalker/Assert.java | 19 +- .../java/com/blinkfox/stalker/Stalker.java | 10 +- .../blinkfox/stalker/config/RunDuration.java | 11 + .../java/com/blinkfox/stalker/kit/StrKit.java | 29 +++ .../stalker/output/MeasureOutput.java | 4 +- .../stalker/output/MeasureOutputContext.java | 8 +- .../stalker/output/OutputConsole.java | 38 ++-- .../stalker/result/MeasureStatistician.java | 204 ++++++------------ .../stalker/result/MeasurementCollector.java | 57 ----- .../stalker/result/StalkerFuture.java | 29 +-- .../blinkfox/stalker/result/StatisResult.java | 183 ++++++++++++++++ .../stalker/result/bean/EasyReadResult.java | 78 ------- .../stalker/result/bean/Measurement.java | 40 ---- .../stalker/result/bean/OverallResult.java | 67 ------ .../stalker/result/bean/StatisResult.java | 92 -------- .../result/statis/DefaultMeasureStatis.java | 92 -------- .../stalker/runner/AbstractMeasureRunner.java | 128 ++++++++--- .../runner/ConcurrentMeasureRunner.java | 10 +- .../ConcurrentScheduledMeasureRunner.java | 8 +- .../stalker/runner/MeasureRunner.java | 43 +++- .../stalker/runner/MeasureRunnerContext.java | 20 +- .../stalker/runner/SimpleMeasureRunner.java | 15 +- .../runner/SimpleScheduledMeasureRunner.java | 15 +- .../blinkfox/stalker/test/StalkerTest.java | 19 +- .../test/output/MeasureOutputContextTest.java | 9 +- .../test/output/OutputConsoleTest.java | 5 +- .../test/result/bean/MeasurementTest.java | 39 ---- .../test/result/bean/OverallResultTest.java | 25 --- .../test/result/bean/StatisResultTest.java | 2 +- .../statis/DefaultMeasureStatisTest.java | 25 --- 30 files changed, 516 insertions(+), 808 deletions(-) delete mode 100644 src/main/java/com/blinkfox/stalker/result/MeasurementCollector.java create mode 100644 src/main/java/com/blinkfox/stalker/result/StatisResult.java delete mode 100644 src/main/java/com/blinkfox/stalker/result/bean/EasyReadResult.java delete mode 100644 src/main/java/com/blinkfox/stalker/result/bean/Measurement.java delete mode 100644 src/main/java/com/blinkfox/stalker/result/bean/OverallResult.java delete mode 100644 src/main/java/com/blinkfox/stalker/result/bean/StatisResult.java delete mode 100644 src/main/java/com/blinkfox/stalker/result/statis/DefaultMeasureStatis.java delete mode 100644 src/test/java/com/blinkfox/stalker/test/result/bean/MeasurementTest.java delete mode 100644 src/test/java/com/blinkfox/stalker/test/result/bean/OverallResultTest.java delete mode 100644 src/test/java/com/blinkfox/stalker/test/result/statis/DefaultMeasureStatisTest.java diff --git a/src/main/java/com/blinkfox/stalker/Assert.java b/src/main/java/com/blinkfox/stalker/Assert.java index 4c06299..c507e28 100644 --- a/src/main/java/com/blinkfox/stalker/Assert.java +++ b/src/main/java/com/blinkfox/stalker/Assert.java @@ -29,26 +29,9 @@ public void fail() { */ public void assertFaster(Options options, Runnable runnable1, Runnable runnable2) { MeasureRunnerContext runnerContext = new MeasureRunnerContext(options); - if (calcSum(runnerContext.run(runnable1).getEachMeasures()) - >= calcSum(runnerContext.run(runnable2).getEachMeasures())) { + if (runnerContext.run(runnable1).getSum() >= runnerContext.run(runnable2).getSum()) { fail(); } } - /** - * 计算long数组中的总和. - * - * @param arr long数组 - * @return 总和 - */ - private long calcSum(long[] arr) { - int sum = 0; - if (arr != null && arr.length > 0) { - for (long n : arr) { - sum += n; - } - } - return sum; - } - } diff --git a/src/main/java/com/blinkfox/stalker/Stalker.java b/src/main/java/com/blinkfox/stalker/Stalker.java index 3ba2196..f54ad0b 100644 --- a/src/main/java/com/blinkfox/stalker/Stalker.java +++ b/src/main/java/com/blinkfox/stalker/Stalker.java @@ -3,7 +3,7 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.output.MeasureOutputContext; import com.blinkfox.stalker.result.StalkerFuture; -import com.blinkfox.stalker.result.bean.Measurement; +import com.blinkfox.stalker.result.StatisResult; import com.blinkfox.stalker.runner.MeasureRunnerContext; import java.util.List; import lombok.experimental.UtilityClass; @@ -71,21 +71,21 @@ public List run(Options options, Runnable... runnables) { * 测量要执行的各个代码的性能并输出统计数据的结果数组. * * @param options 参数选项 - * @param runnables runnable + * @param runnables 可运行的任务 * @return 各个运行结果统计数据的数组 * @author blinkfox on 2020-05-14 * @since v1.1.0 */ - public Measurement[] runStatis(Options options, Runnable... runnables) { + public StatisResult[] runStatis(Options options, Runnable... runnables) { int len; if (options == null || runnables == null || (len = runnables.length) == 0) { throw new IllegalArgumentException("【Stalker 参数异常】options or runnables is null (or empty)!"); } // 循环遍历测量各个 Runnable 实例的性能结果,然后将各个结果存放到数组中,最后统一输出出来. - Measurement[] measurements = new Measurement[len]; + StatisResult[] measurements = new StatisResult[len]; for (int i = 0; i < len; i++) { - measurements[i] = new MeasureRunnerContext(options).runAndCollect(runnables[i]); + measurements[i] = new MeasureRunnerContext(options).run(runnables[i]); } return measurements; } diff --git a/src/main/java/com/blinkfox/stalker/config/RunDuration.java b/src/main/java/com/blinkfox/stalker/config/RunDuration.java index a6fbaa4..49c068e 100644 --- a/src/main/java/com/blinkfox/stalker/config/RunDuration.java +++ b/src/main/java/com/blinkfox/stalker/config/RunDuration.java @@ -1,5 +1,6 @@ package com.blinkfox.stalker.config; +import com.blinkfox.stalker.kit.StrKit; import java.util.concurrent.TimeUnit; import lombok.Getter; @@ -136,4 +137,14 @@ private static void checkAmount(long amount) { } } + /** + * 将对象转换为字符串. + * + * @return 字符串 + */ + @Override + public String toString() { + return StrKit.convertTimeUnit(this.amount, this.timeUnit); + } + } diff --git a/src/main/java/com/blinkfox/stalker/kit/StrKit.java b/src/main/java/com/blinkfox/stalker/kit/StrKit.java index f0063e6..bee1153 100644 --- a/src/main/java/com/blinkfox/stalker/kit/StrKit.java +++ b/src/main/java/com/blinkfox/stalker/kit/StrKit.java @@ -3,6 +3,7 @@ import java.math.BigDecimal; import java.math.RoundingMode; import java.util.UUID; +import java.util.concurrent.TimeUnit; import lombok.experimental.UtilityClass; import org.slf4j.helpers.MessageFormatter; @@ -78,6 +79,34 @@ public String convertTime(Number n) { } } + /** + * 将纳秒的时间转为与其最贴近的时间单位. + * + * @param amount 数量 + * @param timeUnit 时间单位 + * @return 其他单位的时间字符串 + */ + public String convertTimeUnit(long amount, TimeUnit timeUnit) { + switch (timeUnit) { + case NANOSECONDS : + return amount + " ns"; + case MICROSECONDS : + return amount + " μs"; + case MILLISECONDS : + return amount + " ms"; + case SECONDS: + return amount + " s"; + case MINUTES: + return amount + " m"; + case HOURS: + return amount + " h"; + case DAYS: + return amount + " d"; + default : + return String.valueOf(amount); + } + } + /** * 根据double的值和单位,拼接其对应的四舍五入的字符串值. * diff --git a/src/main/java/com/blinkfox/stalker/output/MeasureOutput.java b/src/main/java/com/blinkfox/stalker/output/MeasureOutput.java index 48ee20b..27b9401 100644 --- a/src/main/java/com/blinkfox/stalker/output/MeasureOutput.java +++ b/src/main/java/com/blinkfox/stalker/output/MeasureOutput.java @@ -1,7 +1,7 @@ package com.blinkfox.stalker.output; import com.blinkfox.stalker.config.Options; -import com.blinkfox.stalker.result.bean.Measurement; +import com.blinkfox.stalker.result.StatisResult; /** * 将最终的测量统计结果输出出来. @@ -18,6 +18,6 @@ public interface MeasureOutput { * @param measurements 多种测量结果 * @return 输出结果 */ - Object output(Options options, Measurement... measurements); + Object output(Options options, StatisResult... measurements); } diff --git a/src/main/java/com/blinkfox/stalker/output/MeasureOutputContext.java b/src/main/java/com/blinkfox/stalker/output/MeasureOutputContext.java index 6c79eac..2bebe4c 100644 --- a/src/main/java/com/blinkfox/stalker/output/MeasureOutputContext.java +++ b/src/main/java/com/blinkfox/stalker/output/MeasureOutputContext.java @@ -1,7 +1,7 @@ package com.blinkfox.stalker.output; import com.blinkfox.stalker.config.Options; -import com.blinkfox.stalker.result.bean.Measurement; +import com.blinkfox.stalker.result.StatisResult; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -20,10 +20,10 @@ public final class MeasureOutputContext { * 将测量的相关参数和统计结果等信息输出出来. * * @param options 测量的选项参数 - * @param measurements 多种测量结果 + * @param statisResults 多个测量统计结果 * @return 各个输出结果的集合,v1.1.0 新增的返回结果 */ - public List output(Options options, Measurement... measurements) { + public List output(Options options, StatisResult... statisResults) { // 如果没有指定任何输出形式,则默认将结果输出到控制台中. List outputs = options.getOutputs(); if (outputs == null || outputs.isEmpty()) { @@ -33,7 +33,7 @@ public List output(Options options, Measurement... measurements) { // 如果有多种输出形式,就遍历得到结果,并将各个结果存入到 List 集合中. List results = new ArrayList<>(); - outputs.forEach(measureOutput -> results.add(measureOutput.output(options, measurements))); + outputs.forEach(measureOutput -> results.add(measureOutput.output(options, statisResults))); return results; } diff --git a/src/main/java/com/blinkfox/stalker/output/OutputConsole.java b/src/main/java/com/blinkfox/stalker/output/OutputConsole.java index daf3848..a359906 100644 --- a/src/main/java/com/blinkfox/stalker/output/OutputConsole.java +++ b/src/main/java/com/blinkfox/stalker/output/OutputConsole.java @@ -3,8 +3,7 @@ import com.blinkfox.minitable.MiniTable; import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.kit.StrKit; -import com.blinkfox.stalker.result.bean.EasyReadResult; -import com.blinkfox.stalker.result.bean.Measurement; +import com.blinkfox.stalker.result.StatisResult; import java.util.Arrays; import java.util.List; import lombok.extern.slf4j.Slf4j; @@ -40,7 +39,7 @@ public class OutputConsole implements MeasureOutput { * @param measurements 多种测量结果 */ @Override - public Object output(Options options, Measurement... measurements) { + public Object output(Options options, StatisResult... measurements) { // 渲染并打印结果. String result = this.getRenderResult(options, measurements); log.warn("\n{}", result); @@ -50,30 +49,39 @@ public Object output(Options options, Measurement... measurements) { /** * 获取最终需要渲染的结果. * + * @param options 测量运行的相关选项参数 + * @param measureResults 多个测量结果的不定集合 * @return 结果字符串 */ - private String getRenderResult(Options options, Measurement... measurements) { - if (options == null || measurements == null) { + private String getRenderResult(Options options, StatisResult... measureResults) { + if (options == null || measureResults == null) { throw new IllegalArgumentException("options or measureStatisResult is null."); } // 根据options的值, 拼接title. - String title = StrKit.join("threads: ", options.getThreads(), - ", concurrens: ", options.getConcurrens(), ", warmups:", options.getWarmups(), - ", runs: ", options.getRuns(), ", printErrorLog: ", options.isPrintErrorLog()); + String title; + if (options.getDuration() != null) { + title = StrKit.join("duration: ", options.getDuration().toString(), + ", concurrens: ", options.getConcurrens(), ", warmups:", options.getWarmups(), + ", runs: ", options.getRuns(), ", printErrorLog: ", options.isPrintErrorLog()); + } else { + title = StrKit.join("threads: ", options.getThreads(), + ", concurrens: ", options.getConcurrens(), ", warmups:", options.getWarmups(), + ", runs: ", options.getRuns(), ", printErrorLog: ", options.isPrintErrorLog()); + } String name = options.getName(); title = StrKit.isEmpty(name) ? title : StrKit.join("name: ", name, ", ", title); // 拼接各个测量结果的字符串表头和内容. MiniTable table = new MiniTable(title).addHeaders(HEADERS); - for (int i = 0, len = measurements.length; i < len; i++) { - EasyReadResult result = measurements[i].getEasyReadResult(); - table.addDatas(i + 1, - result.getCosts(), result.getTotal(), result.getSuccess(), result.getFailure(), - result.getThroughput(), result.getSum(), result.getAvg(), result.getMin(), result.getMax(), - result.getStdDev(), result.getLowerConfidence(), result.getUpperConfidence()); + for (int i = 0, len = measureResults.length; i < len; i++) { + StatisResult result = measureResults[i]; + table.addDatas(i + 1, result.getEasyReadCosts(), + result.getTotal(), result.getSuccess(), result.getFailure(), result.getEasyReadThroughput(), + result.getEasyReadSum(), result.getEasyReadAvg(), result.getEasyReadMin(), result.getEasyReadMax(), + result.getEasyReadStdDev(), result.getEasyReadLowerConfidence(), + result.getEasyReadUpperConfidence()); } - return table.render(); } diff --git a/src/main/java/com/blinkfox/stalker/result/MeasureStatistician.java b/src/main/java/com/blinkfox/stalker/result/MeasureStatistician.java index d52c5f0..23e8ff5 100644 --- a/src/main/java/com/blinkfox/stalker/result/MeasureStatistician.java +++ b/src/main/java/com/blinkfox/stalker/result/MeasureStatistician.java @@ -1,18 +1,16 @@ package com.blinkfox.stalker.result; import com.blinkfox.stalker.kit.MathKit; -import com.blinkfox.stalker.result.bean.StatisResult; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.locks.StampedLock; /** - * 针对测量出的消耗时间数据进行统计的统计器类. + * 针对测量出的消耗时间数据进行统计的统计器类,一些通用属性信息集成自 {@link StatisResult}. * * @author blinkfox on 2020-06-05. * @since v1.2.0 */ -public class MeasureStatistician { +public class MeasureStatistician extends StatisResult { /** * 95% 置信区间的 Z 值. @@ -24,62 +22,12 @@ public class MeasureStatistician { */ private static final int THRESHOLD = 100_000; - /** - * 用来高效读写数据的锁. - */ - private final StampedLock stampedLock = new StampedLock(); - /** * 用来记录每次测量出的待测量方法的消耗时间的集合,单位为纳秒(ns), * 当集合中的数量超过设定的阈值时,就会清空本集合中的数据,防止内存移除. */ private final List eachCosts = new ArrayList<>(3325); - /** - * 测量代码在执行过程中所消耗的总耗时,单位为纳秒(ns). - */ - private long costs; - - /** - * 总次数. - */ - private long total; - - /** - * 测量过程中执行成功的次数. - */ - private long success; - - /** - * 测量过程中执行失败的次数. - */ - private long failure; - - /** - * 吞吐率,指单位时间内(每秒)的执行总次数,即:{@code throughput = total / (costs / 10^9)}. - */ - private double throughput; - - /** - * 总耗时. - */ - private long sum; - - /** - * 平均耗时. - */ - private long avg; - - /** - * 最小耗时. - */ - private long min; - - /** - * 最大耗时. - */ - private long max; - /** * 方差和,该值表示各个值与平均值的差的平方和. * @@ -87,21 +35,6 @@ public class MeasureStatistician { */ private double varSum; - /** - * 标准差. - */ - private double stdDev; - - /** - * 95%置信区间下限. - */ - private double lowerConfidence; - - /** - * 95%置信区间上限. - */ - private double upperConfidence; - /** * 更新最新的统计数据. * @@ -112,52 +45,61 @@ public class MeasureStatistician { * @param currCosts 当前累计的总的运行时间 * @param currEachCosts 从上次更新到本次更新期间的每次运行次数的花费时间 */ - public StatisResult updateAndGet(long currSuccess, long currFailure, long currCosts, Long[] currEachCosts) { - long stamp = stampedLock.writeLock(); - try { - // 对基础统计数据进行赋值. - this.success = currSuccess; - this.failure = currFailure; - this.total = this.success + this.failure; - this.costs = currCosts; - this.throughput = MathKit.calcThroughput(this.total, this.costs); - - // 遍历求得所有测量值的和,最大值,最小值和平均值. - for (Long cost : currEachCosts) { - eachCosts.add(cost); - this.sum += cost; - if (this.min > cost) { - this.min = cost; - } - if (this.max < cost) { - this.max = cost; - } + public void update(long currSuccess, long currFailure, long currCosts, List currEachCosts) { + // 对基础统计数据进行赋值. + super.success = currSuccess; + super.failure = currFailure; + super.total = super.success + super.failure; + super.costs = currCosts; + super.throughput = MathKit.calcThroughput(super.total, super.costs); + + // 遍历求得所有测量值的和,最大值,最小值和平均值. + for (Long cost : currEachCosts) { + eachCosts.add(cost); + super.sum += cost; + if (super.min > cost) { + super.min = cost; } - this.avg = this.sum / this.total; - - // 计算方差所需的当前所有数据的平方差之和. - double currVarSum = this.varSum; - for (long measure : eachCosts) { - currVarSum += Math.pow(measure - (double) avg, 2); + if (super.max < cost) { + super.max = cost; } + } + super.avg = super.sum / super.total; - // 分别计算出标准差和95%的置信区间半径, - // 由于数据超过阈值之后,之前的每次花费时间的值会清空,而新的花费时间的平均值发生了变化, - // 因此,计算的总的平法差之和并不准确,从而导致标准差也不准确,但是在大数据情况时,这些误差可以容忍. - this.stdDev = Math.sqrt(currVarSum / this.total); - double radius = (Z * this.stdDev) / Math.sqrt(this.total); - this.lowerConfidence = this.avg - radius; - this.upperConfidence = this.avg + radius; + // 计算方差所需的当前所有数据的平方差之和. + double currVarSum = this.varSum; + for (long measure : eachCosts) { + currVarSum += Math.pow(measure - (double) super.avg, 2); + } - // 当数据量超过阈值之后,为了防止后续程序继续运行时内存溢出,就清空 eachCosts 集合中的数据. - // 并记录当前累计的平法差之和,便于以后再累计,计算方差时使用. - if (this.eachCosts.size() > THRESHOLD) { - this.eachCosts.clear(); - this.varSum = currVarSum; - } - } finally { - stampedLock.unlockWrite(stamp); + // 分别计算出标准差和95%的置信区间半径, + // 由于数据超过阈值之后,之前的每次花费时间的值会清空,而新的花费时间的平均值发生了变化, + // 因此,计算的总的平法差之和并不准确,从而导致标准差也不准确,但是在大数据情况时,这些误差可以容忍. + super.stdDev = Math.sqrt(currVarSum / super.total); + double radius = (Z * super.stdDev) / Math.sqrt(super.total); + this.lowerConfidence = super.avg - radius; + this.upperConfidence = super.avg + radius; + + // 当数据量超过阈值之后,为了防止后续程序继续运行时内存溢出,就清空 eachCosts 集合中的数据. + // 并记录当前累计的平法差之和,便于以后再累计,计算方差时使用. + if (this.eachCosts.size() > THRESHOLD) { + this.eachCosts.clear(); + this.varSum = currVarSum; } + } + + /** + * 更新最新的统计数据. + * + *

为了防止多个线程更新数据时的线程安全问题,这里加了写锁.

+ * + * @param currSuccess 当前累计的成功运行次数 + * @param currFailure 当前累计的失败运行次数 + * @param currCosts 当前累计的总的运行时间 + * @param currEachCosts 从上次更新到本次更新期间的每次运行次数的花费时间 + */ + public StatisResult updateAndGet(long currSuccess, long currFailure, long currCosts, List currEachCosts) { + this.update(currSuccess, currFailure, currCosts, currEachCosts); return this.get(); } @@ -170,37 +112,19 @@ public StatisResult updateAndGet(long currSuccess, long currFailure, long currCo */ public StatisResult get() { StatisResult statisResult = new StatisResult(); - long stamp = stampedLock.tryOptimisticRead(); - this.copyStatisResultData(statisResult); - if (!stampedLock.validate(stamp)) { - stamp = stampedLock.readLock(); - try { - this.copyStatisResultData(statisResult); - } finally { - stampedLock.unlockRead(stamp); - } - } + statisResult.setCosts(super.costs); + statisResult.setTotal(super.total); + statisResult.setSuccess(super.success); + statisResult.setFailure(super.failure); + statisResult.setThroughput(super.throughput); + statisResult.setSum(super.sum); + statisResult.setAvg(super.avg); + statisResult.setMin(super.min); + statisResult.setMax(super.max); + statisResult.setStdDev(super.stdDev); + statisResult.setLowerConfidence(super.lowerConfidence); + statisResult.setUpperConfidence(super.upperConfidence); return statisResult; } - /** - * 复制当前对象中的最新数据到新创建的 StatisResult 对象中. - * - * @param statisResult 新创建的 StatisResult 对象 - */ - private void copyStatisResultData(StatisResult statisResult) { - statisResult.setCosts(this.costs); - statisResult.setTotal(this.total); - statisResult.setSuccess(this.success); - statisResult.setFailure(this.failure); - statisResult.setThroughput(this.throughput); - statisResult.setSum(this.sum); - statisResult.setAvg(this.avg); - statisResult.setMin(this.min); - statisResult.setMax(this.max); - statisResult.setStdDev(this.stdDev); - statisResult.setLowerConfidence(this.lowerConfidence); - statisResult.setUpperConfidence(this.upperConfidence); - } - } diff --git a/src/main/java/com/blinkfox/stalker/result/MeasurementCollector.java b/src/main/java/com/blinkfox/stalker/result/MeasurementCollector.java deleted file mode 100644 index 53019c0..0000000 --- a/src/main/java/com/blinkfox/stalker/result/MeasurementCollector.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.blinkfox.stalker.result; - -import com.blinkfox.stalker.kit.StrKit; -import com.blinkfox.stalker.result.bean.EasyReadResult; -import com.blinkfox.stalker.result.bean.Measurement; -import com.blinkfox.stalker.result.bean.OverallResult; -import com.blinkfox.stalker.result.bean.StatisResult; -import com.blinkfox.stalker.result.statis.DefaultMeasureStatis; - -/** - * 测量结果信息的收集器类,即将测量结果、统计结果等收集、整合起来. - * - * @author blinkfox on 2019-01-22. - * @since v1.0.0 - */ -public final class MeasurementCollector { - - /** - * 根据正式测量的总体结果信息,统计、收集整理出更全面的测量结果信息. - * - * @param overallResult 总体结果 - * @return 测量总结果 - */ - public Measurement collect(OverallResult overallResult) { - Measurement measurement = new Measurement(overallResult); - StatisResult statisResult = new DefaultMeasureStatis().statis(overallResult); - measurement.setStatisResult(statisResult); - measurement.setEasyReadResult(this.buildEasyReadResult(overallResult, statisResult)); - return measurement; - } - - /** - * 由于测量结果单位均为纳秒,这里需要构建使人易读的测量结果信息 EasyReadResult 对象. - * - * @param overallResult 总体结果 - * @param statisResult 统计结果 - * @return 易读的结果 - */ - private EasyReadResult buildEasyReadResult(OverallResult overallResult, StatisResult statisResult) { - EasyReadResult easyReadResult = new EasyReadResult(); - easyReadResult.setCosts(StrKit.convertTime(overallResult.getCosts())); - easyReadResult.setTotal(overallResult.getTotal()); - easyReadResult.setSuccess(overallResult.getSuccess()); - easyReadResult.setFailure(overallResult.getFailure()); - easyReadResult.setThroughput(StrKit.roundToString(overallResult.getThroughput())); - - easyReadResult.setSum(StrKit.convertTime(statisResult.getSum())); - easyReadResult.setAvg(StrKit.convertTime(statisResult.getAvg())); - easyReadResult.setMin(StrKit.convertTime(statisResult.getMin())); - easyReadResult.setMax(StrKit.convertTime(statisResult.getMax())); - easyReadResult.setStdDev(StrKit.convertTime(statisResult.getStdDev())); - easyReadResult.setLowerConfidence(StrKit.convertTime(statisResult.getLowerConfidence())); - easyReadResult.setUpperConfidence(StrKit.convertTime(statisResult.getUpperConfidence())); - return easyReadResult; - } - -} diff --git a/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java b/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java index 1b1951f..d599996 100644 --- a/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java +++ b/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java @@ -2,8 +2,6 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.output.MeasureOutputContext; -import com.blinkfox.stalker.result.bean.Measurement; -import com.blinkfox.stalker.result.bean.OverallResult; import com.blinkfox.stalker.runner.MeasureRunner; import com.blinkfox.stalker.runner.executor.StalkerExecutors; import java.util.List; @@ -155,7 +153,7 @@ public boolean isDoneSuccessfully() { */ @Override public List get() { - return new MeasureOutputContext().output(this.options, this.getMeasurement()); + return new MeasureOutputContext().output(this.options, this.getMeasureResult()); } /** @@ -175,21 +173,21 @@ public List get(long timeout, TimeUnit unit) { } /** - * 实时获取运行中的任务的总体测量结果信息. + * 实时获取任务的测量头统计结果. * - * @return 总体测量结果 {@link OverallResult} 信息 + * @return {@link StatisResult} 结果 */ - public OverallResult getOverallResult() { - return this.measureRunner.buildRunningMeasurement(); + public StatisResult getMeasureResult() { + return this.measureRunner.getStatisResult(); } /** - * 实时获取任务的执行结果. + * 获取任务最终完成时实际所消耗的总的纳秒时间数. * - * @return {@link Measurement} 结果 + * @return 实际所消耗的总的纳秒时间数 */ - public Measurement getMeasurement() { - return new MeasurementCollector().collect(this.measureRunner.buildRunningMeasurement()); + public long getCosts() { + return this.measureRunner.getCosts(); } /** @@ -237,13 +235,4 @@ public long getEndNanoTime() { return this.measureRunner.getEndNanoTime(); } - /** - * 获取任务最终完成时实际所消耗的总的纳秒时间数. - * - * @return 实际所消耗的总的纳秒时间数 - */ - public long getCosts() { - return this.measureRunner.getCosts(); - } - } diff --git a/src/main/java/com/blinkfox/stalker/result/StatisResult.java b/src/main/java/com/blinkfox/stalker/result/StatisResult.java new file mode 100644 index 0000000..168f94f --- /dev/null +++ b/src/main/java/com/blinkfox/stalker/result/StatisResult.java @@ -0,0 +1,183 @@ +package com.blinkfox.stalker.result; + +import com.blinkfox.stalker.kit.StrKit; +import lombok.Getter; +import lombok.Setter; + +/** + * 对测量的耗时时间等信息做统计分析后的统计结果实体类. + * + * @author blinkfox on 2019-01-05. + * @since v1.0.0 + */ +@Getter +@Setter +public class StatisResult { + + /** + * 测量代码在执行过程中所消耗的总耗时,单位为纳秒(ns). + */ + protected long costs; + + /** + * 总次数. + */ + protected long total; + + /** + * 测量过程中执行成功的次数. + */ + protected long success; + + /** + * 测量过程中执行失败的次数. + */ + protected long failure; + + /** + * 吞吐率,指单位时间内(每秒)的执行总次数,即:{@code throughput = total / (costs / 10^9)}. + * + * @since v1.1.1 + */ + protected double throughput; + + /** + * 总耗时. + */ + protected long sum; + + /** + * 平均耗时. + */ + protected long avg; + + /** + * 最小耗时. + */ + protected long min; + + /** + * 最大耗时. + */ + protected long max; + + /** + * 标准差. + */ + protected double stdDev; + + /** + * 95%置信区间下限. + */ + protected double lowerConfidence; + + /** + * 95%置信区间上限. + */ + protected double upperConfidence; + + /** + * 获取易于人阅读的实际任务运行总时间字符串. + * + * @return 实际任务运行总时间字符串 + */ + public String getEasyReadCosts() { + return StrKit.convertTime(this.costs); + } + + /** + * 获取易于人阅读的吞吐量字符串. + * + * @return 吞吐量字符串 + */ + public String getEasyReadThroughput() { + return StrKit.roundToString(this.throughput); + } + + /** + * 获取易于人阅读的每次测量所花费时间总和的字符串. + * + * @return 花费时间总和的字符串 + */ + public String getEasyReadSum() { + return StrKit.convertTime(this.sum); + } + + /** + * 获取易于人阅读的每次测量所花费的平均时间总和的字符串. + * + * @return 花费的平均时间的字符串 + */ + public String getEasyReadAvg() { + return StrKit.convertTime(this.avg); + } + + /** + * 获取易于人阅读的测量所花费的最小时间的字符串. + * + * @return 所花费的最小时间的字符串 + */ + public String getEasyReadMin() { + return StrKit.convertTime(this.min); + } + + /** + * 获取易于人阅读的测量所花费的最大时间的字符串. + * + * @return 所花费的最大时间的字符串 + */ + public String getEasyReadMax() { + return StrKit.convertTime(this.max); + } + + /** + * 获取易于人阅读的测量所花费的时间标准差的字符串. + * + * @return 所花费的时间标准差的字符串 + */ + public String getEasyReadStdDev() { + return StrKit.convertTime(this.stdDev); + } + + /** + * 获取易于人阅读的测量所花费时间的 95% 置信区间下界的时间字符串. + * + * @return 95% 置信区间下界的时间字符串 + */ + public String getEasyReadLowerConfidence() { + return StrKit.convertTime(this.lowerConfidence); + } + + /** + * 获取易于人阅读的测量所花费时间的 95% 置信区间上界的字符串. + * + * @return 95% 置信区间上界的时间字符串 + */ + public String getEasyReadUpperConfidence() { + return StrKit.convertTime(this.upperConfidence); + } + + /** + * 将对象转换为字符串. + * + * @return 字符串 + */ + @Override + public String toString() { + return StrKit.join("MeasureStatisResult = {", + ", costs = ", this.getEasyReadCosts(), + ", total = ", this.getTotal(), + ", success = ", this.getSuccess(), + ", failure = ", this.getFailure(), + ", throughput = ", this.getEasyReadThroughput(), + ", sum = ", this.getEasyReadSum(), + ", avg = ", this.getEasyReadAvg(), + ", min = ", this.getEasyReadMin(), + ", max = ", this.getEasyReadMax(), + ", stdDev = ", this.getEasyReadStdDev(), + ", lowerConfidence = ", this.getEasyReadLowerConfidence(), + ", upperConfidence = ", this.getEasyReadUpperConfidence(), + "}."); + } + +} diff --git a/src/main/java/com/blinkfox/stalker/result/bean/EasyReadResult.java b/src/main/java/com/blinkfox/stalker/result/bean/EasyReadResult.java deleted file mode 100644 index 3868c78..0000000 --- a/src/main/java/com/blinkfox/stalker/result/bean/EasyReadResult.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.blinkfox.stalker.result.bean; - -import lombok.Getter; -import lombok.Setter; - -/** - * 对测量的整体结果和统计结果等信息的时间单位做了易读性处理后的结果实体类. - * - * @author blinkfox on 2019-01-21. - * @since v1.0.0 - */ -@Getter -@Setter -public class EasyReadResult { - - /** - * 正式测量期间消耗的总时间. - */ - private String costs; - - /** - * 正式测量执行的总次数. - */ - private long total; - - /** - * 正式测量执行成功的次数. - */ - private long success; - - /** - * 正式测量执行失败的次数. - */ - private long failure; - - /** - * 吞吐率,指单位时间内(每秒)的执行总次数. - * - * @since v1.1.1 - */ - private String throughput; - - /** - * 正式执行的所有结果的总耗时. - */ - private String sum; - - /** - * 正式执行的所有结果的平均耗时. - */ - private String avg; - - /** - * 最小耗时. - */ - private String min; - - /** - * 最大耗时. - */ - private String max; - - /** - * 标准差. - */ - private String stdDev; - - /** - * 95%置信区间下限. - */ - private String lowerConfidence; - - /** - * 95%置信区间上限. - */ - private String upperConfidence; - -} diff --git a/src/main/java/com/blinkfox/stalker/result/bean/Measurement.java b/src/main/java/com/blinkfox/stalker/result/bean/Measurement.java deleted file mode 100644 index 286813c..0000000 --- a/src/main/java/com/blinkfox/stalker/result/bean/Measurement.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.blinkfox.stalker.result.bean; - -import lombok.Getter; -import lombok.Setter; - -/** - * 正式测量结果的实体类. - * - * @author blinkfox on 2019-01-22. - * @since v1.0.0 - */ -@Getter -@Setter -public class Measurement { - - /** - * 正式测量的整体结果信息. - */ - private OverallResult overallResult; - - /** - * 正式测量的统计结果信息. - */ - private StatisResult statisResult; - - /** - * 正式测量结果和统计结果信息转换成的易读的结果信息. - */ - private EasyReadResult easyReadResult; - - /** - * 构造方法. - * - * @param overallResult 整体测量结果 - */ - public Measurement(OverallResult overallResult) { - this.overallResult = overallResult; - } - -} diff --git a/src/main/java/com/blinkfox/stalker/result/bean/OverallResult.java b/src/main/java/com/blinkfox/stalker/result/bean/OverallResult.java deleted file mode 100644 index 4647adc..0000000 --- a/src/main/java/com/blinkfox/stalker/result/bean/OverallResult.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.blinkfox.stalker.result.bean; - -import com.blinkfox.stalker.kit.StrKit; -import lombok.Getter; -import lombok.Setter; -import lombok.experimental.Accessors; - -/** - * 待测量方法的耗时测量的整体结果实体类. - * - * @author blinkfox on 2019-01-09. - * @since v1.0.0 - */ -@Getter -@Setter -@Accessors(chain = true) -public class OverallResult { - - /** - * 每次成功测量出的待测量方法的测量时间的集合,单位为纳秒(ns). - */ - private long[] eachMeasures; - - /** - * 测量代码在执行过程中所消耗的总耗时,单位为纳秒(ns). - */ - private long costs; - - /** - * 测量过程中执行的总次数. - */ - private long total; - - /** - * 测量过程中执行成功的次数. - */ - private long success; - - /** - * 测量过程中执行失败的次数. - */ - private long failure; - - /** - * 吞吐率,指单位时间内(每秒)的执行总次数,即:{@code throughput = total / (costs / 10^9)}. - * - * @since v1.1.1 - */ - private double throughput; - - /** - * 将该对象转换字符串. - * - * @return 字符串 - */ - @Override - public String toString() { - return StrKit.join("Measurement = {", - "costs = ", StrKit.convertTime(this.costs), - ", total = ", this.total, - ", success = ", this.success, - ", failure = ", this.failure, - ", throughput = ", this.throughput, - "}"); - } - -} diff --git a/src/main/java/com/blinkfox/stalker/result/bean/StatisResult.java b/src/main/java/com/blinkfox/stalker/result/bean/StatisResult.java deleted file mode 100644 index 5bcee58..0000000 --- a/src/main/java/com/blinkfox/stalker/result/bean/StatisResult.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.blinkfox.stalker.result.bean; - -import com.blinkfox.stalker.kit.StrKit; -import lombok.Getter; -import lombok.Setter; - -/** - * 对测量的耗时时间等信息做统计分析后的统计结果实体类. - * - * @author blinkfox on 2019-01-05. - * @since v1.0.0 - */ -@Getter -@Setter -public class StatisResult { - - /** - * 测量代码在执行过程中所消耗的总耗时,单位为纳秒(ns). - */ - private long costs; - - /** - * 总次数. - */ - private long total; - - /** - * 测量过程中执行成功的次数. - */ - private long success; - - /** - * 测量过程中执行失败的次数. - */ - private long failure; - - /** - * 吞吐率,指单位时间内(每秒)的执行总次数,即:{@code throughput = total / (costs / 10^9)}. - * - * @since v1.1.1 - */ - private double throughput; - - /** - * 总耗时. - */ - private long sum; - - /** - * 平均耗时. - */ - private long avg; - - /** - * 最小耗时. - */ - private long min; - - /** - * 最大耗时. - */ - private long max; - - /** - * 标准差. - */ - private double stdDev; - - /** - * 95%置信区间下限. - */ - private double lowerConfidence; - - /** - * 95%置信区间上限. - */ - private double upperConfidence; - - @Override - public String toString() { - return StrKit.join("MeasureStatisResult = {", - ", sum = ", StrKit.convertTime(this.sum), - ", avg = ", StrKit.convertTime(this.avg), - ", min = ", StrKit.convertTime(this.min), - ", max = ", StrKit.convertTime(this.max), - ", stdDev = ", StrKit.convertTime(this.stdDev), - ", lowerConfidence = ", StrKit.convertTime(this.lowerConfidence), - ", upperConfidence = ", StrKit.convertTime(this.upperConfidence), - "}."); - } - -} diff --git a/src/main/java/com/blinkfox/stalker/result/statis/DefaultMeasureStatis.java b/src/main/java/com/blinkfox/stalker/result/statis/DefaultMeasureStatis.java deleted file mode 100644 index 8aba4c2..0000000 --- a/src/main/java/com/blinkfox/stalker/result/statis/DefaultMeasureStatis.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.blinkfox.stalker.result.statis; - -import com.blinkfox.stalker.result.bean.OverallResult; -import com.blinkfox.stalker.result.bean.StatisResult; - -/** - * 默认的测量结果统计实现. - * - * @author blinkfox on 2019-01-10. - * @since v1.0.0 - */ -public class DefaultMeasureStatis { - - /** - * 95%置信区间的 Z 值. - */ - private static final double Z = 1.96; - - /** - * 将测量结果数据做统计分析,得出性能结果数据. - * - * @param overallResult 测量结果数据 - * @return 性能结果数据 - */ - public StatisResult statis(OverallResult overallResult) { - StatisResult statisResult = new StatisResult(); - long[] eachMeasures = overallResult.getEachMeasures(); - if (eachMeasures == null || eachMeasures.length == 0) { - return statisResult; - } - - // 计算出测量结果的相关统计结果. - return this.buildOtherResult(this.buildBaseResult(statisResult, eachMeasures), eachMeasures); - } - - /** - * 构建基础统计结果信息. - * - * @param statisResult 性能结果对象 - * @param eachMeasures 每次测量值的数组 - * @return 性能结果对象 - */ - private StatisResult buildBaseResult(StatisResult statisResult, long[] eachMeasures) { - long sum = 0; - long min = eachMeasures[0]; - long max = min; - - // 遍历求得所有测量值的和,最大值,最小值. - for (long measure : eachMeasures) { - sum += measure; - if (min > measure) { - min = measure; - } - if (max < measure) { - max = measure; - } - } - - statisResult.setMin(min); - statisResult.setMax(max); - statisResult.setSum(sum); - statisResult.setAvg(sum / eachMeasures.length); - return statisResult; - } - - /** - * 构建其它统计结果信息. - * - * @param statisResult 性能结果对象 - * @param eachMeasures 每次测量值的数组 - * @return 性能结果对象 - */ - private StatisResult buildOtherResult(StatisResult statisResult, long[] eachMeasures) { - // 计算方差所需的平方和. - double s = 0; - long avg = statisResult.getAvg(); - for (long measure : eachMeasures) { - s += Math.pow(measure - (double) avg, 2); - } - - // 分别计算出标准差和95%的置信区间半径. - long n = eachMeasures.length; - double stdDev = Math.sqrt(s / n); - double radius = (Z * stdDev) / Math.sqrt(n); - - statisResult.setStdDev(stdDev); - statisResult.setLowerConfidence(avg - radius); - statisResult.setUpperConfidence(avg + radius); - return statisResult; - } - -} diff --git a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java index bf128f1..d5fb87a 100644 --- a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java @@ -1,12 +1,16 @@ package com.blinkfox.stalker.runner; import com.blinkfox.stalker.result.MeasureStatistician; -import com.blinkfox.stalker.result.bean.StatisResult; +import com.blinkfox.stalker.result.StatisResult; +import java.util.ArrayList; +import java.util.List; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.LongAdder; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -34,11 +38,7 @@ public abstract class AbstractMeasureRunner implements MeasureRunner { */ private final MeasureStatistician measureStatistician; - /** - * 直到最后一次累计构建的统计结果信息. - */ - @Getter - protected StatisResult lastStatisResult; + private final Lock statisLock; /** * 每次'成功'测量出的待测量方法的耗时时间,单位为纳秒({@code ns}). @@ -80,13 +80,14 @@ public abstract class AbstractMeasureRunner implements MeasureRunner { /** * 用于记录以前总共读取了 eachMeasures 中的数据总量,即总的偏移量. */ - private long beforeCount; + private long beforeTotalCount; /** * 公共的抽象父构造方法. */ public AbstractMeasureRunner() { this.measureStatistician = new MeasureStatistician(); + this.statisLock = new ReentrantLock(); this.eachMeasures = new ConcurrentLinkedQueue<>(); this.success = new LongAdder(); this.failure = new LongAdder(); @@ -94,23 +95,60 @@ public AbstractMeasureRunner() { this.canceled = new AtomicBoolean(false); } + /** + * 获取当前测量任务已经运行的总花费时间. + * + * @return 运行总花费时间 + */ + public long getCosts() { + return this.completed.get() && this.getTotal() <= this.beforeTotalCount && eachMeasures.isEmpty() + ? this.endNanoTime - this.startNanoTime + : this.startNanoTime == 0 ? 0 : System.nanoTime() - this.startNanoTime; + } + + /** + * 获取任务运行成功的数量. + * + * @return 成功数量 + */ + @Override public long getTotal() { return this.success.longValue() + this.failure.longValue(); } + /** + * 获取任务运行成功的数量. + * + * @return 成功数量 + */ public long getSuccess() { return this.success.longValue(); } + /** + * 获取任务运行失败的数量. + * + * @return 失败数量 + */ public long getFailure() { return this.failure.longValue(); } + /** + * 判断当前任务是否已经执行完成. + * + * @return 是否执行完成的布尔值 + */ @Override public boolean isCompleted() { return this.completed.get(); } + /** + * 判断当前任务是否已经被取消. + * + * @return 是否被取消的布尔值 + */ @Override public boolean isCancelled() { return this.canceled.get(); @@ -128,35 +166,67 @@ public void setEndNanoTimeIfEmpty(long endNanoTime) { } /** - * 更新并获取统计结果信息数据,由于可能会有两个或多个线程去更新和获取统计数据,这里使用 {@code synchronized} 来同步. + * 更新并获取统计结果信息数据. + * + *

如果任务已经完成,就直接返回最终的测试结果数据即可,否则加锁获取正在运行中的任务的统计数据.

* * @return 统计结果信息 */ @Override - public synchronized StatisResult getStatisResult() { - // 如果任务已经完成,就直接返回最终的测试结果数据即可. - if (this.completed.get()) { - return measureStatistician.get(); - } + public StatisResult getStatisResult() { + return this.completed.get() && this.getTotal() <= this.beforeTotalCount && eachMeasures.isEmpty() + ? this.measureStatistician.get() + : this.getRunningStatisResult(); + } - // 获取到截至到当前时间的正确运行次数数、错误运行次数,消耗的时间和每次的运行时间等数据. - long currFailure = this.getFailure(); - long currSuccess = this.getSuccess(); - long currCosts = this.startNanoTime == 0 ? 0 : System.nanoTime() - this.startNanoTime; - int len = (int) (currSuccess + currFailure - beforeCount); - - // 截取复制出最新的 - Long[] currMeasures = new Long[len]; - for (int i = 0; i < len; ++i) { - Long cost = this.eachMeasures.poll(); - if (cost != null) { - currMeasures[i] = cost; + /** + * 获取正在运行中的任务的数据的统计结果信息. + * + *

由于可能会有两个或多个线程去更新和获取统计数据,这里须要加锁来获取正在运行中的任务的统计数据.

+ * + * @return 统计结果信息 + */ + private StatisResult getRunningStatisResult() { + try { + // 读取时加锁. + statisLock.lockInterruptibly(); + + // 计算出运行消耗的总时间,如果已经结束了,就直接使用结束时间戳减去开始时间戳. + final long currCosts = this.completed.get() + ? this.endNanoTime - this.startNanoTime + : this.startNanoTime == 0 ? 0 : System.nanoTime() - this.startNanoTime; + + // 获取到截至到当前时间的正确运行次数数、错误运行次数,消耗的时间和每次的运行时间等数据. + final long currFailure = this.getFailure(); + final long currSuccess = this.getSuccess(); + final long currTotal = currSuccess + currFailure; + int newCount = (int) (currTotal - this.beforeTotalCount); + if (newCount <= 0) { + return this.measureStatistician.get(); } - } - beforeCount = currSuccess + currFailure; - // 更新并获取最新的数据. - return measureStatistician.updateAndGet(currSuccess, currFailure, currCosts, currMeasures); + // 截取复制出最新的测量耗时信息,这里从队列中"出队读取" len 个最新的测量耗时数据. + final List currEachCosts = new ArrayList<>(newCount); + for (int i = 0; i < newCount; ++i) { + Long cost = this.eachMeasures.poll(); + if (cost != null) { + currEachCosts.add(cost); + } + } + this.beforeTotalCount = currTotal; + + // 更新并获取最新的统计数据信息. + return measureStatistician.updateAndGet(currSuccess, currFailure, currCosts, currEachCosts); + } catch (InterruptedException e) { + log.error("【Stalker 错误提示】获取运行中任务的统计结果数据线程被中断!", e); + Thread.currentThread().interrupt(); + return this.measureStatistician.get(); + } catch (Exception e) { + log.error("【Stalker 错误提示】获取运行中任务的统计结果数据时出错,将直接返回之前的数据.", e); + return this.measureStatistician.get(); + } finally { + statisLock.unlock(); + } } } diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java index ab11fe3..64513de 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java @@ -2,7 +2,7 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.kit.ConcurrentHashSet; -import com.blinkfox.stalker.result.bean.OverallResult; +import com.blinkfox.stalker.result.StatisResult; import com.blinkfox.stalker.runner.executor.StalkerExecutors; import java.util.Iterator; import java.util.Set; @@ -40,10 +40,10 @@ public ConcurrentMeasureRunner() { * * @param options 运行的配置选项实例 * @param runnable 可运行实例 - * @return 测量结果 + * @return 测量统计结果 */ @Override - public OverallResult run(Options options, Runnable runnable) { + public StatisResult run(Options options, Runnable runnable) { int threads = options.getThreads(); int concurrens = options.getConcurrens(); int runs = options.getRuns(); @@ -79,7 +79,7 @@ public OverallResult run(Options options, Runnable runnable) { super.setEndNanoTimeIfEmpty(System.nanoTime()); super.completed.compareAndSet(false, true); StalkerExecutors.shutdown(this.executorService); - return super.buildFinalMeasurement(); + return super.getStatisResult(); } /** @@ -94,7 +94,7 @@ protected void loopMeasure(int runs, boolean printErrorLog, final Runnable runna try { long eachStart = System.nanoTime(); runnable.run(); - super.eachMeasures.add(System.nanoTime() - eachStart); + super.eachMeasures.offer(System.nanoTime() - eachStart); super.success.increment(); } catch (Exception e) { // 如果待测量的方法,执行错误则失败数 +1,且根据选项参数来判断是否打印异常错误日志. diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java index b845e75..a182879 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java @@ -2,7 +2,7 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.config.RunDuration; -import com.blinkfox.stalker.result.bean.OverallResult; +import com.blinkfox.stalker.result.StatisResult; import com.blinkfox.stalker.runner.executor.StalkerExecutors; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; @@ -48,10 +48,10 @@ public ConcurrentScheduledMeasureRunner() { * * @param options 运行的配置选项实例 * @param runnable 可运行实例 - * @return 测量结果 + * @return 测量统计结果 */ @Override - public OverallResult run(Options options, Runnable runnable) { + public StatisResult run(Options options, Runnable runnable) { int concurrens = options.getConcurrens(); int runs = options.getRuns(); boolean printErrorLog = options.isPrintErrorLog(); @@ -98,7 +98,7 @@ public OverallResult run(Options options, Runnable runnable) { if (!this.scheduledFuture.isDone()) { this.scheduledFuture.cancel(true); } - return super.buildFinalMeasurement(); + return super.getStatisResult(); } /** diff --git a/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java index ad0dba0..4c27b88 100644 --- a/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java @@ -1,8 +1,7 @@ package com.blinkfox.stalker.runner; import com.blinkfox.stalker.config.Options; -import com.blinkfox.stalker.result.bean.OverallResult; -import com.blinkfox.stalker.result.bean.StatisResult; +import com.blinkfox.stalker.result.StatisResult; /** * 用于测量待执行方法耗时情况等信息的运行器接口. @@ -17,9 +16,9 @@ public interface MeasureRunner { * * @param options 运行的配置选项实例 * @param runnable 可运行实例 - * @return 测量结果 + * @return 测量的统计结果 */ - OverallResult run(Options options, Runnable runnable); + StatisResult run(Options options, Runnable runnable); /** * 判断当前任务是否已经执行完成. @@ -39,6 +38,42 @@ public interface MeasureRunner { */ boolean isCancelled(); + /** + * 获取当前已经运行的总次数. + * + * @return 运行总次数 + * @author blinkfox on 2020-06-06. + * @since v1.2.0 + */ + long getTotal(); + + /** + * 获取当前测量任务已经运行的总花费时间. + * + * @return 运行总花费时间 + * @author blinkfox on 2020-06-06. + * @since v1.2.0 + */ + long getCosts(); + + /** + * 获取到当前时的运行成功的次数. + * + * @return 运行成功的次数 + * @author blinkfox on 2020-06-06. + * @since v1.2.0 + */ + long getSuccess(); + + /** + * 获取当前运行失败的次数. + * + * @return 运行失败的次数 + * @author blinkfox on 2020-06-06. + * @since v1.2.0 + */ + long getFailure(); + /** * 获取任务开始运行时的纳秒时间戳. * diff --git a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java index 7aa47cb..bb27faf 100644 --- a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java +++ b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java @@ -2,10 +2,8 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.kit.StrKit; -import com.blinkfox.stalker.result.MeasurementCollector; import com.blinkfox.stalker.result.StalkerFuture; -import com.blinkfox.stalker.result.bean.Measurement; -import com.blinkfox.stalker.result.bean.OverallResult; +import com.blinkfox.stalker.result.StatisResult; import lombok.extern.slf4j.Slf4j; /** @@ -64,9 +62,9 @@ private static void warmup(Options options, Runnable runnable) { * 检查Options参数是否合法,并进行预热准备,然后执行 runnable 方法,并将执行结果的耗时纳秒(ns)值存入到集合中. * * @param runnable 可运行实例 - * @return 运行的测量结果 + * @return 运行的测量统计结果信息 */ - public OverallResult run(Runnable runnable) { + public StatisResult run(Runnable runnable) { warmup(options, runnable); if (options.getDuration() != null) { return options.getConcurrens() > 1 @@ -79,18 +77,6 @@ public OverallResult run(Runnable runnable) { } } - /** - * 检查Options参数是否合法,并进行预热准备,然后执行 runnable 方法,并将执行结果的耗时纳秒(ns)值存入到集合中. - * - * @param runnable 可运行实例 - * @return 运行的经过测量结果 - * @author blinkfox on 2020-05-23. - * @since v1.2.0 - */ - public Measurement runAndCollect(Runnable runnable) { - return new MeasurementCollector().collect(this.run(runnable)); - } - /** * 检查Options参数是否合法,并进行预热准备,然后执行 runnable 方法,并将执行结果的耗时纳秒(ns)值存入到集合中. * diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java index 52f74af..32840aa 100644 --- a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java @@ -1,8 +1,9 @@ package com.blinkfox.stalker.runner; import com.blinkfox.stalker.config.Options; -import com.blinkfox.stalker.result.bean.OverallResult; +import com.blinkfox.stalker.result.StatisResult; import com.blinkfox.stalker.runner.executor.StalkerExecutors; +import java.util.concurrent.CancellationException; import java.util.concurrent.Future; import lombok.extern.slf4j.Slf4j; @@ -34,10 +35,10 @@ public SimpleMeasureRunner() { * * @param options 运行的配置选项实例 * @param runnable 可运行实例 - * @return 测量结果 + * @return 测量统计结果 */ @Override - public OverallResult run(Options options, Runnable runnable) { + public StatisResult run(Options options, Runnable runnable) { boolean printErrorLog = options.isPrintErrorLog(); int totalCount = options.getThreads() * options.getRuns(); super.executorService = StalkerExecutors.newSingleThreadExecutor("simple-measure-thread"); @@ -65,7 +66,9 @@ public OverallResult run(Options options, Runnable runnable) { // 阻塞调用要执行的测量任务,达到等待任务结束的目的. try { this.measureFuture.get(); - } catch (Exception e) { + } catch (CancellationException e) { + log.info("【Stalker 提示】已取消或完成测量任务."); + } catch (Exception e) { log.error("【Stalker 错误】执行测量任务发生错误!", e); } @@ -73,9 +76,7 @@ public OverallResult run(Options options, Runnable runnable) { super.setEndNanoTimeIfEmpty(System.nanoTime()); super.completed.compareAndSet(false, true); StalkerExecutors.shutdown(super.executorService); - // TODO 待完成. - // return super.buildFinalMeasurement(); - return null; + return super.getStatisResult(); } /** diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java index 52c3f29..4917e79 100644 --- a/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java @@ -2,8 +2,9 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.config.RunDuration; -import com.blinkfox.stalker.result.bean.OverallResult; +import com.blinkfox.stalker.result.StatisResult; import com.blinkfox.stalker.runner.executor.StalkerExecutors; +import java.util.concurrent.CancellationException; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import lombok.extern.slf4j.Slf4j; @@ -38,14 +39,14 @@ public SimpleScheduledMeasureRunner() { } /** - * 持续执行指定时间的 runnable 方法,并将执行成功与否、耗时结果等信息存入到 OverallResult 实体对象中. + * 持续执行指定时间的 runnable 方法,并将执行成功与否、耗时结果等信息存入到 {@link StatisResult} 实体对象中. * * @param options 运行的配置选项实例 * @param runnable 可运行实例 - * @return 测量结果 + * @return 测量统计结果 */ @Override - public OverallResult run(Options options, Runnable runnable) { + public StatisResult run(Options options, Runnable runnable) { boolean printErrorLog = options.isPrintErrorLog(); super.executorService = StalkerExecutors.newSingleThreadExecutor("simple-scheduled-measure-thread"); super.startNanoTime = System.nanoTime(); @@ -57,7 +58,7 @@ public OverallResult run(Options options, Runnable runnable) { // 开始执行测量任务,记录开始时间、执行次数等. long eachStart = System.nanoTime(); runnable.run(); - super.eachMeasures.add(System.nanoTime() - eachStart); + super.eachMeasures.offer(System.nanoTime() - eachStart); super.success.increment(); } catch (Exception e) { super.failure.increment(); @@ -79,6 +80,8 @@ public OverallResult run(Options options, Runnable runnable) { // 阻塞调用要执行的测量任务,达到阻塞等待任务结束的目的. try { this.measureFuture.get(); + } catch (CancellationException e) { + log.info("【Stalker 提示】已取消或完成指定运行时间的测量任务."); } catch (Exception e) { log.error("【Stalker 错误】执行测量任务发生错误!", e); } @@ -87,7 +90,7 @@ public OverallResult run(Options options, Runnable runnable) { super.setEndNanoTimeIfEmpty(System.nanoTime()); super.completed.compareAndSet(false, true); StalkerExecutors.shutdown(super.executorService, this.scheduledExecutorService); - return super.buildFinalMeasurement(); + return super.getStatisResult(); } /** diff --git a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java index b5bcdb7..afc2b84 100644 --- a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java +++ b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java @@ -3,7 +3,7 @@ import com.blinkfox.stalker.Stalker; import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.result.StalkerFuture; -import com.blinkfox.stalker.result.bean.Measurement; +import com.blinkfox.stalker.result.StatisResult; import com.blinkfox.stalker.test.prepare.MyTestService; import java.util.List; import java.util.concurrent.TimeUnit; @@ -196,18 +196,18 @@ public void submitWithSlowMethodDuration() throws InterruptedException { * 测试 queryMeasurement 方法. */ @Test - public void queryMeasurement() throws InterruptedException { + public void queryMeasureResult() throws InterruptedException { StalkerFuture stalkerFuture = Stalker.submit(() -> new MyTestService().hello()); Assert.assertNotNull(stalkerFuture); while (!stalkerFuture.isDone()) { - Measurement measurement = stalkerFuture.getMeasurement(); - Assert.assertNotNull(measurement); + StatisResult statisResult = stalkerFuture.getMeasureResult(); + Assert.assertNotNull(statisResult); Thread.sleep(5L); } Assert.assertFalse(stalkerFuture.isCancelled()); - Assert.assertNotNull(stalkerFuture.getOverallResult()); + Assert.assertNotNull(stalkerFuture.getMeasureResult()); } /** @@ -222,8 +222,13 @@ public void submitWithStop() throws InterruptedException { Thread.sleep(50L); List results = stalkerFuture.get(); Assert.assertNotNull(results.get(0)); - stalkerFuture.cancel(); - log.info("任务已停止,获取停止前的执行结果."); + boolean isCancelld = stalkerFuture.cancel(); + if (isCancelld) { + log.info("任务已停止,获取停止前的执行结果."); + } else { + log.info("任务停止失败."); + } + stalkerFuture.get(); Thread.sleep(100L); diff --git a/src/test/java/com/blinkfox/stalker/test/output/MeasureOutputContextTest.java b/src/test/java/com/blinkfox/stalker/test/output/MeasureOutputContextTest.java index 3a620d5..183895c 100644 --- a/src/test/java/com/blinkfox/stalker/test/output/MeasureOutputContextTest.java +++ b/src/test/java/com/blinkfox/stalker/test/output/MeasureOutputContextTest.java @@ -3,8 +3,7 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.output.MeasureOutput; import com.blinkfox.stalker.output.MeasureOutputContext; -import com.blinkfox.stalker.result.bean.Measurement; -import com.blinkfox.stalker.result.bean.OverallResult; +import com.blinkfox.stalker.result.StatisResult; import java.util.ArrayList; import java.util.List; import org.junit.Test; @@ -23,10 +22,8 @@ public class MeasureOutputContextTest { @Test public void outputWithoutOutputs() { List outputs = null; - Measurement measurement = new Measurement(new OverallResult()); - - new MeasureOutputContext().output(Options.of().outputs(outputs), measurement); - new MeasureOutputContext().output(Options.of().outputs(new ArrayList<>()), measurement); + new MeasureOutputContext().output(Options.of().outputs(outputs), new StatisResult()); + new MeasureOutputContext().output(Options.of().outputs(new ArrayList<>()), new StatisResult()); } } diff --git a/src/test/java/com/blinkfox/stalker/test/output/OutputConsoleTest.java b/src/test/java/com/blinkfox/stalker/test/output/OutputConsoleTest.java index 26f31ce..922e84f 100644 --- a/src/test/java/com/blinkfox/stalker/test/output/OutputConsoleTest.java +++ b/src/test/java/com/blinkfox/stalker/test/output/OutputConsoleTest.java @@ -2,8 +2,7 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.output.OutputConsole; -import com.blinkfox.stalker.result.bean.Measurement; -import com.blinkfox.stalker.result.bean.OverallResult; +import com.blinkfox.stalker.result.StatisResult; import org.junit.Test; /** @@ -19,7 +18,7 @@ public class OutputConsoleTest { */ @Test(expected = IllegalArgumentException.class) public void outputWithNullOptions() { - new OutputConsole().output(null, new Measurement(new OverallResult())); + new OutputConsole().output(null, new StatisResult()); } /** diff --git a/src/test/java/com/blinkfox/stalker/test/result/bean/MeasurementTest.java b/src/test/java/com/blinkfox/stalker/test/result/bean/MeasurementTest.java deleted file mode 100644 index 0ce3132..0000000 --- a/src/test/java/com/blinkfox/stalker/test/result/bean/MeasurementTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.blinkfox.stalker.test.result.bean; - -import com.blinkfox.stalker.result.bean.Measurement; -import com.blinkfox.stalker.result.bean.OverallResult; -import com.blinkfox.stalker.result.bean.StatisResult; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -/** - * MeasurementTest. - * - * @author blinkfox on 2019-02-04. - * @since v1.0.0 - */ -public class MeasurementTest { - - private static Measurement measurement; - - /** - * 初始化. - */ - @BeforeClass - public static void init() { - measurement = new Measurement(new OverallResult()); - measurement.setStatisResult(new StatisResult()); - } - - @Test - public void getOverallResult() { - Assert.assertNotNull(measurement.getOverallResult()); - } - - @Test - public void getStatisResult() { - Assert.assertNotNull(measurement.getStatisResult()); - } - -} diff --git a/src/test/java/com/blinkfox/stalker/test/result/bean/OverallResultTest.java b/src/test/java/com/blinkfox/stalker/test/result/bean/OverallResultTest.java deleted file mode 100644 index a25c4da..0000000 --- a/src/test/java/com/blinkfox/stalker/test/result/bean/OverallResultTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.blinkfox.stalker.test.result.bean; - -import com.blinkfox.stalker.result.bean.OverallResult; -import org.junit.Assert; -import org.junit.Test; - -/** - * OverallResultTest. - * - * @author blinkfox on 2019-02-04. - * @since v1.0.0 - */ -public class OverallResultTest { - - @Test - public void testToString() { - Assert.assertNotNull(new OverallResult() - .setEachMeasures(new long[] {20, 10}) - .setCosts(40) - .setTotal(5) - .setSuccess(5) - .setFailure(0).toString()); - } - -} diff --git a/src/test/java/com/blinkfox/stalker/test/result/bean/StatisResultTest.java b/src/test/java/com/blinkfox/stalker/test/result/bean/StatisResultTest.java index dc83dfd..8254d4b 100644 --- a/src/test/java/com/blinkfox/stalker/test/result/bean/StatisResultTest.java +++ b/src/test/java/com/blinkfox/stalker/test/result/bean/StatisResultTest.java @@ -1,6 +1,6 @@ package com.blinkfox.stalker.test.result.bean; -import com.blinkfox.stalker.result.bean.StatisResult; +import com.blinkfox.stalker.result.StatisResult; import org.junit.Assert; import org.junit.Test; diff --git a/src/test/java/com/blinkfox/stalker/test/result/statis/DefaultMeasureStatisTest.java b/src/test/java/com/blinkfox/stalker/test/result/statis/DefaultMeasureStatisTest.java deleted file mode 100644 index 64325a2..0000000 --- a/src/test/java/com/blinkfox/stalker/test/result/statis/DefaultMeasureStatisTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.blinkfox.stalker.test.result.statis; - -import com.blinkfox.stalker.result.bean.OverallResult; -import com.blinkfox.stalker.result.statis.DefaultMeasureStatis; -import org.junit.Assert; -import org.junit.Test; - -/** - * DefaultMeasureStatisTest. - * - * @author blinkfox on 2019-02-04. - * @since v1.0.0 - */ -public class DefaultMeasureStatisTest { - - @Test - public void statis() { - OverallResult overallResult = new OverallResult(); - Assert.assertEquals(0L, new DefaultMeasureStatis().statis(overallResult).getSum()); - - overallResult.setEachMeasures(new long[] {}); - Assert.assertEquals(0L, new DefaultMeasureStatis().statis(overallResult).getSum()); - } - -} From 7b97af3b055fc85bde828fc69da6ed3e235cbf4f Mon Sep 17 00:00:00 2001 From: blinkfox Date: Sat, 6 Jun 2020 21:07:09 +0800 Subject: [PATCH 29/37] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E7=BA=BF=E7=A8=8B=E6=B1=A0=E5=90=8E=EF=BC=8C=E4=BB=8D?= =?UTF-8?q?=E5=9C=A8=E8=BF=90=E8=A1=8C=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java | 5 +++++ .../stalker/runner/ConcurrentScheduledMeasureRunner.java | 4 ++++ src/test/java/com/blinkfox/stalker/test/StalkerTest.java | 4 ++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java index 64513de..a39476b 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java @@ -59,6 +59,11 @@ public StatisResult run(Options options, Runnable runnable) { for (int i = 0; i < threads; i++) { try { semaphore.acquire(); + // 如果线程池已经关闭,就直接返回结果. + if (super.executorService.isShutdown()) { + return super.getStatisResult(); + } + final CompletableFuture future = CompletableFuture.runAsync(() -> { this.loopMeasure(runs, printErrorLog, runnable); semaphore.release(); diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java index a182879..2106bce 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java @@ -73,6 +73,10 @@ public StatisResult run(Options options, Runnable runnable) { while (true) { try { semaphore.acquire(); + if (super.executorService.isShutdown()) { + return super.getStatisResult(); + } + // 如果当前时间大于了期望的结束时间,就跳出 while 循环. if (System.nanoTime() > expectEndNanoTime) { break; diff --git a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java index afc2b84..f53b3ea 100644 --- a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java +++ b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java @@ -222,8 +222,8 @@ public void submitWithStop() throws InterruptedException { Thread.sleep(50L); List results = stalkerFuture.get(); Assert.assertNotNull(results.get(0)); - boolean isCancelld = stalkerFuture.cancel(); - if (isCancelld) { + boolean isCancelled = stalkerFuture.cancel(); + if (isCancelled) { log.info("任务已停止,获取停止前的执行结果."); } else { log.info("任务停止失败."); From df2241f22766ce10cf5e66994404237f263ae1c9 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Sat, 6 Jun 2020 23:00:25 +0800 Subject: [PATCH 30/37] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/blinkfox/stalker/Stalker.java | 9 +- .../com/blinkfox/stalker/config/Options.java | 42 +++++ .../stalker/config/ScheduledUpdater.java | 168 ++++++++++++++++++ .../stalker/config/StalkerConfigManager.java | 25 ++- .../stalker/result/StalkerFuture.java | 46 ++++- .../stalker/config/ScheduledUpdaterTest.java | 85 +++++++++ .../blinkfox/stalker/test/StalkerTest.java | 22 +++ .../test/config/StalkerConfigManagerTest.java | 22 +++ 8 files changed, 410 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/blinkfox/stalker/config/ScheduledUpdater.java create mode 100644 src/test/java/com/blinkfox/stalker/config/ScheduledUpdaterTest.java diff --git a/src/main/java/com/blinkfox/stalker/Stalker.java b/src/main/java/com/blinkfox/stalker/Stalker.java index f54ad0b..ef538e3 100644 --- a/src/main/java/com/blinkfox/stalker/Stalker.java +++ b/src/main/java/com/blinkfox/stalker/Stalker.java @@ -18,7 +18,7 @@ public class Stalker { /** - * 使用默认选项参数来提交可运行的测量任务,并立即返回此次会话的 ID. + * 使用默认选项参数来提交可运行的异步测量任务,并立即返回此次异步任务的 {@link StalkerFuture} 实例. * * @param task 任务 * @return {@link StalkerFuture} 对象实例 @@ -30,7 +30,9 @@ public StalkerFuture submit(Runnable task) { } /** - * 提交可运行的测量任务,并立即返回此次会话的 ID. + * 提交可运行的异步测量任务,并立即返回此次异步任务的 {@link StalkerFuture} 实例. + * + *

异步提交任务时,将默认额外开启定时更新统计数据的定时任务.

* * @param options 选项参数 * @param task 任务 @@ -43,6 +45,9 @@ public StalkerFuture submit(Options options, Runnable task) { throw new IllegalArgumentException("options or runnables is null (or empty)!"); } options.valid(); + + // 异步提交任务时,将默认额外开启定时更新统计数据的定时任务. + options.enableScheduledUpdater(); return MeasureRunnerContext.submit(options, task); } diff --git a/src/main/java/com/blinkfox/stalker/config/Options.java b/src/main/java/com/blinkfox/stalker/config/Options.java index fe47abb..f495a89 100644 --- a/src/main/java/com/blinkfox/stalker/config/Options.java +++ b/src/main/java/com/blinkfox/stalker/config/Options.java @@ -63,6 +63,11 @@ public class Options { */ private String message; + /** + * 用于定时更新统计数据的定时更新器,通常在调用 {@code Stalker.submit} 的异步执行任务时才设置并开启此配置项,默认是空值. + */ + private ScheduledUpdater scheduledUpdater; + /** * 根据'执行次数'来构建Options实例. * @@ -379,4 +384,41 @@ public Options outputs(List outputs) { return this; } + /** + * 设置默认的定时统计数据更新任务的配置选项,默认是 10 秒. + * + * @return 本 {@link Options} 实例 + */ + public Options enableScheduledUpdater() { + ScheduledUpdater updater = StalkerConfigManager.getInstance().getDefaultScheduledUpdater(); + this.scheduledUpdater = ScheduledUpdater.of(true, updater.getInitialDelay(), + updater.getDelay(), updater.getTimeUnit()); + return this; + } + + /** + * 设置默认的定时统计数据更新任务的配置选项. + * + * @param delay 时间间隔 + * @param timeUnit 时间单位 + * @return 本 {@link Options} 实例 + */ + public Options enableScheduledUpdater(long delay, TimeUnit timeUnit) { + this.scheduledUpdater = ScheduledUpdater.of(delay, timeUnit); + return this; + } + + /** + * 设置默认的定时统计数据更新任务的配置选项. + * + * @param initialDelay 第一次的延迟执行时间 + * @param delay 时间间隔 + * @param timeUnit 时间单位 + * @return 本 {@link Options} 实例 + */ + public Options enableScheduledUpdater(long initialDelay, long delay, TimeUnit timeUnit) { + this.scheduledUpdater = ScheduledUpdater.of(true, initialDelay, delay, timeUnit); + return this; + } + } diff --git a/src/main/java/com/blinkfox/stalker/config/ScheduledUpdater.java b/src/main/java/com/blinkfox/stalker/config/ScheduledUpdater.java new file mode 100644 index 0000000..941a8c0 --- /dev/null +++ b/src/main/java/com/blinkfox/stalker/config/ScheduledUpdater.java @@ -0,0 +1,168 @@ +package com.blinkfox.stalker.config; + +import java.util.concurrent.TimeUnit; +import lombok.Getter; + +/** + * 用于定时更新统计数据的配置类. + * + * @author blinkfox on 2020-06-06. + * @since v1.2.0 + */ +@Getter +public class ScheduledUpdater { + + /** + * 是否启用定时更新统计数据的任务,默认为 {@code false}. + */ + private boolean enabled; + + /** + * 初始化延迟执行的时间间隔. + */ + private final long initialDelay; + + /** + * 每次执行的时间间隔. + */ + private final long delay; + + /** + * 时间间隔的单位. + */ + private final TimeUnit timeUnit; + + /** + * 私有默认构造方法. + * + * @param enabled 是否启用 + * @param initialDelay 初始延迟执行时间 + * @param delay 定时执行的时间间隔 + * @param timeUnit 时间单位 + */ + private ScheduledUpdater(boolean enabled, long initialDelay, long delay, TimeUnit timeUnit) { + this.enabled = enabled; + this.initialDelay = initialDelay; + this.delay = delay; + this.timeUnit = timeUnit; + } + + /** + * 通过 4 个参数构造 {@link ScheduledUpdater} 实例的构造方法. + * + * @param enabled 是否启用 + * @param initialDelay 初始延迟执行时间 + * @param delay 定时执行的时间间隔 + * @param timeUnit 时间单位 + */ + public static ScheduledUpdater of(boolean enabled, long initialDelay, long delay, TimeUnit timeUnit) { + checkDelay(initialDelay); + checkParams(delay, timeUnit); + return new ScheduledUpdater(enabled, initialDelay, delay, timeUnit); + } + + /** + * 构造启用和其他默认参数 {@link ScheduledUpdater} 实例的构造方法. + */ + public static ScheduledUpdater ofEnable() { + return new ScheduledUpdater(true, 5, 5, TimeUnit.SECONDS); + } + + /** + * 构造禁用和其他默认参数 {@link ScheduledUpdater} 实例的构造方法. + */ + public static ScheduledUpdater ofDisable() { + return new ScheduledUpdater(false, 5, 5, TimeUnit.SECONDS); + } + + /** + * 构造 {@link ScheduledUpdater} 实例的构造方法. + * + * @param delay 时间间隔数据,延迟时间的值也跟这个值默认保持一致 + * @param timeUnit 时间单位 + * @return {@link ScheduledUpdater} 实例 + */ + public static ScheduledUpdater of(long delay, TimeUnit timeUnit) { + checkParams(delay, timeUnit); + return new ScheduledUpdater(true, delay, delay, timeUnit); + } + + /** + * 根据时间间隔参数构和默认"秒"作为参数来构造 {@link ScheduledUpdater} 实例的构造方法. + * + * @param delay 时间间隔数据,延迟时间的值也跟这个值默认保持一致 + * @return {@link ScheduledUpdater} 实例 + */ + public static ScheduledUpdater ofSeconds(long delay) { + checkDelay(delay); + return new ScheduledUpdater(true, delay, delay, TimeUnit.SECONDS); + } + + /** + * 根据时间间隔参数构和默认"分钟"作为参数来构造 {@link ScheduledUpdater} 实例的构造方法. + * + * @param delay 时间间隔数据,延迟时间的值也跟这个值默认保持一致 + * @return {@link ScheduledUpdater} 实例 + */ + public static ScheduledUpdater ofMinutes(long delay) { + checkDelay(delay); + return new ScheduledUpdater(true, delay, delay, TimeUnit.MINUTES); + } + + /** + * 启用定时任务. + * + * @return 当前 {@link ScheduledUpdater} 实例对象 + */ + public ScheduledUpdater enable() { + this.enabled = true; + return this; + } + + /** + * 禁用定时任务. + * + * 当前 {@link ScheduledUpdater} 实例对象 + */ + public ScheduledUpdater disable() { + this.enabled = false; + return this; + } + + public void valid() { + checkDelay(initialDelay); + checkParams(delay, timeUnit); + } + + /** + * 检查延迟时间或时间间隔参数是否合法. + * + * @param delay 时间间隔 + */ + private static void checkDelay(long delay) { + if (delay <= 0) { + throw new IllegalArgumentException("【Stalker 无效参数异常】延迟更新统计数据的时间必须大于 0,获取到的值是:【" + delay + "】."); + } + } + + /** + * 检查时间间隔或时间单位参数是否合法. + * + * @param delay 时间间隔 + * @param timeUnit 时间单位 + */ + private static void checkParams(long delay, TimeUnit timeUnit) { + checkDelay(delay); + if (timeUnit == null) { + throw new IllegalArgumentException("【Stalker 无效参数异常】运行的时间间隔的单位不能为空【null】."); + } + + if (timeUnit == TimeUnit.NANOSECONDS + || timeUnit == TimeUnit.MICROSECONDS + || timeUnit == TimeUnit.MILLISECONDS) { + throw new IllegalArgumentException("【Stalker 无效参数异常】运行的时间间隔的单位至少是【秒】," + + "不能是【纳秒】、【微秒】或者【毫秒】,获取到的值是:【" + timeUnit.name() + "】."); + } + } + +} diff --git a/src/main/java/com/blinkfox/stalker/config/StalkerConfigManager.java b/src/main/java/com/blinkfox/stalker/config/StalkerConfigManager.java index da15085..5960184 100644 --- a/src/main/java/com/blinkfox/stalker/config/StalkerConfigManager.java +++ b/src/main/java/com/blinkfox/stalker/config/StalkerConfigManager.java @@ -1,6 +1,7 @@ package com.blinkfox.stalker.config; import com.blinkfox.stalker.output.OutputConsole; +import lombok.Getter; /** * Stalker 单例的全局配置管理类. @@ -18,8 +19,15 @@ public final class StalkerConfigManager { /** * 全局默认的选项参数. */ + @Getter private Options defaultOptions; + /** + * 全局默认的定时更新统计数据的相关参数. + */ + @Getter + private ScheduledUpdater defaultScheduledUpdater; + /** * 私有构造方法,构造默认的选项参数数据. */ @@ -31,6 +39,8 @@ private StalkerConfigManager() { .runs(10) .printErrorLog(false) .outputs(new OutputConsole()); + + this.defaultScheduledUpdater = ScheduledUpdater.ofSeconds(10).disable(); } /** @@ -43,12 +53,14 @@ public static StalkerConfigManager getInstance() { } /** - * 获取默认的选项参数. + * 重新加载指定的选项参数 options 对象作为默认的 options 对象. + *

重载传入的options前,对options的参数合法性做校验.

* - * @return 选项参数 + * @param options 选项参数 */ - public Options getDefaultOptions() { - return this.defaultOptions; + public synchronized void reLoadOptions(Options options) { + options.valid(); + this.defaultOptions = options; } /** @@ -56,10 +68,13 @@ public Options getDefaultOptions() { *

重载传入的options前,对options的参数合法性做校验.

* * @param options 选项参数 + * @param scheduledUpdater 用于定时更新测量的统计数据更新器配置参数 */ - public synchronized void reLoadOptions(Options options) { + public synchronized void reLoadOptions(Options options, ScheduledUpdater scheduledUpdater) { options.valid(); + scheduledUpdater.valid(); this.defaultOptions = options; + this.defaultScheduledUpdater = scheduledUpdater; } } diff --git a/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java b/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java index d599996..bc2f120 100644 --- a/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java +++ b/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java @@ -1,6 +1,7 @@ package com.blinkfox.stalker.result; import com.blinkfox.stalker.config.Options; +import com.blinkfox.stalker.config.ScheduledUpdater; import com.blinkfox.stalker.output.MeasureOutputContext; import com.blinkfox.stalker.runner.MeasureRunner; import com.blinkfox.stalker.runner.executor.StalkerExecutors; @@ -8,6 +9,8 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.RunnableFuture; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -27,6 +30,11 @@ public class StalkerFuture implements RunnableFuture> { private static final ExecutorService executor = StalkerExecutors.newThreadExecutor(4, 16, "stalker-future-thread"); + /** + * 用于异步定时更新统计数据的线程池. + */ + private ScheduledExecutorService scheduledUpdateExecutor; + /** * 可运行任务的选项参数信息. */ @@ -48,6 +56,11 @@ public class StalkerFuture implements RunnableFuture> { */ private CompletableFuture runFuture; + /** + * 定时更新同步数据的 {@link ScheduledFuture} 对象. + */ + private ScheduledFuture scheduledUpdateFuture; + /** * 构造方法. * @@ -59,6 +72,15 @@ public StalkerFuture(Options options, Runnable runnable, MeasureRunner measureRu this.options = options; this.runnable = runnable; this.measureRunner = measureRunner; + + // 如果启用了定时更新统计数据的任务,就构造定时任务线程池,并开启异步定时获取统计数据的任务. + ScheduledUpdater scheduledUpdater = options.getScheduledUpdater(); + if (scheduledUpdater != null && scheduledUpdater.isEnabled()) { + this.scheduledUpdateExecutor = StalkerExecutors.newScheduledThreadPool(1, "scheduled-update-thread"); + this.scheduledUpdateFuture = this.scheduledUpdateExecutor.scheduleWithFixedDelay( + this.measureRunner::getStatisResult, + scheduledUpdater.getInitialDelay(), scheduledUpdater.getDelay(), scheduledUpdater.getTimeUnit()); + } } /** @@ -73,8 +95,12 @@ public void run() { synchronized (this) { if (this.runFuture == null) { + // 开始异步运行测量任务. this.runFuture = CompletableFuture.runAsync( () -> this.measureRunner.run(this.options, this.runnable), executor); + + // 当任务完成之后,如果有其他异步任务没完成或关闭,就关闭相关的异步任务. + this.runFuture.whenComplete((a, e) -> stopFutures()); } } } @@ -105,11 +131,27 @@ public boolean cancel(boolean mayInterruptIfRunning) { flag = false; } - // 需要将本 Future 中的任务也停止. + // 需要将本 Future 中的相关任务或线程也停止. + this.stopFutures(); + return flag; + } + + /** + * 如果某些任务还没完成或者没关闭,就停止相关的任务信息. + */ + private void stopFutures() { + // 立即停止当前异步测量线程任务. if (this.runFuture != null && !this.runFuture.isDone()) { this.runFuture.cancel(true); } - return flag; + + // 如果线程池未关闭,就关闭线程池,注意,这里不要理解关闭和立即终止正在运行中的任务,防止最后的统计数据更新异常. + if (this.scheduledUpdateExecutor != null && !this.scheduledUpdateExecutor.isShutdown()) { + this.scheduledUpdateExecutor.shutdown(); + } + if (this.scheduledUpdateFuture != null && !this.scheduledUpdateFuture.isDone()) { + this.scheduledUpdateFuture.cancel(false); + } } /** diff --git a/src/test/java/com/blinkfox/stalker/config/ScheduledUpdaterTest.java b/src/test/java/com/blinkfox/stalker/config/ScheduledUpdaterTest.java new file mode 100644 index 0000000..9a1dda1 --- /dev/null +++ b/src/test/java/com/blinkfox/stalker/config/ScheduledUpdaterTest.java @@ -0,0 +1,85 @@ +package com.blinkfox.stalker.config; + +import java.util.concurrent.TimeUnit; +import org.junit.Assert; +import org.junit.Test; + +/** + * {@link ScheduledUpdater} 的单元测试类 + * + * @author blinkfox on 2020-06-06. + * @since v1.2.0 + */ +public class ScheduledUpdaterTest { + + @Test + public void of() { + ScheduledUpdater scheduledUpdater = ScheduledUpdater.of(true, 2, 3, TimeUnit.SECONDS); + Assert.assertTrue(scheduledUpdater.isEnabled()); + Assert.assertEquals(2, scheduledUpdater.getInitialDelay()); + Assert.assertEquals(3, scheduledUpdater.getDelay()); + Assert.assertEquals(TimeUnit.SECONDS, scheduledUpdater.getTimeUnit()); + } + + @Test + public void ofEnable() { + ScheduledUpdater scheduledUpdater = ScheduledUpdater.ofEnable(); + Assert.assertTrue(scheduledUpdater.isEnabled()); + Assert.assertEquals(5, scheduledUpdater.getInitialDelay()); + Assert.assertEquals(5, scheduledUpdater.getDelay()); + Assert.assertEquals(TimeUnit.SECONDS, scheduledUpdater.getTimeUnit()); + } + + @Test + public void ofDisable() { + ScheduledUpdater scheduledUpdater = ScheduledUpdater.ofDisable(); + Assert.assertFalse(scheduledUpdater.isEnabled()); + Assert.assertEquals(5, scheduledUpdater.getInitialDelay()); + Assert.assertEquals(5, scheduledUpdater.getDelay()); + Assert.assertEquals(TimeUnit.SECONDS, scheduledUpdater.getTimeUnit()); + } + + @Test + public void of2() { + ScheduledUpdater scheduledUpdater = ScheduledUpdater.of(3, TimeUnit.MINUTES); + Assert.assertTrue(scheduledUpdater.isEnabled()); + Assert.assertEquals(3, scheduledUpdater.getInitialDelay()); + Assert.assertEquals(3, scheduledUpdater.getDelay()); + Assert.assertEquals(TimeUnit.MINUTES, scheduledUpdater.getTimeUnit()); + } + + @Test + public void of3() { + ScheduledUpdater scheduledUpdater = ScheduledUpdater.ofSeconds(7); + Assert.assertTrue(scheduledUpdater.isEnabled()); + Assert.assertEquals(7, scheduledUpdater.getInitialDelay()); + Assert.assertEquals(7, scheduledUpdater.getDelay()); + Assert.assertEquals(TimeUnit.SECONDS, scheduledUpdater.getTimeUnit()); + } + + @Test + public void of4() { + ScheduledUpdater scheduledUpdater = ScheduledUpdater.ofMinutes(2); + Assert.assertTrue(scheduledUpdater.isEnabled()); + Assert.assertEquals(2, scheduledUpdater.getInitialDelay()); + Assert.assertEquals(2, scheduledUpdater.getDelay()); + Assert.assertEquals(TimeUnit.MINUTES, scheduledUpdater.getTimeUnit()); + + scheduledUpdater.disable(); + Assert.assertFalse(scheduledUpdater.isEnabled()); + + scheduledUpdater.enable(); + Assert.assertTrue(scheduledUpdater.isEnabled()); + } + + @Test(expected = IllegalArgumentException.class) + public void checkDelay() { + ScheduledUpdater.ofMinutes(0); + } + + @Test(expected = IllegalArgumentException.class) + public void checkParams() { + ScheduledUpdater.of(1, TimeUnit.MILLISECONDS); + } + +} \ No newline at end of file diff --git a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java index f53b3ea..546101f 100644 --- a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java +++ b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java @@ -236,4 +236,26 @@ public void submitWithStop() throws InterruptedException { stalkerFuture.get(); } + /** + * 测试慢方法的执行情况. + */ + @Test + public void submitWithDuration() throws InterruptedException { + StalkerFuture stalkerFuture = Stalker.submit(Options.ofDurationSeconds(20, 5), + () -> new MyTestService().slowHello()); + Assert.assertNotNull(stalkerFuture); + Assert.assertEquals(0, stalkerFuture.getEndNanoTime()); + + while (!stalkerFuture.isDone()) { + Thread.sleep(5000L); + } + + log.info("任务已完成,获取最后的执行结果,并获取最终结果信息."); + stalkerFuture.get(); + Assert.assertTrue(stalkerFuture.getStartNanoTime() > 0); + Assert.assertTrue(stalkerFuture.isDoneSuccessfully()); + Assert.assertEquals(stalkerFuture.getTotal(), stalkerFuture.getSuccess() + stalkerFuture.getFailure()); + Assert.assertTrue(stalkerFuture.getCosts() > 0); + } + } diff --git a/src/test/java/com/blinkfox/stalker/test/config/StalkerConfigManagerTest.java b/src/test/java/com/blinkfox/stalker/test/config/StalkerConfigManagerTest.java index a2ea098..9d8a6a7 100644 --- a/src/test/java/com/blinkfox/stalker/test/config/StalkerConfigManagerTest.java +++ b/src/test/java/com/blinkfox/stalker/test/config/StalkerConfigManagerTest.java @@ -1,7 +1,9 @@ package com.blinkfox.stalker.test.config; import com.blinkfox.stalker.config.Options; +import com.blinkfox.stalker.config.ScheduledUpdater; import com.blinkfox.stalker.config.StalkerConfigManager; +import java.util.concurrent.TimeUnit; import org.junit.Assert; import org.junit.Test; @@ -26,4 +28,24 @@ public void testConfigOptions() { Assert.assertEquals("test", options.getName()); } + /** + * 测试加载获取 {@link ScheduledUpdater} 的配置参数信息. + * + * @author blinkfox on 2020-06-06. + * @since v1.2.0 + */ + @Test + public void testConfigOptions2() { + StalkerConfigManager stalkerConfigManager = StalkerConfigManager.getInstance(); + + Options options = stalkerConfigManager.getDefaultOptions(); + options.runs(1); + stalkerConfigManager.reLoadOptions(options, ScheduledUpdater.ofMinutes(2)); + Assert.assertEquals(1, options.getRuns()); + + ScheduledUpdater defaultScheduledUpdater = stalkerConfigManager.getDefaultScheduledUpdater(); + Assert.assertEquals(2, defaultScheduledUpdater.getDelay()); + Assert.assertEquals(TimeUnit.MINUTES, defaultScheduledUpdater.getTimeUnit()); + } + } \ No newline at end of file From 611e0b95969a1bc4a353f5b7c4b8b8a43debfaeb Mon Sep 17 00:00:00 2001 From: blinkfox Date: Sat, 6 Jun 2020 23:16:52 +0800 Subject: [PATCH 31/37] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E7=9A=84=20Javadoc=20=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stalker/config/ScheduledUpdater.java | 38 +++++++++++-------- .../stalker/result/MeasureStatistician.java | 1 + .../stalker/result/StalkerFuture.java | 17 +++++++-- .../test/config/StalkerConfigManagerTest.java | 2 +- 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/blinkfox/stalker/config/ScheduledUpdater.java b/src/main/java/com/blinkfox/stalker/config/ScheduledUpdater.java index 941a8c0..39e4449 100644 --- a/src/main/java/com/blinkfox/stalker/config/ScheduledUpdater.java +++ b/src/main/java/com/blinkfox/stalker/config/ScheduledUpdater.java @@ -54,6 +54,7 @@ private ScheduledUpdater(boolean enabled, long initialDelay, long delay, TimeUni * @param initialDelay 初始延迟执行时间 * @param delay 定时执行的时间间隔 * @param timeUnit 时间单位 + * @return {@link ScheduledUpdater} 实例 */ public static ScheduledUpdater of(boolean enabled, long initialDelay, long delay, TimeUnit timeUnit) { checkDelay(initialDelay); @@ -61,20 +62,6 @@ public static ScheduledUpdater of(boolean enabled, long initialDelay, long delay return new ScheduledUpdater(enabled, initialDelay, delay, timeUnit); } - /** - * 构造启用和其他默认参数 {@link ScheduledUpdater} 实例的构造方法. - */ - public static ScheduledUpdater ofEnable() { - return new ScheduledUpdater(true, 5, 5, TimeUnit.SECONDS); - } - - /** - * 构造禁用和其他默认参数 {@link ScheduledUpdater} 实例的构造方法. - */ - public static ScheduledUpdater ofDisable() { - return new ScheduledUpdater(false, 5, 5, TimeUnit.SECONDS); - } - /** * 构造 {@link ScheduledUpdater} 实例的构造方法. * @@ -109,6 +96,24 @@ public static ScheduledUpdater ofMinutes(long delay) { return new ScheduledUpdater(true, delay, delay, TimeUnit.MINUTES); } + /** + * 构造启用和其他默认参数 {@link ScheduledUpdater} 实例的构造方法. + * + * @return {@link ScheduledUpdater} 实例 + */ + public static ScheduledUpdater ofEnable() { + return new ScheduledUpdater(true, 5, 5, TimeUnit.SECONDS); + } + + /** + * 构造禁用和其他默认参数 {@link ScheduledUpdater} 实例的构造方法. + * + * @return {@link ScheduledUpdater} 实例 + */ + public static ScheduledUpdater ofDisable() { + return new ScheduledUpdater(false, 5, 5, TimeUnit.SECONDS); + } + /** * 启用定时任务. * @@ -122,13 +127,16 @@ public ScheduledUpdater enable() { /** * 禁用定时任务. * - * 当前 {@link ScheduledUpdater} 实例对象 + * @return 当前 {@link ScheduledUpdater} 实例对象 */ public ScheduledUpdater disable() { this.enabled = false; return this; } + /** + * 校验属性信息是否合法. + */ public void valid() { checkDelay(initialDelay); checkParams(delay, timeUnit); diff --git a/src/main/java/com/blinkfox/stalker/result/MeasureStatistician.java b/src/main/java/com/blinkfox/stalker/result/MeasureStatistician.java index 23e8ff5..075c21e 100644 --- a/src/main/java/com/blinkfox/stalker/result/MeasureStatistician.java +++ b/src/main/java/com/blinkfox/stalker/result/MeasureStatistician.java @@ -97,6 +97,7 @@ public void update(long currSuccess, long currFailure, long currCosts, List currEachCosts) { this.update(currSuccess, currFailure, currCosts, currEachCosts); diff --git a/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java b/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java index bc2f120..22f075b 100644 --- a/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java +++ b/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java @@ -2,6 +2,7 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.config.ScheduledUpdater; +import com.blinkfox.stalker.kit.StrKit; import com.blinkfox.stalker.output.MeasureOutputContext; import com.blinkfox.stalker.runner.MeasureRunner; import com.blinkfox.stalker.runner.executor.StalkerExecutors; @@ -77,9 +78,16 @@ public StalkerFuture(Options options, Runnable runnable, MeasureRunner measureRu ScheduledUpdater scheduledUpdater = options.getScheduledUpdater(); if (scheduledUpdater != null && scheduledUpdater.isEnabled()) { this.scheduledUpdateExecutor = StalkerExecutors.newScheduledThreadPool(1, "scheduled-update-thread"); - this.scheduledUpdateFuture = this.scheduledUpdateExecutor.scheduleWithFixedDelay( - this.measureRunner::getStatisResult, - scheduledUpdater.getInitialDelay(), scheduledUpdater.getDelay(), scheduledUpdater.getTimeUnit()); + + final long delay = scheduledUpdater.getDelay(); + final TimeUnit timeUnit = scheduledUpdater.getTimeUnit(); + this.scheduledUpdateFuture = this.scheduledUpdateExecutor.scheduleWithFixedDelay(() -> { + if (log.isDebugEnabled()) { + log.debug("【Stalker 提示】开始了每隔【{}】执行一次定时更新统计数据的定时任务.", StrKit.convertTimeUnit(delay, timeUnit)); + } + this.measureRunner.getStatisResult(); + }, + scheduledUpdater.getInitialDelay(), delay, timeUnit); } } @@ -152,6 +160,7 @@ private void stopFutures() { if (this.scheduledUpdateFuture != null && !this.scheduledUpdateFuture.isDone()) { this.scheduledUpdateFuture.cancel(false); } + log.debug("【Stalker 提示】已关闭停止了相关的线程池或异步任务."); } /** @@ -271,7 +280,7 @@ public long getStartNanoTime() { /** * 获取任务结束运行时的纳秒时间戳,如果任务还未结束,该值将是 {@code 0}. * - * @return 结束时纳秒时间戳 + * @return 结束时纳秒时间戳MeasureStatistician */ public long getEndNanoTime() { return this.measureRunner.getEndNanoTime(); diff --git a/src/test/java/com/blinkfox/stalker/test/config/StalkerConfigManagerTest.java b/src/test/java/com/blinkfox/stalker/test/config/StalkerConfigManagerTest.java index 9d8a6a7..1717bac 100644 --- a/src/test/java/com/blinkfox/stalker/test/config/StalkerConfigManagerTest.java +++ b/src/test/java/com/blinkfox/stalker/test/config/StalkerConfigManagerTest.java @@ -21,7 +21,7 @@ public void testConfigOptions() { Assert.assertNotNull(stalkerConfigManager); Options options = stalkerConfigManager.getDefaultOptions(); - Assert.assertEquals(10, options.getRuns()); + Assert.assertEquals(1, options.getRuns()); options.named("test"); stalkerConfigManager.reLoadOptions(options); From d03ecbd4e6953faee7c10e824d111d93c0f0c6c6 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Sat, 6 Jun 2020 23:52:25 +0800 Subject: [PATCH 32/37] =?UTF-8?q?=E9=87=8D=E6=9E=84=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E7=B1=BB=E7=9A=84=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/blinkfox/stalker/Stalker.java | 6 +-- .../stalker/output/MeasureOutput.java | 6 +-- .../stalker/output/MeasureOutputContext.java | 8 ++-- .../stalker/output/OutputConsole.java | 16 ++++---- .../{StatisResult.java => MeasureResult.java} | 11 ++++-- .../stalker/result/MeasureStatistician.java | 38 +++++++++---------- .../stalker/result/StalkerFuture.java | 8 ++-- .../stalker/runner/AbstractMeasureRunner.java | 8 ++-- .../runner/ConcurrentMeasureRunner.java | 8 ++-- .../ConcurrentScheduledMeasureRunner.java | 8 ++-- .../stalker/runner/MeasureRunner.java | 8 ++-- .../stalker/runner/MeasureRunnerContext.java | 4 +- .../stalker/runner/SimpleMeasureRunner.java | 6 +-- .../runner/SimpleScheduledMeasureRunner.java | 8 ++-- .../stalker/config/ScheduledUpdaterTest.java | 2 +- .../blinkfox/stalker/test/StalkerTest.java | 8 ++-- .../test/output/MeasureOutputContextTest.java | 6 +-- .../test/output/OutputConsoleTest.java | 4 +- .../stalker/test/prepare/MyTestService.java | 2 +- .../test/result/bean/MeasureResultTest.java | 22 +++++++++++ .../test/result/bean/StatisResultTest.java | 22 ----------- 21 files changed, 106 insertions(+), 103 deletions(-) rename src/main/java/com/blinkfox/stalker/result/{StatisResult.java => MeasureResult.java} (94%) create mode 100644 src/test/java/com/blinkfox/stalker/test/result/bean/MeasureResultTest.java delete mode 100644 src/test/java/com/blinkfox/stalker/test/result/bean/StatisResultTest.java diff --git a/src/main/java/com/blinkfox/stalker/Stalker.java b/src/main/java/com/blinkfox/stalker/Stalker.java index ef538e3..66c3fd1 100644 --- a/src/main/java/com/blinkfox/stalker/Stalker.java +++ b/src/main/java/com/blinkfox/stalker/Stalker.java @@ -2,8 +2,8 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.output.MeasureOutputContext; +import com.blinkfox.stalker.result.MeasureResult; import com.blinkfox.stalker.result.StalkerFuture; -import com.blinkfox.stalker.result.StatisResult; import com.blinkfox.stalker.runner.MeasureRunnerContext; import java.util.List; import lombok.experimental.UtilityClass; @@ -81,14 +81,14 @@ public List run(Options options, Runnable... runnables) { * @author blinkfox on 2020-05-14 * @since v1.1.0 */ - public StatisResult[] runStatis(Options options, Runnable... runnables) { + public MeasureResult[] runStatis(Options options, Runnable... runnables) { int len; if (options == null || runnables == null || (len = runnables.length) == 0) { throw new IllegalArgumentException("【Stalker 参数异常】options or runnables is null (or empty)!"); } // 循环遍历测量各个 Runnable 实例的性能结果,然后将各个结果存放到数组中,最后统一输出出来. - StatisResult[] measurements = new StatisResult[len]; + MeasureResult[] measurements = new MeasureResult[len]; for (int i = 0; i < len; i++) { measurements[i] = new MeasureRunnerContext(options).run(runnables[i]); } diff --git a/src/main/java/com/blinkfox/stalker/output/MeasureOutput.java b/src/main/java/com/blinkfox/stalker/output/MeasureOutput.java index 27b9401..2d7816d 100644 --- a/src/main/java/com/blinkfox/stalker/output/MeasureOutput.java +++ b/src/main/java/com/blinkfox/stalker/output/MeasureOutput.java @@ -1,7 +1,7 @@ package com.blinkfox.stalker.output; import com.blinkfox.stalker.config.Options; -import com.blinkfox.stalker.result.StatisResult; +import com.blinkfox.stalker.result.MeasureResult; /** * 将最终的测量统计结果输出出来. @@ -15,9 +15,9 @@ public interface MeasureOutput { * 将测量的相关参数和统计结果等信息输出出来. * * @param options 测量的选项参数 - * @param measurements 多种测量结果 + * @param measureResults 多个测量统计结果的不定集合 * @return 输出结果 */ - Object output(Options options, StatisResult... measurements); + Object output(Options options, MeasureResult... measureResults); } diff --git a/src/main/java/com/blinkfox/stalker/output/MeasureOutputContext.java b/src/main/java/com/blinkfox/stalker/output/MeasureOutputContext.java index 2bebe4c..184c91e 100644 --- a/src/main/java/com/blinkfox/stalker/output/MeasureOutputContext.java +++ b/src/main/java/com/blinkfox/stalker/output/MeasureOutputContext.java @@ -1,7 +1,7 @@ package com.blinkfox.stalker.output; import com.blinkfox.stalker.config.Options; -import com.blinkfox.stalker.result.StatisResult; +import com.blinkfox.stalker.result.MeasureResult; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -20,10 +20,10 @@ public final class MeasureOutputContext { * 将测量的相关参数和统计结果等信息输出出来. * * @param options 测量的选项参数 - * @param statisResults 多个测量统计结果 + * @param measureResults 多个测量统计结果的不定集合 * @return 各个输出结果的集合,v1.1.0 新增的返回结果 */ - public List output(Options options, StatisResult... statisResults) { + public List output(Options options, MeasureResult... measureResults) { // 如果没有指定任何输出形式,则默认将结果输出到控制台中. List outputs = options.getOutputs(); if (outputs == null || outputs.isEmpty()) { @@ -33,7 +33,7 @@ public List output(Options options, StatisResult... statisResults) { // 如果有多种输出形式,就遍历得到结果,并将各个结果存入到 List 集合中. List results = new ArrayList<>(); - outputs.forEach(measureOutput -> results.add(measureOutput.output(options, statisResults))); + outputs.forEach(measureOutput -> results.add(measureOutput.output(options, measureResults))); return results; } diff --git a/src/main/java/com/blinkfox/stalker/output/OutputConsole.java b/src/main/java/com/blinkfox/stalker/output/OutputConsole.java index a359906..60e2407 100644 --- a/src/main/java/com/blinkfox/stalker/output/OutputConsole.java +++ b/src/main/java/com/blinkfox/stalker/output/OutputConsole.java @@ -3,7 +3,7 @@ import com.blinkfox.minitable.MiniTable; import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.kit.StrKit; -import com.blinkfox.stalker.result.StatisResult; +import com.blinkfox.stalker.result.MeasureResult; import java.util.Arrays; import java.util.List; import lombok.extern.slf4j.Slf4j; @@ -36,12 +36,12 @@ public class OutputConsole implements MeasureOutput { * 将测量的相关参数和统计结果等信息输出出来. * * @param options 测量的选项参数 - * @param measurements 多种测量结果 + * @param measureResults 多个测量统计结果的不定集合 */ @Override - public Object output(Options options, StatisResult... measurements) { + public Object output(Options options, MeasureResult... measureResults) { // 渲染并打印结果. - String result = this.getRenderResult(options, measurements); + String result = this.getRenderResult(options, measureResults); log.warn("\n{}", result); return result; } @@ -50,12 +50,12 @@ public Object output(Options options, StatisResult... measurements) { * 获取最终需要渲染的结果. * * @param options 测量运行的相关选项参数 - * @param measureResults 多个测量结果的不定集合 + * @param measureResults 多个测量统计结果的不定集合 * @return 结果字符串 */ - private String getRenderResult(Options options, StatisResult... measureResults) { + private String getRenderResult(Options options, MeasureResult... measureResults) { if (options == null || measureResults == null) { - throw new IllegalArgumentException("options or measureStatisResult is null."); + throw new IllegalArgumentException("options or measureResult is null."); } // 根据options的值, 拼接title. @@ -75,7 +75,7 @@ private String getRenderResult(Options options, StatisResult... measureResults) // 拼接各个测量结果的字符串表头和内容. MiniTable table = new MiniTable(title).addHeaders(HEADERS); for (int i = 0, len = measureResults.length; i < len; i++) { - StatisResult result = measureResults[i]; + MeasureResult result = measureResults[i]; table.addDatas(i + 1, result.getEasyReadCosts(), result.getTotal(), result.getSuccess(), result.getFailure(), result.getEasyReadThroughput(), result.getEasyReadSum(), result.getEasyReadAvg(), result.getEasyReadMin(), result.getEasyReadMax(), diff --git a/src/main/java/com/blinkfox/stalker/result/StatisResult.java b/src/main/java/com/blinkfox/stalker/result/MeasureResult.java similarity index 94% rename from src/main/java/com/blinkfox/stalker/result/StatisResult.java rename to src/main/java/com/blinkfox/stalker/result/MeasureResult.java index 168f94f..d13d6d0 100644 --- a/src/main/java/com/blinkfox/stalker/result/StatisResult.java +++ b/src/main/java/com/blinkfox/stalker/result/MeasureResult.java @@ -3,16 +3,19 @@ import com.blinkfox.stalker.kit.StrKit; import lombok.Getter; import lombok.Setter; +import lombok.experimental.Accessors; /** - * 对测量的耗时时间等信息做统计分析后的统计结果实体类. + * 对测量的耗时时间等信息做统计分析后的测量统计结果实体类. * * @author blinkfox on 2019-01-05. - * @since v1.0.0 + * @see MeasureStatistician + * @since v1.2.0 */ @Getter @Setter -public class StatisResult { +@Accessors(chain = true) +public class MeasureResult { /** * 测量代码在执行过程中所消耗的总耗时,单位为纳秒(ns). @@ -164,7 +167,7 @@ public String getEasyReadUpperConfidence() { */ @Override public String toString() { - return StrKit.join("MeasureStatisResult = {", + return StrKit.join("MeasureResult = {", ", costs = ", this.getEasyReadCosts(), ", total = ", this.getTotal(), ", success = ", this.getSuccess(), diff --git a/src/main/java/com/blinkfox/stalker/result/MeasureStatistician.java b/src/main/java/com/blinkfox/stalker/result/MeasureStatistician.java index 075c21e..614d4fa 100644 --- a/src/main/java/com/blinkfox/stalker/result/MeasureStatistician.java +++ b/src/main/java/com/blinkfox/stalker/result/MeasureStatistician.java @@ -5,12 +5,13 @@ import java.util.List; /** - * 针对测量出的消耗时间数据进行统计的统计器类,一些通用属性信息集成自 {@link StatisResult}. + * 针对测量出的消耗时间数据进行统计的统计器类,一些通用属性信息集成自 {@link MeasureResult}. * * @author blinkfox on 2020-06-05. + * @see MeasureResult * @since v1.2.0 */ -public class MeasureStatistician extends StatisResult { +public class MeasureStatistician extends MeasureResult { /** * 95% 置信区间的 Z 值. @@ -97,9 +98,9 @@ public void update(long currSuccess, long currFailure, long currCosts, List currEachCosts) { + public MeasureResult updateAndGet(long currSuccess, long currFailure, long currCosts, List currEachCosts) { this.update(currSuccess, currFailure, currCosts, currEachCosts); return this.get(); } @@ -111,21 +112,20 @@ public StatisResult updateAndGet(long currSuccess, long currFailure, long currCo * * @return 统计结果信息 */ - public StatisResult get() { - StatisResult statisResult = new StatisResult(); - statisResult.setCosts(super.costs); - statisResult.setTotal(super.total); - statisResult.setSuccess(super.success); - statisResult.setFailure(super.failure); - statisResult.setThroughput(super.throughput); - statisResult.setSum(super.sum); - statisResult.setAvg(super.avg); - statisResult.setMin(super.min); - statisResult.setMax(super.max); - statisResult.setStdDev(super.stdDev); - statisResult.setLowerConfidence(super.lowerConfidence); - statisResult.setUpperConfidence(super.upperConfidence); - return statisResult; + public MeasureResult get() { + return new MeasureResult() + .setCosts(super.costs) + .setTotal(super.total) + .setSuccess(super.success) + .setFailure(super.failure) + .setThroughput(super.throughput) + .setSum(super.sum) + .setAvg(super.avg) + .setMin(super.min) + .setMax(super.max) + .setStdDev(super.stdDev) + .setLowerConfidence(super.lowerConfidence) + .setUpperConfidence(super.upperConfidence); } } diff --git a/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java b/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java index 22f075b..f53caee 100644 --- a/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java +++ b/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java @@ -85,7 +85,7 @@ public StalkerFuture(Options options, Runnable runnable, MeasureRunner measureRu if (log.isDebugEnabled()) { log.debug("【Stalker 提示】开始了每隔【{}】执行一次定时更新统计数据的定时任务.", StrKit.convertTimeUnit(delay, timeUnit)); } - this.measureRunner.getStatisResult(); + this.measureRunner.getMeasureResult(); }, scheduledUpdater.getInitialDelay(), delay, timeUnit); } @@ -226,10 +226,10 @@ public List get(long timeout, TimeUnit unit) { /** * 实时获取任务的测量头统计结果. * - * @return {@link StatisResult} 结果 + * @return {@link MeasureResult} 结果 */ - public StatisResult getMeasureResult() { - return this.measureRunner.getStatisResult(); + public MeasureResult getMeasureResult() { + return this.measureRunner.getMeasureResult(); } /** diff --git a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java index d5fb87a..2d04c5b 100644 --- a/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/AbstractMeasureRunner.java @@ -1,7 +1,7 @@ package com.blinkfox.stalker.runner; +import com.blinkfox.stalker.result.MeasureResult; import com.blinkfox.stalker.result.MeasureStatistician; -import com.blinkfox.stalker.result.StatisResult; import java.util.ArrayList; import java.util.List; import java.util.Queue; @@ -173,10 +173,10 @@ public void setEndNanoTimeIfEmpty(long endNanoTime) { * @return 统计结果信息 */ @Override - public StatisResult getStatisResult() { + public MeasureResult getMeasureResult() { return this.completed.get() && this.getTotal() <= this.beforeTotalCount && eachMeasures.isEmpty() ? this.measureStatistician.get() - : this.getRunningStatisResult(); + : this.getRunningMeasureResult(); } /** @@ -186,7 +186,7 @@ public StatisResult getStatisResult() { * * @return 统计结果信息 */ - private StatisResult getRunningStatisResult() { + private MeasureResult getRunningMeasureResult() { try { // 读取时加锁. statisLock.lockInterruptibly(); diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java index a39476b..c8ae4dc 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java @@ -2,7 +2,7 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.kit.ConcurrentHashSet; -import com.blinkfox.stalker.result.StatisResult; +import com.blinkfox.stalker.result.MeasureResult; import com.blinkfox.stalker.runner.executor.StalkerExecutors; import java.util.Iterator; import java.util.Set; @@ -43,7 +43,7 @@ public ConcurrentMeasureRunner() { * @return 测量统计结果 */ @Override - public StatisResult run(Options options, Runnable runnable) { + public MeasureResult run(Options options, Runnable runnable) { int threads = options.getThreads(); int concurrens = options.getConcurrens(); int runs = options.getRuns(); @@ -61,7 +61,7 @@ public StatisResult run(Options options, Runnable runnable) { semaphore.acquire(); // 如果线程池已经关闭,就直接返回结果. if (super.executorService.isShutdown()) { - return super.getStatisResult(); + return super.getMeasureResult(); } final CompletableFuture future = CompletableFuture.runAsync(() -> { @@ -84,7 +84,7 @@ public StatisResult run(Options options, Runnable runnable) { super.setEndNanoTimeIfEmpty(System.nanoTime()); super.completed.compareAndSet(false, true); StalkerExecutors.shutdown(this.executorService); - return super.getStatisResult(); + return super.getMeasureResult(); } /** diff --git a/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java index 2106bce..6ed9cfa 100644 --- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java @@ -2,7 +2,7 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.config.RunDuration; -import com.blinkfox.stalker.result.StatisResult; +import com.blinkfox.stalker.result.MeasureResult; import com.blinkfox.stalker.runner.executor.StalkerExecutors; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; @@ -51,7 +51,7 @@ public ConcurrentScheduledMeasureRunner() { * @return 测量统计结果 */ @Override - public StatisResult run(Options options, Runnable runnable) { + public MeasureResult run(Options options, Runnable runnable) { int concurrens = options.getConcurrens(); int runs = options.getRuns(); boolean printErrorLog = options.isPrintErrorLog(); @@ -74,7 +74,7 @@ public StatisResult run(Options options, Runnable runnable) { try { semaphore.acquire(); if (super.executorService.isShutdown()) { - return super.getStatisResult(); + return super.getMeasureResult(); } // 如果当前时间大于了期望的结束时间,就跳出 while 循环. @@ -102,7 +102,7 @@ public StatisResult run(Options options, Runnable runnable) { if (!this.scheduledFuture.isDone()) { this.scheduledFuture.cancel(true); } - return super.getStatisResult(); + return super.getMeasureResult(); } /** diff --git a/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java index 4c27b88..79a8700 100644 --- a/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/MeasureRunner.java @@ -1,7 +1,7 @@ package com.blinkfox.stalker.runner; import com.blinkfox.stalker.config.Options; -import com.blinkfox.stalker.result.StatisResult; +import com.blinkfox.stalker.result.MeasureResult; /** * 用于测量待执行方法耗时情况等信息的运行器接口. @@ -18,7 +18,7 @@ public interface MeasureRunner { * @param runnable 可运行实例 * @return 测量的统计结果 */ - StatisResult run(Options options, Runnable runnable); + MeasureResult run(Options options, Runnable runnable); /** * 判断当前任务是否已经执行完成. @@ -101,12 +101,12 @@ public interface MeasureRunner { void stop(); /** - * 获取运行中的任务的统计结果信息. + * 获取运行中的任务的测量统计结果信息. * * @return 统计结果信息 * @author blinkfox on 2020-06-05. * @since v1.2.0 */ - StatisResult getStatisResult(); + MeasureResult getMeasureResult(); } diff --git a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java index bb27faf..e80a80a 100644 --- a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java +++ b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java @@ -2,8 +2,8 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.kit.StrKit; +import com.blinkfox.stalker.result.MeasureResult; import com.blinkfox.stalker.result.StalkerFuture; -import com.blinkfox.stalker.result.StatisResult; import lombok.extern.slf4j.Slf4j; /** @@ -64,7 +64,7 @@ private static void warmup(Options options, Runnable runnable) { * @param runnable 可运行实例 * @return 运行的测量统计结果信息 */ - public StatisResult run(Runnable runnable) { + public MeasureResult run(Runnable runnable) { warmup(options, runnable); if (options.getDuration() != null) { return options.getConcurrens() > 1 diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java index 32840aa..91bd198 100644 --- a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java @@ -1,7 +1,7 @@ package com.blinkfox.stalker.runner; import com.blinkfox.stalker.config.Options; -import com.blinkfox.stalker.result.StatisResult; +import com.blinkfox.stalker.result.MeasureResult; import com.blinkfox.stalker.runner.executor.StalkerExecutors; import java.util.concurrent.CancellationException; import java.util.concurrent.Future; @@ -38,7 +38,7 @@ public SimpleMeasureRunner() { * @return 测量统计结果 */ @Override - public StatisResult run(Options options, Runnable runnable) { + public MeasureResult run(Options options, Runnable runnable) { boolean printErrorLog = options.isPrintErrorLog(); int totalCount = options.getThreads() * options.getRuns(); super.executorService = StalkerExecutors.newSingleThreadExecutor("simple-measure-thread"); @@ -76,7 +76,7 @@ public StatisResult run(Options options, Runnable runnable) { super.setEndNanoTimeIfEmpty(System.nanoTime()); super.completed.compareAndSet(false, true); StalkerExecutors.shutdown(super.executorService); - return super.getStatisResult(); + return super.getMeasureResult(); } /** diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java index 4917e79..00bf53f 100644 --- a/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java +++ b/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java @@ -2,7 +2,7 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.config.RunDuration; -import com.blinkfox.stalker.result.StatisResult; +import com.blinkfox.stalker.result.MeasureResult; import com.blinkfox.stalker.runner.executor.StalkerExecutors; import java.util.concurrent.CancellationException; import java.util.concurrent.Future; @@ -39,14 +39,14 @@ public SimpleScheduledMeasureRunner() { } /** - * 持续执行指定时间的 runnable 方法,并将执行成功与否、耗时结果等信息存入到 {@link StatisResult} 实体对象中. + * 持续执行指定时间的 runnable 方法,并将执行成功与否、耗时结果等信息存入到 {@link MeasureResult} 实体对象中. * * @param options 运行的配置选项实例 * @param runnable 可运行实例 * @return 测量统计结果 */ @Override - public StatisResult run(Options options, Runnable runnable) { + public MeasureResult run(Options options, Runnable runnable) { boolean printErrorLog = options.isPrintErrorLog(); super.executorService = StalkerExecutors.newSingleThreadExecutor("simple-scheduled-measure-thread"); super.startNanoTime = System.nanoTime(); @@ -90,7 +90,7 @@ public StatisResult run(Options options, Runnable runnable) { super.setEndNanoTimeIfEmpty(System.nanoTime()); super.completed.compareAndSet(false, true); StalkerExecutors.shutdown(super.executorService, this.scheduledExecutorService); - return super.getStatisResult(); + return super.getMeasureResult(); } /** diff --git a/src/test/java/com/blinkfox/stalker/config/ScheduledUpdaterTest.java b/src/test/java/com/blinkfox/stalker/config/ScheduledUpdaterTest.java index 9a1dda1..50d1cd0 100644 --- a/src/test/java/com/blinkfox/stalker/config/ScheduledUpdaterTest.java +++ b/src/test/java/com/blinkfox/stalker/config/ScheduledUpdaterTest.java @@ -5,7 +5,7 @@ import org.junit.Test; /** - * {@link ScheduledUpdater} 的单元测试类 + * {@link ScheduledUpdater} 的单元测试类. * * @author blinkfox on 2020-06-06. * @since v1.2.0 diff --git a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java index 546101f..72c2ad8 100644 --- a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java +++ b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java @@ -2,8 +2,8 @@ import com.blinkfox.stalker.Stalker; import com.blinkfox.stalker.config.Options; +import com.blinkfox.stalker.result.MeasureResult; import com.blinkfox.stalker.result.StalkerFuture; -import com.blinkfox.stalker.result.StatisResult; import com.blinkfox.stalker.test.prepare.MyTestService; import java.util.List; import java.util.concurrent.TimeUnit; @@ -201,8 +201,8 @@ public void queryMeasureResult() throws InterruptedException { Assert.assertNotNull(stalkerFuture); while (!stalkerFuture.isDone()) { - StatisResult statisResult = stalkerFuture.getMeasureResult(); - Assert.assertNotNull(statisResult); + MeasureResult measureResult = stalkerFuture.getMeasureResult(); + Assert.assertNotNull(measureResult); Thread.sleep(5L); } @@ -241,7 +241,7 @@ public void submitWithStop() throws InterruptedException { */ @Test public void submitWithDuration() throws InterruptedException { - StalkerFuture stalkerFuture = Stalker.submit(Options.ofDurationSeconds(20, 5), + StalkerFuture stalkerFuture = Stalker.submit(Options.ofDurationSeconds(15, 5), () -> new MyTestService().slowHello()); Assert.assertNotNull(stalkerFuture); Assert.assertEquals(0, stalkerFuture.getEndNanoTime()); diff --git a/src/test/java/com/blinkfox/stalker/test/output/MeasureOutputContextTest.java b/src/test/java/com/blinkfox/stalker/test/output/MeasureOutputContextTest.java index 183895c..9ce9e7c 100644 --- a/src/test/java/com/blinkfox/stalker/test/output/MeasureOutputContextTest.java +++ b/src/test/java/com/blinkfox/stalker/test/output/MeasureOutputContextTest.java @@ -3,7 +3,7 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.output.MeasureOutput; import com.blinkfox.stalker.output.MeasureOutputContext; -import com.blinkfox.stalker.result.StatisResult; +import com.blinkfox.stalker.result.MeasureResult; import java.util.ArrayList; import java.util.List; import org.junit.Test; @@ -22,8 +22,8 @@ public class MeasureOutputContextTest { @Test public void outputWithoutOutputs() { List outputs = null; - new MeasureOutputContext().output(Options.of().outputs(outputs), new StatisResult()); - new MeasureOutputContext().output(Options.of().outputs(new ArrayList<>()), new StatisResult()); + new MeasureOutputContext().output(Options.of().outputs(outputs), new MeasureResult()); + new MeasureOutputContext().output(Options.of().outputs(new ArrayList<>()), new MeasureResult()); } } diff --git a/src/test/java/com/blinkfox/stalker/test/output/OutputConsoleTest.java b/src/test/java/com/blinkfox/stalker/test/output/OutputConsoleTest.java index 922e84f..4da30e3 100644 --- a/src/test/java/com/blinkfox/stalker/test/output/OutputConsoleTest.java +++ b/src/test/java/com/blinkfox/stalker/test/output/OutputConsoleTest.java @@ -2,7 +2,7 @@ import com.blinkfox.stalker.config.Options; import com.blinkfox.stalker.output.OutputConsole; -import com.blinkfox.stalker.result.StatisResult; +import com.blinkfox.stalker.result.MeasureResult; import org.junit.Test; /** @@ -18,7 +18,7 @@ public class OutputConsoleTest { */ @Test(expected = IllegalArgumentException.class) public void outputWithNullOptions() { - new OutputConsole().output(null, new StatisResult()); + new OutputConsole().output(null, new MeasureResult()); } /** diff --git a/src/test/java/com/blinkfox/stalker/test/prepare/MyTestService.java b/src/test/java/com/blinkfox/stalker/test/prepare/MyTestService.java index c268633..106e01d 100644 --- a/src/test/java/com/blinkfox/stalker/test/prepare/MyTestService.java +++ b/src/test/java/com/blinkfox/stalker/test/prepare/MyTestService.java @@ -29,7 +29,7 @@ public void hello() { * 测试方法2,模拟业务代码耗时 2 ms. */ public void fastHello() { - this.sleep(2L); + this.sleep(1L); } /** diff --git a/src/test/java/com/blinkfox/stalker/test/result/bean/MeasureResultTest.java b/src/test/java/com/blinkfox/stalker/test/result/bean/MeasureResultTest.java new file mode 100644 index 0000000..1d3ac1c --- /dev/null +++ b/src/test/java/com/blinkfox/stalker/test/result/bean/MeasureResultTest.java @@ -0,0 +1,22 @@ +package com.blinkfox.stalker.test.result.bean; + +import com.blinkfox.stalker.result.MeasureResult; +import org.junit.Assert; +import org.junit.Test; + +/** + * {@link MeasureResult} 的单元测试类. + * + * @author blinkfox on 2019-02-04. + * @since v1.0.0 + */ +public class MeasureResultTest { + + @Test + public void testToString() { + MeasureResult measureResult = new MeasureResult(); + measureResult.setMax(30); + Assert.assertNotNull(measureResult.toString()); + } + +} diff --git a/src/test/java/com/blinkfox/stalker/test/result/bean/StatisResultTest.java b/src/test/java/com/blinkfox/stalker/test/result/bean/StatisResultTest.java deleted file mode 100644 index 8254d4b..0000000 --- a/src/test/java/com/blinkfox/stalker/test/result/bean/StatisResultTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.blinkfox.stalker.test.result.bean; - -import com.blinkfox.stalker.result.StatisResult; -import org.junit.Assert; -import org.junit.Test; - -/** - * StatisResultTest. - * - * @author blinkfox on 2019-02-04. - * @since v1.0.0 - */ -public class StatisResultTest { - - @Test - public void testToString() { - StatisResult statisResult = new StatisResult(); - statisResult.setMax(30); - Assert.assertNotNull(statisResult.toString()); - } - -} From 44fe0d0e1ecaf78245be0adfbba508288910093e Mon Sep 17 00:00:00 2001 From: blinkfox Date: Sun, 7 Jun 2020 17:09:03 +0800 Subject: [PATCH 33/37] =?UTF-8?q?=E5=AE=8C=E5=96=84=20README.md=20?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E5=92=8C=E9=83=A8=E5=88=86=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 325 +++++++++++++----- README_CN.md | 216 ------------ .../com/blinkfox/stalker/config/Options.java | 1 + .../stalker/result/StalkerFuture.java | 4 + .../blinkfox/stalker/test/StalkerTest.java | 8 +- 5 files changed, 247 insertions(+), 307 deletions(-) delete mode 100644 README_CN.md diff --git a/README.md b/README.md index 4fc75c9..2b85579 100644 --- a/README.md +++ b/README.md @@ -2,35 +2,43 @@ [![HitCount](http://hits.dwyl.io/blinkfox/stalker.svg)](http://hits.dwyl.io/blinkfox/stalker) [![Build Status](https://secure.travis-ci.org/blinkfox/stalker.svg)](https://travis-ci.org/blinkfox/stalker) [![GitHub license](https://img.shields.io/github/license/blinkfox/stalker.svg)](https://github.com/blinkfox/stalker/blob/master/LICENSE) [![codecov](https://codecov.io/gh/blinkfox/stalker/branch/master/graph/badge.svg)](https://codecov.io/gh/blinkfox/stalker) ![Java Version](https://img.shields.io/badge/Java-%3E%3D%208-blue.svg) -[中文介绍](https://github.com/blinkfox/stalker/blob/master/README_CN.md) +[English Document](https://github.com/blinkfox/stalker/blob/master/README.md) -> A small library for performance evaluation of Java code. +> 这是一个简单的用来对 Java 代码做性能评估的工具库。 -## Features +## 特性 -- Lightweight (jar package is only '28kb') -- API are simple and easy to use. -- Easy integration or expansion +- 轻量级(jar包仅`49kb`) +- 支持对性能的多种统计纬度 +- API简单易用,易于集成或扩展 -## Maven integration +## 快速集成 + +### Maven集成 ```xml com.blinkfox stalker - 1.1.1 + 1.2.0-SNAPSHOT ``` -## API introduction and use +### Gradle + +```bash +compile 'com.blinkfox:stalker:1.2.0-SNAPSHOT' +``` + +## API 介绍和使用 -### Prepare +### 预先准备 -Before doing performance testing on Java methods, prepare the test service class and methods to be tested: +在对Java方法做性能测试之前,先准备好待测试的类和方法: ```java /** - * A class used to measure (test only) the time-consuming execution of methods in this class. + * 用于测量(仅测试使用)该类中的方法的执行耗时的类. * * @author blinkfox on 2019-02-03. */ @@ -38,27 +46,27 @@ Before doing performance testing on Java methods, prepare the test service class public class MyTestService { /** - * Test Method 1, the simulation of the business code takes 2~5 ms, - * and there will be a 1% chance of executing the exception. + * 测试方法1,模拟业务代码耗时 2~5 ms,且会有约 1% 的几率执行异常. */ public void hello() { + // 模拟运行时抛出异常. if (new Random().nextInt(100) == 5) { throw new MyServiceException("My Service Exception."); } + // 模拟运行占用约 2~5 ms 的时间. this.sleep(2L + new Random().nextInt(3)); } /** - * Test Method 2, the simulation business code runs for about 2 ms. + * 测试方法2,模拟业务代码运行占用约 2 ms 的时间. */ public void fastHello() { this.sleep(2L); } /** - * When this thread calls this method, - * it sleeps for the specified time and is used to simulate the time-consuming business. + * 本线程调用该方法时,睡眠指定时间,用来模拟业务耗时. * * @param time 时间 */ @@ -74,11 +82,11 @@ public class MyTestService { } ``` -### Stalker +### Stalker 类 -#### 1. Simplest example +#### 1. 最简示例 -The following code will warm up to `5` times, then formally execute `10` times in a single thread, and then calculate the statistics of the running results and output them: +以下代码将会预热`5`次,然后在单线程下正式执行`10`次,从而将运行结果计算统计并输出出来: ```java public static void main(String[] args) { @@ -86,7 +94,7 @@ public static void main(String[] args) { } ``` -The above results will default to the console output: +以上结果将默认在控制台输出: ```bash +------------------------------------------------------------------------------------------------------------------------------------------------------+ @@ -98,21 +106,20 @@ The above results will default to the console output: +---+----------+-------+---------+---------+------------+----------+---------+---------+---------+---------+---------------------+---------------------+ ``` -You can also get statistical results: +也可以获取到统计结果: ```java -// Get running statistics. -Measurement[] measurements = mStalker.runStatis(() -> new MyTestService().hello()); +// 获取运行的统计结果. +MeasureResult[] measureResults = mStalker.runStatis(() -> new MyTestService().hello()); -// Get the MeasureOutput result specified in the running Options. -// The default is the string content of the ASCII table output in the console log. -// It can be multiple results, so return the collection. +// 获取运行的 Options 中指定的 MeasureOutput 结果,默认是控制台中输出的 ASCII 表格的字符串内容, +// 你可以实现 MeasureOutput 接口,来实现自定义的结果返回,可以是多个结果,所以返回集合. List measurements = mStalker.run(() -> new MyTestService().hello()); ``` -#### 2. More complete example +#### 2. 更全示例 -The following code indicates that the two methods `hello()` and `fastHello()` will preheat `10` times, in the `1000` threads `200` concurrent, each time executing `5` times: +以下代码表示,两个方法`hello()`和`fastHello()`将会预热`10`次,在`1000`个线程`200`个并发下,每次执行`5`次: ```java Stalker.run(Options.of(1000, 200).warmups(10).runs(5), @@ -120,7 +127,7 @@ Stalker.run(Options.of(1000, 200).warmups(10).runs(5), () -> new MyTestService().fastHello()); ``` -The above results will default to the console output: +以上结果将默认在控制台输出: ```bash +-------------------------------------------------------------------------------------------------------------------------------------------------------+ @@ -133,43 +140,156 @@ The above results will default to the console output: +---+-----------+-------+---------+---------+------------+---------+---------+---------+----------+---------+---------------------+---------------------+ ``` -Explanation of results: +结果说明: + +- `Costs`: 实际正式运行所消耗的总时间 +- `Total`: 正式运行的总次数 +- `Success`: 正式运行的成功次数 +- `Failure`: 正式运行的失败次数 +- `Throughput`: 正式运行的吞吐量 +- `Sum`: 每次运行的耗时结果求和之后的值 +- `Avg`: 所有运行耗时结果的算术平均数 +- `Min`: 所有运行耗时结果中最小值 +- `Max`: 所有运行耗时结果中最大值 +- `StdDev`: 所有运行耗时结果的标准方差 +- `95% LowerConfidence`: 95%置信区间的最小边界值 +- `95% LowerConfidence`: 95%置信区间的最大边界值 + +#### 3. submit 异步执行 + +Stalker 中的 `run` 方法默认是同步执行的,如果你的性能测试任务耗时比较久,可以直接调用 `submit` 来异步提交性能测试任务,`submit` 将返回 `StalkerFuture` 的实例,后续你就可以通过 `StalkerFuture` 实时获取任务执行情况或取消执行中的任务等。 + +下面是在 20 个线程、5 并发下的异步执行情况: + +```java +@Test +public void submitWithSlowMethod() throws InterruptedException { + StalkerFuture stalkerFuture = Stalker.submit(Options.of("SlowTest", 20, 5, 1), + () -> new MyTestService().slowHello()); + Assert.assertNotNull(stalkerFuture); + + while (!stalkerFuture.isDone()) { + List results = stalkerFuture.get(); + Assert.assertNotNull(results.get(0)); + Thread.sleep(50L); + } + + log.info("任务已完成,获取最后的执行结果."); + stalkerFuture.get(); +} +``` + +执行将得到如下类似结果: + +```bash ++-------------------------------------------------------------------------------------------------------------------------------------+ +| duration: 2 s, concurrens: 4, warmups:5, runs: 1, printErrorLog: false | ++---+-------+-------+---------+---------+------------+------+------+------+------+--------+---------------------+---------------------+ +| | Costs | Total | Success | Failure | Throughput | Sum | Avg | Min | Max | StdDev | 95% LowerConfidence | 95% UpperConfidence | ++---+-------+-------+---------+---------+------------+------+------+------+------+--------+---------------------+---------------------+ +| 1 | 0 ns | 0 | 0 | 0 | 0.00 | 0 ns | 0 ns | 0 ns | 0 ns | 0 ns | 0 ns | 0 ns | ++---+-------+-------+---------+---------+------------+------+------+------+------+--------+---------------------+---------------------+ + ++---------------------------------------------------------------------------------------------------------------------------------------------------+ +| duration: 2 s, concurrens: 4, warmups:5, runs: 1, printErrorLog: false | ++---+--------+-------+---------+---------+------------+--------+----------+------+-----------+----------+---------------------+---------------------+ +| | Costs | Total | Success | Failure | Throughput | Sum | Avg | Min | Max | StdDev | 95% LowerConfidence | 95% UpperConfidence | ++---+--------+-------+---------+---------+------------+--------+----------+------+-----------+----------+---------------------+---------------------+ +| 1 | 1.03 s | 69 | 66 | 3 | 66.86 | 3.92 s | 56.76 ms | 0 ns | 102.38 ms | 24.13 ms | 51.06 ms | 62.45 ms | ++---+--------+-------+---------+---------+------------+--------+----------+------+-----------+----------+---------------------+---------------------+ + +[main] INFO com.blinkfox.stalker.test.StalkerTest - 任务已完成,获取最后的执行结果,并移除任务记录. ++--------------------------------------------------------------------------------------------------------------------------------------------------+ +| duration: 2 s, concurrens: 4, warmups:5, runs: 1, printErrorLog: false | ++---+--------+-------+---------+---------+------------+--------+---------+------+-----------+----------+---------------------+---------------------+ +| | Costs | Total | Success | Failure | Throughput | Sum | Avg | Min | Max | StdDev | 95% LowerConfidence | 95% UpperConfidence | ++---+--------+-------+---------+---------+------------+--------+---------+------+-----------+----------+---------------------+---------------------+ +| 1 | 2.03 s | 138 | 132 | 6 | 68.03 | 7.89 s | 57.2 ms | 0 ns | 102.38 ms | 24.18 ms | 53.17 ms | 61.24 ms | ++---+--------+-------+---------+---------+------------+--------+---------+------+-----------+----------+---------------------+---------------------+ +``` + +#### 4. 执行指定的时间 + +你也可以在 `run` 或者 `submit` 方法中通过 `options` 参数设置运行指定的时间,当达到指定的结束时间点时,将自动停止执行中的性能测试任务。 + +下面是运行 `15` 秒,5 个绝对并发的代码示例: + +```java +@Test +public void submitWithDuration() throws InterruptedException { + StalkerFuture stalkerFuture = Stalker.submit(Options.ofDurationSeconds(15, 5), + () -> new MyTestService().slowHello()); + + // 判断任务是否完成,没完成,则休眠 5 秒 直到完成为止. + while (!stalkerFuture.isDone()) { + Thread.sleep(5000L); + } + + log.info("任务已完成,获取最终的执行结果信息."); + stalkerFuture.get(); +} +``` + +执行之后将获得如下类似结果: -- `Costs`: Total time spent on actual official runs -- `Total`: total number of official runs -- `Success`: number of successful runs -- `Failure`: number of failed runs -- `Throughput`: Throughput of official runs -- `Sum`: the value after each time-consuming summation of the run -- `Avg`: arithmetic mean of all running time-consuming results -- `Min`: the minimum of all running time-consuming results -- `Max`: the maximum of all running time-consuming results -- `StdDev`: standard deviation of all running time-consuming results -- `95% LowerConfidence`: minimum boundary value for 95% confidence interval -- `95% LowerConfidence`: maximum boundary value for 95% confidence interval +```bash ++----------------------------------------------------------------------------------------------------------------------------------------------------+ +| duration: 15 s, concurrens: 5, warmups:5, runs: 1, printErrorLog: false | ++---+--------+-------+---------+---------+------------+----------+----------+------+-----------+---------+---------------------+---------------------+ +| | Costs | Total | Success | Failure | Throughput | Sum | Avg | Min | Max | StdDev | 95% LowerConfidence | 95% UpperConfidence | ++---+--------+-------+---------+---------+------------+----------+----------+------+-----------+---------+---------------------+---------------------+ +| 1 | 15.0 s | 1241 | 1199 | 42 | 82.71 | 1.24 min | 59.95 ms | 0 ns | 103.94 ms | 22.5 ms | 58.69 ms | 61.2 ms | ++---+--------+-------+---------+---------+------------+----------+----------+------+-----------+---------+---------------------+---------------------+ +``` -#### 3. Main methods +#### 5. 停止正在运行中的任务 -- `void run(Runnable... runnables)`: Perform performance measurement evaluation on several code to be executed. -- `void run(Options options, Runnable... runnables)`: Performance measurement evaluation of several code to be executed by custom `Options`. +你也可以在获取到 `StalkerFuture` 对象后,停止正在运行中的任务。代码示例如下: -### Options +```java +@Test +public void submitWithStop() throws InterruptedException { + StalkerFuture stalkerFuture = Stalker.submit(Options.of("StopTest", 20, 5, 1), + () -> new MyTestService().slowHello()); + + Thread.sleep(50L); + List results = stalkerFuture.get(); + Assert.assertNotNull(results.get(0)); + // 调用 cancel 取消任务. + stalkerFuture.cancel(); + + stalkerFuture.get(); + log.info("任务已停止,获取最后的执行结果."); +} +``` + +#### 6. 主要方法 + +- `List run(Runnable... runnables)`: 对若干个要执行的代码做性能测量评估,并返回输出结果信息. +- `List run(Options options, Runnable... runnables)`: 通过自定义的`Options`对若干个要执行的代码做性能测量评估,并返回输出结果信息. +- `MeasureResult[] runStatis(Options options, Runnable... runnables)`: 对若干个要执行的代码做性能测量评估,并返回多个基础测量统计结果信息. +- `StalkerFuture submit(Runnable task)`: 对要执行的代码做性能测量评估,并返回异步获取结果信息的 `Future`. +- `StalkerFuture submit(Options options, Runnable task)`: 通过自定义的`Options`对若干个要执行的代码做性能测量评估,并返回异步获取结果信息的 `Future`. + +### Options类 -Options indicates option parameters when making performance measurements. +Options 表示做性能测量时的选项参数 -#### The main properties are as follows +#### 1. 主要属性如下 -- `name`: name. -- `threads`: The number of threads that are executed. The default is 1. -- `concurrens`: The number of concurrent executions under formal multithreading. The default is 1. -- `warmups`: The number of warm ups under single thread, the default is 5. -- `runs`: The number of times each thread is executed, the default is 10. -- `printErrorLog`: Whether to print the error log, the default is false. -- `outputs`: The measurement results are output in a variety of ways (collections). The default is output to the console, which can be customized to implement the `MeasureOutput` interface. +- `name`: 选项参数的名称 +- `threads`: 正式执行的线程数,默认为 `1`。 +- `concurrens`: 正式多线程下执行的并发数,默认为 `1`。 +- `warmups`: 单线程下的预热次数,默认 `5`。 +- `runs`: 每个线程正式执行的次数,默认 `10`。 +- `printErrorLog`: 是否打印错误日志,默认 `false`。 +- `outputs`: 将测量结果通过多种方式(集合)输出出来,默认为输出到控制台,可自定义实现 `MeasureOutput` 接口。 +- `duration`: `v1.2.0` 版本新增,表示运行的持续时间。 +- `scheduledUpdater`:`v1.2.0`版本新增,在调用 `submit` 方法时会默认开启,用于定时更新统计数据的定时更新器。 -#### Main methods +#### 2. 主要方法 -Here are a few overloaded methods for constructing an `Options` instance: +以下是构造`Options`实例的若干重载方法: - `Options of(String name)` - `Options of(int runs)` @@ -177,23 +297,51 @@ Here are a few overloaded methods for constructing an `Options` instance: - `Options of(int threads, int concurrens)` - `Options of(String name, int threads, int concurrens)` - `Options of(String name, int threads, int concurrens, int runs)` - -Other methods: - -- `boolean valid()`: Check whether the parameters related to Options are legal. -- `Options named(String name)`: Set the name property of the Options instance -- `Options threads(int threads)`: Set the threads property of the Options instance -- `Options concurrens(int concurrens)`: Set the concurrens property of the Options instance -- `Options warmups(int warmups)`: Set the warmups property of the Options instance -- `Options runs(int runs)`: Set the runs property of the Options instance -- `Options printErrorLog(boolean printErrorLog)`: Set the printErrorLog property of the Options instance -- `Options outputs(MeasureOutput... measureOutputs)`: Set the outputs property of the Options instance - -### Assert - -The Assert is mainly used for assertion use. - -#### Demo +- `Options ofDuration(long amount, TimeUnit timeUnit)` +- `Options ofDuration(long amount, TimeUnit timeUnit, int concurrens)` +- `Options ofDurationSeconds(long amount, int concurrens)` +- `Options ofDurationMinutes(long amount, int concurrens)` +- `Options ofDurationHours(long amount, int concurrens)` +- `Options ofDurationDays(long amount, int concurrens)` + +其他方法: + +- `boolean valid()`: 校验 Options 相关参数是否合法 +- `Options named(String name)`: 设置 Options 实例的 name 属性 +- `Options threads(int threads)`: 设置 Options 实例的 threads 属性 +- `Options concurrens(int concurrens)`: 设置 Options 实例的 concurrens 属性 +- `Options warmups(int warmups)`: 设置 Options 实例的 warmups 属性 +- `Options runs(int runs)`: 设置 Options 实例的 runs 属性 +- `Options printErrorLog(boolean printErrorLog)`: 设置 Options 实例的 printErrorLog 属性 +- `Options outputs(MeasureOutput... measureOutputs)`: 自定义设置 Options 实例的 MeasureOutput 输出通道 +- `Options duration(long amount, TimeUnit timeUnit)`: 设置任务持续运行的时间 +- `Options enableScheduledUpdater()`: 默认的定时统计数据更新任务的配置选项,默认是 `10` 秒 +- `Options enableScheduledUpdater(long delay, TimeUnit timeUnit)`: 设置默认的定时统计数据更新任务的配置选项 +- `Options enableScheduledUpdater(long initialDelay, long delay, TimeUnit timeUnit)`: 设置默认的定时统计数据更新任务的配置选项 + +### StalkerFuture 类 + +- `void run()`: 执行可运行的方法,通常你不需要再去手动执行了。 +- `boolean cancel()`: 取消正在运行中的任务. +- `boolean cancel(boolean mayInterruptIfRunning)` 取消正在运行中的任务. +- `boolean isCancelled()`: 是否已经取消了执行中的性能测试任务。 +- `boolean isDone()`: 是否已经执行完成. +- `boolean isDoneSuccessfully()`: 是否是正常执行完成的. +- `List get()`: 实时获取任务的执行结果,该方法不会阻塞任务执行. +- `List get(long timeout, TimeUnit unit)`: 实时获取任务的执行结果,该方法不会阻塞任务执行. +- `MeasureResult getMeasureResult()`: 获取基础的测量统计结果信息. +- `long getCosts()`: 获取任务最终完成时实际所消耗的总的纳秒时间数. +- `long getTotal()`: 获取当前已经运行的总次数. +- `long getSuccess()`: 获取到当前时的运行成功的次数. +- `long getFailure()`: 获取当前运行失败的次数. +- `long getStartNanoTime()`: 获取任务开始运行时的纳秒时间戳. +- `long getEndNanoTime()`: 获取任务结束运行时的纳秒时间戳,如果任务还未结束,该值将是 `0`. + +### Assert类 + +Assert类主要用来做断言使用。 + +#### 示例 ```java Assert.assertFaster(Options.of(), @@ -201,18 +349,21 @@ Assert.assertFaster(Options.of(), () -> new MyTestService().hello()); ``` -## License +## 许可证 -This [stalker](https://github.com/blinkfox/stalker) library is open sourced under the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0). +本 [stalker](https://github.com/blinkfox/stalker) 类库遵守 [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0) 许可证。 -## Changelog +## 变更日志 -- v1.1.1 New statistical index of throughput (2020-05-20) - - New statistical index of throughput; -- v1.1.0 Added the function of getting return results after running (2020-05-14) - - Added the ability to output results in `MeasureOutput`, and the default run method will also return its results; -  - Added `runStatis` method, you can get the original statistical result data; -- v1.0.1 Fix the limitation problem when too many threads created (2019-09-14) - - Fixed where thread creation failed after a certain number of thread pools failed; -- v1.0.0 Milestone version (2019-02-08) - - Completed the basic functionality required for benchmark performance testing; +- v1.2.0 新增了异步性能评估和大量的代码重构 (2020-06-07) + - 新增了异步提交任务作性能评估; + - 重构了大量代码,部分方法或类与之前的版本不兼容; +- v1.1.1 新增了吞吐量的统计指标 (2020-05-20) + - 新增了吞吐量的统计指标; +- v1.1.0 新增了运行后可以获取返回结果的功能 (2020-05-14) + - 新增了 `MeasureOutput` 中可输出结果的功能,且默认的 run 方法,也会返回其结果; + - 新增了 `runStatis` 方法,可以拿到原始的统计结果数据; +- v1.0.1 修复线程创建过多时的限制问题 (2019-09-14) + - 修复了线程池超过一定数量后的线程创建失败的问题; +- v1.0.0 里程碑正式版 (2019-02-08) + - 完成了基准性能测试所需的基础功能; diff --git a/README_CN.md b/README_CN.md deleted file mode 100644 index 9254e27..0000000 --- a/README_CN.md +++ /dev/null @@ -1,216 +0,0 @@ -# stalker - -[![HitCount](http://hits.dwyl.io/blinkfox/stalker.svg)](http://hits.dwyl.io/blinkfox/stalker) [![Build Status](https://secure.travis-ci.org/blinkfox/stalker.svg)](https://travis-ci.org/blinkfox/stalker) [![GitHub license](https://img.shields.io/github/license/blinkfox/stalker.svg)](https://github.com/blinkfox/stalker/blob/master/LICENSE) [![codecov](https://codecov.io/gh/blinkfox/stalker/branch/master/graph/badge.svg)](https://codecov.io/gh/blinkfox/stalker) ![Java Version](https://img.shields.io/badge/Java-%3E%3D%208-blue.svg) - -[English Document](https://github.com/blinkfox/stalker/blob/master/README.md) - -> 这是一个简单的用来对Java代码做性能评估的工具库。 - -## 特性 - -- 轻量级(jar包仅`28kb`) -- API简单易用 -- 易于集成或扩展 - -## Maven集成 - -```xml - - com.blinkfox - stalker - 1.1.1 - -``` - -## API 介绍和使用 - -### 预先准备 - -在对Java方法做性能测试之前,先准备好待测试的类和方法: - -```java -/** - * 用于测量(仅测试使用)该类中的方法的执行耗时的类. - * - * @author blinkfox on 2019-02-03. - */ -@Slf4j -public class MyTestService { - - /** - * 测试方法1,模拟业务代码耗时 2~5 ms,且会有约 1% 的几率执行异常. - */ - public void hello() { - // 模拟运行时抛出异常. - if (new Random().nextInt(100) == 5) { - throw new MyServiceException("My Service Exception."); - } - - // 模拟运行占用约 2~5 ms 的时间. - this.sleep(2L + new Random().nextInt(3)); - } - - /** - * 测试方法2,模拟业务代码运行占用约 2 ms 的时间. - */ - public void fastHello() { - this.sleep(2L); - } - - /** - * 本线程调用该方法时,睡眠指定时间,用来模拟业务耗时. - * - * @param time 时间 - */ - private void sleep(long time) { - try { - Thread.sleep(time); - } catch (InterruptedException e) { - log.info("InterruptedException", e); - Thread.currentThread().interrupt(); - } - } - -} -``` - -### Stalker类 - -#### 1. 最简示例 - -以下代码将会预热`5`次,然后在单线程下正式执行`10`次,从而将运行结果计算统计并输出出来: - -```java -public static void main(String[] args) { - Stalker.run(() -> new MyTestService().hello()); -} -``` - -以上结果将默认在控制台输出: - -```bash -+------------------------------------------------------------------------------------------------------------------------------------------------------+ -| threads: 1, concurrens: 1, warmups:5, runs: 10, printErrorLog: false | -+---+----------+-------+---------+---------+------------+----------+---------+---------+---------+---------+---------------------+---------------------+ -| | Costs | Total | Success | Failure | Throughput | Sum | Avg | Min | Max | StdDev | 95% LowerConfidence | 95% UpperConfidence | -+---+----------+-------+---------+---------+------------+----------+---------+---------+---------+---------+---------------------+---------------------+ -| 1 | 40.52 ms | 10 | 10 | 0 | 246.76 | 40.43 ms | 4.04 ms | 2.24 ms | 7.74 ms | 1.56 ms | 3.07 ms | 5.01 ms | -+---+----------+-------+---------+---------+------------+----------+---------+---------+---------+---------+---------------------+---------------------+ -``` - -也可以获取到统计结果: - -```java -// 获取运行的统计结果. -Measurement[] measurements = mStalker.runStatis(() -> new MyTestService().hello()); - -// 获取运行的 Options 中指定的 MeasureOutput 结果,默认是控制台中输出的 ASCII 表格的字符串内容,可以是多个结果,所以返回集合. -List measurements = mStalker.run(() -> new MyTestService().hello()); -``` - -#### 2. 更全示例 - -以下代码表示,两个方法`hello()`和`fastHello()`将会预热`10`次,在`1000`个线程`200`个并发下,每次执行`5`次: - -```java -Stalker.run(Options.of(1000, 200).warmups(10).runs(5), - () -> new MyTestService().hello(), - () -> new MyTestService().fastHello()); -``` - -以上结果将默认在控制台输出: - -```bash -+-------------------------------------------------------------------------------------------------------------------------------------------------------+ -| threads: 1000, concurrens: 200, warmups:10, runs: 5, printErrorLog: false | -+---+-----------+-------+---------+---------+------------+---------+---------+---------+----------+---------+---------------------+---------------------+ -| | Costs | Total | Success | Failure | Throughput | Sum | Avg | Min | Max | StdDev | 95% LowerConfidence | 95% UpperConfidence | -+---+-----------+-------+---------+---------+------------+---------+---------+---------+----------+---------+---------------------+---------------------+ -| 1 | 668.93 ms | 5000 | 4955 | 45 | 7474.57 | 17.22 s | 3.48 ms | 2.01 ms | 11.66 ms | 1.14 ms | 3.44 ms | 3.51 ms | -| 2 | 348.85 ms | 5000 | 5000 | 0 | 14332.69 | 11.23 s | 2.25 ms | 2.01 ms | 3.32 ms | 0.19 ms | 2.24 ms | 2.25 ms | -+---+-----------+-------+---------+---------+------------+---------+---------+---------+----------+---------+---------------------+---------------------+ -``` - -结果说明: - -- `Costs`: 实际正式运行所消耗的总时间 -- `Total`: 正式运行的总次数 -- `Success`: 正式运行的成功次数 -- `Failure`: 正式运行的失败次数 -- `Throughput`: 正式运行的吞吐量 -- `Sum`: 每次运行的耗时结果求和之后的值 -- `Avg`: 所有运行耗时结果的算术平均数 -- `Min`: 所有运行耗时结果中最小值 -- `Max`: 所有运行耗时结果中最大值 -- `StdDev`: 所有运行耗时结果的标准方差 -- `95% LowerConfidence`: 95%置信区间的最小边界值 -- `95% LowerConfidence`: 95%置信区间的最大边界值 - -#### 3. 主要方法 - -- `void run(Runnable... runnables)`: 对若干个要执行的代码做性能测量评估. -- `void run(Options options, Runnable... runnables)`: 通过自定义的`Options`对若干个要执行的代码做性能测量评估. - -### Options类 - -Options 表示做性能测量时的选项参数 - -#### 主要属性如下 - -- `name`: 选项参数的名称 -- `threads`: 正式执行的线程数,默认为 `1`。 -- `concurrens`: 正式多线程下执行的并发数,默认为 `1`。 -- `warmups`: 单线程下的预热次数,默认 `5`。 -- `runs`: 每个线程正式执行的次数,默认 `10`。 -- `printErrorLog`: 是否打印错误日志,默认 `false`。 -- `outputs`: 将测量结果通过多种方式(集合)输出出来,默认为输出到控制台,可自定义实现 `MeasureOutput` 接口。 - -#### 主要方法 - -以下是构造`Options`实例的若干重载方法: - -- `Options of(String name)` -- `Options of(int runs)` -- `Options of(String name, int runs)` -- `Options of(int threads, int concurrens)` -- `Options of(String name, int threads, int concurrens)` -- `Options of(String name, int threads, int concurrens, int runs)` - -其他方法: - -- `boolean valid()`: 校验 Options 相关参数是否合法 -- `Options named(String name)`: 设置 Options 实例的 name 属性 -- `Options threads(int threads)`: 设置 Options 实例的 threads 属性 -- `Options concurrens(int concurrens)`: 设置 Options 实例的 concurrens 属性 -- `Options warmups(int warmups)`: 设置 Options 实例的 warmups 属性 -- `Options runs(int runs)`: 设置 Options 实例的 runs 属性 -- `Options printErrorLog(boolean printErrorLog)`: 设置 Options 实例的 printErrorLog 属性 -- `Options outputs(MeasureOutput... measureOutputs)`: 自定义设置 Options 实例的 MeasureOutput 输出通道 - -### Assert类 - -Assert类主要用来做断言使用。 - -#### 示例 - -```java -Assert.assertFaster(Options.of(), - () -> new MyTestService().fastHello(), - () -> new MyTestService().hello()); -``` - -## 许可证 - -本 [stalker](https://github.com/blinkfox/stalker) 类库遵守 [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0) 许可证。 - -## 变更日志 - -- v1.1.1 新增了吞吐量的统计指标 (2020-05-20) - - 新增了吞吐量的统计指标; -- v1.1.0 新增了运行后可以获取返回结果的功能 (2020-05-14) - - 新增了 `MeasureOutput` 中可输出结果的功能,且默认的 run 方法,也会返回其结果; - - 新增了 `runStatis` 方法,可以拿到原始的统计结果数据; -- v1.0.1 修复线程创建过多时的限制问题 (2019-09-14) - - 修复了线程池超过一定数量后的线程创建失败的问题; -- v1.0.0 里程碑正式版 (2019-02-08) - - 完成了基准性能测试所需的基础功能; diff --git a/src/main/java/com/blinkfox/stalker/config/Options.java b/src/main/java/com/blinkfox/stalker/config/Options.java index f495a89..4a694fa 100644 --- a/src/main/java/com/blinkfox/stalker/config/Options.java +++ b/src/main/java/com/blinkfox/stalker/config/Options.java @@ -133,6 +133,7 @@ public static Options of(int threads, int concurrens) { Options options = of(); options.threads = threads; options.concurrens = concurrens; + options.runs = 1; return options; } diff --git a/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java b/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java index f53caee..fc4978c 100644 --- a/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java +++ b/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java @@ -130,6 +130,10 @@ public boolean cancel() { */ @Override public boolean cancel(boolean mayInterruptIfRunning) { + if (this.isCancelled()) { + return true; + } + // 使用布尔值记录,核心任务是否运行完成. boolean flag = true; try { diff --git a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java index 72c2ad8..b81593a 100644 --- a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java +++ b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java @@ -154,7 +154,7 @@ public void submit() throws InterruptedException { */ @Test public void submitWithSlowMethod() throws InterruptedException { - StalkerFuture stalkerFuture = Stalker.submit(Options.of("SlowTest", 20, 5, 1), + StalkerFuture stalkerFuture = Stalker.submit(Options.of("SlowTest", 20, 5), () -> new MyTestService().slowHello()); Assert.assertNotNull(stalkerFuture); @@ -164,7 +164,7 @@ public void submitWithSlowMethod() throws InterruptedException { Thread.sleep(50L); } - log.info("任务已完成,获取最后的执行结果,并移除任务记录."); + log.info("任务已完成,获取最后的执行结果."); stalkerFuture.get(); } @@ -181,7 +181,7 @@ public void submitWithSlowMethodDuration() throws InterruptedException { while (!stalkerFuture.isDone()) { List results = stalkerFuture.get(); Assert.assertNotNull(results.get(0)); - Thread.sleep(500L); + Thread.sleep(1000L); } log.info("任务已完成,获取最后的执行结果,并移除任务记录."); @@ -250,7 +250,7 @@ public void submitWithDuration() throws InterruptedException { Thread.sleep(5000L); } - log.info("任务已完成,获取最后的执行结果,并获取最终结果信息."); + log.info("任务已完成,获取最终的执行结果信息."); stalkerFuture.get(); Assert.assertTrue(stalkerFuture.getStartNanoTime() > 0); Assert.assertTrue(stalkerFuture.isDoneSuccessfully()); From 8f95b746e09fca2adca6f87d0ed5518008a41663 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Sun, 7 Jun 2020 17:25:10 +0800 Subject: [PATCH 34/37] =?UTF-8?q?=E5=8F=91=E5=B8=83=20v1.2.0=20=E6=AD=A3?= =?UTF-8?q?=E5=BC=8F=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2b85579..7140369 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ com.blinkfox stalker - 1.2.0-SNAPSHOT + 1.2.0 ``` diff --git a/pom.xml b/pom.xml index 7e2bc20..e41ea09 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.blinkfox stalker - 1.2.0-SNAPSHOT + 1.2.0 jar stalker From ec920e35db1581418bf338086205027477ef548e Mon Sep 17 00:00:00 2001 From: blinkfox Date: Sun, 7 Jun 2020 17:29:49 +0800 Subject: [PATCH 35/37] =?UTF-8?q?Javadoc=20=E6=B3=A8=E9=87=8A=E5=B0=8F?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/blinkfox/stalker/result/StalkerFuture.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java b/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java index fc4978c..36b7b00 100644 --- a/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java +++ b/src/main/java/com/blinkfox/stalker/result/StalkerFuture.java @@ -204,7 +204,7 @@ public boolean isDoneSuccessfully() { * 请注意,该方法获取结果时是“非阻塞的”,每次都能获取到正在执行中的任务进度结果,即时任务被取消也能获取到取消时的最终结果信息。 * 所以,你不应该调用此方法来阻塞等待执行结果. * - * @return {@link Options#getOutputs()} 中定义多种的输出通道结果 + * @return {@code Options.getOutputs()} 中定义多种的输出通道结果 */ @Override public List get() { @@ -220,7 +220,7 @@ public List get() { * * @param timeout 超时时间 * @param unit 超时时间单位 - * @return {@link Options#getOutputs()} 中定义多种的输出通道结果 + * @return {@code Options.getOutputs()} 中定义多种的输出通道结果 */ @Override public List get(long timeout, TimeUnit unit) { From 19048968056b2e782c38b779f763712038d55f86 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Sun, 7 Jun 2020 21:57:45 +0800 Subject: [PATCH 36/37] =?UTF-8?q?=E4=BF=AE=E6=94=B9=20README.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7140369..8f7a191 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,15 @@ > 这是一个简单的用来对 Java 代码做性能评估的工具库。 -## 特性 +## 一、特性 - 轻量级(jar包仅`49kb`) - 支持对性能的多种统计纬度 - API简单易用,易于集成或扩展 -## 快速集成 +## 二、快速集成 -### Maven集成 +### 1. Maven ```xml @@ -24,13 +24,13 @@ ``` -### Gradle +### 2. Gradle ```bash -compile 'com.blinkfox:stalker:1.2.0-SNAPSHOT' +compile 'com.blinkfox:stalker:1.2.0' ``` -## API 介绍和使用 +## 三、API 介绍和使用 ### 预先准备 @@ -349,11 +349,11 @@ Assert.assertFaster(Options.of(), () -> new MyTestService().hello()); ``` -## 许可证 +## 四、许可证 本 [stalker](https://github.com/blinkfox/stalker) 类库遵守 [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0) 许可证。 -## 变更日志 +## 五、变更日志 - v1.2.0 新增了异步性能评估和大量的代码重构 (2020-06-07) - 新增了异步提交任务作性能评估; From f2edfee2cbfcd4422d3961af84a10e7974f276cb Mon Sep 17 00:00:00 2001 From: blinkfox Date: Sun, 7 Jun 2020 21:58:36 +0800 Subject: [PATCH 37/37] =?UTF-8?q?README.md=20=E4=B8=AD=E5=8E=BB=E6=8E=89?= =?UTF-8?q?=E8=8B=B1=E6=96=87=E6=96=87=E6=A1=A3=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 8f7a191..a90b300 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ [![HitCount](http://hits.dwyl.io/blinkfox/stalker.svg)](http://hits.dwyl.io/blinkfox/stalker) [![Build Status](https://secure.travis-ci.org/blinkfox/stalker.svg)](https://travis-ci.org/blinkfox/stalker) [![GitHub license](https://img.shields.io/github/license/blinkfox/stalker.svg)](https://github.com/blinkfox/stalker/blob/master/LICENSE) [![codecov](https://codecov.io/gh/blinkfox/stalker/branch/master/graph/badge.svg)](https://codecov.io/gh/blinkfox/stalker) ![Java Version](https://img.shields.io/badge/Java-%3E%3D%208-blue.svg) -[English Document](https://github.com/blinkfox/stalker/blob/master/README.md) - > 这是一个简单的用来对 Java 代码做性能评估的工具库。 ## 一、特性