<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[Snooda]]></title> 
<link>http://www.snooda.com/index</link> 
<description><![CDATA[Snooda's Blog]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[Snooda]]></copyright>
<item>
<link>http://www.snooda.com/read/330</link>
<title><![CDATA[lighttpd的一个日志文件打印切分bug]]></title> 
<author>snooda &lt;admin@snooda.com&gt;</author>
<category><![CDATA[lighttpd]]></category>
<pubDate>Wed, 23 Oct 2013 07:16:32 +0000</pubDate> 
<guid>http://www.snooda.com/read/330</guid> 
<description>
<![CDATA[ 
	&nbsp;&nbsp;&nbsp;&nbsp;lighttpd有一个功能，就是收到SIGHUP信号时会重新打开日志文件。这样在日志切分时很有用。但最近发现了一个bug。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;就是如果有子进程挂掉。父进程新fork出的子进程accesslog会默认打日志到最最开始父进程启动时的那个文件里。<br/><br/><br/>&nbsp;&nbsp; 看了下代码。原来父进程在收到SIGHUP的时候只是把errorlog重新打开了下。没有重新打开accesslog（没办法，这个句柄是mod_accesslog模块搞的）。所以父进程维护的accesslog句柄一直是最老的。它本身不打accesslog日志倒无所谓。但它fork出的子进程是打的。这样就有问题了。<br/><br/><br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;一个最简单方法。就是外部脚本判断进程有更新的时候发一个SIGHUP信号过去。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;根治方法就是父进程重新启动子进程时给其发一个SIGHUP信号。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;至于父进程自己处理SIGHUP时重新打开句柄这个我感觉不太好。毕竟那是模块内部数据。lighttpd主干不应该关心。<br/><br/><br/><br/><br/><br/><br/>Tags - <a href="http://www.snooda.com/tags/lighttpd/" rel="tag">lighttpd</a> , <a href="http://www.snooda.com/tags/%25E6%2597%25A5%25E5%25BF%2597/" rel="tag">日志</a>
]]>
</description>
</item><item>
<link>http://www.snooda.com/read/310</link>
<title><![CDATA[lighttpd中CONST_STR_LEN的用法]]></title> 
<author>snooda &lt;admin@snooda.com&gt;</author>
<category><![CDATA[lighttpd]]></category>
<pubDate>Thu, 05 Jul 2012 11:49:44 +0000</pubDate> 
<guid>http://www.snooda.com/read/310</guid> 
<description>
<![CDATA[ 
	&nbsp;&nbsp;&nbsp;&nbsp;今天遇到一个问题，在获取http头的时候怎么也获取不到，手动core出来和gdb上去调试发现都是空的，应该是开了O2优化的缘故，于是在程序中打印http头，正常。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;不得已，单步gdb进匹配函数里，发现一个四字符的key值长度被判定为8，很奇怪，查代码原来是用了个CONST_STR_LEN来代替了本来应该填ptr, strlen(ptr)的位置。而CONST_STR_LEN是一个宏，内容为：x, x ? sizeof(x) - 1 : 0。显然，如果传一个指针进去的话，sizeof指针的结果是8（字节，x64)，这个宏只能在内容是字符串常量的时候用，不可以用在指针上。当时用的时候看lighttpd代码中一些地方用了这个宏，于是想当然认为到处都可用。。看来还是要注意一些。<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>Tags - <a href="http://www.snooda.com/tags/lighttpd/" rel="tag">lighttpd</a>
]]>
</description>
</item><item>
<link>http://www.snooda.com/read/306</link>
<title><![CDATA[lighttpd打印日志出core的问题]]></title> 
<author>snooda &lt;admin@snooda.com&gt;</author>
<category><![CDATA[lighttpd]]></category>
<pubDate>Fri, 15 Jun 2012 09:17:26 +0000</pubDate> 
<guid>http://www.snooda.com/read/306</guid> 
<description>
<![CDATA[ 
	&nbsp;&nbsp;&nbsp;&nbsp;有一个问题，是在一个环境上的lighttpd一打日志就出core，很奇怪，看堆栈信息是出在mod_accesslog里，今天看了下，发现原来是试图打印%i导致的。<br/>&nbsp;&nbsp;&nbsp;&nbsp;lighttpd支持在日志中打印请求头中的字段，方法是%&#123;key&#125;i，这样就能在请求头中的key字段打印到日志里，打印referer等东西的时候比较方便。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;但如果直接写%i的话，由于没有指定key，导致NULL指针，lighttpd没有校验导致出core。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;恰好%I是打印请求长度，大写I还是比较容易误按成小写的。所以有了这个问题<br/><br/><br/><br/><br/>Tags - <a href="http://www.snooda.com/tags/lighttpd/" rel="tag">lighttpd</a> , <a href="http://www.snooda.com/tags/core/" rel="tag">core</a>
]]>
</description>
</item><item>
<link>http://www.snooda.com/read/287</link>
<title><![CDATA[Transfer-Encoding: chunked http的流式传输]]></title> 
<author>snooda &lt;admin@snooda.com&gt;</author>
<category><![CDATA[lighttpd]]></category>
<pubDate>Tue, 08 May 2012 13:31:30 +0000</pubDate> 
<guid>http://www.snooda.com/read/287</guid> 
<description>
<![CDATA[ 
	&nbsp;&nbsp;&nbsp;&nbsp;最近接触到一个Transfer-Encoding: chunked相关的问题，原来http在应答时，有两种方式来标示应答body的长度，一种就是用content-length方式直接指明body长度，还有一种就是chunk模式。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;在这种模式下，应答正文分段发送，每个chunk由长度段和数据段组成，每个段均由&#92;r&#92;n结束，当服务器发送完数据后，发送一个长度为0的chunk，即：0&#92;r&#92;n&#92;r&#92;n。其中长度段为十六进制表示。<br/><br/><br/>举例一个长度11的chunk：<br/><br/>b&#92;r&#92;n12345678901&#92;r&#92;n<br/><br/><br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;chunk模式多用于结果长度未定的情况下，比如用php输出一个长字符串的时候，就默认使用的chunk模式，当然可以通过header来指定使用content-length模式。不过需要自己算出应答body的长度。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;chunk模式的一个好处是可以进行分段压缩，服务器对每个chunk进行gzip压缩发送给客户端。<br/><br/><br/><br/>Tags - <a href="http://www.snooda.com/tags/chunk/" rel="tag">chunk</a>
]]>
</description>
</item><item>
<link>http://www.snooda.com/read/273</link>
<title><![CDATA[lighttpd中handle_start_backend钩子（hook）函数的理解]]></title> 
<author>snooda &lt;admin@snooda.com&gt;</author>
<category><![CDATA[lighttpd]]></category>
<pubDate>Wed, 25 Apr 2012 11:59:50 +0000</pubDate> 
<guid>http://www.snooda.com/read/273</guid> 
<description>
<![CDATA[ 
	&nbsp;&nbsp;&nbsp;&nbsp;之前按字面意思理解handle_start_backend是说连接后端服务端口（webserver，fastcgi等等），今天发现并非如此。<br/>&nbsp;&nbsp;&nbsp;&nbsp;这个hook是在CON_STATE_HANDLE_REQUEST_HEADER状态时，<br/>如果con->mode仍旧是DIRECT类型且con->physical.path为空，会先调用：<br/>plugins_call_handle_uri_raw<br/>plugins_call_handle_uri_clean<br/>plugins_call_handle_docroot<br/>plugins_call_handle_physical<br/><br/>如果con->mode还是DIRECT，那么会判断con->physical.path指定的文件是否存在，<br/>&nbsp;&nbsp;&nbsp;&nbsp;若存在，如果是软链但是con->conf.follow_symlink为0，那么403，如果是目录但请求url不是路径名，则301跳转到目录路径（在末尾加/）<br/>&nbsp;&nbsp;&nbsp;&nbsp;若无权限访问，返回403<br/>&nbsp;&nbsp;&nbsp;&nbsp;若找不到文件，返回404<br/>&nbsp;&nbsp;&nbsp;&nbsp;若ENOTDIR（对文件做了目录操作），则考虑path_info<br/>&nbsp;&nbsp;&nbsp;&nbsp;若EMFILE（进程句柄不足），则返回HANDLER_WAIT_FOR_FD<br/>&nbsp;&nbsp;&nbsp;&nbsp;若不是以上情况，打印错误日志，返回500<br/><br/><br/>如果con->physical.path存在且无错误发生，或为ENOTDIR，那么又判断了一次是否存在（这里代码设计比较恶心）<br/>&nbsp;&nbsp;&nbsp;&nbsp;如果是普通文件且没有软链问题，那么break出去执行plugins_call_handle_start_backend。<br/>&nbsp;&nbsp;&nbsp;&nbsp;反之则一直向上遍历，直到遍历到一个真实存在的文件，如果找不到，那么404，如果找到了，将pathinfo串放入con->request.pathinfo。然后截短con->uri.path。<br/><br/>然后才会去调用plugins_call_handle_start_backend。<br/>所以对于很多动态请求是不会调用plugins_call_handle_start_backend的。<br/><br/>这个钩子被mod_access调用用来实现deny_all功能。<br/>被mod_indexfile调用用来实现默认文件功能（请求/时映射到index.php等）<br/><br/>Tags - <a href="http://www.snooda.com/tags/handle_start_backend/" rel="tag">handle start backend</a>
]]>
</description>
</item><item>
<link>http://www.snooda.com/read/272</link>
<title><![CDATA[lighttpd中钩子（hook）函数的使用]]></title> 
<author>snooda &lt;admin@snooda.com&gt;</author>
<category><![CDATA[lighttpd]]></category>
<pubDate>Tue, 24 Apr 2012 14:57:44 +0000</pubDate> 
<guid>http://www.snooda.com/read/272</guid> 
<description>
<![CDATA[ 
	&nbsp;&nbsp;&nbsp;&nbsp;lighttpd内部使用了状态机处理每个请求，在状态机中插入了若干个钩子来供扩展使用，在执行到钩子函数那里时，会按扩展载入顺序，依次回调使用了该钩子的各扩展指定的函数，这样会有一些编程中隐藏的易错点。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;1，顺序在后面的钩子不能假定前面的钩子函数一定会被执行到。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 之前遇到过这样的问题，在一个扩展中使用了两个钩子函数，第一个里面申请了一些资源，第二个里面使用并释放，结果实际中发现对于某些请求，第一个钩子可能没有被执行就到了第二个钩子那里，于是出core。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;查了一下原因，原来排在该扩展前面的mod_access扩展在第一个钩子被调用时返回了HANDLER_FINISH，这样，对于后续调用该钩子的其他扩展不会被回调。于是该扩展的第一个钩子函数未被调用到。<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;2，同一个钩子可能会被调用多次。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一些情况下，连接状态会rollback，这样的话同一个hook会被回调多次，还有一些情况会导致调用多次，比如给多个钩子指定了同一个处理函数。<br/><br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;有时我们需要为每个扩展在每个连接生命周期内维护一个变量，这时可以用到con->plugin_ctx[p->id]，这是一个void *指针，把数据指针存入该变量，并在连接释放时释放掉即可。<br/>Tags - <a href="http://www.snooda.com/tags/lighttpd/" rel="tag">lighttpd</a>
]]>
</description>
</item><item>
<link>http://www.snooda.com/read/266</link>
<title><![CDATA[lemon语法分析器模板初探]]></title> 
<author>snooda &lt;admin@snooda.com&gt;</author>
<category><![CDATA[lighttpd]]></category>
<pubDate>Tue, 17 Apr 2012 16:24:24 +0000</pubDate> 
<guid>http://www.snooda.com/read/266</guid> 
<description>
<![CDATA[ 
	&nbsp;&nbsp;&nbsp;&nbsp;今天看了一下lighttpd解析http头的过程，之前一直以为是单纯用遍历字符串的形式做的，今天发现除了遍历字符串，还用到了语法分析器来做解析，生成语法分析器模板的就是lemon，语法比较直观，不看文档就能大概看出逻辑，不过深入研究就要借助文档了，文档比较晦涩，需要仔细研究。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;大概用法：使用lemon的语法编写一个.y文件，然后调用lemon命令或使用lemon源文件将.y转化成.c和.h，转化后的.c看起来就很晕了，完全看不懂的说。<br/>&nbsp;&nbsp;&nbsp;&nbsp;这个东西还是挺有意思的，lighttpd还用它来解析配置文件。不过配置文件用lua也很不错啊。我准备以后多使用lua作为配置文件，方便灵活。<br/>Tags - <a href="http://www.snooda.com/tags/lemon/" rel="tag">lemon</a> , <a href="http://www.snooda.com/tags/lua/" rel="tag">lua</a>
]]>
</description>
</item><item>
<link>http://www.snooda.com/read/262</link>
<title><![CDATA[etag生成规则的配置-lighttpd]]></title> 
<author>snooda &lt;admin@snooda.com&gt;</author>
<category><![CDATA[lighttpd]]></category>
<pubDate>Wed, 11 Apr 2012 07:20:45 +0000</pubDate> 
<guid>http://www.snooda.com/read/262</guid> 
<description>
<![CDATA[ 
	&nbsp;&nbsp;&nbsp;&nbsp;最近两天调试一个程序的时候遇到一个问题，发现把一个文件两行对换位置的时候lighttpd不会载入新文件，增加或删除一行就会，考虑到lighttpd有stat cache，怀疑是不是不考虑mtime，只看inode，于是cp了一下，发现还是不行。没办法开gdb调试了一下，囧，原来生成的etag只用到了文件size这一个参数。怪不得。<br/><br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;# 生成ETag的时候是否考虑文件的inode<br/>&nbsp;&nbsp;&nbsp;&nbsp;etag.use-inode = "enable"<br/>&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;# 生成ETag的时候是否考虑文件的mtime<br/>&nbsp;&nbsp;&nbsp;&nbsp;etag.use-mtime = "enable"<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;# 生成ETag的时候是否考虑文件的size<br/>&nbsp;&nbsp;&nbsp;&nbsp;etag.use-size&nbsp;&nbsp;= "enable"<br/><br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;这是引发困扰的三个参数。平时建议全部开启，或开启后两个。<br/>Tags - <a href="http://www.snooda.com/tags/etag/" rel="tag">etag</a> , <a href="http://www.snooda.com/tags/lighttpd/" rel="tag">lighttpd</a>
]]>
</description>
</item><item>
<link>http://www.snooda.com/read/252</link>
<title><![CDATA[lighttpd日志切分]]></title> 
<author>snooda &lt;admin@snooda.com&gt;</author>
<category><![CDATA[lighttpd]]></category>
<pubDate>Fri, 09 Mar 2012 10:56:01 +0000</pubDate> 
<guid>http://www.snooda.com/read/252</guid> 
<description>
<![CDATA[ 
	&nbsp;&nbsp;&nbsp;&nbsp;之前在读代码的时候发现lighttpd在收到SIGHUP信号后会把日志重新打开一下，一直没有理解这么做的意义是什么。今天终于用到了这个功能。<br/>&nbsp;&nbsp;&nbsp;&nbsp;一个新模块没有使用cronlog等日志切分工具，直接打印日志到文件，（使用管道切分日志有风险，被打印程序一旦hang住，lighttpd也就卡住了），但如何切分日志文件就变成了一个问题。mv的话由于不改变inode，还是往同一个文件打。cp代价太大。直接清空日志的话又太粗暴。这里就用到了sighup功能。只要将文件mv到新名字，然后用killall -s SIGHUP lighttpd，这样lighttpd就会自动重新打开lighttpd.log打印了。<br/>Tags - <a href="http://www.snooda.com/tags/lighttpd/" rel="tag">lighttpd</a> , <a href="http://www.snooda.com/tags/sighup/" rel="tag">sighup</a> , <a href="http://www.snooda.com/tags/inode/" rel="tag">inode</a>
]]>
</description>
</item><item>
<link>http://www.snooda.com/read/246</link>
<title><![CDATA[由一个503问题看配置文件重要性]]></title> 
<author>snooda &lt;admin@snooda.com&gt;</author>
<category><![CDATA[lighttpd]]></category>
<pubDate>Sun, 26 Feb 2012 15:55:20 +0000</pubDate> 
<guid>http://www.snooda.com/read/246</guid> 
<description>
<![CDATA[ 
	&nbsp;&nbsp;&nbsp;&nbsp;最近有一个困扰近两个月的bug终于解决了，心情愉快。503问题之前一直没找到头绪，压力一大就开始有，越大就越多。刚开始一直认为503是正常现象，后端负载能力不足的必然结果。加之之前后端用的自己写的模拟server，性能什么的没什么保证。所以一直在看是不是程序逻辑上有什么漏洞会导致封禁所有后端，一直找不到头绪。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;周五测试同学突然说：现在用的后端server肯定能力要强于前边这个啊，怎么可能会因为负载力不足导致503呢？一想确实啊，肯定是流量调度部分的问题。开gdb仔细一找，结果大跌眼镜，原来是跟后端的连接池的最大允许并发连接数开的太小了。。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;之前那个值设置的比较小是有道理的，因为php是每个进程处理一个请求的，所以并发数肯定不会超过启动的php进程数。但现在后端改用了lighttpd，lighttpd可以承载的并发数是很多的，这样的话在高并发请求的情况下后端连接池很容易就用完了。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;由此有两个感触，一是不同场景下配置项一定要仔细想想如何调优。二是bug并不都是逻辑错误导致的，还有可能是配置错了。。。。<br/>Tags - <a href="http://www.snooda.com/tags/bug/" rel="tag">bug</a>
]]>
</description>
</item>
</channel>
</rss>