I have an API which gives me status of a few sensors (located on the Azure functions)
www.myapp.com/api/sensors/{sensor_id}
My client application needs to check each sensor every one second! so in this case I have 10 sensors and the client need to send 10 http get request every second!
I decided to do it using Parallel and create a pool out of each instance of my method which runs an executes the Get request!
This is the main method which orchestrate and add each sensor to the pool and invoke them all
public override Task Run(List<int> sensors)
{
try
{
List<Action> actions = new List<Action>();
foreach (var prd in sensors)
{
actions.Add(async () => await Process(prd));
}
ParallelOptions parallelOptions = new ParallelOptions
{
MaxDegreeOfParallelism = 20
};
Parallel.Invoke(parallelOptions, actions.ToArray());
}
catch (Exception ex)
{
Run(sensors);
}
return Task.CompletedTask;
}
In the Process() method I get the sensor Id and add it to the API endpoint then run the Get request every 1 second.
private async Task<string> Process(int sensorId)
{
while (true)
{
Thread.Sleep(1000);
try
{
SensorModel response = _httpClientService.GetAsync<SensorModel>(string.Format(apiUrl, sensorId)).Result;
if (response != null)
{
if (response.Status == "success")
{
.
.
.
}
_logger.LogError($"API connection unsuccesful...");
return null;
}
_logger.LogError($"API connection failed...");
return null;
}
catch (Exception ex)
{
_logger.LogError($"Exception in retrieving sensor data {StringExtensions.GetCurrentMethod()} {ex}");
return null;
}
}
}
and my GetAsync in my HttpClient factory
public async Task<T> GetAsync<T>(string uri)
{
string responseData = null;
try
{
var response = await _client.GetAsync(uri);
responseData = await response.Content.ReadAsStringAsync();
if (!string.IsNullOrEmpty(responseData) && response.StatusCode == HttpStatusCode.OK)
return JsonConvert.DeserializeObject<T>(responseData);
return default;
}
catch (Exception ex)
{
_logger.LogError(ex, responseData);
return default;
}
}
Now the above code works fine most of the time, but soemtimes it just starts failing the connections and returning the exception below, the only way I can fix it is to restart the application. Not sure it maybe getting confused with a lot of requests and the fact that they are being processed simultaneously
System.ArgumentNullException: Value cannot be null. (Parameter 'obj')
at System.OrdinalIgnoreCaseComparer.GetHashCode(String obj)
at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
at System.Net.Http.Headers.HttpHeaders.AddHeaders(HttpHeaders sourceHeaders)
at System.Net.Http.Headers.HttpRequestHeaders.AddHeaders(HttpHeaders sourceHeaders)
at System.Net.Http.HttpClient.SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.GetAsync(String requestUri)
at SensorMonitor.Services.Implementations.HttpClientService.GetAsync[T](String uri)
I know it is within my GetAsync method but can't figure out why it is happening after a few minutes of running and not just from the start!
Also note I had to make my GetAsync method not to be async by calling .Result as if I do await then the app crashes before even it starts the first request! It seems it doesn't like being called in a Parallel Pool.
I'd appreciate your help!
Updated with more details:
HttpClientService.cs
public class HttpClientService : IHttpClientService
{
private readonly HttpClient _client;
private readonly ILogger<HttpClientService> _logger;
private static readonly JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };
public HttpClientService(HttpClient client, ILogger<HttpClientService> logger)
{
_client = client;
_logger = logger;
}
.
.
.
And this is how I registered my HttpClient
services.AddHttpClient<IHttpClientService, HttpClientService>();