本節(jié)相對(duì)簡(jiǎn)單, 講講lighttpd中如何處理超時(shí)的連接.
方法很簡(jiǎn)單, lighttpd創(chuàng)建一個(gè)每隔一秒觸發(fā)的定時(shí)器, 被觸發(fā)后查找當(dāng)前的所有連接, 看它們的時(shí)間是否已經(jīng)超過了最長(zhǎng)的生存期, 如果是就關(guān)閉連接.
創(chuàng)建定時(shí)器的代碼在server.c的main函數(shù)中:
#ifdef USE_ALARM
struct itimerval interval;
interval.it_interval.tv_sec = 1;
interval.it_interval.tv_usec = 0;
interval.it_value.tv_sec = 1;
interval.it_value.tv_usec = 0;
#endif

#ifdef USE_ALARM
// 定時(shí)
signal(SIGALRM, signal_handler);
/* setup periodic timer (1 second) */
if (setitimer(ITIMER_REAL, &interval, NULL)) {
log_error_write(srv, __FILE__, __LINE__, "s", "setting timer failed");
return -1;
}
getitimer(ITIMER_REAL, &interval);
#endif
定時(shí)器觸發(fā)的時(shí)候產(chǎn)生ALARM信號(hào),此時(shí)在服務(wù)器主循環(huán)中輪詢所有的連接,這段代碼同樣在server.c的main函數(shù)中:
// 如果產(chǎn)生了alarm信號(hào) 那么一秒鐘過去了
if (handle_sig_alarm) {
/* a new second */
#ifdef USE_ALARM
/* reset notification */
handle_sig_alarm = 0;
#endif
/* get current time */
// 獲得當(dāng)前的時(shí)間
min_ts = time(NULL);
// 如果當(dāng)前時(shí)間不等于server上次記錄的時(shí)間
if (min_ts != srv->cur_ts) {
int cs = 0;
connections *conns = srv->conns;
handler_t r;
switch(r = plugins_call_handle_trigger(srv)) {
case HANDLER_GO_ON:
break;
case HANDLER_ERROR:
log_error_write(srv, __FILE__, __LINE__, "s", "one of the triggers failed");
break;
default:
log_error_write(srv, __FILE__, __LINE__, "d", r);
break;
}
/* trigger waitpid */
// 更新server的當(dāng)前時(shí)間
srv->cur_ts = min_ts;
/* cleanup stat-cache */
// 每秒清空一次stat cache
stat_cache_trigger_cleanup(srv);
/**
* check all connections for timeouts
*/
// 檢查所有連接是否已經(jīng)超時(shí)
for (ndx = 0; ndx < conns->used; ndx++) {
int changed = 0;
connection *con;
int t_diff;
con = conns->ptr[ndx];
if (con->state == CON_STATE_READ ||
con->state == CON_STATE_READ_POST) {
if (con->request_count == 1) {
// 如果當(dāng)前時(shí)間與read_idle_ts之差大于max_read_idle, 超時(shí)
if (srv->cur_ts - con->read_idle_ts > con->conf.max_read_idle) {
/* time - out */
connection_set_state(srv, con, CON_STATE_ERROR);
changed = 1;
}
} else {
// 如果當(dāng)前時(shí)間與read_idle_ts之差大于max_keep_alive_idle, 超時(shí)
if (srv->cur_ts - con->read_idle_ts > con->conf.max_keep_alive_idle) {
/* time - out */
connection_set_state(srv, con, CON_STATE_ERROR);
changed = 1;
}
}
}
if ((con->state == CON_STATE_WRITE) &&
(con->write_request_ts != 0)) {
// 如果當(dāng)前時(shí)間與write_request_ts之差大于max_write_idle, 超時(shí)
if (srv->cur_ts - con->write_request_ts > con->conf.max_write_idle) {
/* time - out */
#if 1
log_error_write(srv, __FILE__, __LINE__, "sbsosds",
"NOTE: a request for",
con->request.uri,
"timed out after writing",
con->bytes_written,
"bytes. We waited",
(int)con->conf.max_write_idle,
"seconds. If this a problem increase server.max-write-idle");
#endif
connection_set_state(srv, con, CON_STATE_ERROR);
changed = 1;
}
}
/* we don't like div by zero */
// 如果接收連接的時(shí)間 = server當(dāng)前時(shí)間
if (0 == (t_diff = srv->cur_ts - con->connection_start))
t_diff = 1;
if (con->traffic_limit_reached && // 如果已經(jīng)達(dá)到了傳輸?shù)臉O限
(con->conf.kbytes_per_second == 0 || // 似乎這個(gè)值一直是0啊
((con->bytes_written / t_diff) < con->conf.kbytes_per_second * 1024))) { // 如果每秒發(fā)送的數(shù)據(jù)量小于kbytes_per_second * 1024
/* enable connection again */
// 傳輸極限不再
con->traffic_limit_reached = 0;
changed = 1;
}
// 如果狀態(tài)發(fā)生了改變, 那么進(jìn)入狀態(tài)機(jī)進(jìn)行處理
if (changed) {
connection_state_machine(srv, con);
}
con->bytes_written_cur_second = 0;
*(con->conf.global_bytes_per_second_cnt_ptr) = 0;
}
if (cs == 1)
fprintf(stderr, "\n");
}
}
需要注意的是, 由于lighttpd采用了這種方式處理超時(shí)連接, 會(huì)觸發(fā)大量的ALARM信號(hào)產(chǎn)生,在編碼的時(shí)候要特別注意被信號(hào)中斷的情況.