非同步作業校正工具awaitility源碼分析

來源:互聯網
上載者:User
1. 背景

之前介紹了一篇awaitility快速入門的文章:非同步校正工具awaitility快速入門,該工具很好地解決了校正非同步作業的問題,其中封裝了很多便捷的用法,重點是在規定時間內,輪詢結果;本文以源碼的方式,介紹一下工具內部是怎麼實現的,理解開發的設計思路,對以後解決工作中的問題是有協助的。 2. 核心源碼梳理 2-1. 例子

    // 非同步任務,每隔1s, count累加1    class CounterServiceImpl implements CounterService {        private volatile int count = 0;        public void run() {            new Thread(new Runnable() {                @Override                public void run() {                    try {                        for (int index = 0; index < 5; index++) {                            Thread.sleep(1000);                            count += 1;                        }                    } catch (InterruptedException e) {                        throw new RuntimeException(e);                    }                }            }).start();        }        public int getCount() {            return count;        }    }    @Test    public void testAsynchronousPoll() {        final CounterService service = new CounterServiceImpl();        service.run();       // 輪詢查詢,pollInterval每隔多少時間段輪詢,pollDelay延遲校正時間        with().atMost(60, SECONDS).and().pollInterval(ONE_HUNDRED_MILLISECONDS).and().pollDelay(50, MILLISECONDS).await("count is greater 6").until(                new Callable<Boolean>() {                    @Override                    public Boolean call() throws Exception {                        return service.getCount() == 6;                    }                });    }
2-2. 核心源碼分析

例子中:

// 設定逾時時間,6satMost(6, SECONDS)// 設定間隔100mspollInterval(ONE_HUNDRED_MILLISECONDS)// 設定延遲50mspollDelay(50, MILLISECONDS)// 設定提示await("count is greater 6")// 串連and()// 等待java.util.concurrent.Callable返回trueuntil(    new Callable<Boolean>() {    @Override    public Boolean call() throws Exception {        return service.getCount() == 6;    }});

ConditionFactory類所在包路徑:

package org.awaitility.core

ConditionFactory類是一個Condition工廠,with()會建立一個ConditionFactory執行個體:

public static ConditionFactory with() {        return new ConditionFactory(defaultWaitConstraint, defaultPollInterval, defaultPollDelay,                defaultCatchUncaughtExceptions, defaultExceptionIgnorer, defaultConditionEvaluationListener);}

ConditionFactory帶參建構函式如下:

/**     * Instantiates a new condition factory.     *     * @param timeoutConstraint       the timeout     * @param pollInterval            the poll interval     * @param pollDelay               The delay before the polling starts     * @param exceptionsIgnorer       the ignore exceptions     * @param catchUncaughtExceptions the catch uncaught exceptions     */    public ConditionFactory(WaitConstraint timeoutConstraint, PollInterval pollInterval, Duration pollDelay,                            boolean catchUncaughtExceptions, ExceptionIgnorer exceptionsIgnorer,                            ConditionEvaluationListener conditionEvaluationListener) {        this(null, timeoutConstraint, pollInterval, pollDelay, catchUncaughtExceptions, exceptionsIgnorer,                conditionEvaluationListener);    }

建構函式的參數值:

/**     * The default poll interval (fixed 100 ms).     */    private static volatile PollInterval defaultPollInterval = DEFAULT_POLL_INTERVAL;    /**     * The default wait constraint (10 seconds).     */    private static volatile WaitConstraint defaultWaitConstraint = AtMostWaitConstraint.TEN_SECONDS;    /**     * The default poll delay     */    private static volatile Duration defaultPollDelay = DEFAULT_POLL_DELAY;    /**     * Catch all uncaught exceptions by default?     */    private static volatile boolean defaultCatchUncaughtExceptions = true;    /**     * Ignore caught exceptions by default?     */    private static volatile ExceptionIgnorer defaultExceptionIgnorer = new PredicateExceptionIgnorer(new Predicate<Exception>() {        public boolean matches(Exception e) {            return false;        }    });    /**     * Default listener of condition evaluation results.     */    private static volatile ConditionEvaluationListener defaultConditionEvaluationListener = null;

通過這些參數可知,預設的逾時時間為10s, 輪詢間隔為100ms;

通過ConditionFactory的成員方法,對成員變數進行替換,比如atMost(60, SECONDS):

public ConditionFactory atMost(Duration timeout) {        return new ConditionFactory(alias, timeoutConstraint.withMaxWaitTime(timeout), pollInterval, pollDelay,                catchUncaughtExceptions, exceptionsIgnorer, conditionEvaluationListener);}

將final成員變數this.timeoutConstraint替換成使用者佈建的new Duration(60, SECONDS);

and()返回this;

當使用者再設定pollInterval(ONE_HUNDRED_MILLISECONDS)時:

public ConditionFactory pollInterval(Duration pollInterval) {        return new ConditionFactory(alias, timeoutConstraint, pollInterval, pollDelay, catchUncaughtExceptions,                exceptionsIgnorer, conditionEvaluationListener);    }

會重新new一個對象,ONE_HUNDRED_MILLISECONDS會賦給final成員變數:pollInterval,之前賦過的值保留,比如:timeoutConstraint還是上一步設定的new Duration(60, SECONDS);依次類推new Duration(50, MILLISECONDS)賦給final成員變數:pollDelay; “count is greater 6”賦給final成員變數:alias。

最終執行個體化的ConditionFactory對象中,成員變數為:

timeoutConstraint為60spollInterval為100mspollDelay為50msalias為"count is greater 6"

ContiditionFactory類成員方法until():

public void until(Callable<Boolean> conditionEvaluator) {        until(new CallableCondition(conditionEvaluator, generateConditionSettings()));}private <T> T until(Condition<T> condition) {        return condition.await();    }

generateConditionSettings()將ConditionFactory的final變數賦值給javabean對象ConditionSettings:

new ConditionSettings(alias, catchUncaughtExceptions, timeoutConstraint, pollInterval, actualPollDelay,                conditionEvaluationListener, exceptionsIgnorer);

執行個體化CallableCondition類,建構函式:

public CallableCondition(final Callable<Boolean> matcher, ConditionSettings settings) {        conditionEvaluationHandler = new ConditionEvaluationHandler<Object>(null, settings);        ConditionEvaluationWrapper conditionEvaluationWrapper = new ConditionEvaluationWrapper(matcher, settings, conditionEvaluationHandler);        conditionAwaiter = new ConditionAwaiter(conditionEvaluationWrapper, settings) {            @SuppressWarnings("rawtypes")            @Override            protected String getTimeoutMessage() {                if (timeout_message != null) {                    return timeout_message;                }                final String timeoutMessage;                if (matcher == null) {                    timeoutMessage = "";                } else {                    final Class<? extends Callable> type = matcher.getClass();                    final Method enclosingMethod = type.getEnclosingMethod();                    if (type.isAnonymousClass() && enclosingMethod != null) {                        timeoutMessage = String.format("Condition returned by method \"%s\" in class %s was not fulfilled",                                enclosingMethod.getName(), enclosingMethod.getDeclaringClass().getName());                    } else {                        final String message;                        if (isLambdaClass(type)) {                            message = "with " + generateLambdaErrorMessagePrefix(type, false);                        } else {                            message = type.getName();                        }                        timeoutMessage = String.format("Condition %s was not fulfilled", message);                    }                }                return timeoutMessage;            }        };    }

同時執行個體化ConditionAwaiter對象,ConditionAwaiter建構函式:

 public ConditionAwaiter(final ConditionEvaluator conditionEvaluator,                            final ConditionSettings conditionSettings) {        if (conditionEvaluator == null) {            throw new IllegalArgumentException("You must specify a condition (was null).");        }        if (conditionSettings == null) {            throw new IllegalArgumentException("You must specify the condition settings (was null).");        }        if (conditionSettings.shouldCatchUncaughtExceptions()) {            Thread.setDefaultUncaughtExceptionHandler(this);        }        this.conditionSettings = conditionSettings;        this.latch = new CountDownLatch(1);        this.conditionEvaluator = conditionEvaluator;        this.executor = initExecutorService();    }

並調用CallableCondition執行個體的await()方法:

 public Void await() {        conditionAwaiter.await(conditionEvaluationHandler);        return null;    }

接著調用ConditionAwaiter執行個體的await():

public <T> void await(final ConditionEvaluationHandler<T> conditionEvaluationHandler) {        final Duration pollDelay = conditionSettings.getPollDelay();        final Duration maxWaitTime = conditionSettings.getMaxWaitTime();        final Duration minWaitTime = conditionSettings.getMinWaitTime();        final long maxTimeout = maxWaitTime.getValue();        final TimeUnit maxTimeoutUnit = maxWaitTime.getTimeUnit();        long pollingStarted = System.currentTimeMillis() - pollDelay.getValueInMS();        pollSchedulingThread(conditionEvaluationHandler, pollDelay, maxWaitTime).start();        try {            try {                final boolean finishedBeforeTimeout;                if (maxWaitTime == Duration.FOREVER) {                    latch.await();                    finishedBeforeTimeout = true;                } else {                    finishedBeforeTimeout = latch.await(maxTimeout, maxTimeoutUnit);                }                Duration evaluationDuration =                        new Duration(System.currentTimeMillis() - pollingStarted, TimeUnit.MILLISECONDS)                                .minus(pollDelay);                if (throwable != null) {                    throw throwable;                } else if (!finishedBeforeTimeout) {                    final String maxWaitTimeLowerCase = maxWaitTime.getTimeUnitAsString();                    final String message;                    if (conditionSettings.hasAlias()) {                        message = String.format("Condition with alias '%s' didn't complete within %s %s because %s.",                                conditionSettings.getAlias(), maxTimeout, maxWaitTimeLowerCase, Introspector.decapitalize(getTimeoutMessage()));                    } else {                        message = String.format("%s within %s %s.", getTimeoutMessage(), maxTimeout, maxWaitTimeLowerCase);                    }                    final ConditionTimeoutException e;                    // Not all systems support deadlock detection so ignore if ThreadMXBean & ManagementFactory is not in classpath                    if (existInCP("java.lang.management.ThreadMXBean") && existInCP("java.lang.management.ManagementFactory")) {                        java.lang.management.ThreadMXBean bean = java.lang.management.ManagementFactory.getThreadMXBean();                        Throwable cause = this.cause;                        try {                            long[] threadIds = bean.findDeadlockedThreads();                            if (threadIds != null) {                                cause = new DeadlockException(threadIds);                            }                        } catch (UnsupportedOperationException ignored) {                            // findDeadLockedThreads() not supported on this VM,                            // don't init cause and move on.                        }                        e = new ConditionTimeoutException(message, cause);                    } else {                        e = new ConditionTimeoutException(message, this.cause);                    }                    throw e;                } else if (evaluationDuration.compareTo(minWaitTime) < 0) {                    String message = String.format("Condition was evaluated in %s %s which is earlier than expected " +                                    "minimum timeout %s %s", evaluationDuration.getValue(), evaluationDuration.getTimeUnit(),                            minWaitTime.getValue(), minWaitTime.getTimeUnit());                    throw new ConditionTimeoutException(message);                }            } finally {                executor.shutdown();                if (!executor.awaitTermination(1, TimeUnit.SECONDS)) {                    try {                        executor.shutdownNow();                        executor.awaitTermination(1, TimeUnit.SECONDS);                    } catch (InterruptedException e) {                        CheckedExceptionRethrower.safeRethrow(e);                    }                }            }        } catch (Throwable e) {            CheckedExceptionRethrower.safeRethrow(e);        }    }

ConditionAwaiter類中有個CountDownLatch成員變數:

private final CountDownLatch latch;

執行個體化時,定義了:

this.latch = new CountDownLatch(1);

CountDownLatch:

A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.A CountDownLatch is initialized with a given count. The await methods block until the current count reaches zero due to invocations of the countDown() method, after which all waiting threads are released and any subsequent invocations of await return immediately.詳情見:<https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html>

在ConditionAwaiter執行個體的await()方法中,建立了一個輪詢線程:

 pollSchedulingThread(conditionEvaluationHandler, pollDelay, maxWaitTime).start();
 private <T> Thread pollSchedulingThread(final ConditionEvaluationHandler<T> conditionEvaluationHandler,                                            final Duration pollDelay, final Duration maxWaitTime) {        final long maxTimeout = maxWaitTime.getValue();        final TimeUnit maxTimeoutUnit = maxWaitTime.getTimeUnit();        return new Thread(new Runnable() {            public void run() {                int pollCount = 0;                try {                    conditionEvaluationHandler.start();                    if (!pollDelay.isZero()) {                        Thread.sleep(pollDelay.getValueInMS());                    }                    Duration pollInterval = pollDelay;                    while (!executor.isShutdown()) {                        if (conditionCompleted()) {                            break;                        }                        pollCount = pollCount + 1;                        Future<?> future = executor.submit(new ConditionPoller(pollInterval));                        if (maxWaitTime == Duration.FOREVER) {                            future.get();                        } else {                            future.get(maxTimeout, maxTimeoutUnit);                        }                        pollInterval = conditionSettings.getPollInterval().next(pollCount, pollInterval);                        Thread.sleep(pollInterval.getValueInMS());                    }                } catch (Throwable e) {                    throwable = e;                }            }        }, "awaitility-poll-scheduling");    }

其中while迴圈中:
Future<?> future = executor.submit(new ConditionPoller(pollInterval));
線程執行體:

private class ConditionPoller implements Runnable {        private final Duration delayed;        /**         * @param delayed The duration of the poll interval         */        public ConditionPoller(Duration delayed) {            this.delayed = delayed;        }        public void run() {            try {                ConditionEvaluationResult result = conditionEvaluator.eval(delayed);                if (result.isSuccessful()) {                    latch.countDown();                } else if (result.hasThrowable()) {                    cause = result.getThrowable();                }            } catch (Exception e) {                if (!conditionSettings.shouldExceptionBeIgnored(e)) {                    throwable = e;                    latch.countDown();                }            }        }    }

具體執行:ConditionEvaluationResult result = conditionEvaluator.eval(delayed);

執行個體化ConditionAwaiter時傳入ConditionEvaluator的實作類別ConditionEvaluationWrapper;

conditionAwaiter = new ConditionAwaiter(conditionEvaluationWrapper, settings)

ConditionEvaluationWrapper中eval()方法:

public ConditionEvaluationResult eval(Duration pollInterval) throws Exception {            boolean conditionFulfilled = matcher.call();            if (conditionFulfilled) {                conditionEvaluationHandler.handleConditionResultMatch(getMatchMessage(matcher, settings.getAlias()), true, pollInterval);            } else {                conditionEvaluationHandler.handleConditionResultMismatch(getMismatchMessage(matcher, settings.getAlias()), false, pollInterval);            }            return new ConditionEvaluationResult(conditionFulfilled);        }

其中:
boolean conditionFulfilled = matcher.call();
call()返回computed result。

matcher執行個體在ConditionEvaluationWrapper建構函式中執行個體化:

     ConditionEvaluationWrapper(Callable<Boolean> matcher, ConditionSettings settings, ConditionEvaluationHandler<Object> conditionEvaluationHandler) {            this.matcher = matcher;            this.settings = settings;            this.conditionEvaluationHandler = conditionEvaluationHandler;        }

本例為:

new Callable<Boolean>() {                    @Override                    public Boolean call() throws Exception {                        return service.getCount() == 6;                    }                }

如果非同步執行結果滿足,latch.countDown();

 ConditionEvaluationResult result = conditionEvaluator.eval(delay

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.