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 > )
{
var cat = JsonConvert.DeserializeObject<LoggerContext>(result[].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 > )
{
var cat = JsonConvert.DeserializeObject<LoggerContext>(result[].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 > )
{
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 > )
{
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[])[].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 > )
{
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 != )
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)[],
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 != )
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)[],
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)[];
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 = )
{
string from = DateTime.Now.AddYears(-).Date.ToString("yyyy-MM-dd");
string to = DateTime.Now.Date.AddDays().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 * + "}"));
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()[].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 学习笔记纯干货(mongoose、增删改查、聚合、索引、连接、备份与恢复、监控等等)
最后更新时间:2017-07-13 11:10:49 原始文章链接:http://www.lovebxm.com/2017/07/13/mongodb_primer/ MongoDB - 简介 官网: ...
- 【转】mongoDB 学习笔记纯干货(mongoose、增删改查、聚合、索引、连接、备份与恢复、监控等等)
mongoDB 学习笔记纯干货(mongoose.增删改查.聚合.索引.连接.备份与恢复.监控等等) http://www.cnblogs.com/bxm0927/p/7159556.html
- MongoDB学习笔记:快速入门
MongoDB学习笔记:快速入门 一.MongoDB 简介 MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统.在高负载的情况下,添加更多的节点,可以保证服务器性能.M ...
- MongoDB学习笔记(四)--索引 && 性能优化
索引 基础索引 ...
- RocketMQ 源码学习笔记————Producer 是怎么将消息发送至 Broker 的?
目录 RocketMQ 源码学习笔记----Producer 是怎么将消息发送至 Broker 的? 前言 项目结构 rocketmq-client 模块 DefaultMQProducerTest ...
- RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的?
目录 RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的? 前言 项目结构 rocketmq-client 模块 DefaultMQProducerTest Roc ...
- MongoDB学习笔记:MongoDB 数据库的命名、设计规范
MongoDB学习笔记:MongoDB 数据库的命名.设计规范 第一部分,我们先说命名规范. 文档 设计约束 UTF-8 字符 不能包含 \0 字符(空字符),这个字符标识建的结尾 . 和 $ ...
- MongoDB学习笔记二- Mongoose
MongoDB学习笔记二 Mongoose Mongoose 简介 之前我们都是通过shell来完成对数据库的各种操作, 在开发中大部分时候我们都需要通过程序来完成对数据库的操作 而Mongoose就 ...
- MongoDB学习笔记系列
回到占占推荐博客索引 该来的总会来的,Ef,Redis,MVC甚至Sqlserver都有了自己的系列,MongoDB没有理由不去整理一下,这个系列都是平时在项目开发时总结出来的,希望可以为各位一些帮助 ...
随机推荐
- 记录001:AS11 BAPI
未知元素(174657434) 15:05:41AS11有没有BAPI呀?有做过的吗 BAPI_FIXEDASSET_OVRTAKE_CREATE
- 初探linux子系统集之led子系统(三)【转】
本文转载自:http://blog.csdn.net/eastmoon502136/article/details/37822837 世界杯结束了,德国战车夺得了大力神杯,阿根廷最终还是失败了.也许3 ...
- SDUT 3033 这题实在不知道起啥名好了(思维巧法)
这题实在不知道起啥名好了 Time Limit: 1000ms Memory limit: 65536K 有疑问?点这里^_^ 题目描述 懒得想背景故事了,开门见山. 有一个长度为n的整数数列A ...
- 目录操作(PHP)
1.创建目录(文件夹)mkdir("./test");2.删除目录(文件夹)只能删除空的文件夹rmdir("./test");3.移动目录(文件夹)rename ...
- CentOS 7中ip命令将逐渐取代 ifconfig
首先看下图: 要安装ip,请点击这里下载iproute2套装工具 .不过,大多数Linux发行版已经预装了iproute2工具. 你也可以使用git命令来下载最新源代码来编译: $ git clone ...
- Android多国语言文件夹汇总
Arabic, Egypt (ar-rEG) —————————–阿拉伯语,埃及 Arabic, Israel (ar-rIL) ——————————-阿拉伯语,以色列 Bulgarian, Bulg ...
- 安装Sublime Text 3插件的方法(转自Rising的博文)
安装Sublime Text 3插件的方法: 朋友们,小站活着不容易,全靠广告费养着了,如果本文对你有帮助.麻烦动下手点下页面的广告吧,谢谢! 直接安装 安装Sublime text 2插件很方便,可 ...
- Python测试框架doctest
doctest是python自带的一个模块.本博客将介绍doctest的两种使用方式:一种是嵌入到python源码中,另外一种是放到一个独立文件. doctest 的概念模型 在python的官方文档 ...
- fastText(二):微博短文本下fastText的应用(一)
众所周知,微博中的内容以短文本居多,文本内容随意性极强,这给建模增加了很大的难度.针对这一问题,这里分享一下fastText在微博短文本的应用. 任务目标简单介绍一下整个任务的目标:给微博内容打上标签 ...
- 移动web开发-------meta
<meta content=”width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0″ name=”v ...