日志系统实战(三)-分布式跟踪的Net实现

介绍

在大型系统开发调试中,跨系统之间联调开始变得不好使了。莫名其妙一个错误爆出来了,日志虽然有记录,但到底是哪里出问题了呢?

是ios端参数传的不对?还是A系统或B系统提供的接口导致?相信大家碰到不少,大多数问题不大,但排查起来比较费劲。

下面,我们来具体看下实现。

目录

1:概述

2:web环境

3:多线程环境

4:异步环境

5:性能,大数据量,隐私安全

6:总结

一:概述

一句话总结:通过一个TraceId把整个请求,形成一个调用链。

这样无论任何地方报错,只要拿TraceId去系统查下,根据上下文就知道是哪一步,哪个函数,哪个参数出错了,能以最快速度处理。

如图:以博客园为例。当博客园收到一个请求后,自动为生产个唯一ID 1000,之后所有处理工作都是用这个1000。

每个处理模块都维持一个上下文ID自增,rpcid++。

处理模块可以是函数级,逻辑层级,服务器级等都可以。

一旦发现有异常后,自动将TraceId发给博客园。这样程序员们,就能根据TraceId最快定位问题了。

我们来看下具体怎么实现。

二: web环境

我们定义跟踪日志需要的参数,进行上下文传递。

   public class LogBody
    {
        /// <summary>
        /// 跟踪ID
        /// </summary>
        public string TraceId { get; set; }
        /// <summary>
        /// 上下文ID
        /// </summary>
        public int RpcId { get; set; }
        /// <summary>
        /// 处理时间
        /// </summary>
        public DateTime LastTime { get; set; }
    }

我们这global.asax,利用HttpContext.Current上下文。开始埋点(跟踪),rpc 0。

   void Application_BeginRequest(object sender, EventArgs e)
        {
            var lb = new LogBody();
            lb.TraceId = Guid.NewGuid().ToString("N");
            lb.RpcId=0;
            lb.LastTime = DateTime.Now;
            HttpContext.Current.Response.AppendHeader("traceID", lb.TraceId);
            HttpContext.Current.Items.Add(lb.TraceId, lb);
            //记录日志,例:用户请求参数,userAgent等。
        }

在default页开始业务逻辑,rpc 1。

 protected void Page_Load(object sender, EventArgs e)
        {
            var traceID = HttpContext.Current.Response.Headers["traceID"];
            LogBody logbody = HttpContext.Current.Items[traceID] as LogBody;
            logbody.RpcId++;
            logbody.LastTime = DateTime.Now;
            //业务逻辑。
            //记录日志。。。
        }

如上我们就完成上下文的传递。

Application_BeginRequest  中在实际使用中,我们只需要对有用的页面(例:aspx,ashx)进行埋点。

日志记录的时候,可以把logbody都存储起来。

LastTime这个字段,我们可以与上一次的相减。这样就得出中间逻辑处理所需时间了。

三:多线程环境

在web程序中我们可以用http服务器的上下文传递。在单线程的程序中,我们按照线性顺序即可。

多线程中我们可以用threadlocal传递。

   public static ThreadLocal<LogBody> Body = new ThreadLocal<LogBody>();
        static void Main(string[] args)
        {
            var t1 = new Thread(() =>
            {
                Body.Value = new LogBody()
                {
                    LastTime = DateTime.Now,
                    RpcId = 0,
                    TraceId = Guid.NewGuid().ToString("N")
                };
                //业务1
                Console.WriteLine("Thread1 log record:" + Body.Value.TraceId + "-" + Body.Value.RpcId + "-" + Body.Value.LastTime);

                Thread.Sleep(5000);

                Body.Value.RpcId++;
                Body.Value.LastTime = DateTime.Now;
                //业务2
                Console.WriteLine("Thread1 log record:" + Body.Value.TraceId + "-" + Body.Value.RpcId + "-" + Body.Value.LastTime);
            });
            t1.Start();

            var t2 = new Thread(() =>
            {
                Body.Value = new LogBody()
                {
                    LastTime = DateTime.Now,
                    RpcId = 0,
                    TraceId = Guid.NewGuid().ToString("N")
                };
                //业务1
                Console.WriteLine("Thread2 log record:" + Body.Value.TraceId + "-" + Body.Value.RpcId + "-" + Body.Value.LastTime);

                Thread.Sleep(5000);
                Body.Value.RpcId++;
                Body.Value.LastTime = DateTime.Now;
                //业务2
                Console.WriteLine("Thread2 log record:" + Body.Value.TraceId + "-" + Body.Value.RpcId + "-" + Body.Value.LastTime);
            });
            t2.Start();
        }

运行如下:

四:异步环境

往往在生产环境中,会有大量的异步操作。如果有异步行为的话,打乱我们的上下文怎么办?这时候我们引入另外一个概念,父节点Id。

这样异步操作的行为,就父节点之下。 最终在日志后台展示,我们看到的是倒着的树形结构。

如图,我们看到业务2异步派生出来的子节点。

我们把上下文rpcid修改成double类型。

 static void Main(string[] args)
        {
            var t2 = new Thread(() =>
            {
                Body.Value = new LogBody()
                {
                    LastTime = DateTime.Now,
                    RpcId = 1,
                    TraceId = Guid.NewGuid().ToString("N")
                };
                var t1 = new Thread((lb) =>
                {
                    var temp = lb as LogBody;
                    Body.Value = new LogBody()
                    {
                        LastTime = DateTime.Now,
                        RpcId = temp.RpcId,
                        TraceId = temp.TraceId
                    };
                    Body.Value.RpcId += 0.1;
                    //业务x
                    Console.WriteLine("async Thread:" + Body.Value.TraceId + "-" + Body.Value.RpcId + "-" + Body.Value.LastTime );

                    Thread.Sleep(5000);

                    Body.Value.RpcId+=0.1;
                    Body.Value.LastTime = DateTime.Now;
                    //业务y
                    Console.WriteLine("async Thread:" + Body.Value.TraceId + "-" + Body.Value.RpcId + "-" + Body.Value.LastTime);
                });
                t1.Start(Body.Value);

                //业务1
                Console.WriteLine("sync Thread:" + Body.Value.TraceId + "-" + Body.Value.RpcId + "-" + Body.Value.LastTime);

                Thread.Sleep(2000);
                Body.Value.RpcId+=1;
                Body.Value.LastTime = DateTime.Now;
                //业务2
                Console.WriteLine("sync Thread:" + Body.Value.TraceId + "-" + Body.Value.RpcId + "-" + Body.Value.LastTime);
            });
            t2.Start();
        }

  

代码中我们用参数传递给了异步线程中。运行如下:

五:性能,大数据量,隐私安全

关于性能

从代码中,我们发现。这种方式对程序性能影响可以忽略不计。

需要注意是:如果在生产环境跑的话,不论你是写文件,还是数据库,或写统一日志平台。都会导致大量IO读写,网络资源消耗。

如果服务器都消耗这上面,都得不偿失了。

我们可以用内存队列+队列+批量push或pull的方式。并且注意设置阀值。

关于大数据量

大量的请求,其实多数是无效的。这里引入采样率的概念。 例如按求余取,或者按地区,时间等。也可以自己写采样规则。

日志可以只记录error以上的级别,只有在排查生产环境的时候才开启debug,info级别信息。

存储这块,可以根据实际需要选择sql server,mongodb,hbase hdfs。

关于隐私安全

如果有敏感数据,建议根据进行加密。

六:总结

本文是基于Google dapper论文的思路展开。大家基于此进行扩展。

示例中,都是手动记录。在实际使用中,是可以简化调用的。 当然也封装成自动构建的,大家可以看前2篇的自动注入。

参考资源

1:Google dapper论文

2:淘宝EagleEye系统

作者:蘑菇先生

出处:http://www.cnblogs.com/mushroom/p/4156468.html

本文品基于知识共享署名 2.5 中国大陆欢迎转载,演绎或用于商业目的,但是必须保留本文的署名蘑菇先生(包含链接)。

时间: 2024-10-20 18:51:43

日志系统实战(三)-分布式跟踪的Net实现的相关文章

日志系统实战(一)-AOP静态注入

背景 近期在写日志系统,需要在运行时在函数内注入日志记录,并附带函数信息.这时候就想到用Aop的方式了. 技术分析 AOP分动态注入和静态注入. 动态注入方式 1:Remoting的ContextAttribute上下文(性能差). 2:动态代理(反射),大多AOP框架都用这种方式. 3:MVC的filter,也是反射. 第一种:性能太差不考虑.第二种:为了记日志,生产环境都用动态代理,性能损耗不小,不推荐.第三种:只有UI层能用.其他层和第二种一样. 静态注入方式 (本文重点). 1:基于IL

Java秒杀系统实战系列~分布式唯一ID生成订单编号

摘要: 本篇博文是"Java秒杀系统实战系列文章"的第七篇,在本博文中我们将重点介绍 "在高并发,如秒杀的业务场景下如何生成全局唯一.趋势递增的订单编号",我们将介绍两种方法,一种是传统的采用随机数生成的方式,另外一种是采用当前比较流行的"分布式唯一ID生成算法-雪花算法"来实现. 内容: 在上一篇博文,我们完成了商品秒杀业务逻辑的代码实战,在该代码中,我们还实现了"当用户秒杀成功后,需要在数据库表中为其生成一笔秒杀成功的订单记录&qu

日志系统实战(二)-AOP动态获取运行时数据

介绍 这篇距上一篇已经拖3个月之久了,批评自己下. 通过前面一篇介绍.我们通过mono反射代码,可以拿出编译好的静态数据.例如方法参数信息之类的.但实际情况是:我更需要运行时的数据,就是用户输入等外界的动态数据. 既然是动态的,那就是未知的.我们怎么通过提前注入的代码获取呢? 其实这是一个思路的问题,下面我们具体细看下. 实现 一 普通写法 public static string GetPoint(int x, int y) { var value=x; } 哇 好简单啊.其实动态获取和我们普

日志系统实战 AOP静态注入

http://www.cnblogs.com/mushroom/p/3932698.html http://www.cnblogs.com/mushroom/p/4124878.html http://www.cnblogs.com/mushroom/p/4156468.html http://www.cnblogs.com/whitewolf/category/312512.html

atitit. 日志系统的原则and设计and最佳实践(1)-----原理理论总结.

atitit. 日志系统的原则and设计and最佳实践总结. 1. 日志系统是一种不可或缺的单元测试,跟踪调试工具 1 2. 日志系统框架通常应当包括如下基本特性 1 1. 所输出的日志拥有自己的分类. 2 2. 日志按照某种标准分成不同级别. 2 3. 支持多线程. 2 4. 稳定性. 2 3. 一个理想的日志模式 2 4. 判断指定的方法是否被调用了 3 5. 给方法的输入输出加上日志通过Aop 3 6. 日志易读,易解析  对日志感兴趣的可以分为两类: 3 7. 输出日志使用的性能 3 8

Android日志系统Logcat源代码简要分析

文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6606957 在前面两篇文章Android日志系统驱动程序Logger源代码分析和Android应用程序框架层和系统运行库层日志系统源代码中,介绍了Android内核空间层.系统运行库层和应用程序框架层日志系统相关的源代码,其中,后一篇文章着重介绍了日志的写入操作.为了描述完整性,这篇文章着重介绍日志的读取操作,这就是我们在开发Android应用

分布式框架-日志系统思路及实现

转自:https://www.jianshu.com/p/ce30c31111ca 背景 随着互联网时代数据规模的爆发式增长,传统的单机系统在性能和可用性上已经无法胜任,分布式应用和服务化应用开始走进大家的视野,但是分布式的部署也会带来另外的问题,日志分散在各个应用服务节点中,出现问题不方便及时排查,尤其是服务化的应用中,分析问题时可能需要查看多个日志文件才能定位问题,如果相关项目不是一个团队维护时沟通成本更是直线上升,怎么将日志文件归集,怎么将日志文件呈现成了很多公司需要面对的问题,因此日志系

日志系统之基于Zookeeper的分布式协同设计

最近这段时间在设计和实现日志系统,在整个日志系统系统中Zookeeper的作用非常重要--它用于协调各个分布式组件并提供必要的配置信息和元数据.这篇文章主要分享一下Zookeeper的使用场景.这里主要涉及到Zookeeper在日志系统中的使用,但其实它在我们的消息总线和搜索模块中也同样非常重要. 日志元数据 日志的类型和日志的字段这里我们统称为日志的元数据.我们构建日志系统的目的最终主要是为了:日志搜索,日志分析.这两大块我们很大程度上依赖于--ElasticSearch(关于什么是Elast

HAProxy + Keepalived + Flume 构建高性能高可用分布式日志系统

一.HAProxy简介 HAProxy提供高可用性.负载均衡以及基于TCP和HTTP应用的代 理,支持虚拟主机,它是免费.快速并且可靠的一种解决方案.HAProxy特别适用于那些负载特大的web站点,这些站点通常又需要会话保持或七层处理. HAProxy运行在当前的硬件上,完全可以支持数以万计的并发连接.并且它的运行模式使得它可以很简单安全的整合进您当前的架构中, 同时可以保护你的web服务器不被暴露到网络上. 二.Keepalived简介 它是一个基于VRRP协议来实现的WEB服务高可用方案,