zoukankan      html  css  js  c++  java
  • MongoDB学习笔记~监控Http请求的消息链

    在微服务架构里,你的一个任务可以需要经过多次中转,去多个接口获取数据,而在这个过程中,出现问题后的解决就成了一个大难点,你无法定位它的问题,这时,大叔的分布式消息树就出现了,费话不多说,主要看一下实现的逻辑。

    大叔对分布式消息链的一些想法

    事情是这样的,前段时间在做接口开发时,可能出现这种情况,一个接口返回的数据,可能来自多个接口,这就出现了一些链条式的调用,这在面向服务SOA开发和微服务开发中经常会遇到,因为你的VO数据来源,可能真的不是一个接口能满足的,这就需要有一个HTTP的链条,而如何对这些请求进行跟踪,就是大叔的消息链要做的事了!

    页面VO

    =>

    接口A请求

    =>

    接口B请求

    =>

    接口C接口

    =>

    接口C返回

    =>

    接口B返回

    =>

    接口A返回

    =>

    请求结束!

    大叔的设计图

    在消息传递过程中,使用这个消息上下文

        /// <summary>
        /// 消息上下文
        /// </summary>
        public class LoggerContext
        {
            /// <summary>
            /// 消息根ID(完整请求)
            /// </summary>
            [BsonId]
            [BsonRepresentation(BsonType.ObjectId)]
            public string Id { get; set; }
            public string RootId { get; set; }
            /// <summary>
            /// 上级消息ID(前一个请求)
            /// </summary>
            public string ParentId { get; set; }
            /// <summary>
            /// 当前消息ID(当前请求)
            /// </summary>
            public string ChildId { get; set; }
            /// <summary>
            /// 消息体
            /// </summary>
            public string MessageBody { get; set; }
            /// <summary>
            /// 当前url
            /// </summary>
            public string Url { get; set; }
            /// <summary>
            /// 时间
            /// </summary>
            public DateTime AddTime { get; set; }
        }

    大叔对消息处理程序的封装

        /// <summary>
        /// 分布式消息树实现
        /// </summary>
        public class LoggerContextImpl
        {
            static ILogger logger = new EmptyLogger();
            #region Fields & Consts
            const string Format_Msg_Before = "请求之前,地址:{0},方式:{1},时间:{2}";
            const string Format_Msg = "响应之后,地址:{0},状态码:{1},时间:{2}";
            /// <summary>
            /// HttpContext上存储的日志上下文
            /// </summary>
            const string LOGGERCONTEXT = "LoggerContext";
            #endregion
    
            #region Private Methods
            /// <summary>
            /// 从请求头中拿到当前的消息树对象
            /// client发布端:SetContextToServer
            /// server接收端:GetContextFromServer
            /// </summary>
            /// <returns></returns>
            static LoggerContext GetContextFromServer()
            {
                try
                {
                    var result = System.Web.HttpContext.Current.Request.Headers.GetValues(LOGGERCONTEXT);
                    if (result != null && result.Length > 0)
                    {
                        var cat = JsonConvert.DeserializeObject<LoggerContext>(result[0].ToString());
                        return cat;
                    }
                    return null;
                }
                catch (Exception ex)
                {
                    logger.Logger_Error(ex);
                    return null;
                }
    
            }
            static LoggerContext GetContextFromServer(HttpClient http)
            {
                try
                {
                    IList<string> result = http.DefaultRequestHeaders.GetValues(LOGGERCONTEXT) as IList<string>;
                    if (result != null && result.Count > 0)
                    {
                        var cat = JsonConvert.DeserializeObject<LoggerContext>(result[0].ToString());
                        return cat;
                    }
                    return null;
                }
                catch (Exception ex)
                {
                    logger.Logger_Error(ex);
                    return null;
                }
    
            }
            /// <summary>
            /// 设置消息树到当前请求头
            /// </summary>
            /// <returns></returns>
            internal static void SetContextToRequestHeader(System.Web.HttpContext http, LoggerContext context)
            {
                try
                {
                    if (http.Request.Headers.GetValues(LOGGERCONTEXT) != null && http.Request.Headers.GetValues(LOGGERCONTEXT).Length > 0)
                    {
                        http.Request.Headers.Remove(LOGGERCONTEXT);
                    }
                    http.Request.Headers.Add(LOGGERCONTEXT, JsonConvert.SerializeObject(context));
                }
                catch (Exception ex)
                {
                    logger.Logger_Error(ex);
                }
    
            }
            /// <summary>
            /// 设置消息树到当前请求头
            /// </summary>
            /// <param name="http"></param>
            /// <param name="context"></param>
            internal static void SetContextToRequestHeader(HttpClient http, LoggerContext context)
            {
                try
                {
                    http.DefaultRequestHeaders.Remove(LOGGERCONTEXT);
                    http.DefaultRequestHeaders.Add(LOGGERCONTEXT, JsonConvert.SerializeObject(context));
                }
                catch (Exception ex)
                {
                    logger.Logger_Error(ex);
                }
            }
            /// <summary>
            /// 设置消息树到当前请求头
            /// </summary>
            /// <param name="http"></param>
            /// <param name="context"></param>
            internal static void SetContextToRequestHeader(System.Web.HttpContextBase http, LoggerContext context)
            {
                try
                {
                    if (http.Request.Headers.GetValues(LOGGERCONTEXT) != null && http.Request.Headers.GetValues(LOGGERCONTEXT).Length > 0)
                    {
                        http.Request.Headers.Remove(LOGGERCONTEXT);
                    }
    
                    http.Request.Headers.Add(LOGGERCONTEXT, JsonConvert.SerializeObject(context));
                }
                catch (Exception ex)
                {
                    logger.Logger_Error(ex);
                }
    
    
            }
            /// <summary>
            /// 设置请求头,它来自某个响应头
            /// </summary>
            /// <param name="response"></param>
            internal static void SetContextToRequestHeader(HttpResponseMessage response, string currentUrl = null)
            {
                try
                {
                    IEnumerable<string> context = new List<string>();
                    if (response.Headers.TryGetValues(LOGGERCONTEXT, out context) || response.RequestMessage.Headers.TryGetValues(LOGGERCONTEXT, out context))
                    {
                        if (context != null)
                        {
                            var cat = JsonConvert.DeserializeObject<LoggerContext>((context as string[])[0].ToString());
                            SetContextToRequestHeader(System.Web.HttpContext.Current, cat);
                            GetCurrentContext("响应结束", currentUrl);
                        }
                    }
                }
                catch (Exception ex)
                {
                    logger.Logger_Error(ex);
                }
    
    
            }
            /// <summary>
            /// 设置LoggerContext到响应头
            /// </summary>
            /// <param name="response"></param>
            /// <param name="context"></param>
            internal static void SetContextToResponseHeader(HttpResponseBase response, LoggerContext context)
            {
                try
                {
                    if (response.Headers.GetValues(LOGGERCONTEXT) != null
                                   && response.Headers.GetValues(LOGGERCONTEXT).Length > 0)
                    {
                        response.Headers.Remove(LOGGERCONTEXT);
                    }
                    response.Headers.Add(LOGGERCONTEXT, JsonConvert.SerializeObject(context));
                }
                catch (Exception ex)
                {
                    logger.Logger_Error(ex);
                }
    
            }
            /// <summary>
            /// 生产一个ROOTID
            /// </summary>
            /// <returns></returns>
            static string GenerateRootID()
            {
                return DateTime.Now.ToString("yyyyMMddHHmmssfff") + Thread.CurrentThread.ManagedThreadId;
            }
            /// <summary>
            /// 递归树
            /// </summary>
            /// <param name="str"></param>
            /// <param name="id"></param>
            /// <param name="timer"></param>
            static void MsgTree(StringBuilder str, string id, List<DateTime> timer)
            {
                var list = NoSql.MongodbManager<LoggerContext>.Instance.Find(i => i.ParentId == id).ToList();
                if (list != null)
                {
                    str.Append("<ul class='treeMsg'>");
                    foreach (var item in list)
                    {
                        timer.Add(item.AddTime);
                        str.AppendFormat("<li><span style='color:red'>{0}</span><span style='color:green'>{1}</span><span>{2}</span></li>"
                         , item.Url
                         , item.MessageBody
                         , item.AddTime);
                        MsgTree(str, item.ChildId, timer);
                    }
                    str.Append("</ul>");
    
                }
    
    
    
            }
            #endregion
    
            #region 分布式消息树的封装(仓储大叔) 
            /// <summary>
            ///  建立一个上下文对象
            /// </summary>
            /// <param name="rootId">根ID</param>
            /// <param name="parentId">上一请求ID</param>
            /// <param name="url"></param>
            /// <returns></returns>
            public static LoggerContext DoTransaction(string rootId, string parentId, string url)
            {
                if (GlobalConfig.ConfigManager.Config.Logger.IsHttpClientLog != 1)
                    return new LoggerContext();
    
                //建立一个日志,返回rootid,parentid(第一个应该是空),currentid,其中currentid将做为下一次请求的parentid
                var filter = Builders<LoggerContext>.Filter.Eq(i => i.RootId, rootId);
                var context = NoSql.MongodbManager<LoggerContext>.Instance.Find(filter).FirstOrDefault();
                if (!string.IsNullOrWhiteSpace(parentId))
                {
                    filter = Builders<LoggerContext>.Filter.Eq(i => i.ParentId, parentId);
                    context = NoSql.MongodbManager<LoggerContext>.Instance.Find(filter).FirstOrDefault();
                }
                if (context == null)
                {
                    context = new LoggerContext
                    {
                        RootId = GenerateRootID(),
                        ParentId = null,
                        ChildId = Domain.PrimaryKey.GenerateNewStringId(),
                        MessageBody = "开启一个新的请求:" + url,
                        Url = System.Web.HttpContext.Current.Request.Url.AbsoluteUri.Split(new char[] { '?' }, StringSplitOptions.RemoveEmptyEntries)[0],
                        AddTime = DateTime.Now,
                    };
                    NoSql.MongodbManager<LoggerContext>.Instance.InsertOne(context);
                }
                context.MessageBody = HttpUtility.UrlEncode(context.MessageBody);
                return context;
            }
            /// <summary>
            /// 添加日志,它依赖于一个会话
            /// root->message->message1->message1.1->message1.1.1
            /// </summary>
            /// <param name="parentId">父会话ID</param>
            /// <param name="url"></param>
            /// <param name="message"></param>
            public static LoggerContext LogEvent(string parentId, string url, string message)
            {
                if (GlobalConfig.ConfigManager.Config.Logger.IsHttpClientLog != 1)
                    return new LoggerContext();
    
                var filter = Builders<LoggerContext>.Filter.Eq(i => i.ChildId, parentId);
                var context = NoSql.MongodbManager<LoggerContext>.Instance.Find(filter).FirstOrDefault();
                if (context != null)
                {
                    context = new LoggerContext
                    {
                        RootId = context.RootId,
                        ParentId = context.ChildId,
                        ChildId = Domain.PrimaryKey.GenerateNewStringId(),
                        MessageBody = message + ":" + url,
                        Url = System.Web.HttpContext.Current.Request.Url.AbsoluteUri.Split(new char[] { '?' }, StringSplitOptions.RemoveEmptyEntries)[0],
                        AddTime = DateTime.Now,
                    };
                    NoSql.MongodbManager<LoggerContext>.Instance.InsertOne(context);
                }
                return context;
            }
            /// <summary>
            /// 返回当前上下文
            /// </summary>
            /// <returns></returns>
            public static LoggerContext GetCurrentContext(string message, string currentUrl = null)
            {
                try
                {
                    currentUrl = (currentUrl ?? System.Web.HttpContext.Current.Request.Url.AbsoluteUri).Split(new char[] { '?' }, StringSplitOptions.RemoveEmptyEntries)[0];
                    var context = GetContextFromServer();
    
                    if (context == null)
                    {
                        context = DoTransaction("", "", currentUrl);
    
                    }
                    else
                    {
                        context = LogEvent(context.ChildId, currentUrl, message);
                    }
                    return context;
                }
                catch (Exception ex)
                {
                    logger.Logger_Error(ex);
                    return new LoggerContext();
                }
    
            }
    
            #endregion
    
            #region 消息树UI
            /// <summary>
            /// 返回UI消息树
            /// </summary>
            /// <returns></returns>
            public static string GetMongoLog(DateTime? fromDate, DateTime? toDate, int page = 1)
            {
                string from = DateTime.Now.AddYears(-1).Date.ToString("yyyy-MM-dd");
                string to = DateTime.Now.Date.AddDays(1).ToString("yyyy-MM-dd");
                if (fromDate.HasValue)
                {
                    from = fromDate.Value.ToString("yyyy-MM-dd");
    
                }
                if (toDate.HasValue)
                {
                    to = toDate.Value.ToString("yyyy-MM-dd");
                }
                var stages = new List<IPipelineStageDefinition>();
                stages.Add(new JsonPipelineStageDefinition<BsonDocument, BsonDocument>("{$match:{AddTime:{$gt:ISODate('" + from + "'),$lt:ISODate('" + to + "')}}}"));
                stages.Add(new JsonPipelineStageDefinition<BsonDocument, BsonDocument>("{$group:{_id: "$RootId", count: {$sum: 1}}}"));
                stages.Add(new JsonPipelineStageDefinition<BsonDocument, BsonDocument>("{$skip:" + page * 5 + "}"));
                stages.Add(new JsonPipelineStageDefinition<BsonDocument, BsonDocument>("{$limit:5}"));
                var pipeline = new PipelineStagePipelineDefinition<BsonDocument, BsonDocument>(stages);
                var result = NoSql.MongodbManager<LoggerContext>.Collection.Aggregate(pipeline);
                StringBuilder str = new StringBuilder();
    
                str.Append("<ol class='treeMsg'>");
                foreach (var item in result.ToList())
                {
                    var timer = new List<DateTime>();
                    var old = NoSql.MongodbManager<LoggerContext>.Instance.Find(i => i.RootId == item.Values.ToArray()[0].ToString() && i.ParentId == null).FirstOrDefault();
                    timer.Add(old.AddTime);
                    str.Append("<li style='margin:5px;border:1px dashed #aaa'>");
                    str.AppendFormat("<span style='color:red;'>{0}</span><span style='color:green'>{1}</span><span>{2}</span>"
                       , old.Url
                       , old.MessageBody
                       , old.AddTime);
                    MsgTree(str, old.ChildId, timer);
                    str.AppendFormat("<p><b><em>本次请求用时{0}毫秒({1}秒)<em></b></p>"
                        , (timer.Max() - timer.Min()).TotalMilliseconds
                        , (timer.Max() - timer.Min()).TotalSeconds);
                    str.Append("</li>");
                }
                str.Append("</ol>");
                return str.ToString();
            }
            #endregion
        }
  • 相关阅读:
    遍历指定目录及其子目录下所有文件
    vim 配置
    解决 Mendeley Linux 中文输入问题
    全角半角字符对照表
    chrome 替换多线程下载管理器
    查看系统日志
    中大东校区iNode For linux 配置笔记
    anaconda 虚拟环境笔记
    linux 网络操作
    deepin 装机
  • 原文地址:https://www.cnblogs.com/lori/p/7028583.html
Copyright © 2011-2022 走看看