2

我正在为我们的服务实施断路器。这是非常标准的断路器,除了它将所有动作包装在超时中,因此当发生太多超时时它也会断开电路。当断路器变为半开状态时,我正在使用MonitorTryEnter方法来确保只有一个线程执行操作(检查服务是否恢复)。请在下面找到代码片段(这是在半开状态下执行的代码):

编辑(粘贴整个功能代码)

public async Task<T> ExecuteWithCircuitBreaker<T>(Func<Task<T>> func)
        {
            if (!_circuitBreakerStateManager.IsInState(CircuitBreakerState.Closed))
            {
                if (HalfOpen())
                {
                    bool lockTaken = false;
                    try
                    {
                        Monitor.TryEnter(_halfOpenSyncObject, ref lockTaken);
                        if (lockTaken)
                        {
                            var timeoutTask = Task.Delay(_options.ActionTimeout);
                            var resultTask = func.Invoke();

                            var firstFinishedTask = await Task.WhenAny(timeoutTask, resultTask);
                            if (firstFinishedTask == timeoutTask)
                            {
                                throw new TimeoutException($"Operation reached timeout configured for this circuit breaker. Timeout: {_options.ActionTimeout.TotalSeconds} s");
                            }
                            else
                            {
                                Reset();
                                return await resultTask;
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Trip(ex);
                        throw;
                    }
                    finally
                    {
                        if (lockTaken)
                        {
                            Monitor.Exit(_halfOpenSyncObject);
                        }
                    }
                }
                throw new CircuitBreakerOpenException(_circuitBreakerStateManager.LastException);
            }

            // Circuit Breaker is closed - execute function normally
            try
            {
                var timeoutTask = Task.Delay(_options.ActionTimeout);
                var resultTask = func.Invoke();
                var firstFinishedTask = await Task.WhenAny(timeoutTask, resultTask);
                if (firstFinishedTask == timeoutTask)
                    throw new TimeoutException($"Operation reached timeout configured for this circuit breaker. Timeout: {_options.ActionTimeout.TotalSeconds} s");             
                else
                    return await resultTask;

            }
            catch (Exception ex)
            {
                Trip(ex);
                throw;
            }
        }    

正如您在上面的代码片段中看到的,TryEnter 被包裹在 Try/Finally 中,确保无论如何都会释放锁。我遇到的问题是,在某些时候Monitor.TryEnter,每次尝试都开始返回 false。我调试了代码,发现在它开始发生之前,每次Monitor.TryEnter调用都会Monitor.Exit被执行——这意味着锁应该被释放,但此时似乎并不是每个调用都返回 false。不知道这怎么可能。

是否有可能Monitor.Exit由于某种原因不释放锁(虽然它不会抛出任何异常)?或者我在这里做错了什么?

编辑。这就是 _halfOpenSyncObject 的声明方式(这个类只有一个实例)

public class TimeoutableCircuitBreaker: ICircuitBreaker
{
    private readonly object _halfOpenSyncObject = new object();    
    ...

编辑半开实现:

private bool HalfOpen()
        {
            var isHalfOpen = _circuitBreakerStateManager.GetLastStateChangedDate() + _options.BreakDuration < DateTime.UtcNow;
            if (isHalfOpen)
                _circuitBreakerStateManager.SetState(CircuitBreakerState.HalfOpen);
            return isHalfOpen;
        }
4

0 回答 0