在 Azure Web 角色中使用 SignalR 性能计数器

作者:Luke Latham

警告

本文档不适用于最新版本的 SignalR。 查看 ASP.NET Core SignalR

SignalR 性能计数器用于监视应用在 Azure Web 角色中的性能。 计数器由 Microsoft Azure 诊断 捕获。 使用 signalr.exe在 Azure 上安装 SignalR 性能计数器,该工具与用于独立应用或本地应用的工具相同。 由于 Azure 角色是暂时性的,需要配置应用以在启动时安装和注册 SignalR 性能计数器。

先决条件

创建公开 SignalR 性能计数器的 Azure Web 角色应用程序

  1. 打开 Visual Studio。

  2. 在 Visual Studio 中,选择“文件”>“新建”>“项目”。

  3. 在“ 新建项目 ”对话框中,选择左侧的 “Visual C#>Cloud ”类别,然后选择“ Azure 云服务” 模板。 将应用命名为 SignalRPerfCounters ,然后选择“ 确定”。

    新建云应用程序

    注意

    如果未看到 模板类别或 Azure 云服务 模板,则需要安装 Visual Studio 2017 的 Azure 开发 工作负载。 选择“新建项目”对话框左下角的“打开Visual Studio 安装程序”链接以打开Visual Studio 安装程序。 选择 “Azure 开发 ”工作负载,然后选择“ 修改 ”以开始安装工作负载。

    Visual Studio 安装程序 中的 Azure 开发工作负载

  4. “新建 Microsoft Azure 云服务 ”对话框中,选择“ ASP.NET Web 角色 ”,然后选择 > 按钮以将角色添加到项目。 选择“确定” 。

    添加 ASP.NET Web 角色

  5. “新建 ASP.NET Web 应用程序 - WebRole1 ”对话框中,选择 MVC 模板,然后选择“ 确定”。

    添加 MVC 和 Web API

  6. 解决方案资源管理器 中,打开 WebRole1 下的 诊断.wadcfgx 文件。

    解决方案资源管理器 诊断.wadcfgx

  7. 将文件的内容替换为以下配置并保存文件:

    <?xml version="1.0" encoding="utf-8"?>
    <DiagnosticsConfiguration xmlns="http://schemas.microsoft.com/ServiceHosting/2010/10/DiagnosticsConfiguration">
      <PublicConfig>
        <WadCfg>
          <DiagnosticMonitorConfiguration overallQuotaInMB="4096">
            <DiagnosticInfrastructureLogs scheduledTransferLogLevelFilter="Error" />
            <Logs scheduledTransferPeriod="PT1M" scheduledTransferLogLevelFilter="Error" />
            <Directories scheduledTransferPeriod="PT1M">
              <IISLogs containerName ="wad-iis-logfiles" />
              <FailedRequestLogs containerName ="wad-failedrequestlogs" />
            </Directories>
            <WindowsEventLog scheduledTransferPeriod="PT1M">
              <DataSource name="Application!*[System[(Level=1 or Level=2 or Level=3)]]" />
              <DataSource name="Windows Azure!*[System[(Level=1 or Level=2 or Level=3 or Level=4)]]" />
            </WindowsEventLog>
            <CrashDumps containerName="wad-crashdumps" dumpType="Mini">
              <CrashDumpConfiguration processName="WaIISHost.exe" />
              <CrashDumpConfiguration processName="WaWorkerHost.exe" />
              <CrashDumpConfiguration processName="w3wp.exe" />
            </CrashDumps>
            <PerformanceCounters scheduledTransferPeriod="PT1M">
              <PerformanceCounterConfiguration counterSpecifier="\Memory\Available MBytes" sampleRate="PT3M" />
              <PerformanceCounterConfiguration counterSpecifier="\Web Service(_Total)\ISAPI Extension Requests/sec" sampleRate="PT3M" />
              <PerformanceCounterConfiguration counterSpecifier="\Web Service(_Total)\Bytes Total/Sec" sampleRate="PT3M" />
              <PerformanceCounterConfiguration counterSpecifier="\ASP.NET Applications(__Total__)\Requests/Sec" sampleRate="PT3M" />
              <PerformanceCounterConfiguration counterSpecifier="\ASP.NET Applications(__Total__)\Errors Total/Sec" sampleRate="PT3M" />
              <PerformanceCounterConfiguration counterSpecifier="\ASP.NET\Requests Queued" sampleRate="PT3M" />
              <PerformanceCounterConfiguration counterSpecifier="\ASP.NET\Requests Rejected" sampleRate="PT3M" />
              <PerformanceCounterConfiguration counterSpecifier="\Processor(_Total)\% Processor Time" sampleRate="PT3M" />
              <PerformanceCounterConfiguration counterSpecifier="\.NET CLR Memory(w3wp)\% Time in GC" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\.NET CLR Exceptions(w3wp)\# of Exceps Thrown / sec" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\.NET CLR LocksAndThreads(w3wp)\# of current logical Threads" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\.NET CLR LocksAndThreads(w3wp)\# of current physical Threads" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\.NET CLR LocksAndThreads(w3wp)\Current Queue Length" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\.NET CLR LocksAndThreads(w3wp)\Contention Rate / sec" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\.NET CLR Memory(w3wp)\# Bytes in all Heaps" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\.NET CLR Memory(w3wp)\# GC Handles" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\.NET CLR Memory(w3wp)\# of Pinned Objects" sampleRate="PT10S" />
    
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Connections Connected" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Connections Reconnected" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Connections Disconnected" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Connections Current" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Connection Messages Received Total" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Connection Messages Sent Total" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Connection Messages Received/Sec" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Connection Messages Sent/Sec" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Message Bus Messages Received Total" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Message Bus Messages Received/Sec" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Scaleout Message Bus Messages Received/Sec" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Message Bus Messages Published Total" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Message Bus Messages Published/Sec" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Message Bus Subscribers Current" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Message Bus Subscribers Total" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Message Bus Subscribers/Sec" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Message Bus Allocated Workers" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Message Bus Busy Workers" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Message Bus Topics Current" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Errors: All Total" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Errors: All/Sec" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Errors: Hub Resolution Total" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Errors: Hub Resolution/Sec" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Errors: Hub Invocation Total" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Errors: Hub Invocation/Sec" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Errors: Tranport Total" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Errors: Transport/Sec" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Scaleout Streams Total" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Scaleout Streams Open" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Scaleout Streams Buffering" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Scaleout Errors Total" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Scaleout Errors/Sec" sampleRate="PT10S" />
              <PerformanceCounterConfiguration counterSpecifier="\SignalR(*)\Scaleout Send Queue Length" sampleRate="PT10S" />
            </PerformanceCounters>
          </DiagnosticMonitorConfiguration>
        </WadCfg>
        <StorageAccount></StorageAccount>
      </PublicConfig>
      <PrivateConfig>
        <StorageAccount name="" key="" endpoint="" />
      </PrivateConfig>
      <IsEnabled>true</IsEnabled>
    </DiagnosticsConfiguration>
    
  8. 工具> NuGet 包管理器打开包管理器控制台。 输入以下命令以安装最新版本的 SignalR 和 SignalR 实用工具包:

    install-package microsoft.aspnet.signalr
    install-package microsoft.aspnet.signalr.utils
    
  9. 将应用配置为在启动或回收时将 SignalR 性能计数器安装到角色实例中。 在“解决方案资源管理器”中,右键单击“WebRole1”项目,然后选择“添加新>文件夹”。 将新文件夹命名为 Startup

    添加启动文件夹

  10. 从项目文件夹>/SignalRPerfCounters/packages /Microsoft.AspNet.SignalR.Utils 复制随 Microsoft.AspNet.SignalR.Utils 包) <添加的signalr.exe(文件。<在上一步中创建的 Startup 文件夹的版本>/工具。

  11. “解决方案资源管理器”中,右键单击“启动”文件夹,然后选择“添加>现有项”。 在出现的对话框中,选择 “signalr.exe ”,然后选择“ 添加”。

    向项目添加signalr.exe

  12. 右键单击创建的 “启动 ”文件夹。 选择“添加”>“新项”。 选择“ 常规 ”节点,选择“ 文本文件”,并将新项命名为 SignalRPerfCounterInstall.cmd。 此命令文件会将 SignalR 性能计数器安装到 Web 角色中。

    创建 SignalR 性能计数器安装批处理文件

  13. 当 Visual Studio 创建 SignalRPerfCounterInstall.cmd 文件时,它将自动在main窗口中打开。 将文件的内容替换为以下脚本,然后保存并关闭该文件。 此脚本执行 signalr.exe,它将 SignalR 性能计数器添加到角色实例。

    SET SignalR_LogDir=%~dp0Log\
    MKDIR "%SignalR_LogDir%"
    cd %~dp0
    signalr.exe ipc >> "%SignalR_LogDir%SignalR_Log.txt" 2>&1
    net localgroup "Performance Monitor Users" "Network Service" /ADD >> "%SignalR_LogDir%NetworkAdd.txt" 2>&1
    
  14. 解决方案资源管理器 中选择signalr.exe文件。 在文件的 “属性”中,将 “复制到输出目录” 设置为 “始终复制”。

    将“复制到输出目录”设置为“始终复制”

  15. SignalRPerfCounterInstall.cmd 文件重复上一步。

  16. 右键单击 SignalRPerfCounterInstall.cmd 文件,然后选择“打开时”。 在出现的对话框中,选择“ 二进制编辑器 ”,然后选择“ 确定”。

    使用二进制编辑器打开

  17. 在二进制编辑器中,选择文件中的任何前导字节并将其删除。 保存并关闭该文件。

    删除前导字节

  18. 打开 ServiceDefinition.csdef 并添加一个启动任务,用于在服务启动时执行 SignalrPerfCounterInstall.cmd 文件:

    <?xml version="1.0" encoding="utf-8"?>
    <ServiceDefinition name="SignalRPerfCounters" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition" schemaVersion="2015-04.2.6">
      <WebRole name="WebRole1" vmsize="Small">
        <Startup>
          <Task commandLine="Startup\SignalRPerfCounterInstall.cmd" executionContext="elevated" taskType="background" />
        </Startup>
        <Sites>
          <Site name="Web">
            <Bindings>
              <Binding name="Endpoint1" endpointName="Endpoint1" />
            </Bindings>
          </Site>
        </Sites>
        <Endpoints>
          <InputEndpoint name="Endpoint1" protocol="http" port="80" />
        </Endpoints>
      </WebRole>
    </ServiceDefinition>
    
  19. 打开 Views/Shared/_Layout.cshtml 并从文件末尾删除 jQuery 捆绑包脚本。

    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; @DateTime.Now.Year - My ASP.NET Application</p>
        </footer>
    </div>
    
    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @RenderSection("scripts", required: false)
    </body>
    </html>
    
  20. 添加在服务器上持续调用 increment 方法的 JavaScript 客户端。 打开 Views/Home/Index.cshtml 并将内容替换为以下代码:

    @{
        ViewBag.Title = "Home Page";
    }
    
    <script src="~/Scripts/jquery-1.10.2.min.js"></script>
    <script src="~/Scripts/jquery.signalR-2.2.1.min.js"></script>
    <script src="~/signalr/hubs" type="text/javascript"></script>
    
    <div id="body">
        <section class="featured">
            <div class="content-wrapper">
                <p>
                    Hello World!
                </p>
                <div style="font-size:large;">
                    My Counter: <span id="counter"></span>
                </div>
            </div>
        </section>
        <section class="content-wrapper main-content clear-fix"></section>
    </div>
    
    <script type="text/javascript">
      $(document).ready(function () {
        var hub = $.connection.myHub;
    
        hub.client.sendResult = function (x) {
          console.log('sendResult(' + x + ')');
          $("#counter").text(x);
          window.setTimeout(function () {
            hub.server.increment(x);
          }, 1000);
        };
    
        $.connection.hub.connected = function () {};
        $.connection.hub.disconnected = function () {};
    
        $.connection.hub.stateChanged(function (change) {
          console.log('new State' + change.newState);
          if (change.newState === $.signalR.connectionState.disconnected) {
            $.connection.hub.start();
          }
          if (change.newState === $.signalR.connectionState.reconnecting) {
            console.log('Re-connecting');
          } else if (change.newState === $.signalR.connectionState.connected) {
            console.log('The server is online');
          }
        });
    
        $.connection.hub.error(function (error) {
          console.log('error ' + error);
        });
        
        $.connection.hub.logging = true;
        
        $.connection.hub.reconnected(function () {
          console.log('Reconnected');
          hub.server.increment(0);
        });
    
        $.connection.hub.start().done(function () {
          console.log('hub started');
          hub.server.increment(0);
        });
      });
    </script>
    
  21. WebRole1 项目中创建一个名为 Hubs 的新文件夹。 右键单击解决方案资源管理器中的“中心”文件夹,然后选择“添加新>”。 在“ 添加新项 ”对话框中,选择“ Web>SignalR ”类别,然后选择“ SignalR 中心类” (v2) 项模板。 将新中心命名为 MyHub.cs ,然后选择“ 添加”。

    将 SignalR 中心类添加到“添加新项”对话框中的“中心”文件夹

  22. MyHub.cs 将在main窗口中自动打开。 将内容替换为以下代码,然后保存并关闭文件:

    using System.Threading.Tasks;
    using Microsoft.AspNet.SignalR;
    
    namespace WebRole1.Hubs
    {
        public class MyHub : Hub
        {
            public async Task Increment(int x)
            {
                await this.Clients.Caller.sendResult(x + 1);
            }
        }
    }
    
  23. Crank.exe 是 SignalR 代码库随附的连接密度测试工具。 由于 Crank 需要持久连接,请向站点添加一个连接,以便在测试时使用。 将名为 PersistentConnections 的新文件夹添加到 WebRole1 项目。 右键单击此文件夹,然后选择“ 添加>”。 将新类文件命名为 MyPersistentConnections.cs ,然后选择“ 添加”。

  24. Visual Studio 将在main窗口中打开 MyPersistentConnections.cs 文件。 将内容替换为以下代码,然后保存并关闭文件:

    using System.Threading.Tasks;
    using Microsoft.AspNet.SignalR;
    using Microsoft.AspNet.SignalR.Infrastructure;
    
    namespace WebRole1.PersistentConnections
    {
        public class MyPersistentConnection : PersistentConnection
        {
            protected override Task OnReceived(IRequest request, string connectionId, string data)
            {
                //Return data to calling user
                return Connection.Send(connectionId, data);        
            }
        }
    }
    
  25. Startup使用 类,SignalR 对象会在 OWIN 启动时启动。 打开或创建 Startup.cs ,并将内容替换为以下代码:

    using Microsoft.Owin;
    using Owin;
    using WebRole1.PersistentConnections;
    
    // Marks this class for automatic OWIN startup
    [assembly: OwinStartup(typeof(WebRole1.Startup))]
    namespace WebRole1
    {
        public partial class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                ConfigureAuth(app);
                // Only needed if "No Authentication" was not selected for the project
                app.MapSignalR();
                app.MapSignalR<MyPersistentConnection>("/echo");
            }
        }
    }
    

    在上面的代码中 OwinStartup , 属性将此类标记为启动 OWIN。 方法 Configuration 启动 SignalR。

  26. F5 在Microsoft Azure 模拟器测试应用程序。

    注意

    如果在 MapSignalR 上遇到 FileLoadException,请将web.config中的绑定重定向更改为以下内容:

    <dependentAssembly>
      <assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
      <bindingRedirect oldVersion="0.0.0.0-2.0.2.0" newVersion="2.0.0.0" />
    </dependentAssembly>
    <dependentAssembly>
      <assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
      <bindingRedirect oldVersion="0.0.0.0-2.0.2.0" newVersion="2.0.0.0" />
    </dependentAssembly>
    
  27. 等待大约一分钟。 在 Visual Studio 中打开 Cloud Explorer 工具窗口 (查看>Cloud Explorer) 并展开路径 (Local)/Storage Accounts/(Development)/Tables。 双击“ WADPerformanceCountersTable”。 应在表数据中看到 SignalR 计数器。 如果看不到该表,可能需要重新输入 Azure 存储凭据。 可能需要选择“ 刷新 ”按钮才能在 云资源管理器 中查看表,或者在打开的表窗口中选择“ 刷新 ”按钮以查看表中的数据。

    在 Visual Studio Cloud Explorer 中选择 WAD 性能计数器表

    显示 WAD 性能计数器表中收集的计数器

  28. 若要在云中测试应用程序,请更新 ServiceConfiguration.Cloud.cscfg 文件, Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString 并将 设置为有效的 Azure 存储帐户连接字符串。

    <?xml version="1.0" encoding="utf-8"?>
    <ServiceConfiguration serviceName="SignalRPerfCounters" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration" osFamily="4" osVersion="*" schemaVersion="2015-04.2.6">
      <Role name="WebRole1">
        <Instances count="1" />
        <ConfigurationSettings>
          <Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="DefaultEndpointsProtocol=https;AccountName=&lt;account-name&gt;;AccountKey=&lt;account-key&gt;" />
        </ConfigurationSettings>
      </Role>
    </ServiceConfiguration>
    
  29. 将应用程序部署到 Azure 订阅。 有关如何将应用程序部署到 Azure 的详细信息,请参阅 如何创建和部署云服务

  30. 稍等几分钟。 在 Cloud Explorer 中,找到上面配置的存储帐户,并在其中找到 WADPerformanceCountersTable 表。 应在表数据中看到 SignalR 计数器。 如果看不到该表,可能需要重新输入 Azure 存储凭据。 可能需要选择“ 刷新 ”按钮才能在 云资源管理器 中查看表,或者在打开的表窗口中选择“ 刷新 ”按钮以查看表中的数据。

特别感谢 Martin Richard 在本教程中使用的原始内容。