.NET LINQ分析AWS ELB日志避免996

前言

小明是个单纯的 .NET开发,一天大哥叫住他,安排了一项任务:

“小明,分析一下我们 超牛逼网站上个月的所有 AWS ELB流量日志,这些日志保存在 AWS S3上,你分析下,看哪个 API的响应时间中位数最长。”

“对了,别用 Excel,哥给你写好了一段 Python脚本,可以自动解析统计一个 AWS ELB文件的日志,你可以利用一下。”

“好的✌,大哥真厉害!”。

小明看了一下,然后傻眼了,在管理控制台中,九月份 AWS ELB日志文件翻了好几页都没翻完,大概算算,大概有 1000个文件不止。想想自己又不懂 Python,又不是搞数据分析专业出身的,这个“看似简单”的工作完不成,这周怕是陪不了女朋友,搞不好还要 996.ICU,小明几乎要流下了没有技术的泪水……

不怕!会.NET就行!

要完成这项工作,光老老实实将文件从管理控制台下载到本地,估计都够喝一壶。若小明稍机灵点,他可能会找到 AWS S3的文件管理器,然后……发现只有付费版才有批量下载功能。

其实要完成这项工作,只需做好两项基本任务即可:

  • 从 AWS S3下载9月份的所有 ELB日志

  • 聚合并分析这1000多个日志文件,然后按响应时间中位数倒排序

AWS资源

能在管理控制台上看到的 AWS资源, AWS都提供了各语言的 SDK可供操作(可在 SDK上操作的东西,如批量下载,反倒不一定能在界面上看到)。SDK支持多种语言,其中(显然)也包括 .NET

对于 AWS S3的访问, Amazon提供的 NuGet包叫:AWSSDK.S3,在 VisualStudio中下载并安装,即可运行本文的示例。

要使用 AWSSDK.S3,首先需要实例化一个 AmazonS3Client,并传入 aws access keyaws secret keyAWS区域等参数:

var credentials = new BasicAWSCredentials(	Util.GetPassword("aws_live_access_key"), 	Util.GetPassword("aws_live_secret_key"));	
var s3 = new AmazonS3Client(credentials, RegionEndpoint.USEast1);

注意:本文的所有代码全部共享这一个 s3的实例。因为根据文档, AmazonS3Client实例是设计为线程安全的。

在下载 AWS S3的文件(对象)之前,首先需要知道有哪些对象可供下载,可通过 ListObjectsV2Async方法列出某个 bucket的文件列表。注意该方法是分页的,经我的测试,无论 MaxKeys参数设置多大,该接口最多一次性返回 1000条数据,但这显然不够,因此需要循环分页去拿。

分页时该响应对象中包含了 NextContinuationTokenIsTruncated属性,如果 IsTruncated=true,则 NextContinuationToken必定有值,此时下次调用 ListObjectsV2Async时的请求参数传入 NextContinuationToken即可实现分页获取 S3文件列表的功能。

这个过程说起来有点绕,但感谢 C#提供了 yield关键字来实现 协程-coroutine,代码写起来非常简单:

IEnumerable<List<S3Object>> Load201909SuperCoolData(AmazonS3Client s3)	
{	ListObjectsV2Response response = null;	do	{	response = s3.ListObjectsV2Async(new ListObjectsV2Request	{	BucketName = "supercool-website",	Prefix = "AWSLogs/1383838438/elasticloadbalancing/us-east-1/2019/09",	ContinuationToken = response?.NextContinuationToken, 	MaxKeys = 100, 	}).Result;	yield return response.S3Objects;	} while (response.IsTruncated);	
}

注意:Prefix为前缀, AWS ELB日志都会按时间会有一个前缀模式,从文件列表中找到这一模式后填入该参数。

接下来就简单了,通过 GetObjectAsync方法即可下载某个对象,要直接分析,最好先转换为字符串,拿到文件流 stream后,最简单的方式是使用 StreamReader将其转换为字符串:

IEnumerable<string> ReadS3Object(AmazonS3Client s3, S3Object x)	
{	using GetObjectResponse obj = s3.GetObjectAsync(x.BucketName, x.Key).Result;	using var reader = new StreamReader(obj.ResponseStream);	while (!reader.EndOfStream)	{	yield return reader.ReadLine();	}	
}

注意:

  1. GetObjectAsync方法返回的 GetObjectResponse类实现了 IDisposable接口,因为它的 ResponseStream实际上是非托管资源,需要单独释放。因此需要使用 using关键字来实现资源的正确释放。

  2. 可以直接调用 StreamReader.ReadToEnd()方法直接获取全部字符串,然后再通过 Split将字符串按行分隔,但这样会浪费大量内存,影响性能。

这时一般会将这个 stream缓存到本地磁盘以供慢慢分析,但也可以一鼓作气直接将该 stream转换为字符串直接分析。本文将采取后者做法。

分析1000多个文件

每个 ELB日志文件的格式如下:

2019-08-31T23:08:36.637570Z SUPER-COOLELB 10.0.2.127:59737 10.0.3.142:86 0.000038 0.621249 0.000041 200 200 6359 291 "POST http://super-coolelb-10086.us-east-1.elb.amazonaws.com:80/api/Super/Cool HTTP/1.1" "-" - -	
2019-08-31T23:28:36.264848Z SUPER-COOLELB 10.0.3.236:54141 10.0.3.249:86 0.00004 0.622208 0.000045 200 200 6359 291 "POST http://super-coolelb-10086.us-east-1.elb.amazonaws.com:80/api/Super/Cool HTTP/1.1" "-" - -

可见该日志有一定格式, Amazon提供了该日志的详细文档中文说明:https://docs.aws.amazon.com/zh_cn/elasticloadbalancing/latest/application/load-balancer-access-logs.html#access-log-entry-format

根据文档,这种日志可以通过按简单的空格分隔来解析,但后面的 RequestInfoUserAgent字段稍微麻烦点,这种可以使用 正则表达式来实现比较精致的效果:

public static LogEntry Parse(string line)	
{	MatchCollection s = Regex.Matches(line, @"[\""].+?[\""]|[^ ]+");	string[] requestInfo = s[11].Value.Replace("\"", "").Split(' ');	return new	{	Timestamp = DateTime.Parse(s[0].Value),	ElbName = s[1].Value,	ClientEndpoint = s[2].Value,	BackendEndpoint = s[3].Value,	RequestTime = decimal.Parse(s[4].Value),	BackendTime = decimal.Parse(s[5].Value),	ResponseTime = decimal.Parse(s[6].Value),	ElbStatusCode = int.Parse(s[7].Value),	BackendStatusCode = int.Parse(s[8].Value),	ReceivedBytes = long.Parse(s[9].Value),	SentBytes = long.Parse(s[10].Value),	Method = requestInfo[0],	Url = requestInfo[1],	Protocol = requestInfo[2],	UserAgent = s[12].Value.Replace("\"", ""),	SslCypher = s[13].Value,	SslProtocol = s[14].Value,	};	
}

LINQ

数据下载好了,解析也成功了,这时即可通过强大的 LINQ来进行分析。这里将用到以下的操作符:

  • SelectMany 数据“打平”(和 js数组的 .flatMap方法类似)

  • Select 数据转换(和 js数组的 .map方法类似)

  • GroupBy 数据分组

首先,通过 AWSSDKListObjectsV2Async方法,获取的是文件列表,可以通过 .SelectMany方法将多个下载批次“打平”:

Load201909SuperCoolData(s3)	.SelectMany(x => x)

然后通过 Select,将单个文件 Key下载并读为字符串:

Load201909SuperCoolData(s3)	.SelectMany(x => x)	.SelectMany(x => ReadS3Object(s3, x))

然后再通过 Select,将文件每一行日志转换为一条 .NET对象:

Load201909SuperCoolData(s3)	.SelectMany(x => x)	.SelectMany(x => ReadS3Object(s3, x))	.Select(LogEntry.Parse)

有了 .NET对象,即可利用 LINQ进行愉快地分析了,如小明需要求,只需加一个 GroupBySelect,即可求得根据 Url分组的响应时间中位数,然后再通过 OrderByDescending即按该数字排序,最后通过 .Dump显示出来:

Load201909SuperCoolData(s3)	.SelectMany(x => x)	.SelectMany(x => ReadS3Object(s3, x))	.Select(LogEntry.Parse)	.GroupBy(x => x.Url)	.Select(x => new	{	Url = x.Key, 	Median = x.OrderBy(x => x.BackendTime).ElementAt(x.Count() / 2)	})	.OrderByDescending(x => x.Median)	.Dump();

运行效果如下:

640?wx_fmt=png

多线程下载

解析和分析都在内存中进行,因此本代码的瓶颈在于下载速度。

上文中的代码是串行、单线程下载,带宽利用率低,下载速度慢。可以改成并行、多线程下载,以提高带宽利用率。

传统的多线程需要非常大的功力,需要很好的技巧才能完成。但 .NET4.0发布了 ParallelLINQ,只需极少的代码改动,即可享受到多线程的便利。在这里,只需将在第二个 SelectMany后加上一个 AsParallel(),即可瞬间获取多线程下载优势:

Load201909SuperCoolData(s3)	.SelectMany(x => x)	.AsParallel() // 重点	.SelectMany(x => ReadS3Object(s3, x))	.Select(LogEntry.Parse)	.GroupBy(x => x.Url)	.Select(x => new	{	Url = x.Key, 	Median = x.OrderBy(x => x.BackendTime).ElementAt(x.Count() / 2)	})	.OrderByDescending(x => x.Median)	.Dump();

注意:写 AsParallel()的位置有讲究,这取决于你对性能瓶颈的把控。总的来说:

  • 太靠后了不行,因为 AsParallel之前的语句都是串行的;

  • 靠前了也不行,因为靠前的代码往往数据量还没扩大,并行没意义;

扩展

到了这一步,如果小明足够机灵,其实还能再扩展扩展,将平均值,总响应时间一并求出来,改动代码也不大,只需将下方那个 Select改成如下即可:

    .Select(x => new	{	Url = x.Key, 	Median = x.OrderBy(x => x.BackendTime).ElementAt(x.Count() / 2), 	Avg = x.Average(x => x.BackendTime), 	Sum  = x.Sum(x => x.BackendTime), 	})

运行效果如下:

640?wx_fmt=png

总结

看来并不需要 python,有了 .NETLINQ两大法宝,看来小明周末又可以陪女朋友了?

640?wx_fmt=jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/313335.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络摄像头实时获取信息

转自&#xff1a;http://blog.csdn.net/yong_hen/article/details/42460387#quote 转自&#xff1a;http://blog.csdn.net/leo2007608/article/details/9885219 代码&#xff1a; openCV版本&#xff1a;2.4.10. 平台&#xff1a;win7 [cpp] view plaincopy #include <op…

微软100题第4题

转载自&#xff1a;http://blog.csdn.net/luxiaoxun/article/details/7537605 主要技术含量在于vector的使用(可变尺寸数组)和栈的概念。 题目&#xff1a;输入一个整数和一棵二元树。从树的根结点开始往下访问一直到叶结点所经过的所有结点形成一条路径。打印出和与输入整数相…

相同类方法之间调用,注解失效的问题

问题&#xff1a; 在Spring管理的项目中&#xff0c;方法A使用了Transactional注解&#xff0c;试图实现事务性。但当同一个class中的方法B调用方法A时&#xff0c;会发现方法A中的异常不再导致回滚&#xff0c;也即事务失效了。 原因&#xff1a; Transactional是Spring提供…

[ASP.NET Core 3框架揭秘] 依赖注入:依赖注入模式

IoC主要体现了这样一种设计思想&#xff1a;通过将一组通用流程的控制权从应用转移到框架之中以实现对流程的复用&#xff0c;并按照“好莱坞法则”实现应用程序的代码与框架之间的交互。我们可以采用若干设计模式以不同的方式实现IoC&#xff0c;比如我们在前面介绍的模板方法…

微软100题第5题

转载自&#xff1a;http://blog.csdn.net/littlestream9527/article/details/8104731 http://blog.csdn.net/v_july_v/article/details/6370650 http://blog.csdn.net/insistgogo/article/details/7689297 下面&#xff0c;我试图用最清晰易懂&#xff0c;最易令人理解的思维…

大样本统计

题目描述 我们对 0 到 255 之间的整数进行采样&#xff0c;并将结果存储在数组 count 中&#xff1a;count[k] 就是整数 k 的采样个数。 我们以 浮点数 数组的形式&#xff0c;分别返回样本的最小值、最大值、平均值、中位数和众数。其中&#xff0c;众数是保证唯一的。 我们…

北京Dotnet分享会 || 精英论坛第三期

编者按&#xff1a;没有一成不变的定律&#xff0c;没有长久不衰的流行&#xff0c;更没有一劳永逸的侥幸&#xff0c;只有自己刻苦努力、脚踏实地、兢兢业业的学习和工作&#xff0c;才会成为这个社会永远不会被淘汰的中流砥柱。一、昨夜西风凋碧树昨夜西风凋碧树&#xff0c;…

一些技术方案

延迟队列&#xff1a; https://juejin.im/post/5b5e52ecf265da0f716c3203

Matlab生成棋盘格点图

转自zhouyelihua**http://blog.csdn.net/zhouyelihua/article/details/46674191** 在摄像机标定过程中常常需要打印棋盘格程序&#xff0c;还有就是在投影仪标定当中常常需要投射和投影仪相同分辨率的 <code class"language--matlab hljs matlab has-numbering" s…

微软开源微服务运行时Dapr,赋能云原生应用开发

Dapr 是一个可移植的、由事件驱动的 Serverless 运行时&#xff0c;用于跨云和边缘构建分布式应用程序。10月9日&#xff0c;正式以 MIT 协议开源。Dapr 使开发人员能够轻松地构建弹性、无状态和有状态的微服务&#xff0c;让它们在云和边缘位置上运行&#xff0c;并包含了开发…

缺失的第一个正数

题目描述 给定一个未排序的整数数组&#xff0c;找出其中没有出现的最小的正整数。 示例 1: 输入: [1,2,0] 输出: 3示例 2: 输入: [3,4,-1,1] 输出: 2示例 3: 输入: [7,8,9,11,12] 输出: 1说明: 你的算法的时间复杂度应为O(n)&#xff0c;并且只能使用常数级别的空间。 …

最大堆和最小堆

堆和栈的区别&#xff1a;一、堆栈空间分配区别&#xff1a;1、栈&#xff08;操作系统&#xff09;&#xff1a;由操作系统自动分配释放 &#xff0c;存放函数的参数值&#xff0c;局部变量的值等。其操作方式类似于数据结构中的栈&#xff1b;2、堆&#xff08;操作系统&…

认知的高度 = 人生的高度

大家好&#xff0c;我是Z哥。我们每个人&#xff0c;每天要和很多不同的人打交道。我相信下面的场景每个程序员都有遇到过&#xff0c;业务方在某个模块下新提出了一个功能&#xff0c;但是你希望做新功能之前先把这个模块的低质量代码重构一下。但是&#xff0c;不管你怎么摆事…

快乐数

题目描述 编写一个算法来判断一个数是不是“快乐数”。 一个“快乐数”定义为&#xff1a;对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和&#xff0c;然后重复这个过程直到这个数变为 1&#xff0c;也可能是无限循环但始终变不到 1。如果可以变为…

Pthread

转自&#xff1a;http://blog.chinaunix.net/uid-21084809-id-2215376.html Outline - 1.线程特点 - 2.pthread创建 - 3.pthread终止 - 4.mutex互斥量使用框架 - 5.cond条件变量 - 6.综合实例 1. 线程特点 线程拥有自己独立的栈、调度优先级和策略、信号屏蔽字&#xff08;…

移除链表元素

题目描述 删除链表中等于给定值 val 的所有节点。 示例: 输入: 1->2->6->3->4->5->6, val 6 输出: 1->2->3->4->5解法 使用哑结点&#xff0c;只需要变更next指针即可。 /*** Definition for singly-linked list.* public class ListNode …

VSCode开发.NETCore项目入门(1)设置中文语言环境

安装VSCode最新地址&#xff1a;https://code.visualstudio.com/&#xff0c;下载后安装即可配置语言环境打开安装好的VSCode软件&#xff0c;可以看到刚刚安装的VSCode软件默认使用的是英文语言环境&#xff0c;如下图&#xff1a;使用快捷键【CtrlShiftP】来配置&#xff0c;…

char *c = abc和char c[]=abc

转自&#xff1a;http://blog.csdn.net/wallwind/article/details/7210474 char *c "abc"和char c[]"abc",前者改变其内 容程序是会崩溃的&#xff0c;而后者完全正确。 程序演示&#xff1a; 测试环境Devc 代码 运行结果 2293628 4199056 abc 2293624 22…

同构字符串

题目描述 给定两个字符串 s 和 t&#xff0c;判断它们是否是同构的。 如果 s 中的字符可以被替换得到 t &#xff0c;那么这两个字符串是同构的。 所有出现的字符都必须用另一个字符替换&#xff0c;同时保留字符的顺序。两个字符不能映射到同一个字符上&#xff0c;但字符可…

微软100题第11题

参照&#xff1a;http://blog.csdn.net/caryaliu/article/details/8107089 参照&#xff1a;http://blog.csdn.net/lalor/article/details/7626678 把二叉树看成一个图&#xff0c;父子节点之间的连线看成是双向的&#xff0c;我们姑且定义"距离"为两个节点之间的个…