MongoDB学习笔记~监控Http请求的消息链

本文涉及的产品
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
简介:

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

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

事情是这样的,前段时间在做接口开发时,可能出现这种情况,一个接口返回的数据,可能来自多个接口,这就出现了一些链条式的调用,这在面向服务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
    }

 本文转自博客园张占岭(仓储大叔)的博客,原文链接:MongoDB学习笔记~监控Http请求的消息链,如需转载请自行联系原博主。

相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。 &nbsp; 相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
目录
相关文章
|
1月前
|
数据采集
Haskell爬虫:连接管理与HTTP请求性能
Haskell爬虫:连接管理与HTTP请求性能
|
16天前
|
监控 网络协议 应用服务中间件
【Tomcat源码分析】从零开始理解 HTTP 请求处理 (第一篇)
本文详细解析了Tomcat架构中复杂的`Connector`组件。作为客户端与服务器间沟通的桥梁,`Connector`负责接收请求、封装为`Request`和`Response`对象,并传递给`Container`处理。文章通过四个关键问题逐步剖析了`Connector`的工作原理,并深入探讨了其构造方法、`init()`与`start()`方法。通过分析`ProtocolHandler`、`Endpoint`等核心组件,揭示了`Connector`初始化及启动的全过程。本文适合希望深入了解Tomcat内部机制的读者。欢迎关注并点赞,持续更新中。如有问题,可搜索【码上遇见你】交流。
【Tomcat源码分析】从零开始理解 HTTP 请求处理 (第一篇)
|
5天前
|
JSON Java 数据格式
java操作http请求针对不同提交方式(application/json和application/x-www-form-urlencoded)
java操作http请求针对不同提交方式(application/json和application/x-www-form-urlencoded)
10 1
|
6天前
|
前端开发 JavaScript
HTTP学习笔记
HTTP学习笔记
12 1
|
25天前
|
JSON JavaScript 前端开发
Haskell中的数据交换:通过http-conduit发送JSON请求
Haskell中的数据交换:通过http-conduit发送JSON请求
|
27天前
|
JSON API 开发者
Python网络编程新纪元:urllib与requests库,让你的HTTP请求无所不能
【9月更文挑战第9天】随着互联网的发展,网络编程成为现代软件开发的关键部分。Python凭借简洁、易读及强大的特性,在该领域展现出独特魅力。本文介绍了Python标准库中的`urllib`和第三方库`requests`在处理HTTP请求方面的优势。`urllib`虽API底层但功能全面,适用于深入控制HTTP请求;而`requests`则以简洁的API和人性化设计著称,使HTTP请求变得简单高效。两者互补共存,共同推动Python网络编程进入全新纪元,无论初学者还是资深开发者都能从中受益。
39 7
|
25天前
|
开发者
HTTP状态码是由网页服务器返回的三位数字响应代码,用于表示请求的处理结果和状态
HTTP状态码是由网页服务器返回的三位数字响应代码,用于表示请求的处理结果和状态
24 1
|
2月前
|
缓存 网络协议 安全
揭秘浏览器背后的神秘之旅:一网打尽HTTP请求流程,让你网络冲浪更顺畅!
【8月更文挑战第31天】当在浏览器中输入网址并按下回车键时,一系列复杂的HTTP请求流程随即启动。此流程始于DNS解析,将域名转化为IP地址;接着是与服务器的TCP三次握手建立连接。连接建立后,浏览器发送HTTP请求,其中包含请求方法、资源及版本等信息。服务器接收请求并处理后返回HTTP响应,包括状态码、描述及页面内容。浏览器解析响应,若状态码为200则渲染页面,否则显示错误页。整个流程还包括缓存处理和HTTPS加密等步骤,以提升效率和保障安全。理解该流程有助于更高效地利用网络资源。通过抓包工具如Wireshark,我们能更直观地观察和学习这一过程。
43 4
|
1月前
|
JSON 监控 API
http 请求系列
XMLHttpRequest(XHR)是一种用于在客户端和服务器之间进行异步HTTP请求的API,广泛应用于动态更新网页内容,无需重新加载整个页面。本文提供了多个官方学习资源,包括MDN Web Docs、WhatWG和W3C的规范文档,涵盖属性、方法、事件及示例代码。XHR的主要应用场景包括动态内容更新、异步表单提交、局部数据刷新等,具有广泛的支持和灵活性,但也存在处理异步请求的复杂性等问题。最佳实践包括使用异步请求、处理请求状态变化、设置请求头、处理错误和超时等。这些资源和实践将帮助你更好地理解和使用XHR。
24 1
|
27天前
|
存储 JSON API
Python编程:解析HTTP请求返回的JSON数据
使用Python处理HTTP请求和解析JSON数据既直接又高效。`requests`库的简洁性和强大功能使得发送请求、接收和解析响应变得异常简单。以上步骤和示例提供了一个基础的框架,可以根据你的具体需求进行调整和扩展。通过合适的异常处理,你的代码将更加健壮和可靠,为用户提供更加流畅的体验。
65 0
下一篇
无影云桌面