I've been debugging the role locally and it takes up to 9% CPU, but when I upload it to an staging instance it takes up to 99.37% of CPU usage.
My role consists in a Multithread TCP Server listening at a certain port for data insertion, it process the data and inserts it into a MySQL database in the same region as the role.
The server starts in a Thread once it starts running.
Here's the code of my the Run method...
public override void Run()
{
Trace.TraceInformation("Data Collector initialized!");
Server tcpServer = new Server();
tcpServer.Start();
while (true)
{
}
}
And here's the Server class I'm using to process all the data. Server creates one thread per accepted socket, I'm currently using just one device that sends data every 15 minutes...
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using Microsoft.WindowsAzure.ServiceRuntime;
using MySql.Data.MySqlClient;
namespace Data_Collector
{
public class Server
{
public TcpListener listener; //Listens fot TCP connections
//Port that the server will be listening.
private int Port;
private Thread listenerThread;
private IPAddress Ip;
private MySqlConnection databaseConnection;
private string server = "x.x.x.x";
private string database = "database";
private string uid = "user";
private string password = @"password";
private string connectionString;
/// <summary>
/// Initialize the server with the service IP and Endpoint.
/// </summary>
public Server()
{
Port = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["Data"].IPEndpoint.Port;
Ip = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["Data"].IPEndpoint.Address;
}
/// <summary>
/// Starts the server.
/// </summary>
public void Start()
{
listener = new TcpListener(Ip, Port);
listener.Start();
listenerThread = new Thread(new ThreadStart(Listen));
listenerThread.Start();
connectionString = "SERVER=" + server + ";" + "DATABASE=" +
database + ";" + "UID=" + uid + ";" + "PASSWORD=" + password + ";";
}
public void Stop()
{
listener.Stop();
listenerThread.Abort();
}
private void Listen()
{
while (true)
{
try
{
//Waits for a connection.
Socket request = listener.AcceptSocket();
//Trace.TraceInformation("Connection accepted from {0}", request.RemoteEndPoint);
new Thread(new ThreadStart(() => HandleRequest(request))).Start();
}
catch (Exception ex)
{
//Trace.TraceInformation(ex.Message);
}
}
}
private void HandleRequest(Socket client)
{
DateTime fecha = DateTime.Now;
byte[] response = new byte[1]{0x00};
byte[] data = new byte[7];
byte crc;
double temperatura;
client.Receive(data);
crc = (byte)(data[1] + data[2] + data[3] + data[4]);
if (data[5] == crc)
{
temperatura = data[3];
temperatura += data[4] / 100.00;
//Trace.TraceInformation("Temperatura actual: {0}ºC", temperatura);
if (RealDevice(data[1]))
{
if (InsertData(data[1], data[2], temperatura))
{
response[0] = 0xFF;
}
}
}
client.Send(response);
client.Close();
}
/// <summary>
/// Checks whether the device exists or not.
/// </summary>
/// <param name="deviceId">The device's ID.</param>
/// <returns>True if exists, false if not.</returns>
private bool RealDevice(byte deviceId)
{
MySqlCommand command;
MySqlDataReader reader;
bool successful = true;
string query = "SELECT id FROM dispositivos WHERE id = @device";
//Trace.TraceInformation("Initializing connection to database...");
databaseConnection = new MySqlConnection(connectionString);
//Trace.TraceInformation("Connection to database successful!");
try
{
using (databaseConnection)
{
command = new MySqlCommand(query, databaseConnection);
command.Parameters.AddWithValue("@device", deviceId);
databaseConnection.Open();
reader = command.ExecuteReader();
while (reader.Read())
{
if (!reader.HasRows)
{
successful = false;
}
}
databaseConnection.Close();
}
}
catch (Exception ex)
{
//Trace.TraceInformation(ex.Message);
}
return successful;
}
/// <summary>
/// Inserts the data from the device.
/// </summary>
/// <param name="deviceId">Device's ID.</param>
/// <param name="sensorId">Sensor that sends the data.</param>
/// <param name="value">Data value.</param>
/// <returns>True if successful, false if not.</returns>
private bool InsertData(byte deviceId, byte sensorId, double value)
{
bool successful = true;
MySqlCommand command;
DateTime date = DateTime.Now.ToUniversalTime();
TimeZoneInfo localTime = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time (Mexico)");
date = TimeZoneInfo.ConvertTimeFromUtc(date, localTime);
string query = "INSERT INTO datos (valor, fecha, hora, id_dispositivo, id_sensor) "+
"VALUES (@valor, @fecha, @hora, @id_dispositivo, @id_sensor)";
databaseConnection = new MySqlConnection(connectionString);
try
{
using (databaseConnection)
{
command = new MySqlCommand(query, databaseConnection);
command.Parameters.AddWithValue("@valor", value);
command.Parameters.AddWithValue("@fecha", date.ToString("yyyy-MM-dd"));
command.Parameters.AddWithValue("@hora", date.ToString("HH:mm:ss"));
command.Parameters.AddWithValue("@id_dispositivo", deviceId);
command.Parameters.AddWithValue("@id_sensor", sensorId);
databaseConnection.Open();
command.ExecuteNonQuery();
databaseConnection.Close();
}
}
catch (Exception ex)
{
//Trace.TraceInformation(ex.Message);
successful = false;
}
return successful;
}
}
}
You should probably move the listening work to the main thread. That solves the blocking issue (because you want blocking). Having a thread dedicated for listening is not necessary.
If you don't want that:
Thread.Sleep(Timeout.Infinite);