Search in sources :

Example 1 with CircuitBreakerStatus

use of com.github.df.restypass.enums.CircuitBreakerStatus in project RestyPass by darren-fu.

the class DefaultCircuitBreaker method shouldPass.

@Override
public boolean shouldPass(RestyCommand restyCommand, ServerInstance serverInstance) {
    // 未启动
    if (!started) {
        return true;
    }
    String metricsKey = getMetricsKey(restyCommand.getPath(), serverInstance.getInstanceId());
    // 强制短路
    CircuitBreakerStatus cbStatus = statusMap.get(metricsKey);
    if (restyCommand.getRestyCommandConfig().isForceBreakEnabled()) {
        statusMap.put(metricsKey, CircuitBreakerStatus.FORCE_BREAK);
        return false;
    } else if (CircuitBreakerStatus.FORCE_BREAK == cbStatus) {
        // 强制短路关闭后,OPEN
        statusMap.put(metricsKey, CircuitBreakerStatus.OPEN);
    }
    // 断路器未启用
    if (!restyCommand.getRestyCommandConfig().isCircuitBreakEnabled()) {
        return true;
    }
    // 获取统计段
    Metrics metrics = getCommandMetrics(metricsKey);
    if (metrics == null) {
        return true;
    }
    // 半开状态说明已经有请求通过断路器,尝试恢复短路,所以其它请求直接拒绝,继续熔断
    if (CircuitBreakerStatus.HALF_OPEN == cbStatus) {
        return false;
    }
    // 是否应该通过
    boolean shouldPass = true;
    // 获取计数器
    Metrics.SegmentMetrics segmentMetrics = metrics.getMetrics();
    // 计数器中失败数量和比例超过阀值,则触发短路判断
    if (// 连续失败次数达到阀值
    segmentMetrics.getContinuousFailCount() >= continuousFailCount || // 失败比例以及失败数量达到阀值
    (segmentMetrics.failCount() >= breakFailCount && segmentMetrics.failPercentage() >= breakFailPercentage)) {
        CircuitBreakerStatus breakerStatus = cbStatus;
        shouldPass = false;
        if (breakerStatus == null || breakerStatus == CircuitBreakerStatus.OPEN) {
            // 短路
            statusMap.put(metricsKey, CircuitBreakerStatus.BREAK);
        } else if (breakerStatus == CircuitBreakerStatus.HALF_OPEN) {
        // noop
        } else {
            // 如果上次的请求距离现在超过阀值,则允许一次试探请求
            long now = System.currentTimeMillis();
            if (segmentMetrics.last() != null && (now - segmentMetrics.last() > halfOpenMilliseconds)) {
                final ReentrantLock lock = getHalfOpenLockInMap(metricsKey);
                lock.lock();
                try {
                    // 判断当前短路状态 确保只有一个请求通过
                    if (statusMap.get(metricsKey) == CircuitBreakerStatus.BREAK) {
                        statusMap.put(metricsKey, CircuitBreakerStatus.HALF_OPEN);
                        shouldPass = true;
                    }
                } finally {
                    lock.unlock();
                }
            }
        }
        if (log.isDebugEnabled()) {
            if (shouldPass) {
                log.debug("尝试恢复短路服务:{}:{},metrics:{}", restyCommand.getServiceName(), restyCommand.getPath(), metrics);
            } else {
                log.debug("熔断服务:{}:{},metrics:{}", restyCommand.getServiceName(), restyCommand.getPath(), metrics);
            }
        }
    }
    return shouldPass;
}
Also used : ReentrantLock(java.util.concurrent.locks.ReentrantLock) ToString(lombok.ToString) CircuitBreakerStatus(com.github.df.restypass.enums.CircuitBreakerStatus)

Example 2 with CircuitBreakerStatus

use of com.github.df.restypass.enums.CircuitBreakerStatus in project RestyPass by darren-fu.

the class DefaultCircuitBreaker method startTask.

/**
 * 消费队列 统计已完成的command信息,更新 segment
 */
private void startTask() {
    Executors.newSingleThreadExecutor().submit(() -> {
        log.info("启动RestyCommand统计线程:" + this.eventKey);
        while (true) {
            try {
                // 阻塞
                RestyCommand firstCommand = commandQueue.take();
                // 取出queue中所有的数据
                List<RestyCommand> commandList = new LinkedList<>();
                commandList.add(firstCommand);
                commandQueue.drainTo(commandList);
                for (RestyCommand restyCommand : commandList) {
                    String key = getMetricsKey(restyCommand.getPath(), restyCommand.getInstanceId());
                    // 获取 计数器
                    Metrics metrics = getCommandMetrics(key);
                    if (metrics == null) {
                        log.warn("获取计数器失败:{}", key);
                        continue;
                    }
                    boolean isSuccess = isCommandSuccessExecuted(restyCommand);
                    boolean forceUseNewMetrics = false;
                    // 如果当前处在短路或半短路状态
                    CircuitBreakerStatus breakerStatus = statusMap.get(key);
                    if ((breakerStatus == CircuitBreakerStatus.BREAK || breakerStatus == CircuitBreakerStatus.HALF_OPEN)) {
                        // 结果成功 则不再短路,打开断路器
                        if (isSuccess) {
                            // 并使用一个新的计数器
                            forceUseNewMetrics = true;
                            statusMap.put(key, CircuitBreakerStatus.OPEN);
                        } else {
                            // 否则恢复到短路状态
                            statusMap.put(key, CircuitBreakerStatus.BREAK);
                        }
                    }
                    metrics.store(isSuccess, forceUseNewMetrics);
                }
                if (log.isTraceEnabled()) {
                    log.trace("处理完成, 处理个数:{},剩余:{}个", commandList.size(), commandQueue.size());
                }
            } catch (Exception ex) {
                log.error("断路器RestyCommand处理失败:{}", ex);
            }
        }
    });
}
Also used : RestyCommand(com.github.df.restypass.command.RestyCommand) ToString(lombok.ToString) CircuitBreakerStatus(com.github.df.restypass.enums.CircuitBreakerStatus) RequestException(com.github.df.restypass.exception.execute.RequestException)

Aggregations

CircuitBreakerStatus (com.github.df.restypass.enums.CircuitBreakerStatus)2 ToString (lombok.ToString)2 RestyCommand (com.github.df.restypass.command.RestyCommand)1 RequestException (com.github.df.restypass.exception.execute.RequestException)1 ReentrantLock (java.util.concurrent.locks.ReentrantLock)1