在 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 下的 diagnostics.wadcfgx 文件。

    解决方案资源管理器 diagnostics.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 项目并选择“添加新>文件夹”。 将新文件夹 命名为“启动”。

    添加启动文件夹

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

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

    向项目添加signalr.exe

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

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

  13. 当 Visual Studio 创建 SignalRPerfCounterInstall.cmd 文件时,它将自动在主窗口中打开。 将文件的内容替换为以下脚本,然后保存并关闭该文件。 此脚本执行 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. 添加一个 JavaScript 客户端,该客户端持续调用 increment 服务器上的方法。 打开 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. 在名为 HubsWebRole1 项目中创建新文件夹。 右键单击解决方案资源管理器中的中心文件夹,然后选择“添加新>项”。 “添加新项”对话框中,选择“Web>SignalR”类别,然后选择 SignalR 中心类(v2)项模板。 将新中心 命名为MyHub.cs 并选择“ 添加”。

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

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

    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 需要持久连接,所以需要将一个连接到站点,以便在测试时使用。 向名为 PersistentConnectionsWebRole1 项目添加新文件夹。 右键单击此文件夹,然后选择“添加>类”。 将新类文件 命名为MyPersistentConnections.cs 并选择“ 添加”。

  24. Visual Studio 将在主窗口中打开 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 工具窗口(查看>云资源管理器),然后展开路径。(Local)/Storage Accounts/(Development)/Tables 双击 WADPerformanceCountersTable。 应在表数据中看到 SignalR 计数器。 如果未看到表,可能需要重新输入Azure 存储凭据。 可能需要选择“刷新”按钮才能在 Cloud Explorer查看表,或在打开的表窗口中选择“刷新”按钮以查看表中的数据。

    在 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 存储凭据。 可能需要选择“刷新”按钮才能在 Cloud Explorer查看表,或在打开的表窗口中选择“刷新”按钮以查看表中的数据。

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