本文为原创,转载请注明: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 == '\0') { /* Last header - Done */
status = ALL_DATA_READ;
free(line);
break;
} /* Check if this is a continuation line */
if (*line == ' ' || *line == '\t') {
if (evhttp_append_to_last_header(headers, line) == -)
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) == -)
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 = ; i < len; i++) {
if (data[i] == '\r' || data[i] == '\n')
break;
} if (i == len)
return (NULL); if ((line = malloc(i + )) == NULL) {
fprintf(stderr, "%s: out of memory\n", __func__);
return (NULL);
} memcpy(line, data, i);
line[i] = '\0'; /*
* Some protocols terminate a line with '\r\n', so check for
* that, too.
*/
if ( i < len - ) {
char fch = data[i], sch = data[i+]; /* Drain one more character if needed */
if ( (sch == '\r' || sch == '\n') && sch != fch )
i += ;
} evbuffer_drain(buffer, i + ); return (line);
}

此函数的功能就是截取一行出来,然后循环解析完所有header。rfc2616规定行分隔符是\r\n,而libevent认定的换行符(eof)却是非常的宽松,比如\r就行。如果收包时恰好将\r和\n分离在两个包里,那么后边这个包解析时第一个字节就是\n,libevent解析时会认为这是一行(其实还是之前的行),而这行并没有内容,即内容是\0,这会造成evhttp_parse_headers函数直接认为header已经全部解析完毕了,也就是说丢失了header

多说一句libevent的2x版本,相比之前的版本代码变化很大,单就解析header来说,已经提供了三种级别的换行符认定标准,不过默认还是最松散级别。

Libevent patch

由于我们这里的环境都是可控的,不会存在那些乱七八糟换行符,所以就按照强约束\r\n(LRCF)来解析,针对libevent1.4.14b的patch如下

--- ./buffer.c  -- ::04.000000000 +
+++ ./buffer.c -- ::25.783883798 +
@@ -, +, @@
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 = ; i < len; i++) {
- if (data[i] == '\r' || data[i] == '\n')
- break;
- }
-
- if (i == len)
- return (NULL);
-
- if ((line = malloc(i + )) == NULL) {
- fprintf(stderr, "%s: out of memory\n", __func__);
- return (NULL);
- }
+ 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] == '\r') {
+ if (i + 1 < len && data[i+1] == '\n') {
+ break;
+ }
+ }
+ }
+
+ if (i == len)
+ return (NULL);
+
+ if ((line = malloc(i + )) == NULL) {
+ fprintf(stderr, "%s: out of memory\n", __func__);
+ return (NULL);
+ } - memcpy(line, data, i);
- line[i] = '\0';
+ memcpy(line, data, i);
+ line[i] = '\0'; - /*
- * Some protocols terminate a line with '\r\n', so check for
- * that, too.
- */
- if ( i < len - 1 ) {
- char fch = data[i], sch = data[i+1];
+ evbuffer_drain(buffer, i + 2); - /* Drain one more character if needed */
- if ( (sch == '\r' || sch == '\n') && sch != fch )
- i += 1;
- }
-
- evbuffer_drain(buffer, i + 1);
-
- return (line);
+ return (line);
} -
char *
evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out,
enum evbuffer_eol_style eol_style)

libevent之丢失header问题的更多相关文章

  1. Feign 调用丢失Header的解决方案

    问题 在 Spring Cloud 中 微服务之间的调用会用到Feign,但是在默认情况下,Feign 调用远程服务存在Header请求头丢失问题. 解决方案 首先需要写一个 Feign请求拦截器,通 ...

  2. ASM丢失disk header导致ORA-15032、ORA-15040、ORA-15042 Diskgroup无法mount

    SQL> select * from v$version; BANNER --------------------------– Oracle Database 11g Enterprise E ...

  3. (转)nginx做转发时,带'_'的header内容丢失

    原本在测试环境测试通过的APP,今天准备切到线上环境做最后测试,结果发现了错误.查看日志发现是APP端发送的http请求中的header内容丢失了.那么代码没有改动,怎么平白无故会丢失头信息? 于是想 ...

  4. 解决SpringCloud使用Feign跨服调用时header请求头中的信息丢失

    在使用SpringCloud进行Feign跨服调用时header请求头中的信息会丢失,是因为Feign是不会带上当前请求的Cookie信息和头信息的,这个时候就需要重写请求拦截. 1.需要重写Requ ...

  5. WKWebView 网络请求Header 丢失

    WKWebView 是苹果手机上主要的H5加载控件,它相比UIWebView 有诸多优势.在次不做比较,但是它的坑缺比较多.网上也有很多的例子但是做的比较好的真不多,我在这里推荐俩博客供大家参考.ht ...

  6. spring boot 并发请求,其他系统接口,丢失request的header信息【多线程、线程池、@Async 】

    场景:一次迭代在灰度环境发版时,测试反馈说我开发的那个功能,查询接口有部分字段数据是空的,后续排查日志,发现日志如下: feign.RetryableException: cannot retry d ...

  7. 关于mui header在手机上运行丢失问题

    并不需要换header, 只需要把引用的例子自带的CSS文件 app.css.里的两个样式:.mui-plus.mui-android header.mui-bar {display: none;}. ...

  8. 微信公众号支付JSAPI网页,total_fee错误不正确,header重定向参数丢失,无法获取订单号和金额解决

    微信公众号支付官方demo错误, 公众号支付只能用在微信里,也就是微信内部浏览器. 1.到WxPayHubHelper.php文件 JsApi_pub()类下createOauthUrlForCode ...

  9. nginx反向代理导致请求header头信息丢失

    背景:前端与后端调试接口,后端拿不到前段发过去的请求头信息,导致接口不通.(但是在本地是可以拿到的) 原因:nginx做了反向代理,没有请求时候加头信息的配置 报错如下: 解决方法: 方法一:NGIN ...

随机推荐

  1. js生成[n,m]的随机数 以及实际运用

    Math.ceil();  //向上取整. Math.floor();  //向下取整. Math.round();  //四舍五入. Math.random();  //0.0 ~ 1.0 之间的一 ...

  2. MicroERP软件更新记录1.0

    版本号:1.0.256 本次: 1\修复了选择货位时的BUG; 2\增加了物品资料由EXCEL表批量导入的功能; 3\物品资料增加了三个自定义属性; 4\优化了科目汇总账(余额表)算法; 5\应大家建 ...

  3. 使用WebDriverWait类处理等待(sleep)的问题

    用selenium进行web UI的自动化开发时,经常遇到loading需要等待的时候,或者需要验证一个action之后某个dialog是否呈现或者消失.对于这类情况是不建议用sleep(xx)来死等 ...

  4. Qt之C语言有符号数与无符号数运算

    以32位的stm32f4为例: 1.  uint32_t t_int_k = 239773, t_int_km1 = 4294859707; 则t_int_k - t_int_km1 > 0; ...

  5. Ubuntu下用wireshark抓取802.11封包并进行过滤分析

    要用wireshark抓802.11的包 需要在linux下进行. 要在linux下抓802.11的包 需要在linux下安装无线网卡驱动. 所以 在正式抓取之前先把这两样东西搞起来. *没有特殊说明 ...

  6. Maven 导出依赖Jar,生成source.jar,javadoc.jar

    下载最新版的Maven http://maven.apache.org/download.cgi    解压到本地文件夹 新建环境变量    MAVEN_HOME   maven解压目录 在path加 ...

  7. what is SVD and how to calculate it

    http://web.mit.edu/be.400/www/SVD/Singular_Value_Decomposition.htm SVD是研究地震波运动极性化的一个方法.

  8. html canvas 弹球(模仿)

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  9. WPF环境下多点触屏开发的一些经验(转)

    本系列将介绍Multi-Touch(MT)多点触控技术的相关内容,使开发人员了解如何在Windows 平台中开发出具有MT 功能的应用程序.众所周知Windows 7 操作系统自身已经支持具有MT 功 ...

  10. 用JAVA写查询一个字符串中是否包含另外一个字符串以及出现的次数

    package JAVA; import java.awt.List;import java.util.ArrayList;/** *  * @author 梁小鱼 * */public class ...