我将性能计数器的值发送到 Graphite 的方法非常慢。瓶颈是啥?以及如何改进?
Posted
技术标签:
【中文标题】我将性能计数器的值发送到 Graphite 的方法非常慢。瓶颈是啥?以及如何改进?【英文标题】:My method to send values of performance counters to Graphite is very slow. What is the bottleneck? And how to improve?我将性能计数器的值发送到 Graphite 的方法非常慢。瓶颈是什么?以及如何改进? 【发布时间】:2014-01-13 15:51:50 【问题描述】:下面我有一些代码来获取性能计数器实例的值(一旦访问页面就会实例化)并将它们发送到 Graphite 以显示以下格式的图形:
[Path in Graphite (e.g., metric.pages.Counter1)] [value of counter] [epoch time]
为此,我编写了以下代码,其中 writer
配置正确:
# Get all paths to MultipleInstance counters and averages that start with "BLABLA" and
# put them into an array and get the epoch time
$pathsWithInstances = (get-counter -ListSet BLABLA*) | select -ExpandProperty PathsWithInstances
$epochtime = [int][double]::Parse((Get-Date -UFormat %s))
# This functions splits the path (e.g., \BLABLA Web(welcome)\Page Requests) into three
# parts: the part before the
# opening brace (the CounterCategory, e.g., "\BLABLA Web"), the part in between the braces
# (the page or
# service, e.g., "welcome"), and the part after the closing brace (the name of the test,
# e.g.,
# "\Page Requests"). We obtain the metric out of this information and send it to
# Graphite.
enter code here
foreach ($pathWithInstance in $pathsWithInstances)
$instanceProperties = $pathWithInstance.Split('()')
$counterCategory = $instanceProperties[0]
if ($counterCategory -eq ("\BLABLA Web") )
# Replace the * with nothing so that counters that are used to display the
# average (e.g., \BLABLAWeb(*)\Page Requests) are displayed on top in the
# Graphite directory.
$pagePath = $instanceProperties[1].Replace('*','')
$nameOfTheTest = $instanceProperties[2]
# Countername which is used in Graphite path gets whitespace and backslash
# removed in the name used for the path in Graphite (naming conventions)
$counterName = $nameOfTheTest.Replace(' ','').Replace('\','')
$pathToPerfCounter = $pathWithInstance
$pathInGraphite = "metrics.Pages." + $pagePath + $counterName
#Invoked like this since otherwise the get-counter [path] does not seem to work
$metricValue = [int] ((Get-Counter "$pathToPerfCounter").countersamples | select -
property cookedvalue).cookedvalue
$metric = ($pathInGraphite + " " + $metricValue + " " + $epochTime)
$writer.WriteLine($metric)
$writer.Flush()
不幸的是,这段代码非常慢。每个计数器发送一个值大约需要一秒钟。有人知道它为什么这么慢以及如何改进吗?
【问题讨论】:
【参考方案1】:您一次获取一个计数器,Get-Counter 需要一秒钟来获取并“烹饪”这些值。 Get-Counter 将接受一个计数器数组,并将采样、“烹饪”并在同一秒内将它们全部返回。您可以通过一次全部采样,然后从结果数组中解析值来加快速度:
$CounterPaths = (
'\\Server1\Memory\Page Faults/sec',
'\\Server1\Memory\Available Bytes'
)
(Measure-Command
foreach ($CounterPath in $CounterPaths)
Get-Counter -counter $counterpath
).TotalMilliseconds
(Measure-Command
Get-Counter $CounterPaths
).TotalMilliseconds
2017.4693
1012.3012
例子:
foreach ($CounterSample in (Get-Counter $CounterPaths).Countersamples)
"Path = $($CounterSample.path)"
"Metric = $([int]$CounterSample.CookedValue)"
Path = \\Server1\memory\page faults/sec
Metric = 193
Path = \\Server1\memory\available bytes
Metric = 1603678208
【讨论】:
嗯嗯,我只需要名称中带有BLABLA
的计数器...我什么时候进行解析?
这似乎是 真正的 解决方案。我会尽力的。
是否可以使用其他东西然后煮熟的价值来加速它?
您的 $pathInGraphite 似乎是从计数器路径名称派生的,因此您只需要从每个样本中获取该名称和指标的熟值即可。您可以通过循环进行解析以读取示例,或者您可以预先进行解析,创建一个哈希表以将计数器路径名称与 Graphite 路径名称相关联。
它适用于我,您可以在示例代码中替换您自己的计算机名称来验证它。【参考方案2】:
使用Start-Job
cmdlet,为每个计数器创建单独的线程。
这是一个简单的例子,说明如何获取计数器路径并将它们传递到异步 ScriptBlock:
$CounterPathList = (Get-Counter -ListSet Processor).PathsWithInstances.Where( $PSItem -like '*% Processor Time' );
foreach ($CounterPath in $CounterPathList)
Start-Job -ScriptBlock (Get-Counter -Counter $args[0]).CounterSamples.CookedValue; -ArgumentList $CounterPath;
# Call Receive-Job down here, once all jobs are finished
重要提示:上面的示例使用 PowerShell 4.0 版的“方法语法”来过滤对象。请确保您运行的是 PowerShell 4.0 版,或将 Where
方法更改为使用传统的 Where-Object
。
【讨论】:
在您的 foreach 循环中,不要简单地执行每个计数器的代码,而是将命令包装在Start-Job
中。如果你有很多柜台,这应该会加快这个过程。
在 if 语句中(我在实际代码中有两个 if 语句来检查类别名称)还是在 foreach 循环中?
你可以把它放在任何一个地方,这取决于你想在哪里进行处理。传递给Start-Job
cmdlet 的ScriptBlock
可以解析计数器路径,或者您可以在“主”线程中进行解析,然后将计数器传递给ScriptBlock
以进行检索并提交给Graphite。
嘿,我刚刚发布了一个示例,向您展示了如何使用-ArgumentList
参数将计数器路径传递到ScriptBlock
。
一位同事正在使用CounterCategory
类和GetInstance
然后循环遍历它们,这样更快。可能也可以使用这个类。以上是关于我将性能计数器的值发送到 Graphite 的方法非常慢。瓶颈是啥?以及如何改进?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Graphite 比较累积计数器与最佳、平均和最差?