1

I am trying to iterate over a list of 20,000+ customer records. I am using a Parallel.ForEach() loop to attempt to speed up the processing. Inside the delegate function, I am making an HTTP POST to an external web service to verify the customer information. In doing so, the loop is limited to 2 threads, or logical cores. If I attempt to increase the Degree of Parallelism, the process throws an error "The underlying connection was closed: A connection that was expected to be kept alive was closed by the server"

Is this default behavior of the loop when working with external processes or a limitation of the receiving web server?

My code is rather straight forward:

Parallel.ForEach ( customerlist, new ParallelOptions {MaxDegreeOfParallelism = 3 },( currentCustomer ) =>
{
    if ( IsNotACustomer ( currentCustomer.TIN ) == true ) <--IsNotCustomer is where the HTTP POST takes place
    {
        ...Write data to flat file...
    }
});

If I change the MaxDegreesOfParallelism to 2 the loop runs fine.

This code takes about 80 minutes to churn through 20,000 records. While that is not unacceptable, if I could shorten that time by increasing the number of threads, then all the better.

Full exception message (without stack trace):

System.Net.WebException: The underlying connection was closed: A connection that was expected to be kept alive was closed by the server.

at System.Net.HttpWebRequest.GetResponse()

Any assistance would be greatly appreciated.

EDIT

The HTTP POST code is:

HttpWebRequest request = ( HttpWebRequest )WebRequest.Create ( AppConfig.ESLBridgeURL + action );
request.Method = "POST";
request.GetRequestStream ( ).Write ( Encoding.UTF8.GetBytes ( body ), 0, body.Length );

Stream stream = request.GetResponse ( ).GetResponseStream ( );
StreamReader reader = new StreamReader ( stream );

output = reader.ReadToEnd ( );

The URL is to an in-house server running proprietary Web Sphere MQ services. The gist of which is to check internal data sources to see whether or not we have a relationship with the customer.

We run this same process in our customer relationship management process in hundreds of sites per day. So I do not believe there is any licensing issue and I am certain these MQ services can accept multiple calls per client.

EDIT 2

A little more research has shown the 2 connection limit is valid. However, using a ServicePointManager may be able to bypass this limitation. What I cannot find is a C# example of using the ServicePointManager with HttpWebRequests.

Can anyone point me to a valid resource or provide a code example?

Paul Stoner
  • 1,359
  • 21
  • 44
  • 3
    Most likely a limitation of the receiving web server, or possibly licensing restrictions, depending on the actual web service you are calling. Also could be non-thread-safe code in IsNotACustomer, so you might want to post the code for that if you verify that the web server allows more than 2 simultaneous connections from the same source machine. – Matt Jordan Apr 08 '16 at 16:26
  • @MattJordan please see my edits, and thank you – Paul Stoner Apr 08 '16 at 16:43
  • What are the MAXINST and MAXINSTC configurations on the WebSphere MQ server? MAXINST is almost certainly high, since you hit it from hundreds of different sites, but the per-client MAXINSTC could still be a problem. https://www.ibm.com/support/knowledgecenter/SSFKSJ_7.5.0/com.ibm.mq.con.doc/q015640_.htm – Matt Jordan Apr 08 '16 at 18:39

2 Answers2

1

You might be running up against the default 2 client limit. See System.Net.ServicePointManager.DefaultConnectionLimit on MSDN.

The maximum number of concurrent connections allowed by a ServicePoint object. The default value is 2.

Possibly relevant question: How Can I programmatically remove the 2 connection limit in WebClient?

Community
  • 1
  • 1
Matt Stephenson
  • 8,442
  • 1
  • 19
  • 19
0

Thank you Matt Stephenson and Matt Jordan for pointing me in the correct direction.

I found a solution that has cut my processing in half. I will continue to tweak to get the best results, but here is what I arrived at.

I added the following to the application config file:

<system.net>
    <connectionManagement>
        <add address="*" maxconnection="100"/>
    </connectionManagement>
</system.net>

I then figured out how to use the ServicePointManager and set the following:

int dop = Environment.ProcessorCount;
ServicePointManager.MaxServicePoints = 4;
ServicePointManager.MaxServicePointIdleTime = 10000;
ServicePointManager.UseNagleAlgorithm = true;
ServicePointManager.Expect100Continue = false;
ServicePointManager.DefaultConnectionLimit = dop * 10;

ServicePoint sp = ServicePointManager.FindServicePoint ( new Uri ( AppConfig.ESLBridgeURL ) );

For my development machine, the Processor Count is 8.

This code, as is, allows me to process my 20,000+ records in roughly 45 minutes (give or take).

Paul Stoner
  • 1,359
  • 21
  • 44