背景

在一个页面中需要获取几家保险公司的报价数据,请求内容除了保险公司的标识外基本相似。有10家保险公司,就需要10个请求,这个10个请求在 Controller 对应一个 Action。我期待的结果,任何一个请求返回数据,就可以立即进入该结果详情页面详情内容在这次请求中已经获取)。事实上,我点击以后,需要等待好久才可以进入详情页面。

页面是这样的: 页面

Ajax 代码大体是这样的:

for (var i = 0; i < 10; i++) {
    getQuote(i); //ajax请求
}

详情页面除了做微信 SDK 注册也没有其他复杂的逻辑。如果单独请求,速度还可以。但我从某一个 Ajax 返回结果后,立即点击进入,慢的很不正常。

跟踪

面对这种情况,首先想到的是详情页面响应问题。先让详情页面变的足够简单。仍然解决不了问题。尝试在跳转处加上记录。

console.log("进入:"+new Date().getTime());
location.href='@Url.Action("PriceDetails")'+'?source='+_sid;
console.log("结束:"+new Date().getTime());

结束标志很快出现,确定还是详情页面响应慢导致,但没有想到慢的原因。跟踪网络,结果发现比较夸张的一幕。

TTFB

太夸张了,不过有一部分是本地网络问题。

通过监测请求得知,最慢的环节 Waiting(TTFB)。 TTFB

TTFB (Time To First Byte),是最初的网络请求被发起到从服务器接收到第一个字节这段时间,它包含了 TCP 连接时间,发送HTTP 请求时间和获得响应消息第一个字节的时间。

对 TTFB 优化方式通常是:

  • 减少DNS查询
  • 减少DNS查询
  • 提早Flush
  • 添加周期头

这种优化方式对我这个页面而言不需要。但可以肯定还是服务端响应比较慢造成。Google TTFB 时间过长原因,看到这篇文章Diagnosing Slow Web Servers with Time to First Byte。其中列举了造成慢的一些因素。我比较关注这两个 Too many processes / connectionsExternal resource delays

然后 Google Ajax ASP.NET Block 。看到了这篇文章ASP.NET Concurrent Ajax Requests and Session State Blocking。作者遇到和我类似问题,他的解决方案,我拿来即用。

原理

ASP.NET Session 在所有 ASP.NET 应用域开启。 ASP.NET 的每个请求保持一个会话状态。这意味着如果两个不同的用户进行并发请求,每个请求占用一个会话信息。如果两个并发请求通过使用相同的 SessionID 值请求,第一个请求获取会话信息的独占访问。第二个请求需要等到第一个请求完成后才能执行。

怎么解决呢?

通过设置 EnableSessionState 状态只读,只读的会话信息不对导致对其他会话数据产生排他锁。在 ASP.NET MVC这样设置

[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
public class HomeController
{
    //Action
}

ASP.NET WebForm 需要在 @Page 指令下设置。

<% @Page EnableSessionState="ReadOnly" %>

总结

并行请求在客户端利用 Ajax 发起看似很简单,但是如果服务端没有处理好,仍然达不到好的效果。在分布式的架构中有更多方式,对于我遇到的问题,简单一句就解决困扰我几天的问题。对于并行请求,还是一个大课题,值得好好研究。