我将性能计数器的值发送到Graphite的方法非常慢。瓶颈是什么?以及如何改善?

时间:2014-01-13 15:51:50

标签: performance powershell performancecounter

下面我有一些代码来获取性能计数器实例的值(一旦访问页面就会实例化)并将它们发送到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() 

    }

}

不幸的是这段代码很慢。每个计数器发送一个值大约需要一秒钟。有人知道为什么它如此缓慢以及如何改进它?

2 个答案:

答案 0 :(得分:1)

使用Start-Job cmdlet为每个计数器创建单独的线程。

这是一个如何获取Counter Paths并将它们传递给异步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

答案 1 :(得分: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