本文为原创,转载请注明:http://www.cnblogs.com/gistao/
背景
分享一个hhvm使用http server方式来处理请求的问题及对应的patch。hhvm3+版本支持fastcgi模式,而之前的版本都只能用http serve模式来响应请求,由于hhvm的http server支持的功能比较弱,现在大部分使用场景都选择fastcgi了,而这次要说的问题仅存在于使用了http server模式的所有版本hhvm。
HHVM问题
我们发现线上的hhvm会有丢失http header的极小概率问题,在单台日请求量近千万情况下只有2个左右的发生概率,并且将出问题的url在线下复现,也没有复现,瞬间没有一点点线索了。没有办法,用笨办法:看代码+排除法,没有办法的办法。但事实证明往往笨办法也是最终的办法。
问题相关
简单的说,我们的服务架构是nginx作为反向代理服务器,来请求hhvm。Facebook的工程师基于libevent1.4.14b,增加了设置backlog功能和修复了一些内存泄漏问题,http的header的解析工作还是在libevent里独立完成的,截取http.c部分代码如下
enum message_read_status evhttp_parse_headers(struct evhttp_request *req, struct evbuffer* buffer) { char *line; enum message_read_status status = MORE_DATA_EXPECTED; struct evkeyvalq* headers = req->input_headers; while ((line = evbuffer_readline(buffer)) != NULL) { char *skey, *svalue; if (*line == ' ') { /* Last header - Done */ status = ALL_DATA_READ; free(line); break; } /* Check if this is a continuation line */ if (*line == ' ' || *line == ' ') { if (evhttp_append_to_last_header(headers, line) == -1) goto error; free(line); continue; } /* Processing of header lines */ svalue = line; skey = strsep(&svalue, ":"); if (svalue == NULL) goto error; svalue += strspn(svalue, " "); if (evhttp_add_header(headers, skey, svalue) == -1) goto error; free(line); } return (status); error: free(line); return (DATA_CORRUPTED); }
能看出问题吗,由于定位问题过程比较曲折,这里不罗嗦了。
Libevent问题
bug出现在buffer.c里的readline函数,如下
char * evbuffer_readline(struct evbuffer *buffer) { u_char *data = EVBUFFER_DATA(buffer); size_t len = EVBUFFER_LENGTH(buffer); char *line; unsigned int i; for (i = 0; i < len; i++) { if (data[i] == ' ' || data[i] == ' ') break; } if (i == len) return (NULL); if ((line = malloc(i + 1)) == NULL) { fprintf(stderr, "%s: out of memory ", __func__); return (NULL); } memcpy(line, data, i); line[i] = '