用户带宽突飞猛进,视频网站铺天盖地,我们公司也耐不住寂寞,加入视频点播的行列大军中。技术很简单,flash播放器+http半下载+mp4视频源。为了支持start和end标签,我们用了一个三方nginx模块: 。一直运行很正常。
突然昨天收到cdn报障,说有一个视频文件下载过程中总是不能下载完全。手工用curl -vo /dev/null -x IP:80 测试,果然视频下载过程中会中断,而且中断时间和大小不等。
因为使用了LVS,首先排除它的影响,直接指定LVS后端real server的IP,测试结果同使用LVS,排除LVS的问题。
查看源站nginx的log,我去,大量的segment fault的报错信息,nginx被干掉,难怪下不完。
我们同时还有一台服务器虽然不提供线上服务,但所有的数据和服务都是可用的,测试使用此服务器访问同样的文件,完全下载,于是认为只要找到此两个服务器之间服务的区别就OK了(后边会发现这个决定有多错误)。仔细对比此两个服务器,除了java一个用的是官方的,一个时openjdk之外,没有任何区别。把出错服务器的java替换为javaTM版,测试,结果,还是不行。
看来真不能不知其所以然的绕过去,只好一步步debug了。先把系统的core dump打开(此处记着一定要让core dump的目录出错进程有写权限,我找这个问题用了很长时间):
- ulimit -c unlimited
- chown web:web /home/nginx/sbin/
- /home/nginx/sbin/nginx -s stop && /home/nginx/sbin/nginx
core文件瀑布似的就把我的目录充满了,赶紧先把core dump 关掉。用gdb查core文件信息:
- (gdb) bt
- #0 0x00007f7d3073e1b5 in raise () from /lib/libc.so.6
- #1 0x00007f7d30740fc0 in abort () from /lib/libc.so.6
- #2 0x00007f7d3077437b in ?? () from /lib/libc.so.6
- #3 0x00007f7d3077dbd6 in ?? () from /lib/libc.so.6
- #4 0x00007f7d3078294c in free () from /lib/libc.so.6
- #5 0x00007f7d3076eb1d in fclose () from /lib/libc.so.6
- #6 0x00000000004a1d84 in mp4_context_exit (mp4_context=0x1425620) at ../nginx_mod_h264_streaming-2.2.7/src/mp4_io.c:548
- #7 0x00000000004a209f in mp4_open (
- filename=0x14440e9 <optimized output>, filesize=10403840, flags=MP4_OPEN_ALL, verbose=0) at ../nginx_mod_h264_streaming-2.2.7/src/mp4_io.c:623
- #8 0x000000000049f623 in mp4_process (
- filename=0x14440e9 <optimized output>, filesize=10403840, verbose=0, buckets=0x7fffe364fa38, options=0x1425590) at ../nginx_mod_h264_streaming-2.2.7/src/mp4_process.c:146
- #9 0x000000000049fb97 in ngx_streaming_handler (r=0x1439760) at ../nginx_mod_h264_streaming-2.2.7/src/ngx_http_streaming_module.c:297
- #10 0x000000000043c8e3 in ngx_http_core_content_phase (r=0x1439760, ph=0x1456388) at src/http/ngx_http_core_module.c:1339
- #11 0x000000000043b510 in ngx_http_core_run_phases (r=0x1439760) at src/http/ngx_http_core_module.c:837
- #12 0x000000000043b487 in ngx_http_handler (r=0x1439760) at src/http/ngx_http_core_module.c:820
- #13 0x0000000000448255 in ngx_http_process_request (r=0x1439760) at src/http/ngx_http_request.c:1650
- #14 0x0000000000446d4c in ngx_http_process_request_headers (rev=0x14867d0) at src/http/ngx_http_request.c:1093
- #15 0x000000000044653b in ngx_http_process_request_line (rev=0x14867d0) at src/http/ngx_http_request.c:893
- #16 0x0000000000445ce3 in ngx_http_init_request (rev=0x14867d0) at src/http/ngx_http_request.c:518
- #17 0x00000000004288bd in ngx_event_process_posted (cycle=0x14259c0, posted=0x6de758) at src/event/ngx_event_posted.c:39
- #18 0x00000000004267e4 in ngx_process_events_and_timers (cycle=0x14259c0) at src/event/ngx_event.c:272
- #19 0x0000000000433223 in ngx_worker_process_cycle (cycle=0x14259c0, data=0x0) at src/os/unix/ngx_process_cycle.c:800
- #20 0x000000000042fd66 in ngx_spawn_process (cycle=0x14259c0, proc=0x433089 <ngx_worker_process_cycle>, data=0x0,
- name=0x4b29bb "worker process", respawn=-3) at src/os/unix/ngx_process.c:196
- #21 0x00000000004320bd in ngx_start_worker_processes (cycle=0x14259c0, n=8, type=-3) at src/os/unix/ngx_process_cycle.c:360
- #22 0x000000000043172c in ngx_master_process_cycle (cycle=0x14259c0) at src/os/unix/ngx_process_cycle.c:136
- #23 0x0000000000403fdd in main (argc=1, argv=0x7fffe3650448) at src/core/nginx.c:405
试了几个core文件,发现所有文件filesize都是一样的,而且问题发生在mp4_open这个过程,突然想,不会是某一个文件故障造成nginx崩溃,而事实上跟我收到报障的文件没关系吧?赶紧又测了另外几个大文件,果然,其他文件也出现错误,而且每次测试的频率不等。
正在以为问题解决时,发现为什么filename那项是?google大神才发现,原来是 gcc -O 作祟,这个就简单了,把源码重新configure一遍,然后把objs/Makefile里面 CFLAGS的 -O参数去除。
再调试,文件名出来了。用ffmpeg -i 跑一下:
- ffmpeg -i E84IE281L-mobile.mp4.des
- FFmpeg version SVN-r25741, Copyright (c) 2000-2010 the FFmpeg developers
- built on Apr 11 2011 09:30:23 with gcc 4.3.2
- configuration: --prefix=/usr --enable-gpl --enable-shared --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libvorbis --enable-libxvid --enable-postproc --enable-libfaac --enable-libx264 --enable-pthreads --enable-nonfree --enable-version3
- libavutil 50.33. 0 / 50.33. 0
- libavcore 0.12. 1 / 0.12. 1
- libavcodec 52.94. 4 / 52.94. 4
- libavformat 52.84. 0 / 52.84. 0
- libavdevice 52. 2. 2 / 52. 2. 2
- libavfilter 1.62. 0 / 1.62. 0
- libswscale 0.12. 0 / 0.12. 0
- libpostproc 51. 2. 0 / 51. 2. 0
- [mov,mp4,m4a,3gp,3g2,mj2 @ 0x25004c0] error reading header: -1
- E84IE281L-mobile.mp4.des: Operation not permitted
文件的header被破坏了。再查core文件的堆栈,错误发生在mp4_context_exit阶段,双重fclose了。往上追一点代码,果然read_box在读取文件失败时调用了fclose()。其实这个fclose是没有必要的,因为出错都是要mp4_context_exit收尾的,所以完全可以不用处理。把这行注释掉:
- diff nginx_mod_h264_streaming-2.2.7/src/mp4_io.c nginx_mod_h264_streaming-2.2.7.fix/src/mp4_io.c
- 515,517c515
- < /* This close action should be completed in the mp4_context_exit function
- < fclose(infile);
- < */
- ---
- > fclose(infile);
重新编译,果然一切OK,收关。
阅读(4106) | 评论(0) | 转发(0) |