c# - parallel.foreach and httpclient - strange behaviour -
i have piece of code loops on collection , calls httpclient each iteration. api httpclient calls, takes on average 30-40ms execute. calling sequentially, expected outcome, use parallel.foreach, takes longer. looking closely in logs, can see quite few httpclient calls take more 1000ms execute , time drops 30-40ms. looking in api logs, can see barely goes on 100ms. not sure why spike.
the code
using (var client = new httpclient()) { var content = new stringcontent(parameters, encoding.utf8, "application/json"); var response = client.postasync(url, content); _log.info(string.format("took {0} ms send post", watch.elapsedmilliseconds)); watch.restart(); var responsestring = response.result.content.readasstringasync(); _log.info(string.format("took {0} ms readstring after post", watch.elapsedmilliseconds)); }
the parallel call this
console.writeline("starting parallel..."); parallel.foreach(recipientcollections, recipientcollection => { // lot of processing happens here create relevant content var secondarycountryrecipientlist = string.join(",",refinedcountryrecipients); var emailapiparams = new sendemailparametersmodel(countrysubscriberapplicationid, queueitem.sitecoreid, queueitem.version, queueitem.language, countryfeeditem.subject, countryfeeditem.html, countryfeeditem.from, _recipientsformatter.format(secondarycountryrecipientlist)); log.info(string.format("sending email request {0}. recipients {1}", queueitem.sitecoreid, secondarycountryrecipientlist)); var response = _notificationsapi.invoke(emailapiparams); });
thanks
by default .net allows 2 connections per server. change have change value of servicepointmanager.defaultconnectionlimit larger value, eg 20 or 100.
this won't prevent flooding server or consuming memory if make many requests though. better option use actionblock< t> buffer requests , send them in parallel in controlled function, eg:
servicepointmanager.defaultconnectionlimit =20; var client = new httpclient(); var blockoptions=new executiondataflowblockoptions{maxdegreeofparallelism=10}; var emailblock=new actionblock<sendemailparametersmodel>(async arameters=> { var watch=new stopwatch(); var content = new stringcontent(parameters, encoding.utf8, "application/json"); var response = await client.postasync(url, content); _log.info(..); watch.restart(); var responsestring = await response.result.content.readasstringasync(); _log.info(...); });
sending emails doesn't require parallel invocation more:
foreach(var recipientcollection in recipientcollections) { var secondarycountryrecipientlist = string.join(",",refinedcountryrecipients); var emailapiparams = new sendemailparametersmodel(countrysubscriberapplicationid, queueitem.sitecoreid, queueitem.version, queueitem.language, countryfeeditem.subject,countryfeeditem.html, countryfeeditem.from, _recipientsformatter.format(secondarycountryrecipientlist)); emailblock.post(emailapiparams); log.info(...); } emailblock.complete(); await emailblock.completion();
httpclient
thread-safe allows use same client requests.
the code above buffer requests , execute them 10 @ time. calling complete()
tells block complete , stop processing new messages. await emailblock.completion()
waits existing messages finish before proceeding
Comments
Post a Comment