1. 程式人生 > >手把手教你用nginx開發自己的伺服器------利用nginx開發一個helloWorld程式(二)

手把手教你用nginx開發自己的伺服器------利用nginx開發一個helloWorld程式(二)

現在我們正式開始編寫nginx的helloWorld功能,該從哪下手呢?別急,我們在上一篇文章中提到了事件驅動對吧。nginx是怎麼樣事件驅動的呢?

我們來看看ngx_worker_process_cycle()這個函式的一部分

for ( ;; ) {

        if (ngx_exiting) {
            if (ngx_event_no_timers_left() == NGX_OK) {
                ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
                ngx_worker_process_exit(cycle);
            }
        }

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");

        ngx_process_events_and_timers(cycle);//重點關注一下
是不是很像上一篇文章中提到了epoll伺服器的邏輯,OK,我們再來看下ngx_process_events_and_timers中是怎麼處理事件的,看下這個部分
 (void) ngx_process_events(cycle, timer, flags);

完蛋了,是個回撥函式,還是個巨集定義,實體在哪呢?我們gdb看一下(nginx的gdb需要在auto/cc/conf檔案中的ngx_compile_opt加上一個-g選項)

原來是ngx_epoll_process_events,出現了epoll字眼,越來越接近我們的要求了,現在我們啟動nginx,然後gdb attach到worker程序上(一定要在ps -aux|grep nginx時看到哪個是worker程序哦,因為worker程序才是處理連線的,master程序是用來接收系統的一些訊息和你輸入的指令,然後通過signal的方式傳送給worker程序的),在ngx_epoll_process_events上打個斷電,然後用瀏覽器訪問下我們的伺服器。


是不是和上篇文章的epoll邏輯一樣,迴圈處理每個epoll事件了,留意下rev,rev就是read event的意思,這個rev儲存著整個事件的一些上下文資訊,以及這個事件需要呼叫的handler(也就是函式),我們可以看到rev->handler事件就是ngx_event_accept函式,是不是又回到了echo伺服器的基本邏輯,accept()?越來越清晰了,我們去ngx_event_accept這個函式裡看一下:呃,一大堆各種判斷用accept還是accept4等等亂七八糟的邏輯,好麻煩。。。別慌,我們只需要關注這個位置:

   log->data = NULL;
        log->handler = NULL;

        ls->handler(c);//重點關注

這個handler又是幹嘛的(各種回撥快要瘋掉了好嗎),別慌,我們gdb來看一下


ngx_http_init_connection,可以,初始化連線了開始,快接近事實的真相了,來,看下這個函式又在幹嘛,呃。。。。又是一大坨,各種上下文,日誌等東西的初始化,別急,我們只需要關注這一坨:

 rev = c->read;
    rev->handler = ngx_http_wait_request_handler;
    c->write->handler = ngx_http_empty_handler;

終於找到你了,原來讀寫事件的處理控制代碼是ngx_http_wait_request_handler,美滋滋,走進去一看,果然是處理各種訊息的,想列印個helloWorld還不簡單,直接在這個函式里加一行printf("helloWorld"),大功告成,儲存再編譯,咦,怎麼沒顯示,不急不急,這不是你的問題,這是由於nginx以守護程序的形式在執行,所以標準輸出沒有定向到std中了,想測試這個helloworld,還是老老實實fopen一個檔案,列印進去吧。

這裡再說一下,rev->handler雖然掛上了,怎麼呼叫的呢,回想下事件驅動機制,就是epoll_ctrl(add)嘛,然後去epoll迴圈裡處理就好咯,回到ngx_http_init_connection中,看一下這個地方

 ngx_add_timer(rev, c->listening->post_accept_timeout);
    ngx_reusable_connection(c, 1);

    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
        ngx_http_close_connection(c);
        return;

點進ngx_handler_read_event看一下,這裡就是我們找了好久的epoll_ctrl(add)咯,美滋滋,原來這裡就註冊好了,等等,ngx_add_timer又是什麼鬼,別慌,我來給你介紹下,ngx_add_timer是用來新增一個超時事件,nginx內部維護了一個事件紅黑樹,ngx_add_timer之後就會把這個時間和事件註冊到紅黑樹裡,只要時間一到,就會呼叫這個事件啦。所以,怪不得之前處理事件的函式叫ngx_process_events_and_timers嘛。

學會了寫helloworld,在這裡把功能替換成別的,也一樣灑灑水啦,複雜的伺服器邏輯,也就是把這個handler裡面的功能寫複雜,希望通過這兩篇文章,能讓你學會把握伺服器開發最本質的幾個地方,以後再去看其他的伺服器程式碼,媽媽也再也不用擔心啦。