eachMeasures;
+
+ /**
+ * 测量过程中执行成功的次数.
+ */
+ protected LongAdder success;
+
+ /**
+ * 测量过程中执行失败的次数.
+ */
+ protected LongAdder failure;
+
+ /**
+ * 是否已经运行完成.
+ */
+ protected AtomicBoolean completed;
+
+ /**
+ * 是否已经被取消.
+ */
+ protected AtomicBoolean canceled;
+
+ /**
+ * 运行开始时的纳秒时间戳,单位为纳秒({@code ns}).
+ */
+ @Getter
+ protected long startNanoTime;
+
+ /**
+ * 运行结束时的纳秒时间戳,单位为纳秒({@code ns}).
+ */
+ @Getter
+ protected long endNanoTime;
+
+ /**
+ * 用于记录以前总共读取了 eachMeasures 中的数据总量,即总的偏移量.
+ */
+ 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();
+ this.completed = new AtomicBoolean(false);
+ 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();
+ }
+
+ /**
+ * 如果结束时间的值是 0,那么就设置结束时的纳秒时间.
+ *
+ * @param endNanoTime 结束纳秒时间.
+ */
+ public void setEndNanoTimeIfEmpty(long endNanoTime) {
+ if (this.endNanoTime == 0) {
+ this.endNanoTime = endNanoTime;
+ }
+ }
+
+ /**
+ * 更新并获取统计结果信息数据.
+ *
+ * 如果任务已经完成,就直接返回最终的测试结果数据即可,否则加锁获取正在运行中的任务的统计数据.
+ *
+ * @return 统计结果信息
+ */
+ @Override
+ public MeasureResult getMeasureResult() {
+ return this.completed.get() && this.getTotal() <= this.beforeTotalCount && eachMeasures.isEmpty()
+ ? this.measureStatistician.get()
+ : this.getRunningMeasureResult();
+ }
+
+ /**
+ * 获取正在运行中的任务的数据的统计结果信息.
+ *
+ * 由于可能会有两个或多个线程去更新和获取统计数据,这里须要加锁来获取正在运行中的任务的统计数据.
+ *
+ * @return 统计结果信息
+ */
+ private MeasureResult getRunningMeasureResult() {
+ 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();
+ }
+
+ // 截取复制出最新的测量耗时信息,这里从队列中"出队读取" 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 9b5bad5..c8ae4dc 100644
--- a/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java
+++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentMeasureRunner.java
@@ -1,15 +1,14 @@
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 com.blinkfox.stalker.kit.ConcurrentHashSet;
+import com.blinkfox.stalker.result.MeasureResult;
+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 java.util.concurrent.atomic.AtomicLong;
import lombok.extern.slf4j.Slf4j;
/**
@@ -19,29 +18,12 @@
* @since v1.0.0
*/
@Slf4j
-public class ConcurrentMeasureRunner implements MeasureRunner {
-
- private static final int N_1024 = 1024;
-
- /**
- * 每次'成功'测量出的待测量方法的耗时时间,单位为纳秒(ns).
- */
- private final Queue eachMeasures;
+public class ConcurrentMeasureRunner extends AbstractMeasureRunner {
/**
- * 测量过程中执行的总次数.
+ * 用于存放正在运行中的 Future 线程,便于在手动"停止"运行时,能取消正在执行中的任务.
*/
- private final AtomicLong total;
-
- /**
- * 测量过程中执行成功的次数.
- */
- private final AtomicLong success;
-
- /**
- * 测量过程中执行失败的次数.
- */
- private final AtomicLong failure;
+ protected final Set> runningFutures;
/**
* 构造方法.
@@ -49,10 +31,8 @@ public class ConcurrentMeasureRunner implements MeasureRunner {
* 这个类中的属性,需要支持高并发写入.
*/
public ConcurrentMeasureRunner() {
- this.eachMeasures = new ConcurrentLinkedQueue<>();
- this.total = new AtomicLong(0);
- this.success = new AtomicLong(0);
- this.failure = new AtomicLong(0);
+ super();
+ this.runningFutures = new ConcurrentHashSet<>();
}
/**
@@ -60,40 +40,51 @@ public ConcurrentMeasureRunner() {
*
* @param options 运行的配置选项实例
* @param runnable 可运行实例
- * @return 测量结果
+ * @return 测量统计结果
*/
@Override
- public OverallResult run(Options options, Runnable runnable) {
+ public MeasureResult run(Options options, Runnable runnable) {
int threads = options.getThreads();
int concurrens = options.getConcurrens();
int runs = options.getRuns();
boolean printErrorLog = options.isPrintErrorLog();
// 初始化存储的集合、线程池、并发工具类中的对象实例等.
- 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();
+ Semaphore semaphore = new Semaphore(concurrens);
+ CountDownLatch countLatch = new CountDownLatch(threads);
+ super.executorService = StalkerExecutors.newFixedThreadExecutor(threads, "concurrent-measure-thread");
+ super.startNanoTime = System.nanoTime();
// 在多线程下控制线程并发量,与循环搭配来一起执行和测量.
for (int i = 0; i < threads; i++) {
- executorService.submit(() -> {
- try {
- semaphore.acquire();
+ try {
+ semaphore.acquire();
+ // 如果线程池已经关闭,就直接返回结果.
+ if (super.executorService.isShutdown()) {
+ return super.getMeasureResult();
+ }
+
+ final CompletableFuture future = CompletableFuture.runAsync(() -> {
this.loopMeasure(runs, printErrorLog, runnable);
semaphore.release();
- } catch (InterruptedException e) {
- log.error("测量方法耗时信息在多线程下出错!", e);
- Thread.currentThread().interrupt();
- } finally {
- countDownLatch.countDown();
- }
- });
+ countLatch.countDown();
+ }, super.executorService);
+
+ // 将 future 添加到正在运行的 Future 信息集合中,并在 future 完成时,异步移除已经完成了的 future.
+ runningFutures.add(future);
+ future.whenCompleteAsync((a, b) -> runningFutures.remove(future));
+ } catch (InterruptedException e) {
+ log.error("【Stalker 错误提示】在多线程并发情况下测量任务执行的耗时信息的线程已被中断!", e);
+ Thread.currentThread().interrupt();
+ }
}
- // 等待所有线程执行完毕,并关闭线程池,最后将结果封装成实体信息.
- this.awaitAndShutdown(countDownLatch, executorService);
- return this.buildMeasurement(System.nanoTime() - start);
+ // 等待所有线程执行完毕,记录是否完成和完成时间,并关闭线程池等资源,最后将结果封装成实体信息返回.
+ this.await(countLatch);
+ super.setEndNanoTimeIfEmpty(System.nanoTime());
+ super.completed.compareAndSet(false, true);
+ StalkerExecutors.shutdown(this.executorService);
+ return super.getMeasureResult();
}
/**
@@ -103,17 +94,16 @@ 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++) {
- this.total.incrementAndGet();
try {
long eachStart = System.nanoTime();
runnable.run();
- this.eachMeasures.add(System.nanoTime() - eachStart);
- this.success.incrementAndGet();
+ super.eachMeasures.offer(System.nanoTime() - eachStart);
+ super.success.increment();
} catch (Exception e) {
// 如果待测量的方法,执行错误则失败数 +1,且根据选项参数来判断是否打印异常错误日志.
- this.failure.incrementAndGet();
+ super.failure.increment();
if (printErrorLog) {
log.error("测量方法耗时信息在多线程下出错!", e);
}
@@ -122,44 +112,52 @@ private void loopMeasure(int runs, boolean printErrorLog, final Runnable runnabl
}
/**
- * 等待所有线程执行完毕,并最终关闭线程池.
+ * 停止相关的运行测量任务.
*
- * @param countDownLatch countDownLatch实例
- * @param executorService 线程池
+ * 注意:如果任务未完成,则立即停止线程池,但是还不能停止正在运行中的若干任务线程,
+ * 暂时还没想到一个更好的、高性能的停止所有运行中的任务的方法.
+ *
+ * @author blinkfox on 2020-05-25.
+ * @since v1.2.0
*/
- private void awaitAndShutdown(CountDownLatch countDownLatch, ExecutorService executorService) {
- try {
- countDownLatch.await();
- } catch (InterruptedException e) {
- log.error("在多线程下等待测量结果结束时出错!", e);
- Thread.currentThread().interrupt();
- } finally {
- executorService.shutdown();
+ @Override
+ public void stop() {
+ if (!isCompleted()) {
+ super.setEndNanoTimeIfEmpty(System.nanoTime());
+ super.completed.compareAndSet(false, true);
+ super.canceled.compareAndSet(false, true);
+
+ // 停止时直接关闭线程池.
+ StalkerExecutors.shutdownNow(this.executorService);
+
+ // 迭代删除正在运行中的 Future,并取消正在运行中的任务.
+ Iterator> futureIterator = this.runningFutures.iterator();
+ while (futureIterator.hasNext()) {
+ CompletableFuture future = futureIterator.next();
+ this.runningFutures.remove(future);
+ if (!future.isDone()) {
+ future.cancel(true);
+ }
+ }
}
}
/**
- * 构造测量的结果信息的 Measurement 对象.
+ * 等待所有线程执行完毕,并最终关闭线程池.
*
- * @param costs 消耗的总耗时,单位是纳秒
- * @return Measurement对象
+ * @param countLatch 计数锁
+ * @author blinkfox on 2020-05-25.
+ * @since v1.2.0
*/
- 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();
+ private void await(CountDownLatch countLatch) {
+ try {
+ if (countLatch != null) {
+ countLatch.await();
+ }
+ } catch (InterruptedException e) {
+ log.error("【Stalker 错误提示】在并发执行下等待任务执行结束时出错!", e);
+ Thread.currentThread().interrupt();
}
-
- 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/ConcurrentScheduledMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java
new file mode 100644
index 0000000..6ed9cfa
--- /dev/null
+++ b/src/main/java/com/blinkfox/stalker/runner/ConcurrentScheduledMeasureRunner.java
@@ -0,0 +1,122 @@
+package com.blinkfox.stalker.runner;
+
+import com.blinkfox.stalker.config.Options;
+import com.blinkfox.stalker.config.RunDuration;
+import com.blinkfox.stalker.result.MeasureResult;
+import com.blinkfox.stalker.runner.executor.StalkerExecutors;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.Semaphore;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 继承自 {@link ConcurrentMeasureRunner},在多线程并发情况下的运行指定的持续时间的测量运行器.
+ *
+ * @author blinkfox on 2020-06-01.
+ * @since v1.2.0
+ */
+@Slf4j
+public class ConcurrentScheduledMeasureRunner extends ConcurrentMeasureRunner {
+
+ /**
+ * 用于异步定时调度任务的线程池.
+ */
+ private final ScheduledExecutorService scheduledExecutorService;
+
+ /**
+ * 执行中的测量任务的 {@link Future} 实例.
+ *
+ * @since v1.2.0
+ */
+ protected Future> scheduledFuture;
+
+ /**
+ * 构造方法.
+ *
+ * 这个类中的属性,需要支持高并发写入.
+ */
+ public ConcurrentScheduledMeasureRunner() {
+ super();
+ this.scheduledExecutorService = StalkerExecutors.newScheduledThreadPool(1, "concurrent-scheduled-thread");
+ super.executorService = StalkerExecutors.newFixedThreadExecutor(
+ StalkerExecutors.MAX_POOL_SIZE, "concurrent-measure-thread");
+ }
+
+ /**
+ * 持续并发的执行指定时间的 runnable 方法,并将执行成功与否、耗时结果等信息存入到 OverallResult 实体对象中.
+ *
+ * @param options 运行的配置选项实例
+ * @param runnable 可运行实例
+ * @return 测量统计结果
+ */
+ @Override
+ public MeasureResult run(Options options, Runnable runnable) {
+ int concurrens = options.getConcurrens();
+ int runs = options.getRuns();
+ boolean printErrorLog = options.isPrintErrorLog();
+
+ // 初始化存储的集合、线程池、并发工具类中的对象实例等.
+ final Semaphore semaphore = new Semaphore(concurrens);
+
+ // 到指定的持续时间之后,就取消执行中的任务,并关闭线程池,
+ // 注意,由于是定时任务,所以“是否取消”也设置为 false,用于区分是否是人为取消了任务,只有人为取消的才是 true.
+ final RunDuration duration = options.getDuration();
+ 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);
+
+ while (true) {
+ try {
+ semaphore.acquire();
+ if (super.executorService.isShutdown()) {
+ return super.getMeasureResult();
+ }
+
+ // 如果当前时间大于了期望的结束时间,就跳出 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));
+ } catch (InterruptedException e) {
+ log.error("【Stalker 错误提示】在多线程并发情况下测量任务执行的耗时信息的线程已被中断!", e);
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ // 等待所有线程执行完毕,记录是否完成和完成时间,并关闭线程池等资源,最后将结果封装成实体信息返回.
+ super.setEndNanoTimeIfEmpty(System.nanoTime());
+ super.completed.compareAndSet(false, true);
+ StalkerExecutors.shutdown(this.executorService, this.scheduledExecutorService);
+ if (!this.scheduledFuture.isDone()) {
+ this.scheduledFuture.cancel(true);
+ }
+ return super.getMeasureResult();
+ }
+
+ /**
+ * 停止相关的运行测量任务.
+ */
+ @Override
+ public void stop() {
+ super.stop();
+
+ // 关闭定时任务线程池和取消对应的定时任务.
+ StalkerExecutors.shutdown(this.scheduledExecutorService);
+ if (this.scheduledFuture != null && !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 78f3171..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.bean.OverallResult;
+import com.blinkfox.stalker.result.MeasureResult;
/**
* 用于测量待执行方法耗时情况等信息的运行器接口.
@@ -16,8 +16,97 @@ public interface MeasureRunner {
*
* @param options 运行的配置选项实例
* @param runnable 可运行实例
- * @return 测量结果
+ * @return 测量的统计结果
*/
- OverallResult run(Options options, Runnable runnable);
+ MeasureResult run(Options options, Runnable runnable);
+
+ /**
+ * 判断当前任务是否已经执行完成.
+ *
+ * @return 是否执行完成的布尔值
+ * @author blinkfox on 2020-05-23.
+ * @since v1.2.0
+ */
+ boolean isCompleted();
+
+ /**
+ * 判断当前任务是否已经被取消.
+ *
+ * @return 是否被取消的布尔值
+ * @author blinkfox on 2020-06-02.
+ * @since v1.2.0
+ */
+ 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();
+
+ /**
+ * 获取任务开始运行时的纳秒时间戳.
+ *
+ * @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();
+
+ /**
+ * 停止相关的运行测量任务.
+ *
+ * @author blinkfox on 2020-06-03.
+ * @since v1.2.0
+ */
+ void stop();
+
+ /**
+ * 获取运行中的任务的测量统计结果信息.
+ *
+ * @return 统计结果信息
+ * @author blinkfox on 2020-06-05.
+ * @since v1.2.0
+ */
+ 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 3d3ef40..e80a80a 100644
--- a/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java
+++ b/src/main/java/com/blinkfox/stalker/runner/MeasureRunnerContext.java
@@ -2,7 +2,8 @@
import com.blinkfox.stalker.config.Options;
import com.blinkfox.stalker.kit.StrKit;
-import com.blinkfox.stalker.result.bean.OverallResult;
+import com.blinkfox.stalker.result.MeasureResult;
+import com.blinkfox.stalker.result.StalkerFuture;
import lombok.extern.slf4j.Slf4j;
/**
@@ -35,7 +36,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();
@@ -61,13 +62,50 @@ private void warmup(Options options, Runnable runnable) {
* 检查Options参数是否合法,并进行预热准备,然后执行 runnable 方法,并将执行结果的耗时纳秒(ns)值存入到集合中.
*
* @param runnable 可运行实例
- * @return 运行的测量结果
+ * @return 运行的测量统计结果信息
*/
- public OverallResult run(Runnable runnable) {
- this.warmup(options, runnable);
- return options.getThreads() > 1 && options.getConcurrens() > 1
- ? new ConcurrentMeasureRunner().run(options, runnable)
- : new SimpleMeasureRunner().run(options, runnable);
+ public MeasureResult run(Runnable runnable) {
+ warmup(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);
+ }
+ }
+
+ /**
+ * 检查Options参数是否合法,并进行预热准备,然后执行 runnable 方法,并将执行结果的耗时纳秒(ns)值存入到集合中.
+ *
+ * @param options 运行的选项参数
+ * @param runnable 可运行实例
+ * @return 此次运行的会话 ID
+ * @author blinkfox on 2020-05-23.
+ * @since v1.2.0
+ */
+ public static StalkerFuture submit(final Options options, final Runnable runnable) {
+ // 预热运行.
+ warmup(options, runnable);
+
+ // 获取对应的 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();
+ }
+
+ // 构造 StalkerFuture 对象,并开始运行任务.
+ StalkerFuture stalkerFuture = new StalkerFuture(options, runnable, measureRunner);
+ stalkerFuture.run();
+ return stalkerFuture;
}
}
diff --git a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java
index f6f7572..91bd198 100644
--- a/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java
+++ b/src/main/java/com/blinkfox/stalker/runner/SimpleMeasureRunner.java
@@ -1,8 +1,10 @@
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 com.blinkfox.stalker.result.MeasureResult;
+import com.blinkfox.stalker.runner.executor.StalkerExecutors;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.Future;
import lombok.extern.slf4j.Slf4j;
/**
@@ -12,76 +14,91 @@
* @since v1.0.0
*/
@Slf4j
-public class SimpleMeasureRunner implements MeasureRunner {
+public class SimpleMeasureRunner extends AbstractMeasureRunner {
/**
- * 每次'成功'测量出的待测量方法的耗时时间,单位为纳秒(ns).
- */
- private long[] eachMeasures;
-
- /**
- * 测量过程中执行的总次数.
- */
- private long total;
-
- /**
- * 测量过程中执行成功的次数.
+ * 执行中的测量任务的 {@link Future} 实例.
+ *
+ * @since v1.2.0
*/
- private long success;
+ protected Future> measureFuture;
/**
- * 测量过程中执行失败的次数.
+ * 构造方法.
*/
- private long failure;
+ public SimpleMeasureRunner() {
+ super();
+ }
/**
* 执行 runnable 方法,并将执行成功与否、耗时结果等信息存入到 OverallResult 实体对象中.
- * 这里由于是单线程的重写方法,不再需要`new Thread`了,直接调用`runnable.run()`即可.
*
* @param options 运行的配置选项实例
* @param runnable 可运行实例
- * @return 测量结果
+ * @return 测量统计结果
*/
@Override
- public OverallResult run(Options options, Runnable runnable) {
+ public MeasureResult 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.executorService = StalkerExecutors.newSingleThreadExecutor("simple-measure-thread");
+ 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++;
- } catch (RuntimeException e) {
- this.failure++;
- if (printErrorLog) {
- log.error("【stalker 错误】测量方法耗时信息出错!", e);
+ // 由于并发数是 1,直接单线程循环执行 (runs * threads) 次即可,
+ // 将执行的相关任务以 Future 的形式来执行,便于程序动态取消任务或判断任务执行情况等.
+ this.measureFuture = executorService.submit(() -> {
+ for (int i = 0; i < totalCount; ++i) {
+ try {
+ // 开始执行测量任务,记录开始时间、执行次数等.
+ long eachStart = System.nanoTime();
+ runnable.run();
+ super.eachMeasures.offer(System.nanoTime() - eachStart);
+ super.success.increment();
+ } catch (Exception e) {
+ super.failure.increment();
+ if (printErrorLog) {
+ log.error("【stalker 错误】测量方法耗时信息出错!", e);
+ }
}
}
+ });
+
+ // 阻塞调用要执行的测量任务,达到等待任务结束的目的.
+ try {
+ this.measureFuture.get();
+ } catch (CancellationException e) {
+ log.info("【Stalker 提示】已取消或完成测量任务.");
+ } catch (Exception e) {
+ log.error("【Stalker 错误】执行测量任务发生错误!", e);
}
- return this.buildMeasurement(System.nanoTime() - start);
+ // 等待所有线程执行完毕,并关闭线程池,最后将结果封装成实体信息.
+ super.setEndNanoTimeIfEmpty(System.nanoTime());
+ super.completed.compareAndSet(false, true);
+ StalkerExecutors.shutdown(super.executorService);
+ return super.getMeasureResult();
}
/**
- * 构造测量的结果信息的 Measurement 对象.
+ * 停止相关的运行测量任务.
*
- * @param costs 消耗的总耗时,单位是纳秒
- * @return Measurement对象
+ * @author blinkfox on 2020-05-25.
+ * @since v1.2.0
*/
- 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));
+ @Override
+ public void stop() {
+ if (!isCompleted()) {
+ super.setEndNanoTimeIfEmpty(System.nanoTime());
+ super.completed.compareAndSet(false, true);
+ super.canceled.compareAndSet(false, true);
+
+ // 立即关闭线程池.
+ StalkerExecutors.shutdownNow(super.executorService);
+ // 取消正在执行中的任务.
+ if (this.measureFuture != null && !this.measureFuture.isDone()) {
+ this.measureFuture.cancel(true);
+ }
+ }
}
}
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..00bf53f
--- /dev/null
+++ b/src/main/java/com/blinkfox/stalker/runner/SimpleScheduledMeasureRunner.java
@@ -0,0 +1,113 @@
+package com.blinkfox.stalker.runner;
+
+import com.blinkfox.stalker.config.Options;
+import com.blinkfox.stalker.config.RunDuration;
+import com.blinkfox.stalker.result.MeasureResult;
+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;
+
+/**
+ * 继承自 {@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 = StalkerExecutors.newScheduledThreadPool(1, "simple-scheduled-thread");
+ }
+
+ /**
+ * 持续执行指定时间的 runnable 方法,并将执行成功与否、耗时结果等信息存入到 {@link MeasureResult} 实体对象中.
+ *
+ * @param options 运行的配置选项实例
+ * @param runnable 可运行实例
+ * @return 测量统计结果
+ */
+ @Override
+ public MeasureResult run(Options options, Runnable runnable) {
+ boolean printErrorLog = options.isPrintErrorLog();
+ super.executorService = StalkerExecutors.newSingleThreadExecutor("simple-scheduled-measure-thread");
+ super.startNanoTime = System.nanoTime();
+
+ // 将单线程中执行的任务放在 while 循环中,一直执行下去.
+ super.measureFuture = executorService.submit(() -> {
+ while (true) {
+ try {
+ // 开始执行测量任务,记录开始时间、执行次数等.
+ long eachStart = System.nanoTime();
+ runnable.run();
+ super.eachMeasures.offer(System.nanoTime() - eachStart);
+ super.success.increment();
+ } catch (Exception e) {
+ super.failure.increment();
+ if (printErrorLog) {
+ log.error("【stalker 错误】测量方法耗时信息出错!", e);
+ }
+ }
+ }
+ });
+
+ // 到指定的持续时间之后,就取消执行中的任务,并关闭线程池.
+ // 注意,由于是定时任务,所以“是否取消”也设置为 false,用于区分是否是人为取消了任务,只有人为取消的才是 true.
+ final RunDuration duration = options.getDuration();
+ this.scheduledFuture = this.scheduledExecutorService.schedule(() -> {
+ this.stop();
+ super.canceled.compareAndSet(true, false);
+ }, duration.getAmount(), duration.getTimeUnit());
+
+ // 阻塞调用要执行的测量任务,达到阻塞等待任务结束的目的.
+ try {
+ this.measureFuture.get();
+ } catch (CancellationException e) {
+ log.info("【Stalker 提示】已取消或完成指定运行时间的测量任务.");
+ } catch (Exception e) {
+ log.error("【Stalker 错误】执行测量任务发生错误!", e);
+ }
+
+ // 如果没有设置相关的结束信息资源,就设置,没有关闭相关的资源就进行关闭.
+ super.setEndNanoTimeIfEmpty(System.nanoTime());
+ super.completed.compareAndSet(false, true);
+ StalkerExecutors.shutdown(super.executorService, this.scheduledExecutorService);
+ return super.getMeasureResult();
+ }
+
+ /**
+ * 停止相关的运行测量任务.
+ *
+ * @author blinkfox on 2020-05-25.
+ * @since v1.2.0
+ */
+ @Override
+ public void stop() {
+ super.stop();
+
+ // 关闭定时任务线程池和取消对应的定时任务.
+ StalkerExecutors.shutdown(this.scheduledExecutorService);
+ if (this.scheduledFuture != null && !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
new file mode 100644
index 0000000..0013b72
--- /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 = 524280;
+
+ 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 警示】线程池队列任务已满,将拒绝接收新的执行任务,建议你调低运行的【并发数】。");
+ }
+
+}
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..50d1cd0
--- /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 2a1f3d2..b81593a 100644
--- a/src/test/java/com/blinkfox/stalker/test/StalkerTest.java
+++ b/src/test/java/com/blinkfox/stalker/test/StalkerTest.java
@@ -2,7 +2,12 @@
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.test.prepare.MyTestService;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Test;
@@ -12,6 +17,7 @@
* @author blinkfox on 2019-02-03.
* @since v1.0.0
*/
+@Slf4j
public class StalkerTest {
/**
@@ -30,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());
+ }
+
/**
* 测试简单无并发的执行情况.
*/
@@ -92,4 +122,140 @@ public void runWithConcurrentException() {
() -> new MyTestService().helloException());
}
+ /**
+ * 测试慢方法的执行情况.
+ */
+ @Test
+ public void runWithSlowMethod() {
+ Stalker.run(Options.of("SlowTest", 20, 5, 1),
+ () -> new MyTestService().slowHello());
+ }
+
+ /**
+ * 测试没有Options选项参数时的执行情况.
+ */
+ @Test
+ public void submit() throws InterruptedException {
+ StalkerFuture stalkerFuture = Stalker.submit(() -> new MyTestService().hello());
+ Assert.assertNotNull(stalkerFuture);
+
+ while (!stalkerFuture.isDone()) {
+ List results = stalkerFuture.get();
+ Assert.assertNotNull(results.get(0));
+ Thread.sleep(2L);
+ }
+
+ log.info("任务已完成,获取最后的执行结果,并移除任务记录.");
+ stalkerFuture.get();
+ }
+
+ /**
+ * 测试慢方法的执行情况.
+ */
+ @Test
+ public void submitWithSlowMethod() throws InterruptedException {
+ StalkerFuture stalkerFuture = Stalker.submit(Options.of("SlowTest", 20, 5),
+ () -> 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();
+ }
+
+ /**
+ * 测试慢方法的执行情况.
+ */
+ @Test
+ public void submitWithSlowMethodDuration() throws InterruptedException {
+ StalkerFuture stalkerFuture = Stalker.submit(Options.ofDurationSeconds(2, 4),
+ () -> new MyTestService().slowHello());
+ Assert.assertNotNull(stalkerFuture);
+ Assert.assertEquals(0, stalkerFuture.getEndNanoTime());
+
+ while (!stalkerFuture.isDone()) {
+ List results = stalkerFuture.get();
+ Assert.assertNotNull(results.get(0));
+ Thread.sleep(1000L);
+ }
+
+ 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);
+ }
+
+ /**
+ * 测试 queryMeasurement 方法.
+ */
+ @Test
+ public void queryMeasureResult() throws InterruptedException {
+ StalkerFuture stalkerFuture = Stalker.submit(() -> new MyTestService().hello());
+ Assert.assertNotNull(stalkerFuture);
+
+ while (!stalkerFuture.isDone()) {
+ MeasureResult measureResult = stalkerFuture.getMeasureResult();
+ Assert.assertNotNull(measureResult);
+ Thread.sleep(5L);
+ }
+
+ Assert.assertFalse(stalkerFuture.isCancelled());
+ Assert.assertNotNull(stalkerFuture.getMeasureResult());
+ }
+
+ /**
+ * 测试慢方法的执行情况.
+ */
+ @Test
+ public void submitWithStop() throws InterruptedException {
+ StalkerFuture stalkerFuture = Stalker.submit(Options.of("StopTest", 20, 5, 1),
+ () -> new MyTestService().slowHello());
+ Assert.assertNotNull(stalkerFuture);
+
+ Thread.sleep(50L);
+ List results = stalkerFuture.get();
+ Assert.assertNotNull(results.get(0));
+ boolean isCancelled = stalkerFuture.cancel();
+ if (isCancelled) {
+ log.info("任务已停止,获取停止前的执行结果.");
+ } else {
+ log.info("任务停止失败.");
+ }
+
+ stalkerFuture.get();
+ Thread.sleep(100L);
+
+ log.info("任务已停止,再次获取最后的执行结果,判断内容是否一致.");
+ stalkerFuture.get();
+ }
+
+ /**
+ * 测试慢方法的执行情况.
+ */
+ @Test
+ public void submitWithDuration() throws InterruptedException {
+ StalkerFuture stalkerFuture = Stalker.submit(Options.ofDurationSeconds(15, 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..1717bac 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;
@@ -19,11 +21,31 @@ 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);
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
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/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
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..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,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.MeasureResult;
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 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 26f31ce..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,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.MeasureResult;
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 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 72611a5..106e01d 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() {
// 模拟运行抛出异常.
@@ -29,7 +29,20 @@ public void hello() {
* 测试方法2,模拟业务代码耗时 2 ms.
*/
public void fastHello() {
- this.sleep(2L);
+ this.sleep(1L);
+ }
+
+ /**
+ * 测试方法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));
}
/**
@@ -41,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());
}
}
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/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
deleted file mode 100644
index dc83dfd..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.bean.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());
- }
-
-}
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());
- }
-
-}