文章出處

寫在前面

    當管理多臺Windows Server服務器時(無論是DB、AD、WEB以及其他的應用服務器),當出現性能或其他問題后,參閱性能計數器都是一個非常好的維度從而推測出問題可能出現的原因,再不濟也能縮小需要考慮的問題范圍,因此定期收集每一臺服務器的計數器就會使得問題有據可循。并且收集到的數據也可以作為BaseLine,即使沒有出現問題也可以預先判斷一些問題。

    之前看到網上的大多數收集性能計數器的文章都比較局限,一般是只收集單臺服務器,因此我分享一個多服務器的寫法。

    至于為什么使用PowerShell,因為在微軟系產品來說像Python等腳本語言雖然有豐富的開源代碼沒有太好的對應接口,而PowerShell每一個微軟自己的產品都提供了大量的Cmdlet,調用起來甚是方便:-)

 

核心Cmdlet

    獲取性能計數器的核心cmdlet就是Get-Counter了,該Get-Counter主要使用兩個參數,分別為要獲取的計算機名稱-ComputerName與性能計數器列表-Counter,這里要注意的是,獲取性能計數器需要在被獲取服務器有對應權限(Performance Monitor Users組),我這里的例子是使用域管理員帳號收集域內服務器,因此不考慮權限問題。

image

圖1.獲取到的遠程服務器性能計數器

 

    然后將獲取到的結果保存到變量中,如圖2所示。

image

圖2.將計數器結果保存到變量中

 

收集多臺服務器的多個計數器

    將所需收集的服務器以及所需收集的計數器保存到記事本內,方便隨時添加或減少服務器或者計數器,記事本寫法如下:

image

圖3.計數器與服務器配置

 

    在PowerShell中使用Get-Content讀取配置文件內容,如下:

$currentPath=Split-Path ((Get-Variable MyInvocation -Scope 0).Value).MyCommand.Path
#讀取需要收集的性能計數器列表
$ServerNeedScan=get-content $currentPath\ServerNeedScan.txt
$ServerNeedScanArray=$ServerNeedScan.Split(",")
 
 
#讀取需要收集的性能計數器
$PerfCounter=get-content $currentPath\PerfmonCounter.txt
$PerfCounterArray=$PerfCounter.Split(",")

代碼1.讀取服務器列表和計數器列表

現在由于收集的計數器中部分計數器是關于SQL Server的,而部分服務器可能帶有實例名稱,而對于帶有SQL Server實例名稱的計數器需要把實例和及其名分開,然后把計數器名稱中實例名部分進行替換,代碼如下:

foreach ($fcomputer in $ServerNeedScanArray)
{
   
    if($fcomputer.Trim() -eq "")
    {
        continue
    }
    #檢查是否為默認實例
    $computer=""
    if($fcomputer -like "*\*")
    {
        $instanceName=$fcomputer.Substring($fcomputer.IndexOf("\")+1,$fcomputer.Length-$fcomputer.IndexOf("\")-1)  
        $computer=$fcomputer.Substring(0,$fcomputer.IndexOf('\'))
    }
    else
    {
        $computer=$fcomputer
        $instanceName=""
    }
 
#遍歷所有計數器
    $fullCounter=@()
    foreach($counter in $PerfCounterArray)
    {
        $c=""
        $c+="\"
        $c+=$counter
        $fullCounter+=$c
     }
 
$NoDeaultInstanceName="MSSQL`$"
$NoDeaultInstanceName+=$instanceName
#如果是默認實例
if($instanceName -eq "")
{
   $fullCounter | % { 
   if($_ -like "*network*")
   {
        $finalCounter+= $_.ToLower()
       
   }
   else
   {
       $finalCounter+= $_.ToLower().Replace("*","_total")
   }
   }
}
#如果是非默認實例
else
{
   $a=$fullCounter | % { 
   if($_ -like "*network*")
   {
        $finalCounter+= $_.ToLower().Replace("sqlserver", $NoDeaultInstanceName)
       
   }
   else
   {
        $finalCounter+= $_.ToLower().Replace("*","_total").Replace("sqlserver", $NoDeaultInstanceName)
   }
 
   } 
}

代碼2.替換SQL Server計數器中的非默認實例

 

將結果插入一臺SQL Server

    上述情況就已經準備好了計數器和服務器名稱,現在就可以將這些數據插入到一臺集中的SQL Server服務器,代碼如下:

$a=(Get-Counter -ComputerName $computer -Counter $finalCounter).CounterSamples |Select-Object Path,CookedValue
 
 
      $InsertSQL=""
      $curentTime=Get-Date
      foreach($PerformanceCounter in $a)
      {
            
          $realvalue=$PerformanceCounter.CookedValue
          $InsertSQL+="INSERT INTO PerfCounter(instancename,event_timestamp,Counter,CounterValue)
              VALUES(''"+$fcomputer+"'',''"+$curentTime+"'',''"+$PerformanceCounter.Path+"'',''"+$realvalue.ToString()+"'');"
          
       
             
       }
      $connectionString3="data source=服務器IP;database=test;uid=perf_writer;pwd=123123;"
      $conn2=new-object system.Data.SqlClient.SqlConnection($connectionString3)
      $conn2.open()
 
      $cmd2=$conn2.CreateCommand()
 
       $cmd2.CommandText=$InsertSQL
       $cmd2.ExecuteNonQuery()
       $conn2.Close()

代碼3.讀取計數器后插入SQL Server

    現在,讀取一臺服務器并將計數器記錄到數據庫中的代碼就寫好了,并且已經可以靈活配置需要讀取的計數器和機器名。

 

多線程讀取

    如果需要記錄計數器的服務器比較多時,那么循環遍歷每一臺服務器就會花費比較長的時間,因此需要多線程來加快這一個速度,在PowerShell中,啟用多線程的cmdlet是start-job,我們首先需要將代碼2和代碼3的腳本封裝到一個script block中,并設置可傳入的參數,如代碼4。

$sb = [scriptblock]::Create('
  param($instanceName,$NoDeaultInstanceName,$fullCounter,$fcomputer,$computer)
#這里寫其他代碼
 
')
 
#開始異步線程,并傳入參數
start-job -scriptblock $sb -Argument $instanceName,$NoDeaultInstanceName,$fullCounter,$fcomputer,$computer 
 

代碼4.利用異步線程讀取計數器數據并插入SQL Server

 

    經過測試,PowerShell對同時可以并發的線程做了限制,這個限制很奇怪,我在每臺服務器上測試的結果并不相同,因此如果同時全部并發執行這些線程,某些線程會因為限制而不起作用,因此如果需要記錄性能計數器的服務器比較多的話,會丟失一部分服務器信息,我的解決辦法是限制同時并發的進程數量,如果進程數量超過規定數值,則等待1秒再次檢測,如果檢測通過再啟動新進程,代碼如代碼5所示。

While (@(Get-Job | Where { $_.State -eq "Running" }).Count -gt 5) {
        Write-host "Waiting for background jobs..."
        Start-Sleep -Seconds 1
      }

代碼5.檢測處于“運行中”進程的數量是否大于5

 

定期執行腳本

    現在,上面腳本就可以收集多臺服務器的性能計數器,并將結果保存到SQL Server了,現在只需要定期(比如2分鐘一次)執行該腳本即可。使用Windows計劃任務是定期執行PowerShell腳本推薦的方式,如圖4所示。

image

圖4.使用計劃任務2分鐘收集一次性能計數器信息

 

    在圖4中,我們注意到使用了-NonInteractive參數,該參數用于在執行時,不彈出PowerShell窗口。

 

結果

    現在,我們可以看到收集后的性能計數器信息,如圖5所示。

image

圖5.收集到的性能計數器信息

 

    有了上述性能計數器信息,我們可以使用一些可視化工具分析這些信息,比如我將數據導入到ElasticSearch中,出幾張簡單的報表,如圖6所示。

image

圖6.使用這些性能計數器出簡單的報表

 

    這些報表可以幫助我們直觀的看出一些問題,比如圖6中的forward record可以看到,某些實例大量缺少聚集索引,或者下面的Top Lock Wait可以看到某些實例定期會產生大量的鎖阻塞,從而我們可以更容易提前發現問題,進行解決。

 

小結

    定期收集一些服務器的信息可以幫助在運維工作中掌握主動,在業務中現在流行所謂的“大數據促進決策”,其實在IT運維本身中,收集大量的數據同樣重要,通過數據我們甚至可以在問題出現之前發現問題。

    在WIndows下PowerShell無疑是最適合做這一工作的語言。


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()