高性能webserver
    lighttpd有一个功能,就是收到SIGHUP信号时会重新打开日志文件。这样在日志切分时很有用。但最近发现了一个bug。

    就是如果有子进程挂掉。父进程新fork出的子进程accesslog会默认打日志到最最开始父进程启动时的那个文件里。


   看了下代码。原来父进程在收到SIGHUP的时候只是把errorlog重新打开了下。没有重新打开accesslog(没办法,这个句柄是mod_accesslog模块搞的)。所以父进程维护的accesslog句柄一直是最老的。它本身不打accesslog日志倒无所谓。但它fork出的子进程是打的。这样就有问题了。



    一个最简单方法。就是外部脚本判断进程有更新的时候发一个SIGHUP信号过去。

    根治方法就是父进程重新启动子进程时给其发一个SIGHUP信号。

    至于父进程自己处理SIGHUP时重新打开句柄这个我感觉不太好。毕竟那是模块内部数据。lighttpd主干不应该关心。





Tags: ,

lighttpd中CONST_STR_LEN的用法

[| 不指定 2012/07/05 19:49]
    今天遇到一个问题,在获取http头的时候怎么也获取不到,手动core出来和gdb上去调试发现都是空的,应该是开了O2优化的缘故,于是在程序中打印http头,正常。

    不得已,单步gdb进匹配函数里,发现一个四字符的key值长度被判定为8,很奇怪,查代码原来是用了个CONST_STR_LEN来代替了本来应该填ptr, strlen(ptr)的位置。而CONST_STR_LEN是一个宏,内容为:x, x ? sizeof(x) - 1 : 0。显然,如果传一个指针进去的话,sizeof指针的结果是8(字节,x64),这个宏只能在内容是字符串常量的时候用,不可以用在指针上。当时用的时候看lighttpd代码中一些地方用了这个宏,于是想当然认为到处都可用。。看来还是要注意一些。









Tags:
    有一个问题,是在一个环境上的lighttpd一打日志就出core,很奇怪,看堆栈信息是出在mod_accesslog里,今天看了下,发现原来是试图打印%i导致的。
    lighttpd支持在日志中打印请求头中的字段,方法是%{key}i,这样就能在请求头中的key字段打印到日志里,打印referer等东西的时候比较方便。

    但如果直接写%i的话,由于没有指定key,导致NULL指针,lighttpd没有校验导致出core。

    恰好%I是打印请求长度,大写I还是比较容易误按成小写的。所以有了这个问题



Tags: ,
    最近接触到一个Transfer-Encoding: chunked相关的问题,原来http在应答时,有两种方式来标示应答body的长度,一种就是用content-length方式直接指明body长度,还有一种就是chunk模式。

    在这种模式下,应答正文分段发送,每个chunk由长度段和数据段组成,每个段均由\r\n结束,当服务器发送完数据后,发送一个长度为0的chunk,即:0\r\n\r\n。其中长度段为十六进制表示。


举例一个长度11的chunk:

b\r\n12345678901\r\n



    chunk模式多用于结果长度未定的情况下,比如用php输出一个长字符串的时候,就默认使用的chunk模式,当然可以通过header来指定使用content-length模式。不过需要自己算出应答body的长度。

    chunk模式的一个好处是可以进行分段压缩,服务器对每个chunk进行gzip压缩发送给客户端。


Tags:
    之前按字面意思理解handle_start_backend是说连接后端服务端口(webserver,fastcgi等等),今天发现并非如此。
    这个hook是在CON_STATE_HANDLE_REQUEST_HEADER状态时,
如果con->mode仍旧是DIRECT类型且con->physical.path为空,会先调用:
plugins_call_handle_uri_raw
plugins_call_handle_uri_clean
plugins_call_handle_docroot
plugins_call_handle_physical

如果con->mode还是DIRECT,那么会判断con->physical.path指定的文件是否存在,
    若存在,如果是软链但是con->conf.follow_symlink为0,那么403,如果是目录但请求url不是路径名,则301跳转到目录路径(在末尾加/)
    若无权限访问,返回403
    若找不到文件,返回404
    若ENOTDIR(对文件做了目录操作),则考虑path_info
    若EMFILE(进程句柄不足),则返回HANDLER_WAIT_FOR_FD
    若不是以上情况,打印错误日志,返回500


如果con->physical.path存在且无错误发生,或为ENOTDIR,那么又判断了一次是否存在(这里代码设计比较恶心)
    如果是普通文件且没有软链问题,那么break出去执行plugins_call_handle_start_backend。
    反之则一直向上遍历,直到遍历到一个真实存在的文件,如果找不到,那么404,如果找到了,将pathinfo串放入con->request.pathinfo。然后截短con->uri.path。

然后才会去调用plugins_call_handle_start_backend。
所以对于很多动态请求是不会调用plugins_call_handle_start_backend的。

这个钩子被mod_access调用用来实现deny_all功能。
被mod_indexfile调用用来实现默认文件功能(请求/时映射到index.php等)
    lighttpd内部使用了状态机处理每个请求,在状态机中插入了若干个钩子来供扩展使用,在执行到钩子函数那里时,会按扩展载入顺序,依次回调使用了该钩子的各扩展指定的函数,这样会有一些编程中隐藏的易错点。

    1,顺序在后面的钩子不能假定前面的钩子函数一定会被执行到。
         之前遇到过这样的问题,在一个扩展中使用了两个钩子函数,第一个里面申请了一些资源,第二个里面使用并释放,结果实际中发现对于某些请求,第一个钩子可能没有被执行就到了第二个钩子那里,于是出core。
          查了一下原因,原来排在该扩展前面的mod_access扩展在第一个钩子被调用时返回了HANDLER_FINISH,这样,对于后续调用该钩子的其他扩展不会被回调。于是该扩展的第一个钩子函数未被调用到。
    
    2,同一个钩子可能会被调用多次。
          一些情况下,连接状态会rollback,这样的话同一个hook会被回调多次,还有一些情况会导致调用多次,比如给多个钩子指定了同一个处理函数。


    有时我们需要为每个扩展在每个连接生命周期内维护一个变量,这时可以用到con->plugin_ctx[p->id],这是一个void *指针,把数据指针存入该变量,并在连接释放时释放掉即可。
Tags:

lemon语法分析器模板初探

[| 不指定 2012/04/18 00:24]
    今天看了一下lighttpd解析http头的过程,之前一直以为是单纯用遍历字符串的形式做的,今天发现除了遍历字符串,还用到了语法分析器来做解析,生成语法分析器模板的就是lemon,语法比较直观,不看文档就能大概看出逻辑,不过深入研究就要借助文档了,文档比较晦涩,需要仔细研究。

    大概用法:使用lemon的语法编写一个.y文件,然后调用lemon命令或使用lemon源文件将.y转化成.c和.h,转化后的.c看起来就很晕了,完全看不懂的说。
    这个东西还是挺有意思的,lighttpd还用它来解析配置文件。不过配置文件用lua也很不错啊。我准备以后多使用lua作为配置文件,方便灵活。
Tags: ,

etag生成规则的配置-lighttpd

[| 不指定 2012/04/11 15:20]
    最近两天调试一个程序的时候遇到一个问题,发现把一个文件两行对换位置的时候lighttpd不会载入新文件,增加或删除一行就会,考虑到lighttpd有stat cache,怀疑是不是不考虑mtime,只看inode,于是cp了一下,发现还是不行。没办法开gdb调试了一下,囧,原来生成的etag只用到了文件size这一个参数。怪不得。


    # 生成ETag的时候是否考虑文件的inode
    etag.use-inode = "enable"
  
    # 生成ETag的时候是否考虑文件的mtime
    etag.use-mtime = "enable"

    # 生成ETag的时候是否考虑文件的size
    etag.use-size  = "enable"


    这是引发困扰的三个参数。平时建议全部开启,或开启后两个。
Tags: ,

lighttpd日志切分

[| 不指定 2012/03/09 18:56]
    之前在读代码的时候发现lighttpd在收到SIGHUP信号后会把日志重新打开一下,一直没有理解这么做的意义是什么。今天终于用到了这个功能。
    一个新模块没有使用cronlog等日志切分工具,直接打印日志到文件,(使用管道切分日志有风险,被打印程序一旦hang住,lighttpd也就卡住了),但如何切分日志文件就变成了一个问题。mv的话由于不改变inode,还是往同一个文件打。cp代价太大。直接清空日志的话又太粗暴。这里就用到了sighup功能。只要将文件mv到新名字,然后用killall -s SIGHUP lighttpd,这样lighttpd就会自动重新打开lighttpd.log打印了。
Tags: , ,
    最近有一个困扰近两个月的bug终于解决了,心情愉快。503问题之前一直没找到头绪,压力一大就开始有,越大就越多。刚开始一直认为503是正常现象,后端负载能力不足的必然结果。加之之前后端用的自己写的模拟server,性能什么的没什么保证。所以一直在看是不是程序逻辑上有什么漏洞会导致封禁所有后端,一直找不到头绪。

    周五测试同学突然说:现在用的后端server肯定能力要强于前边这个啊,怎么可能会因为负载力不足导致503呢?一想确实啊,肯定是流量调度部分的问题。开gdb仔细一找,结果大跌眼镜,原来是跟后端的连接池的最大允许并发连接数开的太小了。。

    之前那个值设置的比较小是有道理的,因为php是每个进程处理一个请求的,所以并发数肯定不会超过启动的php进程数。但现在后端改用了lighttpd,lighttpd可以承载的并发数是很多的,这样的话在高并发请求的情况下后端连接池很容易就用完了。

    由此有两个感触,一是不同场景下配置项一定要仔细想想如何调优。二是bug并不都是逻辑错误导致的,还有可能是配置错了。。。。
Tags:
分页: 1/2 第一页 1 2 下页 最后页 [ 显示模式: 摘要 | 列表 ]