Я только начал работать над 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();
}
}
}
}
Что я упускаю / делаю неправильно?