使用线程池扩展 Web 客户端
我必须使用 webclient 创建对同一 URL 的多个 Web 请求。我使用线程池 QueueUserWorkItem 生成线程。下面的代码显示了我如何生成线程并使用 WebClient 进行发布。我必须派生 Web 客户端类来设置 Expect100Continue 和 Timeout 属性。
当我在没有 Thread.Sleep(生成线程代码中的注释行)的情况下运行此代码时,我得到的响应时间超过 1500 毫秒。请检查PostToURL方法中计算响应时间的逻辑。但是如果我在生成另一个线程之前休眠 1 秒,我得到的响应时间将少于 300 毫秒。我找不到如此不同的原因。实际上我需要将响应时间提高到 300 毫秒。这是我为测试服务而编写的测试工具代码。您能指出为什么响应时间不同吗?
这是生成线程的代码
foreach (var nameValueCollection in requestCollections)
{
NameValueCollection collection = nameValueCollection;
ThreadPool.QueueUserWorkItem(a => { if (collection != null) PostToURL
(collection); });
//Thread.Sleep(TimeSpan.FromSeconds(1));
}
这是发布到 URL
public void PostToURL(NameValueCollection collection)
{
DateTime requestStartDate = DateTime.Now;
DateTime requestEndDate = DateTime.Now;
string encodedResponse;
try
{
using (var transportType2 = new DerivedWebClient())
{
transportType2.Expect100Continue = false;
transportType2.Timeout = TimeSpan.FromMilliseconds(2000);
ServicePointManager.ServerCertificateValidationCallback =
new RemoteCertificateValidationCallback(delegate { return true; });
requestStartDate = DateTime.Now;
var responseArray = transportType2.UploadValues("http://xyz/Post.ashx?AccountName=myaccount",
"POST", collection);
requestEndDate = DateTime.Now;
Console.WriteLine(requestEndDate.Subtract(requestStartDate).TotalMilliseconds);
encodedResponse = Encoding.ASCII.GetString(responseArray);
transportType2.Dispose();
}
}
catch (Exception exception)
{
requestEndDate = DateTime.Now;
encodedResponse = exception.ToString();
}
//Global variable to record request and response
responses.Add(new ServiceResponse
{
ResponseInMs = (int) requestEndDate.Subtract(requestStartDate).TotalMilliseconds,
StartTime = requestStartDate,
EndTime = requestEndDate,
Response = encodedResponse,
Request = string.Join(";", collection.Cast<string>()
.Select(col => String.Concat(col, "=", collection[col])).ToArray()),
ApplicationId = collection["ApplicationId"]
});
}
UPDATED 的方法 这是派生webclient的代码
public class DerivedWebClient: WebClient
{
public bool Expect100Continue
{
get;
set;
}
public TimeSpan Timeout
{
get;
set;
}
protected override WebRequest GetWebRequest(Uri address)
{
var request = (HttpWebRequest)base.GetWebRequest(address);
if (request != null)
{
request.ServicePoint.Expect100Continue = Expect100Continue;
request.KeepAlive = false;
request.Timeout = (int) Timeout.TotalMilliseconds;
}
return request;
}
}
I have to create mutiple web request to same URL using webclient. I spawn thread using thread pool QueueUserWorkItem. Code below show how I am spawning thread and posting using WebClient. I had to derive web client class to set Expect100Continue and Timeout property.
When I run this code without Thread.Sleep (commented line in spawn thread code), I am getting response time over 1500 millisecond. Please check the logic to calculate response time in PostToURL method. But If I put 1 seconds sleep before spawning another thread, I am getting response time less than 300 milliseconds. I could not find reason why is that so different. Actually I need to bring this response time to 300 millisecond. This is test harness code that I am writing to test a service. Can you point why is different response time?
Here is the code that spawns the thread
foreach (var nameValueCollection in requestCollections)
{
NameValueCollection collection = nameValueCollection;
ThreadPool.QueueUserWorkItem(a => { if (collection != null) PostToURL
(collection); });
//Thread.Sleep(TimeSpan.FromSeconds(1));
}
Here is method that post to a URL
public void PostToURL(NameValueCollection collection)
{
DateTime requestStartDate = DateTime.Now;
DateTime requestEndDate = DateTime.Now;
string encodedResponse;
try
{
using (var transportType2 = new DerivedWebClient())
{
transportType2.Expect100Continue = false;
transportType2.Timeout = TimeSpan.FromMilliseconds(2000);
ServicePointManager.ServerCertificateValidationCallback =
new RemoteCertificateValidationCallback(delegate { return true; });
requestStartDate = DateTime.Now;
var responseArray = transportType2.UploadValues("http://xyz/Post.ashx?AccountName=myaccount",
"POST", collection);
requestEndDate = DateTime.Now;
Console.WriteLine(requestEndDate.Subtract(requestStartDate).TotalMilliseconds);
encodedResponse = Encoding.ASCII.GetString(responseArray);
transportType2.Dispose();
}
}
catch (Exception exception)
{
requestEndDate = DateTime.Now;
encodedResponse = exception.ToString();
}
//Global variable to record request and response
responses.Add(new ServiceResponse
{
ResponseInMs = (int) requestEndDate.Subtract(requestStartDate).TotalMilliseconds,
StartTime = requestStartDate,
EndTime = requestEndDate,
Response = encodedResponse,
Request = string.Join(";", collection.Cast<string>()
.Select(col => String.Concat(col, "=", collection[col])).ToArray()),
ApplicationId = collection["ApplicationId"]
});
}
UPDATED
Here is code that derive webclient
public class DerivedWebClient: WebClient
{
public bool Expect100Continue
{
get;
set;
}
public TimeSpan Timeout
{
get;
set;
}
protected override WebRequest GetWebRequest(Uri address)
{
var request = (HttpWebRequest)base.GetWebRequest(address);
if (request != null)
{
request.ServicePoint.Expect100Continue = Expect100Continue;
request.KeepAlive = false;
request.Timeout = (int) Timeout.TotalMilliseconds;
}
return request;
}
}
默认情况下,.NET 的 HTTP 客户端会限制一定数量的并发连接。我怀疑您遇到了这个限制,因此每个线程在发出新请求之前都在等待空闲连接。
默认限制将根据您是否在 ASP.net 内运行客户端应用程序等而有所不同,但您可以更改它。使用
ServicePointManager.DefaultConnectionLimit
在全球范围内提高此限制。.NET's HTTP client, by default, is throttled to a certain number of concurrent connections. I suspect you are running into this limit, so each thread is waiting for a free connection before making a new request.
The default limit is will vary depending on whether you're running a client app, inside ASP.net, etc. but you can change it. Use
ServicePointManager.DefaultConnectionLimit
to raise this limit globally.