实现.Net程序中OpenTracing采样和上报配置的自动更新
前言
OpenTracing是一个链路跟踪的开放协议,已经有开源的.net实现:opentracing-csharp,同时支持.net framework和.net core,Github地址:https://github.com/opentracing/opentracing-csharp。
这个库支持多种链路跟踪模式,不过仅提供了最基础的功能,想用在实际项目中还需要做很多增强,还好也有人做了开源项目:opentracing-contrib,Github地址:https://github.com/opentracing-contrib/csharp-netcore。
opentracing-contrib中集成了一个名为Jaeger的类库,这个库实现了链路跟踪数据的采样和上报,支持将数据上传到Jaeger进行分析统计。
为了同时保障性能和跟踪关键数据,能够远程调整采样率是很重要的,Jaeger本身也提供了远程配置采样率的支持。
不过我这里用的阿里云链路跟踪不支持,配置的设计也和想要的不同,所以自己做了一个采样和上报配置的动态更新,也才有了这篇文章。
思路
使用Jaeger初始化Tracer大概是这样的:
var tracer = new Tracer.Builder(serviceName)
.WithSampler(sampler)
.WithReporter(reporter)
.Build();
GlobalTracer.Register(tracer);
首先是提供当前服务的名字,然后需要提供一个采样器,再提供一个上报器,Build下生成ITracer的一个实例,最后注册到全局。
可以分析得出,采样和上报配置的更新就是更新采样器和上报器。
不过Tracer并没有提供UpdateSampler和UdapteReporter的方法,被卡住了,怎么办呢?
前文提到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();
}
}
catch (System.Exception ex)
{
_logger.LogWarning(ex, "Updating sampler failed");
_metrics.SamplerQueryFailure.Inc();
}
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();
}
}
catch (System.Exception ex)
{
_logger.LogWarning(ex, "Updating reporter failed");
_metrics.ReporterFailure.Inc();
}
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大概是这样的:
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));
...
}
这里就不写如何监听配置的改变了,使用Timer或者阻塞查询等等都可以。
后记
opentracing-contrib这个项目只支持.net core,如果想用在.net framwork中还需要自己搞,这个方法会单独写一篇文章,这里就不做介绍了。
实现.Net程序中OpenTracing采样和上报配置的自动更新的更多相关文章
- 浅析 .Net Core中Json配置的自动更新
Pre 很早在看 Jesse 的Asp.net Core快速入门的课程的时候就了解到了在Asp .net core中,如果添加的Json配置被更改了,是支持自动重载配置的,作为一名有着严重" ...
- IIS如何避免子web应用程序中继承根目录web.config配置
1.一种方式,需要改动根目录的web.config(不是很推荐) <?xml version="1.0"?> <configuration> <loc ...
- 关于微信小程序中遇到的各种问题汇总(持续更新)
1.关于 <input />标签容易忽略的问题: 使用<input />标签时容易忘记绑定bindblur()方法(输入框失去焦点时触发),因为用户用键盘输入时不一定会点击完成 ...
- .net程序中http请求的超时配置
请求时的超时 // // 摘要: // 获取或设置 System.Net.HttpWebRequest.GetResponse() 和 System.Net.HttpWebRequest.GetReq ...
- Mysql中,update语句引起的时间戳自动更新问题
前几天遇到一个奇怪的问题. 在Mysql数据库中有一张表,表中有一个字段是timestamp类型的.我在update别的字段时,这个timestamp字段的时间会自动更新为当前时间. 后来发现,是My ...
- nw.js桌面程序自动更新(node.js表白记)
Hello Google Node.js 一个基于Google V8 的JavaScript引擎. 一个伟大的端至端语言,或许我对你的热爱源自于web这门极富情感的技术吧! 注: 光阴似水,人生若梦, ...
- C# WINFORM的自动更新程序
自动更新程序AutoUpdate.exe https://git.oschina.net/victor596jm/AutoUpdate.git 1.获取源码 http://git.oschina.ne ...
- 游戏《Minecraft》或其他应用程序 实现 自动更新 客户端版本
本渣又来写(水)博客了. 先说一下,我这个解决方案的安全性并不是企业级的,咱们就是一群穷开服的Minecraft玩家. 如果你要投入到企业级应用(容易被黑客攻击的场景),请自己写,思路凑合看看.不然安 ...
- 如何在程序中调用Caffe做图像分类
Caffe是目前深度学习比较优秀好用的一个开源库,采样c++和CUDA实现,具有速度快,模型定义方便等优点.学习了几天过后,发现也有一个不方便的地方,就是在我的程序中调用Caffe做图像分类没有直接的 ...
随机推荐
- Codeforces Round #622 (Div. 2) 1313 C1
C1. Skyscrapers (easy version) time limit per test1 second memory limit per test512 megabytes inputs ...
- python(MD5 单向加密)
import hashlib m3 = hashlib.md5() #定义加密方式 src = bytes(", encoding="utf-8") #定义一个需要加密的 ...
- mysql的group by
Group By 有几个规律: Group by的语法:"Group by <字段>“意为按照字段进行分类汇总.这里需要注意四点: (1)按照你的分类要求Group ...
- 【NOI Online 2020】入门组 总结&&反思
前言: 这次的NOI Online 2020 入门组我真的无力吐槽CCF的网站了,放段自己写的diss的文章,供一乐 如下:(考试后当天晚上有感而发) 今天是个好日子!!!(我都经历了什么...... ...
- 多线程高并发编程(7) -- Future源码分析
一.概念 A Future计算的结果. 提供方法来检查计算是否完成,等待其完成,并检索计算结果. 结果只能在计算完成后使用方法get进行检索,如有必要,阻塞,直到准备就绪. 取消由cancel方法执行 ...
- vue-infinite-scroll------vue的无线滚动插件
vue-infinite-scroll------vue的无线滚动插件 博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢! 说明 V ...
- POJ2376Cleaning Shifts(区间覆盖贪心)
应该还是蛮简单的一题,但是因为模拟太差,一直没调出来....... \(显而易见的应该按照左区间从小到大排序,相等按照右区间大到小排序\). \(那么第一个区间的l一定要是1,而且必拿(否则没有区间能 ...
- 实时(RTC)时钟,系统时钟和CPU时钟
最近在学stm32的时候看到RTC时钟和系统时钟,不知道区别在哪里,于是上网查了一下. 实时时钟:RTC时钟,用于提供年.月.日.时.分.秒和星期等的实时时间信息,由后备电池供电,当你晚上关闭系统和早 ...
- 新书《OpenShift云原生架构:原理与实践》第一章第三节:企业级PaaS平台OpenShift
近十年来,信息技术领域在经历一场技术大变革,这场变革正将我们由传统IT架构及其所支撑的臃肿应用系统时代,迁移至云原生架构及其所支撑的敏捷应用系统时代.在这场变革中,新技术的出现.更新和淘汰之迅速,以及 ...
- 实现简单网页rtmp直播:nginx+ckplayer+linux
一.安装nginx 安装带有rtmp模块的nginx服务器(其它支持rtmp协议的流媒体服务器像easydarwin.srs等+Apache等web服务器也可以),此处使用nginx服务器,简单方便. ...