原文地址:http://blog.csdn.net/mba16c35/article/details/75734176
一、问题
这周遇到一个用户反馈,说邮箱的记事本列表一直空白,无法加载任何记事。于是查了日志,发现用户由于记事太多一直网络超时。
long sTime = SystemClock.elapsedRealtime();
responseCode = connection.getResponseCode();// 阻塞型
long elapse = SystemClock.elapsedRealtime() - sTime;
/**
* http
* 数据超时 50000ms
*/
public static final int READ_TIME_OUT = 15000;
1.直接原因:超时太短
最简单的做法当然是直接把超时改长,但这真的是本质原因吗?修改超时值是否会引起其他的影响?
2.需要思考些什么
(1)问题发生的本质原因,细化问题产生的过程
要分析问题,首先要拆分问题产生的每个阶段。
看一下http发起请求到收到数据的过程:
//1.打开connection
step = STEP_OPEN;
startTime = SystemClock.elapsedRealtime();
connection = openConnection(request, url);
//2.设置connection的property
setProperty(connection, request, bodyData);
setUserHeads(connection, request);
setUserCookies(connection, request);
...
//3.开始connect
step = STEP_CONNECT;
startTime = SystemClock.elapsedRealtime();
QMLog.log(Log.DEBUG, TAG, "start to establish http connection, url: " + url + ", requestProperties: " + connection.getRequestProperties());
connection.connect();
QMHttpReportManager.connect(SystemClock.elapsedRealtime() - startTime, url, true, null);
//4.上传数据
step = STEP_POST_DATA;
startTime = SystemClock.elapsedRealtime();
...
QMHttpReportManager.post(request.getHttpMethod() == QMHttpMethod.QMHttpMethod_MULTIPART, SystemClock.elapsedRealtime() - startTime, url, true, null);
request.setStatus(QMNetworkRequest.STATUS_SEND_DATA_END);
//5.开始回包
step = STEP_RESPOND;
startTime = SystemClock.elapsedRealtime();
long sTime = SystemClock.elapsedRealtime();
responseCode = connection.getResponseCode();// 阻塞型
long elapse = SystemClock.elapsedRealtime() - sTime;
boolean responseCodeOK = responseCode >= 200 && responseCode < 300;
QMHttpReportManager.response(elapse, url, responseCodeOK, responseCode, null);
//6.接收数据
step = STEP_RECEIVE_DATA;
startTime = SystemClock.elapsedRealtime();
if (responseCodeOK) {
response = handleSendSuccess(request, connection);
QMHttpReportManager.read(SystemClock.elapsedRealtime() - startTime, url, true, null);
step = STEP_HANDLE_DATA;
error = response.getError();
request.responseSuccess(response);
if (subscriber != null) subscriber.onNext(response);
if (subscriber != null) subscriber.onCompleted();
} else {
error = handleSendError(request, connection);
QMHttpReportManager.read(SystemClock.elapsedRealtime() - startTime, url, true, null);
step = STEP_HANDLE_DATA;
response = getResponse(request, connection);
response.setResponseCode(responseCode);
request.responseError(response, error);
if (subscriber != null) subscriber.onError(error);
}
//7.完成请求
step = STEP_COMPLETE;
而这期间的耗时又由4个部分组成:
a.app的数据到达cgi所耗费的网络耗时。
b.cgi调用后台服务和处理数据的耗时。(cgi相当于后台服务器的代理)
c.cgi写回包的io耗时。
d.cgi的数据到达app端的网络耗时。
分析
由于app的请求很小,于是耗时a是比较小的;
cgi的io读写能力应该是比较强的,于是耗时c也是比较小的;
然后和cgi的同事联调发现,cgi调用后台服务花费了21s,看来瓶颈在b这部分。
既然知道原因,讨论之后我们有3个可选的技术方案:
a.实现分段加载。app给服务器一个时间戳和limit,服务器可以返回时间戳往前最多limit封记事。当返回的数量少于limit时,才是完全加载完。这样每次网络请求的大小可控,超时也就可控。这个方案在性能和用户体验上都是最好的,但是实现的代价比较大。
b.服务器限制只下发前5000封记事,更旧的记事用户的就看不到了。这个方案实现代价很小。
c.app端增加读超时的限制。
(2)衡量方案是否具有可扩展性
c方案是最不具备可扩展性的。当用户10000封记事时,超时需要设成50s,那当有十万封,二十万封时,难道超时数值一直往上调吗?用户等待太久,用户体验也不好。所以c方案不可行。
(3)衡量性价比
实际上记事数量这么多的用户并不多(当然只是一个猜想,可以加上监控证实),所以其实性价比最高的是b方案。当然后面如果有用户投诉的话,还是得使用a方案。