2016-05-13 89 views
1

我的团队维护一个工具,负责对1000多个不同的客户端网站进行快速验证。该工具是一个Windows服务(.NET 4.5.2,C#),它从队列读取请求,并为每个请求执行“健康检查”。它通常每分钟处理500多个请求,但可以负责更多。每个请求需要一两秒才能执行。使用HttpClient C对许多不同网站的快速Web请求#

请求包含执行健康检查所需的Uri和凭据。健康检查是针对具有凭证的AUTH页面的POST(应用程序具有自定义身份验证,它不是基于标头的身份验证),然后是主页上的GET,并快速验证它是我们期望的主页。然后进入应用程序中的状态页面,并对其进行一些快速检查。 GET请求必须使用来自验证帖子中Set-Cookie标头的cookie。

我们一直在使用该工具进行缩放时遇到性能问题。它目前会为每个帖子创建一个新的HttpWebRequest对象并进入该过程。有一个共享CookieContainer由第一篇文章填充,以便我们可以进入主页,然后进入状态页面。

我想要更改此服务,以使用.NET 4.5中提供的HttpClient对象。这个问题无处不在,我在网上阅读说,你想避免HttpClients快速创建和销毁。您宁愿让一个实例在应用程序的整个生命周期中保持活跃状态​​。我遇到的问题是HttpClient似乎对一个终端非常有效,并不是很多。

我已经看过成几个选项,我不知道这是最好进行:

  1. 为每个请求创建一个新的HttpClient,并将其用于该请求的持续时间。这意味着它会活几秒钟,并用于3个电话。这不易实现,但我担心一分钟内创建并销毁数百个HttpClients的开销。
  2. 找出是否有可能通过避免使用BaseAddres s,并使用SendAsync来通过HttpRequestMessages来为不同端点使用一个HttpClient实例。 我还没有能够找出用这种方法的饼干。为了避免在 HttpClient店的饼干,我设置 UseCookies为false在 HttpClientHandler,并试图通过头在 HttpRequest/ ResponseMessages管理Cookie本身,但它看起来当 UseCookies设置为 falseHttpClient简单地剥离饼干,所以我无法在请求之间传递Cookie。 编辑:cookies工作正常,因为它们存储在每个域。
  3. 将几百个不同的HttpClient实例存储在某种字典中,并在请求进入时为每个Uri拉出相应的实例。但我不确定这种内存开销。另外每个独特的Uri仅每5分钟验证一次,所以我不确定是否每5分钟使用一次HttpClient就会使不必要数量的端口打开。
  4. 继续使用HttpWebRequests。也许这种旧方法在这种情况下仍然表现更好。

如果有人遇到过类似的问题,我很乐意就此进行处理。

谢谢!

+0

为什么你不想存储cookie?从原来的描述来看,它几乎听起来像是你想重用它们,以便我们可以进入主页,然后进入状态页面。 – Colin

+0

@Colin - 我们将同时对几十个网站运行并行请求。这些网站中的每一个都是同一个应用程序的不同实例,因此所有的cookie名称都是相同的。我不确定HttpClient的一个实例如何处理它。如果我对客户端A进行认证,然后客户端B转到获取状态页面,它会使用刚才在共享CookieContainer中设置的客户端A的Cookie吗?那会造成一些怪异。 – aalkema

+0

你可以使用一个HttpClient每个网站? – Colin

回答

0

为每个请求创建新的HttpClient的问题是,HttpClientHandler将关闭底层的TCP/IP连接。但是,如果您将每个HttpClient用于一个主机的3个请求,然后点击另一个主机,那么当您移动到新的主机时,保持连接打开无效。所以,你可能不会看到每个主机有一个客户端的性能问题。 HttpClient本身是一个非常轻量级的对象。创建一个并不会花费太多。

但是,HttpClient只是将实际工作委托给使用HttpWebRequest的HttpClientHandler,因此不会比直接使用HttpWebRequest有更好的性能。

如果您正在寻找更好的性能,那么我建议您考虑用新的WinHttpHandler代替HttpClientHandler,它绕过HttpWebRequest并直接进入Win32 API进行调用。

完整的源代码可用于WinHttpHandler on GitHub,因此您可以准确了解它如何处理Cookie和凭证。

我真的很感兴趣,如果你确实用WinHttpHandler获得更好的性能。

+0

感谢评论这么快!您的评论排除了选项编号2.选项#3会有什么优势吗?每个独特的Uri只有每5分钟运行一次,因此大多数时间每个Uri特定的HttpClient实例都会坐在那里无所事事。我们唯一的优势就是避免每个Uri每5分钟轮换一个新的HttpClient。 我也会看看这个WinHttpHandler,看起来像我们可以利用的东西。 – aalkema

+0

@aalkema HttpClient本身并不实际管理cookie。它将该作业委托给HttpWebRequest使用的CookieContainer。避免使用BaseAddress并使用SendAsync代替应该没有问题。 GetAsync只是在封面下隧道SendAsync。 CookieContainer实例由HttpClientHandler持有,因此处置HttpClient将销毁cookie容器 –

+0

@aalkema我不确定为什么当您使用SendAsync时您有Cookie问题。这应该工作得很好。 Cookie与域相关联,因此不应将Cookie从一个域重叠到另一个域。 –

0

首先,您需要修改哪部分以满足您的需求?

var urisToCheck = new List<Uri>(); //get these somehow 

//basic auth work? 
var credentials = new NetworkCredential("user", "pass"); 
var handler = new HttpClientHandler { Credentials = credentials }; 

var client = new HttpClient(handler); 

Parallel.ForEach(urisToCheck, 
    async uri => 
    { 
     var response = await client.GetAsync(uri.AbsoluteUri); 
     //check for whatever you want here 
    } 
); 
+0

感谢您的快速回复! URI列表实际上应该是“请求”列表,并且每个请求都有一个Uri,用户和通行证。这里的问题是更多的工作应该在并行Foreach中 - 首先你必须通过针对该站点的唯一用户/传递组合对Uri进行PostAsync,然后针对状态页执行GetAsync。 所以我遇到的问题是HttpClient如何处理具有相同名称的Cookie,但对于不同的Uris具有不同的值?我想它只有一个CookieCredential对象,所以这可能不起作用。 – aalkema

+0

@aalkema - 一组请求是否重用凭证,还是每个请求都有自己的凭证?我知道你提到有几台不同的服务器,但是每台服务器至少有一套凭证? – Colin

+0

每个请求都有自己的凭证。 Uri是一个Web应用程序 - 它通过负载均衡器使用这些信誉每5分钟为每个独特的Uri登录。 – aalkema