How to optimize this code and call Api every 40ms - c#

I want to interrogate one sensor that returns a JSON Rest Api response. I make Api call every 40 milliseconds but it gave me this error :
in System.Threading.Tasks.Task1.GetResultCore(Boolean waitCompletionNotification) in System.Threading.Tasks.Task1.get_Result()
I have timer where interval = 40. And this is the code how I call tha Api :
private void Timer(object sender, EventArgs e)
{
tmrPollingSick.Stop();
string strJson = "";
HttpClient client = new HttpClient();
string baseUrl = "http://9999.99999.99999.8";
client.BaseAddress = new Uri(baseUrl);
var contentType = new MediaTypeWithQualityHeaderValue("application/json");
client.DefaultRequestHeaders.Accept.Add(contentType);
string strAltezza = string.Empty;
try
{
strJson = "Here I set HEADERS... DATA ect " + Convert.ToChar(34) +
"header" + Convert.ToChar(34) + ": {............"
var contentData = new StringContent(strJson, System.Text.Encoding.UTF8, "application/json");
using (var responseMessage = client.PostAsync("/bla/bla/bla", contentData).Result)
{
if (responseMessage.IsSuccessStatusCode)
{
string strContext = responseMessage.Content.ReadAsStringAsync().Result;
Object dec = JsonConvert.DeserializeObject(strContext); // deserializing Json string (it will deserialize Json string)
JObject obj = JObject.Parse(strContext);
//Process Data In
JObject obj1 = JObject.Parse(obj["bla"].ToString());
JObject obj2 = JObject.Parse(obj1["processDataIn"].ToString());
strAltezza = obj2["1"].ToString();
textBox1.Text = strAltezza;
}
}
}
catch(WebException ex1)
{
MessageBox.Show("web: "+ex1.StackTrace.ToString() + " - " + ex1.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.StackTrace.ToString() + " - " + ex.Message);
}
tmrPollingSick.Start();
}
Everything works fine but after a while it gives me that error.
I allready read this (How to implement real time data for a web page and this) but I haven't tried them yet.
Any suggestions how to fix this?
Is there another way how to get the result in real-time without crashing?

May I baptize this as stubborn pooling?
You don't want to use a timer. What you want is x time between request-response cycles. (This will solve the socket exhaustion).
Split your code into phases (client init, request fetch, response processing). See #Oliver answer.
Make a function to execute everything. And run some sort of infinite foreach loop where you can sleep for x time after calling the fetch function (and the process, but you could defer this to another thread or do it async).

When you call this method all 40ms you'll run out of send sockets, because you create every time a new HttpClient. Even putting this into a using statement (cause HttpClient implements IDisposable) wouldn't solve this problem, cause the underlying socket will be blocked for 3 minutes from the OS (take a look at this answer for further explanations).
You should split this stuff into some initialization phase where you setup the client, build up the request as far as possible and within this timer method just call the PostAsync() method and check the response.

Related

PostAsync() and WaitAll() break instantly

I'm working on an API that enables C# to communicate and manage GNS projects easily. Now I'm looking for an efficient way to run and stop the projects. You can do it pretty much easily by simple POST requests synchronously. However, this process take some time so I'm trying to make it asynchronous since PostAsync let you do if I'm not mistaken.
Sadly, when I try to run my code, this breaks so bad. This is part where all the problem comes up:
// Change the status of the project (start or stop)
private void ChangeProjectStatus(string status){
// First part of the URL
string URLHeader = $"http://{host}:{port}/v2/projects/{projectID}/nodes";
// Number of nodes
int numNodes = nodes.Length;
// Pack the content we will send
string content = JsonConvert.SerializeObject(new Dictionary<string, string> { { "-d", "{}" } });
ByteArrayContent byteContent = new ByteArrayContent(System.Text.Encoding.UTF8.GetBytes(content));
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
if (status.Equals("start"))
Console.WriteLine("Activating all the nodes in the project...");
else
Console.WriteLine("Deactivating all the nodes in the project...");
Task<System.Net.Http.HttpResponseMessage>[] tasks = new Task<System.Net.Http.HttpResponseMessage>[nodes.Length];
for (ushort i = 0; i < nodes.Length; i++){
try{
tasks[i] = HTTPclient.PostAsync($"{URLHeader}/{nodes[i].ID}/{status}", byteContent);
} catch(Exception err){
Console.Error.WriteLine("Impossible to {2} node {0}: {1}", nodes[i].Name, err.Message, status);
}
}
Task.WaitAll(tasks);
Console.WriteLine("...ok");
}
The error I get (from the WaitAll() block actually) is:
An item with the same key has already been added. Key: Content-Length
Any idea on how to fix it?

Xamarin Android app - Problems adding async/await to an API call to improve performance

I found that my Xamarin Android app is running slowly so I added some async/await code to improve the performance. I wanted to excluded my API calls from the UI thread. I thought this would be a perfect opportunity to use async/await. So, I added async to the signature of the function and wrapped Task around my return value type. Then I updated the RestSharp GET call with "await client.ExecuteTaskAsync." Once I did this I found I needed to update my call to the GetCustInfo function. I simply needed to add .Result to the end of the call and it showed no errors. The problem is it hangs on the call to GetCustInfo and just doesn't work.
What am I doing wrong here?
public async Task<List<CustInfo>> GetCustInfo(string strBranchNumber, string dblCurrentXCoordinate, string dblCurrentYCoordinate)
{
if (this.strBearerToken == string.Empty)
{
throw new ArgumentException("No Bearer Token Found");
}
try
{
var restUrl = this.strCustomerInfoAPIURL;
var uri = new Uri(string.Format(restUrl, string.Empty));
var client = new RestClient(uri);
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "bearer " + this.strBearerToken);
request.AddParameter("intBranchNumber", strBranchNumber);
request.AddParameter("intZipCode", this.strZipCode);
request.AddParameter("intCustomerType", this.strCustomerType);
request.AddParameter("intMinTotalAmount", this.strMinRevenue);
request.AddParameter("dblCurrentXCoordinate", dblCurrentXCoordinate);
request.AddParameter("dblCurrentYCoordinate", dblCurrentYCoordinate);
request.AddParameter("bolGetLocation", true);
var response = await client.ExecuteTaskAsync(request);
return JsonConvert.DeserializeObject<List<CustInfo>>(response.Content).OrderBy(x => x.ApproxDistance).ToList();
}
catch (Exception ex)
{
return null;
}
}
So what is happening is when I call the async/await function from my OnCreate it just stops when I try to call customer.GetCustomerInfo().
protected override void OnCreate(Bundle bundle)
{
....
this.tableItems = customer.GetCustInfo(
"xxxxxxx",
this.currentLocation.Latitude.ToString(),
this.currentLocation.Longitude.ToString()).Result;
this.listView.Adapter = new ListOfLocationAdapter(this, this.tableItems);
}
Change the call to
this.tableItems = await customer.GetCustInfo(..
and let us know..
In the next line you are using the result from the not-awaited call, it obviously would hangs, crash whatever:
this.listView.Adapter = new ListOfLocationAdapter(this, **this.tableItems**); //this.tableItems is just empty

Xamarin cannot access disposed object error

I am using the below code to read JSON from an endpoint in my Xamarin crossplatform project and I am getting error
Cannot read disposed object exception or it fires ObjectDisposedException
IS it something wrong with code Can I write it in a better way ?
public async Task<APISchoolDetailModel> GetSchooDetailsAsync()
{
APISchoolDetailModel api_data = new APISchoolDetailModel();
try
{
var client = new System.Net.Http.HttpClient();
client.DefaultRequestHeaders.Add("Accept", "application/json");
var web_client = await client.GetAsync("http://appapitest.net/APIs/Student/Schooldetails");
var response_string= web_client.Content.ReadAsStringAsync().Result;
DataContractJsonSerializer serializer = new DataContractJsonSerializer(api_data.GetType());
MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(response_string));
api_data = serializer.ReadObject(ms) as APISchoolDetailModel;
}
catch (Exception ex) { }
return api_data;
}
The controller comes till the line var web_client = await client.GetAsync(" and then its not going further and after few seconds I am getting exception
Is any better way to write this code for reading and parsing JSON
#Gserg pointed out something important you should not do this:
var response_string= web_client.Content.ReadAsStringAsync().Result;
in stead of that use:
var response_string= await web_client.Content.ReadAsStringAsync();
within an async Task method:
is you use .Result this may be causing deadlocks within threads or the same stuff that you are experiencing because a thread may be trying to update or use a variable that is already collected from the GC.

Shared object among different requests

I'm working with .NET 3.5 with a simple handler for http requests. Right now, on each http request my handler opens a tcp connection with 3 remote servers in order to receive some information from them. Then closes the sockets and writes the server status back to Context.Response.
However, I would prefer to have a separate object that every 5 minutes connects to the remote servers via tcp, gets the information and keeps it. So the HttpRequest, on each request would be much faster just asking this object for the information.
So my questions here are, how to keep a shared global object in memory all the time that can also "wake" an do those tcp connections even when no http requests are coming and have the object accesible to the http request handler.
A service may be overkill for this.
You can create a global object in your application start and have it create a background thread that does the query every 5 minutes. Take the response (or what you process from the response) and put it into a separate class, creating a new instance of that class with each response, and use System.Threading.Interlocked.Exchange to replace a static instance each time the response is retrieved. When you want to look the the response, simply copy a reference the static instance to a stack reference and you will have the most recent data.
Keep in mind, however, that ASP.NET will kill your application whenever there are no requests for a certain amount of time (idle time), so your application will stop and restart, causing your global object to get destroyed and recreated.
You may read elsewhere that you can't or shouldn't do background stuff in ASP.NET, but that's not true--you just have to understand the implications. I have similar code to the following example working on an ASP.NET site that handles over 1000 req/sec peak (across multiple servers).
For example, in global.asax.cs:
public class BackgroundResult
{
public string Response; // for simplicity, just use a public field for this example--for a real implementation, public fields are probably bad
}
class BackgroundQuery
{
private BackgroundResult _result; // interlocked
private readonly Thread _thread;
public BackgroundQuery()
{
_thread = new Thread(new ThreadStart(BackgroundThread));
_thread.IsBackground = true; // allow the application to shut down without errors even while this thread is still running
_thread.Name = "Background Query Thread";
_thread.Start();
// maybe you want to get the first result here immediately?? Otherwise, the first result may not be available for a bit
}
/// <summary>
/// Gets the latest result. Note that the result could change at any time, so do expect to reference this directly and get the same object back every time--for example, if you write code like: if (LatestResult.IsFoo) { LatestResult.Bar }, the object returned to check IsFoo could be different from the one used to get the Bar property.
/// </summary>
public BackgroundResult LatestResult { get { return _result; } }
private void BackgroundThread()
{
try
{
while (true)
{
try
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("http://example.com/samplepath?query=query");
request.Method = "GET";
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (StreamReader reader = new StreamReader(response.GetResponseStream(), System.Text.Encoding.UTF8))
{
// get what I need here (just the entire contents as a string for this example)
string result = reader.ReadToEnd();
// put it into the results
BackgroundResult backgroundResult = new BackgroundResult { Response = result };
System.Threading.Interlocked.Exchange(ref _result, backgroundResult);
}
}
}
catch (Exception ex)
{
// the request failed--cath here and notify us somehow, but keep looping
System.Diagnostics.Trace.WriteLine("Exception doing background web request:" + ex.ToString());
}
// wait for five minutes before we query again. Note that this is five minutes between the END of one request and the start of another--if you want 5 minutes between the START of each request, this will need to change a little.
System.Threading.Thread.Sleep(5 * 60 * 1000);
}
}
catch (Exception ex)
{
// we need to get notified of this error here somehow by logging it or something...
System.Diagnostics.Trace.WriteLine("Error in BackgroundQuery.BackgroundThread:" + ex.ToString());
}
}
}
private static BackgroundQuery _BackgroundQuerier; // set only during application startup
protected void Application_Start(object sender, EventArgs e)
{
// other initialization here...
_BackgroundQuerier = new BackgroundQuery();
// get the value here (it may or may not be set quite yet at this point)
BackgroundResult result = _BackgroundQuerier.LatestResult;
// other initialization here...
}

HttpClient - Could not create SSL/TLS secure channel

I've been writing some code that pulls data from an external source. Essentially first it gets a list of the events that relate to today, processes it, then gets a list of events that relate to tomorrow. Depending upon how many events there are in a given day the processing can take a number of hours. Here is the problem:
If I run ScrapeToday() and ScrapeTomorrow() immediately after one another without the processing everything is gravy. However, as in the normal program flow, if there is a large gap between the operations I catch the following error;
The request was aborted: Could not create SSL/TLS secure channel.
My first instinct is that this must be due to a bug in HttpClient, likely something expiring due to the long duration between requests. However as a new client is created for each request I wouldn't've thought it possible. I've done some digging around on SO and some other sites but have not been able to reach the root of the problem - any help would be appreciated!
Code below.
public static async Task<Dictionary<string, Calendar.Event>> ScrapeToday()
{
try
{
var client = new HttpClient();
WriteLine("Requesting today's calendar...");
var json = await client.GetStringAsync(calendarTodayUrl);
var results = JsonConvert.DeserializeObject<Dictionary<string, Calendar.Event>>(json);
return results;
}
catch (Exception e)
{
Scraper.Instance.WriteLine("Error retrieving today's calendar: " + e);
return new Dictionary<string, Calendar.Event>();
}
}
public static async Task<Dictionary<string, Calendar.Event>> ScrapeTomorrow()
{
try {
var client = new HttpClient();
WriteLine("Requesting tomorrow's calendar...");
var json = await client.GetStringAsync(calendarTomorrowUrl);
var results = JsonConvert.DeserializeObject<Dictionary<string, Calendar.Event>>(json);
return results;
}
catch (Exception e)
{
Scraper.Instance.WriteLine("Error retrieving tomorrow's calendar: " + e);
return new Dictionary<string, Calendar.Event>();
}
}
Edit: Since posting I have tried making a global httpclient and using that for both requests. That also did not work.
Edit2: The bug is reproduceable with an elapsed time of 30 minutes between the calls.
Edit3: If the call is retried after the failure it always works.

Categories

Resources