实现.Net程序中OpenTracing采样和上报配置的自动更新

前文提到Jaeger是支持采样率的动态调整的,看看它怎么做的:

复制代码
private RemoteControlledSampler(Builder builder)
{

        _pollTimer = new Timer(_ => UpdateSampler(), null, TimeSpan.Zero, builder.PollingInterval);
    }

    /// <summary>
    /// Updates <see cref="Sampler"/> to a new sampler when it is different.
    /// </summary>
    internal void UpdateSampler()
    {
        try
        {
            SamplingStrategyResponse response = _samplingManager.GetSamplingStrategyAsync(_serviceName)
                .ConfigureAwait(false).GetAwaiter().GetResult();

            ...

                UpdateRateLimitingOrProbabilisticSampler(response);
        }
        catch (Exception ex)
        {
            ...
        }
    }

    private void UpdateRateLimitingOrProbabilisticSampler(SamplingStrategyResponse response)
    {
      ...
        lock (_lock)
        {
            if (!Sampler.Equals(sampler))
            {
                Sampler.Close();
                Sampler = sampler;
                ...
            }
        }
    }

复制代码
这里只留下关键代码,可以看到核心就是:通过一个Timer定时获取采样策略,然后替换原来的Sampler。

这是一个很好理解的办法,下边就按照这个思路来搞。

方案
分别提供一个可更新的Sampler和可更新的Reporter,Build Tracer时使用这两个可更新的类。这里延续开源项目中Samper和Reporter的创建方式,给出这两个类。

可更新的Sampler:

复制代码
internal class UpdatableSampler : ValueObject, ISampler
{
public const string Type = “updatable”;

    private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
    private readonly string _serviceName;
    private readonly ILoggerFactory _loggerFactory;
    private readonly ILogger _logger;
    private readonly IMetrics _metrics;

    internal ISampler Sampler { get; private set; }

    private UpdatableSampler(Builder builder)
    {
        _serviceName = builder.ServiceName;
        _loggerFactory = builder.LoggerFactory;
        _logger = _loggerFactory.CreateLogger<UpdatableSampler>();
        _metrics = builder.Metrics;
        Sampler = builder.InitialSampler;
    }

    /// <summary>
    /// Updates <see cref="Sampler"/> to a new sampler when it is different.
    /// </summary>
    public void UpdateSampler(ISampler sampler)
    {
        try
        {
            _lock.EnterWriteLock();
            if (!Sampler.Equals(sampler))
            {
                Sampler.Close();
                Sampler = sampler;
                _metrics.SamplerUpdated.Inc(1);
            }
        }
        catch (System.Exception ex)
        {
            _logger.LogWarning(ex, "Updating sampler failed");
            _metrics.SamplerQueryFailure.Inc(1);
        }
        finally
        {
            _lock.ExitWriteLock();
        }
    }

    public SamplingStatus Sample(string operation, TraceId id)
    {
        try
        {
            _lock.EnterReadLock();
            var status=  Sampler.Sample(operation, id);
            return status;
        }
        finally
        {
            _lock.ExitReadLock();
        }
    }

    public override string ToString()
    {
        try
        {
            _lock.EnterReadLock();
            return $"{nameof(UpdatableSampler)}(Sampler={Sampler})";
        }
        finally
        {
            _lock.ExitReadLock();
        }
    }

    public void Close()
    {
        try
        {
            _lock.EnterWriteLock();
            Sampler.Close();
        }
        finally
        {
            _lock.ExitWriteLock();
        }
    }

    protected override IEnumerable<object> GetAtomicValues()
    {
        yield return Sampler;
    }

    public sealed class Builder
    {
        internal string ServiceName { get; }
        internal ILoggerFactory LoggerFactory { get; private set; }
        internal ISampler InitialSampler { get; private set; }
        internal IMetrics Metrics { get; private set; }

        public Builder(string serviceName)
        {
            ServiceName = serviceName ?? throw new ArgumentNullException(nameof(serviceName));
        }

        public Builder WithLoggerFactory(ILoggerFactory loggerFactory)
        {
            LoggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
            return this;
        }

        public Builder WithInitialSampler(ISampler initialSampler)
        {
            InitialSampler = initialSampler ?? throw new ArgumentNullException(nameof(initialSampler));
            return this;
        }

        public Builder WithMetrics(IMetrics metrics)
        {
            Metrics = metrics ?? throw new ArgumentNullException(nameof(metrics));
            return this;
        }

        public UpdatableSampler Build()
        {
            if (LoggerFactory == null)
            {
                LoggerFactory = NullLoggerFactory.Instance;
            }
            if (InitialSampler == null)
            {
                InitialSampler = new ProbabilisticSampler();
            }
            if (Metrics == null)
            {
                Metrics = new MetricsImpl(NoopMetricsFactory.Instance);
            }

            return new UpdatableSampler(this);
        }
    }
}

复制代码

可更新的Reporter:

复制代码
internal class UpdatableReporter : IReporter
{
public const string Type = “updatable”;

    private readonly string _serviceName;
    private readonly ILoggerFactory _loggerFactory;
    private readonly ILogger _logger;
    private readonly IMetrics _metrics;
    private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();

    internal IReporter Reporter { get; private set; }

    private UpdatableReporter(Builder builder)
    {
        _serviceName = builder.ServiceName;
        _loggerFactory = builder.LoggerFactory;
        _logger = _loggerFactory.CreateLogger<UpdatableReporter>();
        _metrics = builder.Metrics;
        Reporter = builder.InitialReporter;
    }

    /// <summary>
    /// Updates <see cref="Reporter"/> to a new reporter when it is different.
    /// </summary>
    public void UpdateReporter(IReporter reporter)
    {
        try
        {
            _lock.EnterWriteLock();

            if (!Reporter.Equals(reporter))
            {
                Reporter.CloseAsync(CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult();
                Reporter = reporter;
                _metrics.SamplerUpdated.Inc(1);
            }
        }
        catch (System.Exception ex)
        {
            _logger.LogWarning(ex, "Updating reporter failed");
            _metrics.ReporterFailure.Inc(1);
        }
        finally
        {
            _lock.ExitWriteLock();
        }
    }

    public void Report(Span span)
    {
        try
        {
            _lock.EnterReadLock();
            Reporter.Report(span);
        }
        finally
        {
            _lock.ExitReadLock();
        }
    }

    public override string ToString()
    {
        try
        {
            _lock.EnterReadLock();
            return $"{nameof(UpdatableReporter)}(Reporter={Reporter})";
        }
        finally
        {
            _lock.ExitReadLock();
        }
    }

    public async Task CloseAsync(CancellationToken cancellationToken)
    {
        try
        {
            _lock.EnterWriteLock();
            await Reporter.CloseAsync(cancellationToken);
        }
        finally
        {
            _lock.ExitWriteLock();
        }
    }

    public sealed class Builder
    {
        internal string ServiceName { get; }
        internal ILoggerFactory LoggerFactory { get; private set; }
        internal IReporter InitialReporter { get; private set; }
        internal IMetrics Metrics { get; private set; }

        public Builder(string serviceName)
        {
            ServiceName = serviceName ?? throw new ArgumentNullException(nameof(serviceName));
        }

        public Builder WithLoggerFactory(ILoggerFactory loggerFactory)
        {
            LoggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
            return this;
        }

        public Builder WithInitialReporter(IReporter initialReporter)
        {
            InitialReporter = initialReporter ?? throw new ArgumentNullException(nameof(initialReporter));
            return this;
        }

        public Builder WithMetrics(IMetrics metrics)
        {
            Metrics = metrics ?? throw new ArgumentNullException(nameof(metrics));
            return this;
        }

        public UpdatableReporter Build()
        {
            if (LoggerFactory == null)
            {
                LoggerFactory = NullLoggerFactory.Instance;
            }
            if (InitialReporter == null)
            {
                InitialReporter = new NoopReporter();
            }
            if (Metrics == null)
            {
                Metrics = new MetricsImpl(NoopMetricsFactory.Instance);
            }

            return new UpdatableReporter(this);
        }
    }
}

复制代码

注意这里边用到了读写锁,因为要做到不停止服务的更新,而且大部分情况下都是读,使用lock就有点大柴小用了。

现在初始化Tracer大概是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
sampler = new UpdatableSampler.Builder(serviceName)
.WithInitialSampler(BuildSampler(configuration))
.Build();

reporter = new UpdatableReporter.Builder(serviceName)
.WithInitialReporter(BuildReporter(configuration))
.Build();

var tracer = new Tracer.Builder(serviceName)
.WithSampler(sampler)
.WithReporter(reporter)
.Build();

当配置发生改变时,调用sampler和reporter的更新方法:

复制代码
private void OnTracingConfigurationChanged(TracingConfiguration newConfiguration, TracingConfigurationChangedInfo changedInfo)
{

((UpdatableReporter)_reporter).UpdateReporter(BuildReporter(newConfiguration));
((UpdatableSampler)_sampler).UpdateSampler(BuildSampler(newConfiguration));

}
亚马逊测评 www.yisuping.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值