【问题标题】:SignalR and SQLDependency Performance issues on Windows 8.1/IIS 8.5.9600.16384Windows 8.1/IIS 8.5.9600.16384 上的 SignalR 和 SQLDependency 性能问题
【发布时间】:2016-03-09 13:38:05
【问题描述】:

我刚刚开始使用 POC 来实现 SignalR 2 + SQL Dependency 以创建实时仪表板(尽管我正在使用的 POC 与项目要求略有不同)。

此 POC 测量每个逻辑核心的服务器 CPU 使用率。

POC 测试页面预览

我已经成功实现了依赖关系预览,并且我可以在测试 html 页面上看到实时预览,即在我的本地 SQL DB 中进行任何更改的那一刻。

这些更改由一个独立的命令行应用程序通过一个简单的更新查询进行更新。而且我的数据库也有一个 4 列的简单单行结构(至少现在是这样)。

问题是 CPU 使用率在通过“dependency_OnChange”事件触发 20-30 次更改后达到 95-100%(即使仅打开 Visual Studio 和单个浏览器并且命令行应用程序运行以进行更新) em>。

余震预览

我在 Windows 8.1 和 IIS 8.5 中使用 Visual Studio(2015 社区版)。 POC 页面以“​​http://localhost:55725/index.html”运行,尚未托管在 IIS 中。

我尝试了这里描述的解决方案:https://github.com/SignalR/SignalR/wiki/Performance

并且还查看了此线程以寻求解决方案: SignalR & SqlDependency Query performance

我还不知道,还没有花哨的东西(如果性能很好,将在 Asp.Net MVC 下实现),但即使是最简单的愚蠢的东西也没有运气。

Global.asax 的代码

protected void Application_Start(object sender, EventArgs e)
{
    SqlDependency.Start(connString);
}

protected void Application_End(object sender, EventArgs e)
{
    SqlDependency.Stop(connString);
}

Startup.cs 代码

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=316888
        app.MapSignalR();
    }
}

Hub 类代码

public class AppHub : Hub
    {
        private static string conString = ConfigurationManager.ConnectionStrings["DBConn"].ToString();

        [HubMethodName("sendMessages")]
        public static void SendMessages()
        {
            IHubContext context = GlobalHost.ConnectionManager.GetHubContext<AppHub>();
            context.Clients.All.updateMessages();
        }
    }

测试html页面的代码(index.html):

<head>
    <script src="http://bernii.github.io/gauge.js/dist/gauge.min.js"></script>
    <script src="/Scripts/jquery-1.10.2.min.js"></script>
    <script src="/Scripts/jquery.signalR-2.1.2.js"></script>
    <!--Reference the autogenerated SignalR hub script. -->
    <script src="/signalr/hubs"></script>

    <script type="text/javascript">
        $(function () {
            // Declare a proxy to reference the hub.
            var notifications = $.connection.appHub;
            //debugger;
            // Create a function that the hub can call to broadcast messages.
            notifications.client.updateMessages = function () {
                //alert("connection updated.")
                getAllMessages()

            };

            // Start the connection.
            $.connection.hub.start().done(function () {
                //alert("connection started")
                getAllMessages();
            }).fail(function (e) {
                alert(e);
            });
        });


        function getAllMessages() {
            var tbl = $('#messagesTable');
            $.ajax({
                url: '/appdata.ashx',
                contentType: 'application/html ; charset:utf-8',
                type: 'GET',
                dataType: 'json'
            }).success(function (result) {
                //                tbl.empty().append(result);
                DrawGauges(result);
            }).error(function () {

            });
        }

        function DrawGauges(jsondata) {
            var opts = {
                lines: 12, // The number of lines to draw
                angle: 0.15, // The length of each line
                lineWidth: 0.44, // The line thickness
                pointer: {
                    length: 0.9, // The radius of the inner circle
                    strokeWidth: 0.035, // The rotation offset
                    color: '#000000' // Fill color
                },
                limitMax: 'false',   // If true, the pointer will not go past the end of the gauge
                colorStart: '#9BF0E9',   // Colors
                colorStop: '#61D2D6',    // just experiment with them
                strokeColor: '#E0E0E0',   // to see which ones work best for you
                generateGradient: true
            };

            for (var i = 1; i < 5; i++) {

                var target = document.getElementById('canvas' + i); // your canvas element
                var textlabel = document.getElementById('text' + i); // your canvas element
                var gauge = new Gauge(target).setOptions(opts); // create sexy gauge!

                gauge.maxValue = 100; // set max gauge value
                gauge.animationSpeed = 32; // set animation speed (32 is default value)
                gauge.set(jsondata[0]["Core" + i + "Usage"]); // set actual value
                textlabel.innerText = jsondata[0]["Core" + i + "Usage"];
            }

        }
    </script>
    <style>
        h4 {
            text-align: center;
            width: 100%;
            font-family:'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
            font-size:27pt;
        }
        span{
            position:relative;
            left:-12%;
            top:-75px;
            font-family:'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
            font-size:14pt;
            font-weight:bold;
            color:#000000;
        }
    </style>
</head>
<body>
    <div class="row">
        <div class="col-md-12">

            <h4>CPU Usage Per Core</h4>
            <div id="realtimepreview">
                <canvas width="300" height="120" id="canvas1"></canvas><span id="text1"></span>
                <canvas width="300" height="120" id="canvas2"></canvas><span id="text2"></span>
                <canvas width="300" height="120" id="canvas3"></canvas><span id="text3"></span>
                <canvas width="300" height="120" id="canvas4"></canvas><span id="text4"></span>
            </div>

ASPX 通用处理程序的代码 - 由客户端根据请求调用

public class AppData : IHttpHandler
        {

            public void ProcessRequest(HttpContext context)
            {
                context.Response.ContentType = "text/json";
                CPUUsageHistory _messageRepository = new CPUUsageHistory();
                context.Response.Write(
                    JsonConvert.SerializeObject(_messageRepository.GetAllMessages()));
            }

            public bool IsReusable
            {
                get
                {
                    return true;
                }
            }
        }

SQL 依赖代码

        public class CPUUsage
        {
            public string Core1Usage { get; set; }
            public string Core2Usage { get; set; }
            public string Core3Usage { get; set; }
            public string Core4Usage { get; set; }
        }

    public class CPUUsageHistory
    {
        readonly string _connString = ConfigurationManager.ConnectionStrings["DBConn"].ConnectionString;

        public IEnumerable<CPUUsage> GetAllMessages()
        {
            var cpuusage = new List<CPUUsage>();

            using (var connection = new SqlConnection(_connString))
            {
                connection.Open();
                using (var command = new SqlCommand(@"SELECT [Core1Usage],[Core2Usage],[Core3Usage],[Core4Usage]  FROM [dbo].[CpuUsageDetails]", connection))
                {
                    command.Notification = null;

                    var dependency = new SqlDependency(command);
                    dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);

                    if (connection.State == ConnectionState.Closed)
                        connection.Open();

                    var reader = command.ExecuteReader();

                    while (reader.Read())
                    {
                        cpuusage.Add(item: new CPUUsage
                        {
                            Core1Usage = reader["Core1Usage"].ToString(),
                            Core2Usage = reader["Core2Usage"].ToString(),
                            Core3Usage = reader["Core3Usage"].ToString(),
                            Core4Usage = reader["Core4Usage"].ToString()
                        });
                    }
                }

            }
            return cpuusage;


        }
        private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
        {
            if (e.Type == SqlNotificationType.Change)
            {
                AppHub.SendMessages();
            }
        }
    }
}

我错过了什么/做错了什么?

【问题讨论】:

  • 使用 2 -3 浏览器实例(主要是 Chrome 48)...如果一起使用,系统 chocks 和 FireFox 45b 会崩溃。

标签: asp.net signalr real-time signalr-hub sqldependency


【解决方案1】:

你可以做的几件事

  1. 延迟命令行工具执行的更新查询以避免频繁更新。例如。在执行更新查询之前等待至少 200 毫秒。如果在这 200 毫秒内有任何新更新,则忽略前一个计时器并再次将其重置 200 毫秒。我了解此解决方法可能不适合您的应用程序,因为它会增加 200 毫秒的延迟来更新客户端,而不是实时更新。

  2. 当 SQL DB 发生任何变化时,为什么不直接与 HUB 通信。我的意思是命令行工具可以直接向 HUB 提供更新,而不是 HUB 从 SqlDependency 更改事件中获取信息。

【讨论】:

  • 1.我确实检查了该选项。 2. 我会尽快检查中心更新选项。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-28
相关资源
最近更新 更多