当管理多台 Windows Server 服务器时(无论是 DB、AD、web 以及其他的应用服务器),当出现性能或其他问题后,参阅性能计数器都是一个非常好的维度从而推测出问题可能出现的原因,再不济也能缩小需要考虑的问题范围,因此定期收集每一台服务器的计数器就会使得问题有据可循。并且收集到的数据也可以作为 BaseLine,即使没有出现问题也可以预先判断一些问题。
之前看到网上的大多数收集性能计数器的文章都比较局限,一般是只收集单台服务器,因此我分享一个多服务器的写法。
至于为什么使用 PowerShell,因为在微软系产品来说像 Python 等脚本语言虽然有丰富的开源代码没有太好的对应接口,而 PowerShell 每一个微软自己的产品都提供了大量的 Cmdlet,调用起来甚是方便:-)
获取性能计数器的核心 cmdlet 就是 Get-Counter 了,该 Get-Counter 主要使用两个参数,分别为要获取的计算机名称 - ComputerName 与性能计数器列表 - Counter,这里要注意的是,获取性能计数器需要在被获取服务器有对应权限(Performance Monitor Users 组),我这里的例子是使用域管理员帐号收集域内服务器,因此不考虑权限问题。
图 1. 获取到的远程服务器性能计数器
然后将获取到的结果保存到变量中,如图 2 所示。
图 2. 将计数器结果保存到变量中
将所需收集的服务器以及所需收集的计数器保存到记事本内,方便随时添加或减少服务器或者计数器,记事本写法如下:
图 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 服务器,代码如下:
- $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 所示。
图 4. 使用计划任务 2 分钟收集一次性能计数器信息
在图 4 中,我们注意到使用了 - NonInteractive 参数,该参数用于在执行时,不弹出 PowerShell 窗口。
现在,我们可以看到收集后的性能计数器信息,如图 5 所示。
图 5. 收集到的性能计数器信息
有了上述性能计数器信息,我们可以使用一些可视化工具分析这些信息,比如我将数据导入到 ElasticSearch 中,出几张简单的报表,如图 6 所示。
图 6. 使用这些性能计数器出简单的报表
这些报表可以帮助我们直观的看出一些问题,比如图 6 中的 forward record 可以看到,某些实例大量缺少聚集索引,或者下面的 Top Lock Wait 可以看到某些实例定期会产生大量的锁阻塞,从而我们可以更容易提前发现问题,进行解决。
定期收集一些服务器的信息可以帮助在运维工作中掌握主动,在业务中现在流行所谓的 "大数据促进决策",其实在 IT 运维本身中,收集大量的数据同样重要,通过数据我们甚至可以在问题出现之前发现问题。
在 WIndows 下 PowerShell 无疑是最适合做这一工作的语言。
来源: http://www.cnblogs.com/CareySon/p/Collect_perfmonCounter_using_PowerShell.html