当前位置: 首页 > 科技观察

Log4j2漏洞检测工具一览

时间:2023-03-12 18:29:07 科技观察

万物皆有限度吧?汽车只能开那么快,进程只能使用那么多内存,程序员只能喝那么多咖啡。我们的生产力受到我们的资源以及我们更好或更坏地使用它们的能力的限制。我们的目标是尽可能地使用我们的每一个资源,我们想要使用我们的CPU和内存的每一点,否则我们会为一台昂贵的机器多付钱。但是,如果我们使用过多的资源,我们可能会导致性能问题、服务不可用问题以及程序崩溃和崩溃。软件开发看似简单,但是一旦遇到性能问题,就会变得非常棘手,这就是我们今天要讨论的内容。定义最佳基准让我们尝试描述我们的最佳应用程序行为。假设我们有许多服务器机器需要处理高吞吐量的请求。为简单起见,让我们暂时忘记高峰时间或周末。我们的服务器负载在一天中的所有时间都大致相同。我们为这些服务器机器付出了很多钱,我们希望从它们身上获得尽可能多的价值,这意味着处理尽可能多的请求。为了保持我们对简单性的承诺,我们还假设服务器仅使用内存和CPU来处理所述请求,并且没有其他瓶颈,例如慢速网络或锁争用。在描述的场景中,我们最好的行为是在任何给定时间使用尽可能多的CPU和内存,对吗?这样,我们可以使用更少的机器来处理相同数量的请求。但是您可能不想利用这些资源的99.9%,因为负载的轻微增加会导致性能问题、服务器崩溃、数据丢失和其他令人头疼的问题。所以我们应该选择一个有足够缓冲问题的值。平均85%或90%的CPU和内存利用率听起来不错。我们应该首先优化什么?我们的应用程序并不是为了平等利用CPU和内存而构建的。或者到它托管的机器的确切限制。因此,您首先要看的是您的服务器是CPU-bound还是Memory-bound。当服务器受CPU限制时,这意味着服务器可以处理的吞吐量受其CPU限制。换句话说,如果您尝试处理更多请求,CPU将在其他资源(例如内存)达到极限之前达到100%。同样的逻辑适用于内存绑定服务器。服务器的吞吐量将受到它可以分配的内存的限制,在其他资源(例如CPU)尝试处理更多负载时达到其极限之前,内存将达到100%。还有其他资源可以限制服务器,例如I/O,其中吞吐量受磁盘或网络的读取或写入限制。但我们在这篇文章中忽略了这一点,乐观地假设我们的I/O是快速且无限的。一旦知道是什么限制了服务器的性能,您就会知道首先要尝试和优化什么。如果您的服务器受CPU限制,则优化内存使用没有意义,因为它不会增加处理的吞吐量。事实上,它会损害吞吐量,因为您可能会因CPU使用率增加而增加内存使用量。对于内存受限的服务器也是如此,在这种情况下,您应该先优化内存使用,然后再查看CPU。测量.NET服务器中的CPU和内存消耗CPU和内存消耗的实际测量最容易使用性能计数器[1]完成。CPU使用率的指标是Process|%处理器时间。内存有多种指标,但我建议查看Process|私有字节。您可能还对.NETCLR内存感兴趣|#所有堆中的字节表示托管内存(CLR占用的部分,不是所有内存,即托管+本机内存)。要查看性能计数器,您可以在Windows机器上使用ProcessExplorer[2]或PerfMon,或者在.NETCore服务器上使用dotnet-counters。[3]如果您的应用程序部署在云端,您可以使用ApplicationInsights[4](AzureMonitor[5]的一部分)等APM工具来显示此信息。或者,您可以在代码中获取性能计数器值并每10秒左右记录一次,使用AzureDataExplorer[6]等工具在图表中显示数据。提示:检查机器级指标和进程级指标。您可能会发现其他进程正在限制您的性能。一旦您确定哪些资源限制了您的.NET服务器,就该优化该资源消耗了。如果您受CPU限制,让我们减少CPU使用率。如果您的内存受限,让我们减少内存使用量。至少如果您在云中运行,一个简单的方法就是更改机器规格。如果内存受限,请增加内存。如果您受CPU限制,请增加内核数量或获得更快的CPU。这会增加成本,但在此之前,您可以检查一些容易实现的成果以优化CPU或内存消耗。在更改机器规格之前尝试这些优化,因为在优化之后一切都会改变。您可能会优化CPU使用率并成为内存限制。然后优化内存使用并再次成为CPU密集型。因此,如果您想避免必须不断更改机器资源以适应最新的优化,最好将其保存到最后。那么让我们来谈谈一些内存优化。优化内存使用在.NET中有很多方法可以优化内存使用。深入讨论它们需要一整本书,而且已经有好几本了。但我会尽量给你一些方向和想法。1.知道是什么占用了你的内存当试图优化内存时,你应该做的第一件事就是了解大局。什么占用了大部分内存?有哪些数据类型?他们分配在哪里?他们在记忆中停留多久?有几种工具可以获取此信息:捕获转储文件[7]并使用内存分析器[8]或WinDbg[9]打开它。使用新的GC转储[10](.NETCore3.1+)并使用VisualStudio进行调查。捕获堆快照并使用MemoryAnalyzer[11]、PerfView[12]或VisualStudioDiagnosticTools[13]探索它们。此分析将显示哪些对象占用了大部分内存。如果您发现它是从MyProgram.CustomerData中获取的,那就更好了。但通常,最大的对象类型是string、byte[]或byte[][]。由于应用程序中的几乎所有内容都可以使用这些类型,因此您需要找到引用它们的人。为此,查看正在使用的包含内存(也称为保留内存)很重要。这个指标不仅包括对象本身占用的内存,还包括它引用的对象占用的内存。例如,您可能会发现MyProgram.Inventory.Item本身并不占用太多内存,但它引用了一个字节[],它在内存中保存图像并占用高达70%的内存。上面描述的所有工具都可以显示包含最多字节的对象和到GC根的引用路径(即到根的最短路径[14])。2.知道谁把内存放在哪里找出谁在引用最大的内存块固然很好,但可能还不够。有时您需要知道这些内存是如何分配的。你可能从引用路径中知道,一些占用大部分内存的对象都在缓存中,但是谁把它们放在那里的呢?来自单个时间点的内存快照无法提供该答案。为此,您需要分配堆栈跟踪。探查器使您能够记录您的应用程序并保存每次分配的调用堆栈。例如,您可能会发现创建有问题的MyProgram.Inventory.Item对象的进程将它们分配到调用堆栈App.OnShowHistoryClicked|App.SeeItemHistory|App.GetItemFromDatabase中。要获取分配堆栈,您可以:使用商业内存分析器来显示分配情况[15]。使用PerfView的GCHeap[]Stacks之一进行分配可以让您很好地了解什么占用了大部分内存以及它是如何生成的。一旦你知道了这一点,你就可以开始切碎最大的块并优化它们以减少内存使用。3.检查内存泄漏在.NET中很容易造成内存泄漏。如果泄漏足够多,内存消耗会随着时间的推移而增加,您可能会遇到各种问题。内存瓶颈是其中之一,但您也可能因GC压力而导致CPU问题。当您不再需要对象但由于某种原因它们仍然被引用并且垃圾收集器永远不会释放它们时,就会发生内存泄漏。发生这种情况的原因有很多[16]。要查看是否存在严重的内存泄漏,请查看一段时间内的内存消耗图(进程|私有字节计数器)。如果内存一直在增加而没有偏离某个水平,则可能存在内存泄漏。使用内存分析器[17]调试泄漏非常简单。4.切换到GC工作站模式.NET中有几种垃圾收集器模式。两种主要模式是工作站GC和服务器GC。工作站GC针对更短的GC暂停和更快的交互性进行了优化,使其成为桌面应用程序的理想选择。服务器GC具有更长的GC暂停时间,并针对更高的吞吐量进行了优化。在ServerGC模式下,应用程序可以在垃圾回收之间处理更多数据。服务器GC为每个CPU内核创建不同的托管堆。这意味着不同的GenerationX内存空间需要更长的时间来填满,因此内存消耗会更高。您基本上是在用内存换取吞吐量。从GC服务器模式(.NET服务器的默认模式)更改为GC工作站模式将减少内存使用。这在请求负载不重的小型应用程序中可能是合理的。可能在与主应用程序一起运行的IIS主机中的工作进程中。SergeyTepliakov[18]对此有一篇很棒的文章。5.检查你的缓存在第1步之后,你应该能够看到哪些对象正在占用你的内存,但我想特别强调一下缓存。每当涉及到高内存消耗时,根据我的经验,它总是以内存泄漏或缓存结束。缓存似乎是解决许多问题的灵丹妙药。当您可以将结果保存在内存中并重复使用时,为什么要重复两次呢?但是缓存是有代价的。天真的实现会将对象永远保存在内存中。您应该限制时间或以其他方式使缓存无效。缓存还会将临时对象在内存中保留相对较长的时间,这会导致更多的Gen1和Gen2收集,进而导致GC压力[19]。以下是优化内存缓存的一些想法:使用.NET[20]中的现有缓存实现可以轻松创建失效策略。考虑为某些事情选择退出缓存。您可能会用CPU或IO来换取内存,但是当您的内存受限时,您应该这样做。考虑使用内存不足的缓存。这可能是将数据保存在文件或本地数据库中。或者使用像Redis[21]这样的分布式缓存解决方案。6.定期调用GC.Collect()这个建议是违反直觉的,因为最好的做法是永远不要调用GC.Collect()。垃圾收集器很聪明,它应该知道什么时候触发收集。但问题是垃圾收集器只考虑自己的进程。如果它没有足够的内存,它会小心地触发一次收集并腾出空间。但是如果它确实有足够的内存,GC会很乐意忍受过多的内存消耗。因此,GC的自私本质可能是同一台机器上的其他进程的问题,可能托管在同一台IIS上。这种多余的内存可能会导致其他进程更快地达到其极限,或者导致它们各自的垃圾收集器更加努力地工作,因为它们可能错误地认为它们即将耗尽内存。您可能会认为,如果其他进程的GC达到他们认为我们内存不足的程度并因此更加努力地工作,我们自己的进程也会有同样的想法并触发垃圾收集来解决问题。但我们不能做出这样的假设。一方面,这些进程可能正在运行不同的GC实现版本(因为不同的CLR版本)。此外,您有不同的应用程序行为,可以使GC以不同的方式工作。例如,进程可能会以更高的速率分配内存,因此GC将更快地开始“强调”空闲内存。底线是软件很难,当你在一台机器上有多个进程时,比如IIS,你需要考虑到这一点,并可能采取一些不寻常的步骤。优化CPU使用率硬币的另一面是CPU使用率。一旦您发现CPU是应用程序吞吐量的瓶颈,您还有很多工作要做。1.分析你的应用程序优化CPU的第一步是了解它。究竟是什么原因造成的?哪些方法负责?哪些请求是最大的CPU消耗者,哪些是流量?这都可以通过分析应用程序来解决。分析允许您记录执行范围并显示调用的所有方法以及它们在记录期间使用了多少CPU。分析器通常允许以普通列表、调用树甚至火焰图的形式查看这些结果。这是PerfView中的一个简单列表视图:这是同一场景的火焰图:您可以通过以下方式分析您的应用程序:如果场景在本地重现,请使用性能分析器,如PerfView[22]、dotTrace[23]、ANTSperf分析器[24]],或在您的开发计算机上使用VisualStudio。[25]在生产环境中,最简单的分析方法是使用应用程序性能监控(APM)工具,例如AzureApplicationInsights分析器[26]或RayGun[27]。您可以通过将代理复制到生产机器并记录快照来分析没有APM的生产环境。使用PerfView,您应该复制整个程序。它结构紧凑,无需安装。使用dotTrace,您可以复制允许在生产中记录快照的轻量级代理。[28]在.NETCore3.0+应用中,可以安装.NETCore3.0SDK并使用dotnet-trace命令行工具记录快照[29],然后复制到开发机器上进行分析使用性能视图。2.检查垃圾收集器的使用我想说优化.NETCPU使用最重要的事情是适当的内存管理。在这种情况下要问的重要问题是:“垃圾收集浪费了多少CPU?”。GC的工作方式是在收集期间,您的执行线程被冻结。这意味着垃圾收集直接影响性能。因此,如果您受CPU限制,我建议您首先检查性能计数器[30]。NETCLR内存|%GC时间。我不能给你一个表示问题的神奇数字,但根据经验,当这个值超过20%时,你可能会遇到问题。如果它超过40%,那么你肯定有问题。如此高的百分比表明GC压力大,并且有一些方法可以解决它[31]。3.使用数组和对象池来重用内存数组的分配和不可避免的释放可能非常昂贵。以高频率执行这些分配会导致GC压力并消耗大量CPU时间。解决此问题的一个好方法是使用内置的ArrayPoolObjectPool(仅限[32].NETCore)。这个想法很简单。为数组或对象分配一个共享缓冲区,然后在不分配和释放新内存的情况下重用它。下面是一个简单的ArrayPool用法示例:publicvoidFoo(){varpool=ArrayPool.Shared;int[]array=pool.Rent(ArraySize);//做stufpool.Return(array);}4。切换到GC服务器模式我们已经讨论过切换到GC工作站模式[33]以节省内存。但是如果你受CPU限制,考虑切换到服务器模式以节省CPU。权衡是服务器模式以更多内存为代价允许更高的吞吐量。因此,如果您保持相同的吞吐量,您最终会节省CPU时间,否则这些时间将被垃圾收集所花费。.NET服务器很可能默认具有GC服务器模式,因此可能不需要此更改。但是之前可能有人将其更改为工作站模式,在这种情况下,您应该小心地将其更改回来,因为他们可能有充分的理由。更改时一定要监控内存消耗和GC中的%时间。您可能想查看第2代收集率,但如果这个数字很高,它会反映在更高百分比的GC时间中。5.检查其他流程当试图将您的服务器推向最佳极限时,您可能希望深入了解它,这意味着不要放弃流程之外存在的问题。其他进程很可能会不时消耗大量CPU,并导致一段时间内的性能下降。这些可能是您在IIS上部署的其他应用程序、周期性Web作业、操作系统触发的某些东西、反程序或一千种其他东西。对此进行分析的一种方法是使用PerfView跨系统记录ETW事件。PerfView从所有进程中捕获CPU堆栈。您可以长时间运行它,而性能开销很小。您可以在达到某个CPU峰值时自动停止收集[34]并开始挖掘。您可能会对结果感到惊讶。总结在我看来,自上而下地处理大规模性能问题是很有趣的。你可能有一个团队花费几个月的时间来优化一段代码,相比之下,资源分配的简单变化会产生更大的影响。而且,如果您的企业足够大,那小小的改变可以转化为大量资金。您是否记得在合同中要求佣金条款?无论如何,我希望这篇文章对您有用,如果您找到了它,您可能会对我的书《.NET开发人员实用调试》[35]感兴趣,我在其中深入讨论了性能和内存问题的故障排除。参考资料[1]性能计数器:https://michaelscodingspot.com/performance-counters/[2]ProcessExplorer:https://docs.microsoft.com/en-us/sysinternals/downloads/process-explorer[3]dotnet-计数器。:https://docs.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-counters[4]应用洞察:https://docs.microsoft.com/en-us/azure/cloud-services/diagnostics-performance-counters#application-insights[5]AzureMonitor:https://azure.microsoft.com/en-us/services/monitor/#overview[6]Azure数据资源管理器:https://azure。microsoft.com/en-us/services/data-explorer/#getting-started[7]捕获转储文件:https://michaelscodingspot.com/how-to-create-use-and-debug-net-application-crash-dumps-in-2019/[8]内存分析器:https://memprofiler.com/online-docs/manual/importmemorydumpfiles.html[9]WinDbg:https://michaelscodingspot.com/how-to-create-use-and-debug-net-application-crash-dumps-in-2019/#Investigate-Dumps-with-WinDbg[10]GC转储:https://devblogs.microsoft.com/dotnet/collecting-and-analysing-memory-dumps/[11]内存分析器:https://michaelscodingspot.com/memory-profilers-principles#snapshots[12]PerfView:https://bennettadelson.wordpress.com/2013/04/11/using-perfview-to-诊断-a-net-memory-leak-2/[13]VisualStudio诊断工具:https://docs.microsoft.com/en-us/visualstudio/profiling/memory-usage?view=vs-2022[14]到root最短路径:https://www.jetbrains.com/help/dotmemory/Shortest_Paths_to_Roots.html[15]显示分配的内存分析器:https://www.jetbrains.com/help/dotmemory/Analyze_Memory_Allocation.html#types[16]为什么会这样:https://michaelscodingspot.com/ways-to-cause-memory-leaks-in-dotnet/[17]使用内存分析器调试泄漏:https://michaelscodingspot.com/find-fix-and-avoid-memory-leaks-in-c-net-8-best-practices/#profiler[18]SergeyTepliakov:https://devblogs.microsoft.com/premier-developer/understanding-different-gc-modes-with-concurrency-visualizer/[19]GC压力:https://michaelscodingspot.com/avoid-gc-pressure/[20].NET中的现有缓存实现:https://michaelscodingspot.com/cache-implementations-in-csharp-net/[21]Redis:https://redis.io/[22]PerfView:https://github.com/microsoft/perfview[23]dotTrace:https://www.jetbrains.com/profiler/[24]ANTS性能分析器:https://www.red-gate.com/products/dotnet-development/ants-performance-profiler/[25]使用VisualStudio:https://docs.microsoft.com/en-us/visualstudio/profiling/beginners-guide-to-performance-profiling?view=vs-2022[26]AzureApplicationInsights探查器:https://docs.microsoft.com/en-us/azure/azure-monitor/app/profiler-overview[27]RayGun:https://raygun.com/for/dotnet-performance-monitoring[28]的轻量级代理。:https://blog.jetbrains.com/dotnet/2012/09/10/dottrace-remote-profiling/[29]使用dotnet-trace命令行工具记录快照:https://michaelscodingspot.com/dotnet-trace/[30]性能计数器:https://michaelscodingspot.com/performance-counters/[31]处理方法:https://michaelscodingspot.com/avoid-gc-pressure/[32](:https://docs.microsoft.com/en-us/aspnet/core/performance/objectpool?view=aspnetcore-6.0[33]GC工作站模式:https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/workstation-server-gc[34]在达到某个CPU峰值时自动停止收集:https://www.drware.com/perfview-command-for-capturing-automated-high-cpu-dumps/[35].NET开发人员实用调试:https://practicaldebugging.net/