private void DoConfigWork(Object state) { var list = AppConfig.FindAll(); var next = DateTime.MinValue; foreach (var item in list) { if (!item.Enable || item.PublishTime.Year < 2000) { continue; } using var span = _tracer?.NewSpan("AutoPublish", item); // 时间到了,发布,或者计算最近一个到期应用 if (item.PublishTime <= DateTime.Now) { item.Publish(); } else if (item.PublishTime < next || next.Year < 2000) { next = item.PublishTime; } } // 如果下一个到期应用时间很短,主动调整定时器 if (next.Year > 2000) { var ts = next - DateTime.Now; if (ts.TotalMilliseconds < _timer.Period) { _timer.SetNext((Int32)ts.TotalMilliseconds); } } }
Span BuildSpan() { if (!ignoreActiveSpan) { var parent = tracer.ScopeManager.Current?.Span; if (parent != null) { AddReference(References.ChildOf, new OTSpanContext(parent.Context)); } } Span span = null; if (references != null && references.Count > 0) { if (references[0].Context is OTSpanContext parentRef) { span = tracer.NewSpan(operationName, parentRef.TraceContext); } else { span = tracer.NewSpan(operationName); } } else { span = tracer.NewSpan(operationName); } span.Kind = GetSpanKind(); span.RemoteEndpoint = GetRemote(); CombineTags(span); return(span); }
/// <summary>打开</summary> /// <returns>是否成功</returns> public virtual Boolean Open() { if (Disposed) { throw new ObjectDisposedException(GetType().Name); } if (Active) { return(true); } lock (this) { if (Active) { return(true); } using var span = Tracer?.NewSpan($"net:{Name}:Open", Remote); try { _RecvCount = 0; var rs = OnOpen(); if (!rs) { return(false); } var timeout = Timeout; if (timeout > 0) { Client.SendTimeout = timeout; Client.ReceiveTimeout = timeout; } // Tcp需要初始化管道 if (Local.IsTcp) { Pipeline?.Open(CreateContext(this)); } Active = true; ReceiveAsync(); // 触发打开完成的事件 Opened?.Invoke(this, EventArgs.Empty); } catch (Exception ex) { span?.SetError(ex, null); throw; } } return(true); }
/// <summary>调用</summary> /// <param name="ctx"></param> /// <returns></returns> public async Task Invoke(HttpContext ctx) { // APM跟踪 //var span = Tracer?.NewSpan(ctx.Request.Path); ISpan span = null; if (Tracer != null) { var action = GetAction(ctx); if (!action.IsNullOrEmpty()) { span = Tracer.NewSpan(action); span.Tag = ctx.Request.GetRawUrl() + ""; span.Detach(ctx.Request.Headers.ToDictionary(e => e.Key, e => (Object)e.Value)); } } try { await _next.Invoke(ctx); } catch (Exception ex) { span?.SetError(ex, null); throw; } finally { span?.Dispose(); } }
/// <summary>调用</summary> /// <param name="ctx"></param> /// <returns></returns> public async Task Invoke(HttpContext ctx) { // APM追踪 //var span = Tracer?.NewSpan(ctx.Request.Path); ISpan span = null; if (Tracer != null) { var action = GetAction(ctx); if (!action.IsNullOrEmpty()) { span = Tracer.NewSpan(action); span.Detach(ctx.Request.Headers.ToDictionary(e => e.Key, e => (Object)e.Value)); } } ManageProvider.UserHost = ctx.GetUserHost(); try { await _next.Invoke(ctx); } catch (Exception ex) { span?.SetError(ex, ctx.Request.QueryString + ""); throw; } finally { ManageProvider.UserHost = null; span?.Dispose(); } }
void OnInit(Object sender, EventArgs e) { var app = sender as HttpApplication; var ctx = app?.Context; if (ctx != null && Tracer != null) { #if NET45_OR_GREATER if (ctx.IsWebSocketRequest) { return; } #endif var action = GetAction(ctx); if (!action.IsNullOrEmpty()) { var span = Tracer.NewSpan(action); ctx.Items["__span"] = span; // 聚合请求头作为强制采样的数据标签 var req = ctx.Request; var vs = new Dictionary <String, String>(StringComparer.OrdinalIgnoreCase); foreach (var item in req.Headers.AllKeys) { vs[item] = req.Headers[item]; } span.Tag = $"{req.UserHostAddress} {req.HttpMethod} {req.RawUrl}"; if (span is DefaultSpan ds && ds.TraceFlag > 0) { span.Tag += Environment.NewLine + vs.Join(Environment.NewLine, e => $"{e.Key}:{e.Value}"); } span.Detach(vs); } } }
/// <summary>队列消费大循环,处理消息后自动确认</summary> /// <typeparam name="T">消息类型</typeparam> /// <param name="queue">队列</param> /// <param name="onMessage">消息处理。如果处理消息时抛出异常,消息将延迟后回到队列</param> /// <param name="onException">异常处理</param> /// <param name="cancellationToken">取消令牌</param> /// <param name="tracer">性能跟踪</param> /// <returns></returns> public static async Task RunLoopAsync <T>(this IProducerConsumer <String> queue, Func <T, Task> onMessage, Action <Exception> onException = null, CancellationToken cancellationToken = default, ITracer tracer = null) { // 主题 var topic = (queue as RedisBase).Key; if (topic.IsNullOrEmpty()) { topic = queue.GetType().Name; } // 超时时间,用于阻塞等待 var timeout = 2; if (queue is RedisBase rb && rb.Redis != null) { timeout = rb.Redis.Timeout / 1000 - 1; } while (!cancellationToken.IsCancellationRequested) { ISpan span = null; try { // 异步阻塞消费 var msg = await queue.TakeOneAsync(timeout); if (msg != null) { // 反序列化消息 var message = msg.ToJsonEntity <T>(); span = tracer?.NewSpan($"mq:{topic}", msg); // 处理消息 await onMessage(message); // 确认消息 queue.Acknowledge(msg); } else { // 没有消息,歇一会 await TaskEx.Delay(1000); } } catch (ThreadAbortException) { break; } catch (ThreadInterruptedException) { break; } catch (Exception ex) { span?.SetError(ex, null); onException?.Invoke(ex); } finally { span?.Dispose(); } } }
/// <summary>读取线圈,0x01</summary> /// <param name="host">主机。一般是1</param> /// <param name="address">地址。例如0x0002</param> /// <param name="count">线圈个数</param> /// <returns></returns> public Byte[] ReadCoil(Byte host, UInt16 address, UInt16 count) { using var span = Tracer?.NewSpan("modbus:ReadCoil", $"{host} {address:X4} {count:X4}"); var rs = SendCommand(host, FunctionCodes.ReadCoil, address, count); if (rs == null || rs.Length <= 0) { return(null); } return(rs); }
private void ProcessAppTracer(AppTracer app) { // 应用是否需要告警 if (app == null || !app.Enable || app.AlarmThreshold <= 0) { return; } var appId = app.ID; if (!RobotHelper.CanAlarm(app.Category, app.AlarmRobot)) { return; } using var span = _tracer?.NewSpan($"Alarm:{nameof(AppTracer)}"); // 最近一段时间的5分钟级数据 var time = DateTime.Now; var minute = time.Date.AddHours(time.Hour).AddMinutes(time.Minute / 5 * 5); var st = AppMinuteStat.FindByAppIdAndTime(appId, minute); if (st == null) { return; } // 判断告警 if (st.Errors >= app.AlarmThreshold) { // 一定时间内不要重复报错,除非错误翻倍 var error2 = _cache.Get <Int32>("alarm:AppTracer:" + appId); if (error2 == 0 || st.Errors > error2 * 2) { _cache.Set("alarm:AppTracer:" + appId, st.Errors, 5 * 60); var msg = GetMarkdown(app, st, true); RobotHelper.SendAlarm(app.Category, app.AlarmRobot, "系统告警", msg); } } }
void OnInit(Object sender, EventArgs e) { var app = sender as HttpApplication; var ctx = app?.Context; if (ctx != null && Tracer != null) { var action = GetAction(ctx); if (!action.IsNullOrEmpty()) { var span = Tracer.NewSpan(action); ctx.Items["__span"] = span; } } }
/// <summary>为Http请求创建Span</summary> /// <param name="tracer">跟踪器</param> /// <param name="request">Http请求</param> /// <returns></returns> public static ISpan NewSpan(this ITracer tracer, HttpRequestMessage request) { if (tracer == null) { return(null); } var uri = request.RequestUri; var span = tracer.NewSpan(uri.ToString().TrimEnd(uri.Query)); span.Tag = uri.PathAndQuery; //span.Tag = request.Headers.UserAgent + ""; span.Attach(request); return(span); }
/// <summary>为Http请求创建Span</summary> /// <param name="tracer">跟踪器</param> /// <param name="request">Http请求</param> /// <returns></returns> public static ISpan NewSpan(this ITracer tracer, WebRequest request) { if (tracer == null) { return(null); } var url = request.RequestUri.ToString(); var p1 = url.IndexOf('?'); var p2 = url.IndexOf('/', "https://".Length); var span = tracer.NewSpan(p1 < 0 ? url : url.Substring(0, p1)); span.Tag = p2 < 0 ? url : url.Substring(p2); span.Attach(request); return(span); }
/// <summary>异步调用,等待返回结果</summary> /// <param name="client">Http客户端</param> /// <param name="method">请求方法</param> /// <param name="action">服务操作</param> /// <param name="args">参数</param> /// <param name="onRequest">请求头回调</param> /// <param name="dataName">数据字段名称,默认data。同一套rpc体系不同接口的code/message一致,但data可能不同</param> /// <returns></returns> public static async Task <TResult> InvokeAsync <TResult>(this HttpClient client, HttpMethod method, String action, Object args = null, Action <HttpRequestMessage> onRequest = null, String dataName = "data") { //if (client?.BaseAddress == null) throw new ArgumentNullException(nameof(client.BaseAddress)); var returnType = typeof(TResult); // 构建请求 var request = BuildRequest(method, action, args); // 指定返回类型 if (returnType == typeof(Byte[]) || returnType == typeof(Packet)) { request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/octet-stream")); } else { request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); } // 可能附加头部 onRequest?.Invoke(request); // 开始跟踪,注入TraceId var span = action.IsNullOrEmpty() ? null : Tracer?.NewSpan(action.EnsureStart("/")); span.Attach(request); try { // 发起请求 var msg = await client.SendAsync(request); return(await ProcessResponse <TResult>(msg, dataName)); } catch (Exception ex) { // 跟踪异常 span?.SetError(ex, args); throw; } finally { span?.Dispose(); } }
private ProxyBase CreateProxy(ProxyItem item, Boolean debug) { var xs = ProxyHelper.GetAll(); var type = xs.FirstOrDefault(e => item.Provider.EqualIgnoreCase(e.Name, e.FullName, e.Name.TrimEnd("Proxy"))); if (type == null) { return(null); } if (type.CreateInstance() is not ProxyBase proxy) { return(null); } using var span = _tracer?.NewSpan("CreateProxy", item); proxy.Name = item.Name; proxy.Tracer = _tracer; XTrace.WriteLine("创建代理 {0}", item.ToJson()); proxy.Init(item.Config); // 配置本地、远程参数。高级参数直接修改这里,解析item.Value proxy.Local = new NetUri(item.Local); if (proxy is NATProxy nat && !item.Remote.IsNullOrEmpty()) { nat.RemoteServer = new NetUri(item.Remote); } // 配置日志 proxy.Log = XTrace.Log; if (debug) { proxy.SessionLog = XTrace.Log; } // 启动服务 proxy.Start(); return(proxy); }
/// <summary>调用</summary> /// <param name="ctx"></param> /// <returns></returns> public async Task Invoke(HttpContext ctx) { // APM跟踪 //var span = Tracer?.NewSpan(ctx.Request.Path); ISpan span = null; if (Tracer != null) { var action = GetAction(ctx); if (!action.IsNullOrEmpty()) { span = Tracer.NewSpan(action); span.Tag = ctx.GetUserHost() + " " + ctx.Request.GetRawUrl(); span.Detach(ctx.Request.Headers.ToDictionary(e => e.Key, e => (Object)e.Value)); } } try { await _next.Invoke(ctx); // 根据状态码识别异常 if (span != null) { var code = ctx.Response.StatusCode; if (code >= 400) { span.SetError(new HttpRequestException($"Http Error {code} {(HttpStatusCode)code}"), null); } } } catch (Exception ex) { span?.SetError(ex, null); throw; } finally { span?.Dispose(); } }