Проблемы производительности SignalR и SQLDependency в Windows 8.1 / IIS 8.5.9600.16384

Я только начал работать над POC для реализации SignalR 2 + SQL Dependency для создания панели мониторинга в реальном времени (хотя POC, с которым я работаю, немного отличается от требований проекта).

Этот POC измеряет использование ЦП сервера на логическое ядро.

Предварительный просмотр тестовой страницы POC

введите здесь описание изображения

Я успешно реализовал предварительный просмотр зависимостей, и я могу видеть предварительный просмотр в реальном времени на тестовой html-странице в момент внесения каких-либо изменений в мою локальную базу данных SQL.

Изменения обновляются независимым приложением командной строки с помощью простого обновленного запроса. и моя база данных также имеет простую однострочную структуру из 4 столбцов (по крайней мере, на данный момент).

введите здесь описание изображения

Проблема в том, что загрузка ЦП увеличивается на 95–100 процентов после 20–30 изменений, инициированных событием dependency_OnChange (даже если открыты только Visual Studio и один браузер, а приложение командной строки запущено для обновления).

Предварительный просмотр After-Shock

введите здесь описание изображения

Я использую Visual Studio (2015 Community Edition) в Windows 8.1 и IIS 8.5. Страница POC запускается как http://localhost:55725/index.html и не размещается в IIS пока нет.

Я попробовал решение, изображенное здесь: https://github.com/SignalR/SignalR/wiki/Performance < / а>

а также искал в этой теме решение: Производительность запросов SignalR и SqlDependency

Я еще не знаю, пока нет причудливых вещей (будет реализовано под 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();
            }
        }
    }
}

Что я упускаю / делаю неправильно?


person NBaua    schedule 09.03.2016    source источник
comment
С 2-3 экземплярами браузера (в основном Chrome 48) ... Система зависает, и FireFox 45b дает сбой при использовании вместе с.   -  person NBaua    schedule 10.03.2016


Ответы (1)


Мало что ты можешь сделать

  1. Отложите запрос на обновление, выполняемый инструментом командной строки, чтобы избежать частых обновлений. Например. подождите не менее 200 миллисекунд перед выполнением запроса на обновление. Если в течение этих 200 миллисекунд появятся какие-либо новые обновления, проигнорируйте предыдущий таймер и снова сбросьте его на 200 мс. Я понимаю, что этот обходной путь может не подходить для вашего приложения, потому что он добавляет задержку в 200 мс для обновления клиентов, а не в реальном времени.

  2. Почему бы вам напрямую не связываться с HUB всякий раз, когда в базе данных SQL происходят какие-либо изменения. Я имею в виду, что инструмент командной строки может напрямую предоставлять обновления HUB вместо того, чтобы HUB получать информацию из события изменения SqlDependency.

person Ronak Patel    schedule 03.05.2016
comment
1. Я проверил этот вариант. 2. Скоро проверю вариант обновления хаба. - person NBaua; 10.05.2016