當前位置:首頁 » 操作系統 » nginx源碼閱讀

nginx源碼閱讀

發布時間: 2023-01-14 02:14:20

『壹』 為什麼說學習計算機首先要學會「抽象」

學計算機的過程,是一個從抽象到具體的過程,等將一切具象化以後,再重新理解抽象,將得到前所未有的酣暢。


以HTTP協議為例


抽象過程學習


在學HTTP協議的時候,很多人都是從「文字描述」(書本或者文章)開始,頂多是配合一些實踐,例如:如何發送一個HTTP請求,如何接受一個HTTP請求。當具有這些知識以後,再加上這個世界HTTP相關的輪子太多了,大部分情況下不需要我們親自去造。

客戶端想用一個網路框架,找找開源的,發現有一堆:Apache的HTTPClient、Square的OKHTTP,就算沒有,一些語言的系統庫也提供的一些基本的封裝(例如Java的HttpURLConnection)

服務端要用搭建一個簡單的web站點,找找開源的HTTP伺服器,也是有不少:Apache、Nginx、Tomcat等


經過一些實踐之後,對HTTP這個協議裡麵包含的一些內容肯定有比較寬的了解,知道了如下內容:

  • URIs/URLs/URNs,

  • HTTP Request,並且知道HTTP Request裡麵包含請求起始行(Request start line)、請求頭(Request Headers)和請求體(Request Bodys);請求起始行裡面又包含HTTP Method(GET POST PUT等),HTTP協議版本等

  • HTTP Respone,並且知道HTTP Response裡麵包含響應起始行(Response start line)、響應頭(Response Headers)和響應體(Response Bodys);響應起始行裡麵包含響應狀態碼,常用的狀態碼有200、304、404、500、502等

  • 具象過程學習

    如果HTTP協議學到此處,那麼還只是停留在表象(抽象層),當被問及一些稍微深的問題或者進行操刀HTTP通信方面優化時,依然無從作答或者無法下手。

    你可以通過以下問題(都是各大公司經典的面試題),檢測下自己對HTTP協議(網路知識)的理解程度:

    1. get和post方法的區別?

    我想這個問題的答案有一定了解的你能回答出如下重要的兩點:

    (1)get用於獲取數據,post用於提交數據

    (2)get不安全,post相對來說安全

    那再問更深一點,get能用於提交數據嗎?為什麼?

    post能用於獲取數據嗎?為什麼?

    post提交的數據放在哪兒?

    你覺得這個兩種請求的方法,哪一種更高效,還是說效率上沒有差別,為什麼?

    2. 當在瀏覽器地址欄鍵入:http://www.taobao.com,回車之後,後續發生了哪些事情?

    3. 如果讓你著手進行一些網路優化,你會從哪些方面入手?

    我很喜歡的一種學習方式,就是問題式學習,這是符合客觀規律。我們每天學的東西,做的事情,往大了看,不都是為了解決問題嘛。

    通過對以上問題的思考和研究,我們一定會得出以下具象的結論:

    1. HTTP協議,這個詞,大部分人只是把焦點放在了「HTTP」上,而忘記了另外一個關鍵詞「協議」。協議是什麼?

    Wikipedia的核心定義如下:

  • In telecommunication, a communication protocol is a system of rules that allow two or more entities of a communications system to transmit information via any kind of variation of a physical quantity. The protocol defines the rules syntax,semantics and synchronization of communication and possible error recovery methods. Protocols may be implemented by hardware,software, or a combination of both.[1]
  • 精簡就是:協議就是規則,通信領域也是如此。

    HTTP協議就是一套規則,大家都是按照這套規則來通信。

    如果我和你對話,我用這套規則問你話,你沒有遵循這套規則,你將聽不懂,我也收不到回應,就這么簡單。

    客戶端那些開源的HTTP網路框架和服務端所使用的HTTP伺服器,就是用計算機編程語言按照這套規則來實現的軟體,我們用這套軟體間接用HTTP通信,我們也可以直接用HTTP協議來,但是太麻煩了。

    2. HTTP協議是應用層協議,建立在傳輸層之上,依賴操作系統的套接字介面。研讀一下HTTP網路框架的源碼或者HTTP伺服器源碼便可得知(我研讀的是OKHTTP)。如果你覺得沒有親眼看到TCP/IP的影子,你再看看linux關於socket的源碼,底層用的就是TCP/IP。

    通過以上深入的思考、理論學習、源碼閱讀,我想我們已經進入到了HTTP具象的世界:他是一套規范,最終他被表達的方式就是代碼,然後提供給成千上萬的開發者去使用。

    再抽象過程學習

    當進入具象的世界之後,我們再回過頭來想想,為什麼網路世界有那麼多協議,為什麼要有經典TCP/IP四層經典模型? 一個協議不行嗎?

    行。可是很麻煩。為了簡單,為了使用方便,大師們進行了層層抽象。而抽象是計算機領域一個非常重要的本質。

    還記得曾經看到過一句話:計算機科學領域的任何問題都可以通過增加一個間接的中間層來解決。這句話反應的便是這個「抽象」二字的神奇功效!

    我的HTTP學習方法參考

    主要閱讀的書籍是:《圖解HTTP》(入門用) 《HTTP權威指南》(進階,知識點細化)《TCP/IP詳解》

    實踐:編寫客戶端程序和伺服器程序,通過HTTP通信

    源碼閱讀(任意選一個大眾常用的軟體即可)

『貳』 如何解決Nginx服務自動關閉問題

首先排除是否為網路問題:檢查了iptable等。同時思考,如果真的是網路問題,不應該運行一段時間後,才出現無法接受新連接的現象。
為了驗證,當出現問題時,我又重新啟動Nginx,發現又可以接收新的請求了。也就是說出現問題時,只需要重啟Nginx就可以解決,那麼自然不是網路因素。
判定是否是Nginx本身的問題(不一定是指代碼包括我寫的配置文件):因為這個代理伺服器是為了測試fastsocket項目的穩定性,所以 Nginx是載入了fastsocket優化服務的。這時,就需要最純粹的Nginx環境。我去掉了fastsocket服務,然後再用同樣的配置啟動 Nginx。這時,就排除了Nginx本身的問題。那麼,究竟是否是fastsocket的bug呢?
這里先做一個小廣告: fastsocket是新浪主導的一個開源項目,其通過封裝socket套接字調用,無需改動服務程序,即可大幅提升服務程序性能。作者也是其中的維護者之一。這里小小推廣一下:https://github.com/fastos/fastsocket。當使用fastsocket默認載入參數時,nginx運行一段時間就無法接受新連接請求了。
定位fastsocket問題:fastsocket的大部分優化功能都是有功能開關的,默認會使用一些功能,同時可以在載入動態模塊時,使用參數指定是否打開開關。這時,先做實驗,從所有功能關閉開始,逐漸打開功能開關,最後定位到enable_listen_spawn功能打開時,就會出現問題。並多次做實驗,確定這是一個必現的問題。
當確定可以重現後,想這難道是一個fastsocket的bug嗎?於是,先跟林曉峰同學說了一聲,告訴他我的發現,畢竟fastsocket是他在sina時的工作,他最為熟悉代碼。他說這可能是Nginx的配置使用了accept_mutex。我的配置文件雖然沒有配置accept_mutex,但是沒想到Nginx的accept_mutex是默認打開的。但是他忘了為什麼會這樣了?依稀記得是Nginx hang在了mutex中,具體原因記不清了。所以fastsocket的說明也是要求disable accept_mutex。
因為我一直以來有這還不錯的求知慾,所以一定要搞清楚這個問題。同時我認為,如果真的是一啟用accept_mutex,fastsocket和nginx就會有兼容問題,那也應該算是fastsocket的bug,應該將其解決掉。
定位Nginx hang在什麼位置:這個很簡單,使用strace -p跟蹤Nginx的每個worer進程。發現大部分worker進程是在不斷的epoll_wait,而其中一個worker進程,始終停留在epoll_wait中。重試多次,每次都是停留在epoll_wait中。
現在已經確定了本次問題,當使用fastsocket的enable_listen_spawn功能時,也就是fastsocket自動為當前CPU創建本地的listen socket套接字時,就會出現問題。
解決問題
當定位到問題時,就需要一步一步的找到原因,查看為什麼一個worker進程始終停留在epoll_wait中。這時候,其實思考還是要優於動手。先思考,再動手,動手之後,看到結果,再做進一步思考。
查看該worker進程停留在epoll_wait的什麼位置:只能通過日誌形式來判斷hang在epoll_wait的哪個位置?這時,不能用內核普通的printk來列印日誌,不然就會淹沒於大量正常工作worker進程列印的日誌中。我們需要根據pid來列印日誌。
再做一個小廣告:我做了一個內核小工具[email protected]:gfreewind/unit_perf.git。是用來定位內核代碼的性能瓶頸工具,和一些輔助工具。大家覺得還可以的話,就給贊個星星。
它提供一個宏UP_PID_INFO_LOG用於列印指定PID的日誌,pid可以通過proc來指定。
這樣我在epoll_wait中增加了大量的日誌。在Nginx啟動後,通過proc指定就列印某個worker進程的日誌。
最後發現epoll_wait是因為指定了無限等待時間,所以該worker進程一直在hang住。
Nginx讓一個worker進程無限等待,這稍微顛覆了我對Nginx的認識。我認為Nginx一直都是使用無阻塞的系統調用,至少核心模塊是這樣處理的。那麼為什麼會出現這個現象呢?這時候,就需要思考,而不是動手了。
毫無疑問,accept_mutex是一個關鍵。它本身是用於均衡不同worker進程的負載。稍微閱讀一點Nginx相關的 代碼,就可以明白。在Nginx無法接收新連接請求時,一定是該輪到hang住的進程接收新連接請求。所以盡管其它進程沒有hang住,但是它們是無法接 受新請求,而能夠接收新請求的進程卻hang住,這樣就導致了問題的產生。
為什麼hang住的進程無法接收到新的請求呢?這時還是思考優先。首先要勾畫標準的內核TCP連接的過程,然後對比啟用fastsocket 後,TCP連接的過程。很可能是這兩者之間的區別,造成了問題。尤其是啟用了spawn socket時,與標准流程的不同。spawn socket時,實際上為每個cpu都創建了一個本地listen 套接字的hash表,與全局的listen表區分開。這樣一方面訪問全局hash表時需要的鎖,另一方面也做到了將TCP會話做到本地,可以盡量命中 cache。 對於同一個CPU,由於有兩個listen表的存在,所以在收到新的TCP連接請求時,必須先檢查本地的listen表,然後再檢查全局表。 根據這樣的流程和現象,應該是所有的連接請求,都被發到其它的CPU,並且匹配中了其它CPU的本地listen表,所以全局表中的listen socket套接字一直沒有被匹配到。
那麼hang住的進程,既沒有連接請求匹配本地listen表中的套接字,而全局表也一樣,因為被請求都被其它CPU命中了本地的套接字。
所以問題更為明朗了,hang住的進程所在的CPU不能收到任何新連接請求。
這時其實已經到了沖刺的時候了。開始的時候,我還想著,是否是fastsocket影響了數據包的分發,還想檢查一下代碼。但一想,還是先看看 RPS的設置吧——雖然我沒有設置網卡的任何RPS。結果出乎我意料,原來阿里雲ECS伺服器默認就把網卡的RPS設置了,唯一的外網網卡的RPS設置為 了0000,所以只有CPU 0能收到新連接請求,而另外的CPU1收不到任何的連接請求,這就造成了運行在CPU1上的worker進程hang住。
最後我修改了該網卡的RPS設置,使其可以將數據包分發到不同的CPU上。這樣在載入了fastsocket後,即使打開了accept_mutex,Nginx也可以正常工作了。
本次過程,雖然最後發現只是伺服器配置的問題,但整個兒過程還是收獲不少。唯一的遺憾,是還沒有定位Nginx對與epoll_wait的超時計算。開始的時候,都是500ms,後面因為什麼因素變成了無限。這留到有時間的時候,再閱讀Nginx源碼吧。

『叄』 面試必問的epoll技術,從內核源碼出發徹底搞懂epoll

epoll是linux中IO多路復用的一種機制,I/O多路復用就是通過一種機制,一個進程可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程序進行相應的讀寫操作。當然linux中IO多路復用不僅僅是epoll,其他多路復用機制還有select、poll,但是接下來介紹epoll的內核實現。

events可以是以下幾個宏的集合:

epoll相比select/poll的優勢

epoll相關的內核代碼在fs/eventpoll.c文件中,下面分別分析epoll_create、epoll_ctl和epoll_wait三個函數在內核中的實現,分析所用linux內核源碼為4.1.2版本。

epoll_create用於創建一個epoll的句柄,其在內核的系統實現如下:

sys_epoll_create:

可見,我們在調用epoll_create時,傳入的size參數,僅僅是用來判斷是否小於等於0,之後再也沒有其他用處。
整個函數就3行代碼,真正的工作還是放在sys_epoll_create1函數中。

sys_epoll_create -> sys_epoll_create1:

sys_epoll_create1 函數流程如下:

sys_epoll_create -> sys_epoll_create1 -> ep_alloc:


sys_epoll_create -> sys_epoll_create1 -> ep_alloc -> get_unused_fd_flags:

linux內核中,current是個宏,返回的是一個task_struct結構(我們稱之為進程描述符)的變數,表示的是當前進程,進程打開的文件資源保存在進程描述符的files成員裡面,所以current->files返回的當前進程打開的文件資源。rlimit(RLIMIT_NOFILE) 函數獲取的是當前進程可以打開的最大文件描述符數,這個值可以設置,默認是1024。

相關視頻推薦:

支撐億級io的底層基石 epoll實戰揭秘

網路原理tcp/udp,網路編程epoll/reactor,面試中正經「八股文」

學習地址:C/C++Linux伺服器開發/後台架構師【零聲教育】-學習視頻教程-騰訊課堂

需要更多C/C++ Linux伺服器架構師學習資料加群 812855908 獲取(資料包括C/C++,Linux,golang技術,Nginx,ZeroMQ,Mysql,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協程,DPDK,ffmpeg等),免費分享

__alloc_fd的工作是為進程在[start,end)之間(備註:這里start為0, end為進程可以打開的最大文件描述符數)分配一個可用的文件描述符,這里就不繼續深入下去了,代碼如下:

sys_epoll_create -> sys_epoll_create1 -> ep_alloc -> get_unused_fd_flags -> __alloc_fd:

然後,epoll_create1會調用anon_inode_getfile,創建一個file結構,如下:

sys_epoll_create -> sys_epoll_create1 -> anon_inode_getfile:

anon_inode_getfile函數中首先會alloc一個file結構和一個dentry結構,然後將該file結構與一個匿名inode節點anon_inode_inode掛鉤在一起,這里要注意的是,在調用anon_inode_getfile函數申請file結構時,傳入了前面申請的eventpoll結構的ep變數,申請的file->private_data會指向這個ep變數,同時,在anon_inode_getfile函數返回來後,ep->file會指向該函數申請的file結構變數。

簡要說一下file/dentry/inode,當進程打開一個文件時,內核就會為該進程分配一個file結構,表示打開的文件在進程的上下文,然後應用程序會通過一個int類型的文件描述符來訪問這個結構,實際上內核的進程裡面維護一個file結構的數組,而文件描述符就是相應的file結構在數組中的下標。

dentry結構(稱之為「目錄項」)記錄著文件的各種屬性,比如文件名、訪問許可權等,每個文件都只有一個dentry結構,然後一個進程可以多次打開一個文件,多個進程也可以打開同一個文件,這些情況,內核都會申請多個file結構,建立多個文件上下文。但是,對同一個文件來說,無論打開多少次,內核只會為該文件分配一個dentry。所以,file結構與dentry結構的關系是多對一的。

同時,每個文件除了有一個dentry目錄項結構外,還有一個索引節點inode結構,裡面記錄文件在存儲介質上的位置和分布等信息,每個文件在內核中只分配一個inode。 dentry與inode描述的目標是不同的,一個文件可能會有好幾個文件名(比如鏈接文件),通過不同文件名訪問同一個文件的許可權也可能不同。dentry文件所代表的是邏輯意義上的文件,記錄的是其邏輯上的屬性,而inode結構所代表的是其物理意義上的文件,記錄的是其物理上的屬性。dentry與inode結構的關系是多對一的關系。

sys_epoll_create -> sys_epoll_create1 -> fd_install:

總結epoll_create函數所做的事:調用epoll_create後,在內核中分配一個eventpoll結構和代表epoll文件的file結構,並且將這兩個結構關聯在一塊,同時,返回一個也與file結構相關聯的epoll文件描述符fd。當應用程序操作epoll時,需要傳入一個epoll文件描述符fd,內核根據這個fd,找到epoll的file結構,然後通過file,獲取之前epoll_create申請eventpoll結構變數,epoll相關的重要信息都存儲在這個結構裡面。接下來,所有epoll介面函數的操作,都是在eventpoll結構變數上進行的。

所以,epoll_create的作用就是為進程在內核中建立一個從epoll文件描述符到eventpoll結構變數的通道。

epoll_ctl介面的作用是添加/修改/刪除文件的監聽事件,內核代碼如下:

sys_epoll_ctl:

根據前面對epoll_ctl介面的介紹,op是對epoll操作的動作(添加/修改/刪除事件),ep_op_has_event(op)判斷是否不是刪除操作,如果op != EPOLL_CTL_DEL為true,則需要調用_from_user函數將用戶空間傳過來的event事件拷貝到內核的epds變數中。因為,只有刪除操作,內核不需要使用進程傳入的event事件。

接著連續調用兩次fdget分別獲取epoll文件和被監聽文件(以下稱為目標文件)的file結構變數(備註:該函數返回fd結構變數,fd結構包含file結構)。

接下來就是對參數的一些檢查,出現如下情況,就可以認為傳入的參數有問題,直接返回出錯:

當然下面還有一些關於操作動作如果是添加操作的判斷,這里不做解釋,比較簡單,自行閱讀。

在ep裡面,維護著一個紅黑樹,每次添加註冊事件時,都會申請一個epitem結構的變數表示事件的監聽項,然後插入ep的紅黑樹裡面。在epoll_ctl裡面,會調用ep_find函數從ep的紅黑樹裡面查找目標文件表示的監聽項,返回的監聽項可能為空。

接下來switch這塊區域的代碼就是整個epoll_ctl函數的核心,對op進行switch出來的有添加(EPOLL_CTL_ADD)、刪除(EPOLL_CTL_DEL)和修改(EPOLL_CTL_MOD)三種情況,這里我以添加為例講解,其他兩種情況類似,知道了如何添加監聽事件,其他刪除和修改監聽事件都可以舉一反三。

為目標文件添加監控事件時,首先要保證當前ep裡面還沒有對該目標文件進行監聽,如果存在(epi不為空),就返回-EEXIST錯誤。否則說明參數正常,然後先默認設置對目標文件的POLLERR和POLLHUP監聽事件,然後調用ep_insert函數,將對目標文件的監聽事件插入到ep維護的紅黑樹裡面:

sys_epoll_ctl -> ep_insert:

前面說過,對目標文件的監聽是由一個epitem結構的監聽項變數維護的,所以在ep_insert函數裡面,首先調用kmem_cache_alloc函數,從slab分配器裡面分配一個epitem結構監聽項,然後對該結構進行初始化,這里也沒有什麼好說的。我們接下來看ep_item_poll這個函數調用:

sys_epoll_ctl -> ep_insert -> ep_item_poll:

ep_item_poll函數裡面,調用目標文件的poll函數,這個函數針對不同的目標文件而指向不同的函數,如果目標文件為套接字的話,這個poll就指向sock_poll,而如果目標文件為tcp套接字來說,這個poll就是tcp_poll函數。雖然poll指向的函數可能會不同,但是其作用都是一樣的,就是獲取目標文件當前產生的事件位,並且將監聽項綁定到目標文件的poll鉤子裡面(最重要的是注冊ep_ptable_queue_proc這個poll callback回調函數),這步操作完成後,以後目標文件產生事件就會調用ep_ptable_queue_proc回調函數。

接下來,調用list_add_tail_rcu將當前監聽項添加到目標文件的f_ep_links鏈表裡面,該鏈表是目標文件的epoll鉤子鏈表,所有對該目標文件進行監聽的監聽項都會加入到該鏈表裡面。

然後就是調用ep_rbtree_insert,將epi監聽項添加到ep維護的紅黑樹裡面,這里不做解釋,代碼如下:

sys_epoll_ctl -> ep_insert -> ep_rbtree_insert:

前面提到,ep_insert有調用ep_item_poll去獲取目標文件產生的事件位,在調用epoll_ctl前這段時間,可能會產生相關進程需要監聽的事件,如果有監聽的事件產生,(revents & event->events 為 true),並且目標文件相關的監聽項沒有鏈接到ep的准備鏈表rdlist裡面的話,就將該監聽項添加到ep的rdlist准備鏈表裡面,rdlist鏈接的是該epoll描述符監聽的所有已經就緒的目標文件的監聽項。並且,如果有任務在等待產生事件時,就調用wake_up_locked函數喚醒所有正在等待的任務,處理相應的事件。當進程調用epoll_wait時,該進程就出現在ep的wq等待隊列裡面。接下來講解epoll_wait函數。

總結epoll_ctl函數:該函數根據監聽的事件,為目標文件申請一個監聽項,並將該監聽項掛人到eventpoll結構的紅黑樹裡面。

epoll_wait等待事件的產生,內核代碼如下:

sys_epoll_wait:

首先是對進程傳進來的一些參數的檢查:

參數全部檢查合格後,接下來就調用ep_poll函數進行真正的處理:

sys_epoll_wait -> ep_poll:

ep_poll中首先是對等待時間的處理,timeout超時時間以ms為單位,timeout大於0,說明等待timeout時間後超時,如果timeout等於0,函數不阻塞,直接返回,小於0的情況,是永久阻塞,直到有事件產生才返回。

當沒有事件產生時((!ep_events_available(ep))為true),調用__add_wait_queue_exclusive函數將當前進程加入到ep->wq等待隊列裡面,然後在一個無限for循環裡面,首先調用set_current_state(TASK_INTERRUPTIBLE),將當前進程設置為可中斷的睡眠狀態,然後當前進程就讓出cpu,進入睡眠,直到有其他進程調用wake_up或者有中斷信號進來喚醒本進程,它才會去執行接下來的代碼。

如果進程被喚醒後,首先檢查是否有事件產生,或者是否出現超時還是被其他信號喚醒的。如果出現這些情況,就跳出循環,將當前進程從ep->wp的等待隊列裡面移除,並且將當前進程設置為TASK_RUNNING就緒狀態。

如果真的有事件產生,就調用ep_send_events函數,將events事件轉移到用戶空間裡面。

sys_epoll_wait -> ep_poll -> ep_send_events:

ep_send_events沒有什麼工作,真正的工作是在ep_scan_ready_list函數裡面:

sys_epoll_wait -> ep_poll -> ep_send_events -> ep_scan_ready_list:

ep_scan_ready_list首先將ep就緒鏈表裡面的數據鏈接到一個全局的txlist裡面,然後清空ep的就緒鏈表,同時還將ep的ovflist鏈表設置為NULL,ovflist是用單鏈表,是一個接受就緒事件的備份鏈表,當內核進程將事件從內核拷貝到用戶空間時,這段時間目標文件可能會產生新的事件,這個時候,就需要將新的時間鏈入到ovlist裡面。

僅接著,調用sproc回調函數(這里將調用ep_send_events_proc函數)將事件數據從內核拷貝到用戶空間。

sys_epoll_wait -> ep_poll -> ep_send_events -> ep_scan_ready_list -> ep_send_events_proc:

ep_send_events_proc回調函數循環獲取監聽項的事件數據,對每個監聽項,調用ep_item_poll獲取監聽到的目標文件的事件,如果獲取到事件,就調用__put_user函數將數據拷貝到用戶空間。

回到ep_scan_ready_list函數,上面說到,在sproc回調函數執行期間,目標文件可能會產生新的事件鏈入ovlist鏈表裡面,所以,在回調結束後,需要重新將ovlist鏈表裡面的事件添加到rdllist就緒事件鏈表裡面。

同時在最後,如果rdlist不為空(表示是否有就緒事件),並且由進程等待該事件,就調用wake_up_locked再一次喚醒內核進程處理事件的到達(流程跟前面一樣,也就是將事件拷貝到用戶空間)。

到這,epoll_wait的流程是結束了,但是有一個問題,就是前面提到的進程調用epoll_wait後會睡眠,但是這個進程什麼時候被喚醒呢?在調用epoll_ctl為目標文件注冊監聽項時,對目標文件的監聽項注冊一個ep_ptable_queue_proc回調函數,ep_ptable_queue_proc回調函數將進程添加到目標文件的wakeup鏈表裡面,並且注冊ep_poll_callbak回調,當目標文件產生事件時,ep_poll_callbak回調就去喚醒等待隊列裡面的進程。

總結一下epoll該函數: epoll_wait函數會使調用它的進程進入睡眠(timeout為0時除外),如果有監聽的事件產生,該進程就被喚醒,同時將事件從內核裡面拷貝到用戶空間返回給該進程。

『肆』 開源閱讀好開發嘛

開源閱讀不太好開發,有一定的難度性,你可以購買一些這樣那樣的開源軟體的教程或者圖書(包括電子書)去學習,但一定不要以這些學習材料為主要的學習這些開源軟體的方法和途徑,有機會的話,或者說你想要學習的開源軟體所使用的開發語言正好是你熟悉或者使用的編程語言,那麼你應該盡量多去以閱讀這些開源項目的源碼本身為主。舉個例子,如果你是 C/C++ 後端開發者,那麼像 Redis、Nginx(它們都是使用 C 編寫的)這樣的開源項目的源碼你應該認真的去研讀一下;如果你是做 Windows C/C++ 客戶端或者一名 QT 客戶端開發人員,那麼像 MFC、DUILIB、金山衛士等源碼,你可以拿來讀一讀;如果你是 Java 程序員,Netty、Spring 等源碼是你進階路上必須邁過去的一關。為什麼建議以閱讀相關源碼為主,而不是其他相關教程呢?首先,任何其他相關教程介紹的內容都是基於這個軟體的源碼實現創作出來的,雖然能幫助你快速理解一些東西,但是不同的教程作者在閱讀同樣一份代碼時的注意點和側重點不一樣,加上如果作者在某些地方有理解偏差的,這種偏差會被引入你所學習的教程或者圖書裡面,也就是說,你學習的這些東西其實不是第一手的,而是經過別人加工或者理解意譯過的,在這個過程中如果別人理解有偏差,那麼你或多或少的會受一點影響。所以,為了"不受制於人」,親自去閱讀一些源碼是非常有必要的。以上是開源閱讀開發的部分內容,具體還是得自己去驗證和設計祝您生活愉快,謝謝提問😊

『伍』 nginx源代碼怎麼建立 vs工程

」, 除了閱讀代碼以外, 沒有更好的方法. 7.在尋找bug時, 請從問題的表現形式到問題的根源來分析代碼. 不要沿著不相關的路徑(誤入歧途). 8.我們要充分利用調試器|編譯器給出的警告或輸出的符號代碼|系統調用跟蹤器|資料庫結構化查詢語言的日誌機制...

『陸』 php新手學習路線是怎樣的

第一階段:基礎階段(基礎PHP程序員)

重點:把LNMP搞熟練(核心是安裝配置基本操作) 目標:能夠完成基本的LNMP系統安裝,簡單配置維護;能夠做基本的簡單系統的PHP開發;能夠在PHP中型系統中支持某個PHP功能模塊的開發。

時間:完成本階段的時間因人而異,有的成長快半年一年就過了,成長慢的兩三年也有。

  1. Linux

    基本命令、操作、啟動、基本服務配置(包括rpm安裝文件,各種服務配置等);會寫簡單的shell腳本和awk/sed 腳本命令等。

  2. Nginx

    做到能夠安裝配置nginx+php,知道基本的nginx核心配置選項,知道 server/fastcgi_pass/access_log 等基礎配置,目標是能夠讓nginx+php_fpm順利工作。

  3. MySQL

    會自己搭建mysql,知道基本的mysql配置選項;知道innodb和myisam的區別,知道針對InnoDB和MyISAM兩個引擎的不同配置選項;知道基本的兩個引擎的差異和選擇上面的區別;能夠純手工編譯搭建一個MySQL資料庫並且配置好編碼等正常穩定運行;核心主旨是能夠搭建一個可運行的MySQL資料庫。

  4. PHP

    基本語法數組、字元串、資料庫、XML、Socket、GD/ImageMgk圖片處理等等;熟悉各種跟MySQL操作鏈接的api(mysql/mysqli/PDO),知道各種編碼問題的解決;知道常規熟練使用的PHP框架(ThinkPHP、Zendframework、Yii、Yaf等);了解基本MVC的運行機制和為什麼這么做,稍微知道不同的PHP框架之間的區別;能夠快速學習一個MVC框架。能夠知道開發工程中的文件目錄組織,有基本的良好的代碼結構和風格,能夠完成小系統的開發和中型系統中某個模塊的開發工作。

  5. 前端

    如果條件時間允許,可以適當學習下 HTML/CSS/JS 等相關知識,知道什麼web標准,div+css的web/wap頁面模式,知道HTML5和HTML4的區別;了解一些基本的前端只是和JS框架(jQuery之類的);了解一些基本的JavaScript編程知識;(本項不是必須項,如果有時間,稍微了解一下是可以的,不過不建議作為重點,除非個人有強烈興趣)。

  6. 系統設計

    能夠完成小型系統的基本設計,包括簡單的資料庫設計,能夠完成基本的:瀏覽器 -> Nginx+PHP -> 資料庫 架構的設計開發工作;能夠支撐每天幾十萬到數百萬流量網站的開發維護工作;

    第二階段:提高階段 (中級PHP程序員)

    重點:提高針對LNMP的技能,能夠更全面的對LNMP有熟練的應用。 目標:能夠隨時隨地搭建好LNMP環境,快速完成常規配置;能夠追查解決大部分遇到的開發和線上環境的問題;能夠獨立承擔中型系統的構架和開發工作;能夠在大型系統中承擔某個中型模塊的開發工作。

    1. Linux

    在第一階段的基礎上面,能夠流暢的使用Shell腳本來完成很多自動化的工作;awk/sed/perl 也操作的不錯,能夠完成很多文本處理和數據統計等工作;基本能夠安裝大部分非特殊的Linux程序(包括各種庫、包、第三方依賴等等,比如MongoDB/Redis/Sphinx/Luncene/SVN之類的);了解基本的Linux服務,知道如何查看Linux的性能指標數據,知道基本的Linux下面的問題跟蹤等。

    2. Nginx

    在第一階段的基礎上面,了解復雜一些的Nginx配置;包括 多核配置、events、proxy_pass,sendfile/tcp_*配置,知道超時等相關配置和性能影響;知道nginx除了web server,還能夠承擔代理伺服器、反向靜態伺服器等配置;知道基本的nginx配置調優;知道如何配置許可權、編譯一個nginx擴展到nginx;知道基本的nginx運行原理(master/worker機制,epoll),知道為什麼nginx性能比apache性能好等知識。

    3. MySQL/MongoDB

    在第一階段的基礎上面,在MySQL開發方面,掌握很多小技巧,包括常規SQL優化(group by/order by/rand優化等);除了能夠搭建MySQL,還能夠冷熱備份MySQL數據,還知道影響innodb/myisam性能的配置選項(比如key_buffer/query_cache/sort_buffer/innodb_buffer_pool_size/innodb_flush_log_at_trx_commit等),也知道這些選項配置成為多少值合適;另外也了解一些特殊的配置選項,比如 知道如何搭建mysql主從同步的環境,知道各個binlog_format的區別;知道MySQL的性能追查,包括slow_log/explain等,還能夠知道基本的索引建立處理等知識;原理方面了解基本的MySQL的架構(Server+存儲引擎),知道基本的InnoDB/MyISAM索引存儲結構和不同(聚簇索引,B樹);知道基本的InnoDB事務處理機制;了解大部分MySQL異常情況的處理方案(或者知道哪兒找到處理方案)。條件允許的情況,建議了解一下NoSQL的代表MongoDB資料庫,順便對比跟MySQL的差別,同事能夠在合適的應用場景安全謹慎的使用MongoDB,知道基本的PHP與MongoDB的結合開發。

    4. Redis/Memcached

    在大部分中型系統裡面一定會涉及到緩存處理,所以一定要了解基本的緩存;知道Memcached和Redis的異同和應用場景,能夠獨立安裝 Redis/Memcached,了解Memcahed的一些基本特性和限制,比如最大的value值,知道PHP跟他們的使用結合;Redis了解基本工作原理和使用,了解常規的數據類型,知道什麼場景應用什麼類型,了解Redis的事務等等。原理部分,能夠大概了解Memcached的內存結構(slab機制),redis就了解常用數據類型底層實現存儲結構(SDS/鏈表/SkipList/HashTable)等等,順便了解一下Redis的事務、RDB、AOF等機制更好。

    5. PHP

    除了第一階段的能力,安裝配置方面能夠隨意安裝PHP和各種第三方擴展的編譯安裝配置;了解php-fpm的大部分配置選項和含義(如max_requests/max_children/request_terminate_timeout之類的影響性能的配置),知道mod_php/fastcgi的區別;在PHP方面已經能夠熟練各種基礎技術,還包括各種深入些的PHP,包括對PHP面向對象的深入理解/SPL/語法層面的特殊特性比如反射之類的;在框架方面已經閱讀過最少一個以上常規PHP MVC框架的代碼了,知道基本PHP框架內部實現機制和設計思想;在PHP開發中已經能夠熟練使用常規的設計模式來應用開發(抽象工廠/單例/觀察者/命令鏈/策略/適配器 等模式);建議開發自己的PHP MVC框架來充分讓開發自由化,讓自己深入理解MVC模式,也讓自己能夠在業務項目開發里快速升級;熟悉PHP的各種代碼優化方法,熟悉大部分PHP安全方面問題的解決處理;熟悉基本的PHP執行的機制原理(Zend引擎/擴展基本工作機制)。

    6. C/C++

    開始涉獵一定的C/C++語言,能夠寫基本的C/C++代碼,對基本的C/C++語法熟悉(指針、數組操作、字元串、常規標准API)和數據結構(鏈表、樹、哈希、隊列)有一定的熟悉下;對Linux下面的C語言開發有基本的了解概念,會簡單的makefile文件編寫,能夠使用簡單的GCC/GDB的程序編譯簡單調試工作;對基本的網路編程有大概了解。(本項是為了向更高層次打下基礎)。

    7. 前端

    在第一階段的基礎上面,熟悉基本的HTTP協議(協議代碼200/300/400/500,基本的HTTP交互頭);條件允許,可以在深入寫出稍微優雅的HTML+CSS+JavaScript,或者能夠大致簡單使用某些前端框架(jQuery/YUI/ExtJS/RequireJS/BootStrap之類);如果條件允許,可以深入學習JavaScript編程,比如閉包機制、DOM處理;再深入些可以讀讀jQuery源碼做深入學習。(本項不做重點學習,除非對前端有興趣)。

    8. 系統設計

    能夠設計大部分中型系統的網站架構、資料庫、基本PHP框架選型;性能測試排查處理等;能夠完成類似:瀏覽器 -> CDN(Squid) -> Nginx+PHP -> 緩存 -> 資料庫 結構網站的基本設計開發維護;能夠支撐每天數百萬到千萬流量基本網站的開發維護工作;

    第三階段:高級階段 (高級PHP程序員)

    重點:除了基本的LNMP程序,還能夠在某個方向或領域有深入學習。(縱深維度發展) 目標:除了能夠完成基本的PHP業務開發,還能夠解決大部分深入復雜的技術問題,並且可以獨立設計完成中大型的系統設計和開發工作;自己能夠獨立hold深入某個技術方向,在這塊比較專業。(比如在MySQL、Nginx、PHP、Redis等等任一方向深入研究)

    1. Linux

    除了第二階段的能力,在Linux下面除了常規的操作和性能監控跟蹤,還能夠使用很多高級復雜的命令完成工作(watch/tcpmp/starce/ldd/ar等);在shell腳本方面,已經能夠編寫比較復雜的shell腳本(超過500行)來協助完成很多包括備份、自動化處理、監控等工作的shell;對awk/sed/perl 等應用已經如火純青,能夠隨意操作控制處理文本統計分析各種復雜格式的數據;對Linux內部機制有一些了解,對內核模塊載入,啟動錯誤處理等等有個基本的處理;同時對一些其他相關的東西也了解,比如NFS、磁碟管理等等;

    2. Nginx

    在第二階段的基礎上面,已經能夠把Nginx操作的很熟練,能夠對Nginx進行更深入的運維工作,比如監控、性能優化,復雜問題處理等等;看個人興趣,更多方面可以考慮側重在關於Nginx工作原理部分的深入學習,主要表現在閱讀源碼開始,比如具體的master/worker工作機制,Nginx內部的事件處理,內存管理等等;同時可以學習Nginx擴展的開發,可以定製一些自己私有的擴展;同時可以對Nginx+Lua有一定程度的了解,看看是否可以結合應用出更好模式;這個階段的要求是對Nginx原理的深入理解,可以考慮成為Nginx方向的深入專業者。

    3. MySQL/MongoDB

    在第二階段的基礎上面,在MySQL應用方面,除了之前的基本SQL優化,還能夠在完成一些復雜操作,比如大批量數據的導入導出,線上大批量數據的更改表結構或者增刪索引欄位等等高危操作;除了安裝配置,已經能夠處理更多復雜的MySQL的問題,比如各種問題的追查,主從同步延遲問題的解決、跨機房同步數據方案、MySQL高可用架構等都有涉及了解;對MySQL應用層面,對MySQL的核心關鍵技術比較熟悉,比如事務機制(隔離級別、鎖等)、對觸發器、分區等技術有一定了解和應用;對MySQL性能方面,有包括磁碟優化(SAS遷移到SSD)、伺服器優化(內存、伺服器本身配置)、除了二階段的其他核心性能優化選項(innodb_log_buffer_size/back_log/table_open_cache/thread_cache_size/innodb_lock_wait_timeout等)、連接池軟體選擇應用,對show *(show status/show profile)類的操作語句有深入了解,能夠完成大部分的性能問題追查;MySQL備份技術的深入熟悉,包括災備還原、對Binlog的深入理解,冷熱備份,多IDC備份等;在MySQL原理方面,有更多了解,比如對MySQL的工作機制開始閱讀部分源碼,比如對主從同步(復制)技術的源碼學習,或者對某個存儲引擎(MyISAM/Innodb/TokuDB)等等的源碼學習理解,如果條件允許,可以參考CSV引擎開發自己簡單的存儲引擎來保存一些數據,增強對MySQL的理解;在這個過程,如果自己有興趣,也可以考慮往DBA方向發展。MongoDB層面,可以考慮比如說在寫少讀多的情況開始在線上應用MongoDB,或者是做一些線上的數據分析處理的操作,具體場景可以按照工作來,不過核心是要更好的深入理解RMDBS和NoSQL的不同場景下面的應用,如果條件或者興趣允許,可以開始深入學習一下MongoDB的工作機制。

    4. Redis/Memcached

    在第二階段的基礎上面,能夠更深入的應用和學習。因為Memcached不是特別復雜,建議可以把源碼進行閱讀,特別是內存管理部分,方便深入理解;Redis部分,可以多做一些復雜的數據結構的應用(zset來做排行榜排序操作/事務處理用來保證原子性在秒殺類場景應用之類的使用操作);多涉及aof等同步機制的學習應用,設計一個高可用的Redis應用架構和集群;建議可以深入的學習一下Redis的源碼,把在第二階段積累的知識都可以應用上,特別可以閱讀一下包括核心事件管理、內存管理、內部核心數據結構等充分學習了解一下。如果興趣允許,可以成為一個Redis方面非常專業的使用者。

    5. PHP

    作為基礎核心技能,我們在第二階段的基礎上面,需要有更深入的學習和應用。從基本代碼應用上面來說,能夠解決在PHP開發中遇到95%的問題,了解大部分PHP的技巧;對大部分的PHP框架能夠迅速在一天內上手使用,並且了解各個主流PHP框架的優缺點,能夠迅速方便項目開發中做技術選型;在配置方面,除了常規第二階段會的知識,會了解一些比較偏門的配置選項(php auto_prepend_file/auto_append_file),包括擴展中的一些復雜高級配置和原理(比如memcached擴展配置中的memcache.hash_strategy、apc擴展配置中的apc.mmap_file_mask/apc.slam_defense/apc.file_update_protection之類的);對php的工作機制比較了解,包括php-fpm工作機制(比如php-fpm在不同配置機器下面開啟進程數量計算以及原理),對zend引擎有基本熟悉(vm/gc/stream處理),閱讀過基本的PHP內核源碼(或者閱讀過相關文章),對PHP內部機制的大部分核心數據結構(基礎類型/Array/Object)實現有了解,對於核心基礎結構(zval/hashtable/gc)有深入學習了解;能夠進行基本的PHP擴展開發,了解一些擴展開發的中高級知識(minit/rinit等),熟悉php跟apache/nginx不同的通信交互方式細節(mod_php/fastcgi);除了開發PHP擴展,可以考慮學習開發Zend擴展,從更底層去了解PHP。

    6. C/C++

    在第二階段基礎上面,能夠在C/C++語言方面有更深入的學習了解,能夠完成中小型C/C++系統的開發工作;除了基本第二階段的基礎C/C++語法和數據結構,也能夠學習一些特殊數據結構(b-tree/rb-tree/skiplist/lsm-tree/trie-tree等)方便在特殊工作中需求;在系統編程方面,熟悉多進程、多線程編程;多進程情況下面了解大部分多進程之間的通信方式,能夠靈活選擇通信方式(共享內存/信號量/管道等);多線程編程能夠良好的解決鎖沖突問題,並且能夠進行多線程程序的開發調試工作;同時對網路編程比較熟悉,了解多進程模型/多線程模型/非同步網路IO模型的差別和選型,熟悉不同非同步網路IO模型的原理和差異(select/poll/epoll/iocp等),並且熟悉常見的非同步框架(ACE/ICE/libev/libevent/libuv/Boost.ASIO等)和使用,如果閑暇也可以看看一些國產自己開發的庫(比如muo);同時能夠設計好的高並發程序架構(leader-follow/master-worker等);了解大部分C/C++後端Server開發中的問題(內存管理、日誌列印、高並發、前後端通信協議、服務監控),知道各個後端服務RPC通信問題(struct/http/thirft/protobuf等);能夠更熟絡的使用GCC和GDB來開發編譯調試程序,在線上程序core掉後能夠迅速追查跟蹤解決問題;通用模塊開發方面,可以積累或者開發一些通用的工具或庫(比如非同步網路框架、日誌庫、內存池、線程池等),不過開發後是否應用要謹慎,省的埋坑去追bug。

    7. 前端

    深入了解HTTP協議(包括各個細致協議特殊協議代碼和背後原因,比如302靜態文件緩存了,502是nginx後面php掛了之類的);除了之前的前端方面的各種框架應用整合能力,前端方面的學習如果有興趣可以更深入,表現形式是,可以自己開發一些類似jQuery的前端框架,或者開發一個富文本編輯器之類的比較瑣碎考驗JavaScript功力。

    8. 其他領域語言學習

    在基礎的PHP/C/C++語言方面有基本積累,建議在當前階段可以嘗試學習不同的編程語言,看個人興趣愛好,腳本類語言可以學學 python/Ruby 之類的,函數式編程語言可以試試 Lisp/Haskell/Scala/Erlang 之類的,靜態語言可以試試 Java/Golang,數據統計分析可以了解了解R語言,如果想換個視角做後端業務,可以試試 Node.js還有前面提到的跟Nginx結合的Nginx_Lua等。學習不同的語言主要是提升自己的視野和解決問題手段的差異,比如會了解除了進程/線程,還有輕量級協程;比如在跨機器通信場景下面,Erlang的解決方案簡單的驚人;比如在不想選擇C/C++的情況下,還有類似高效的Erlang/Golang可用等等;主要是提升視野。

    9. 其他專業方向學習

    在本階段裡面,會除了基本的LNMP技能之外,會考慮一些其他領域知識的學習,這些都是可以的,看個人興趣和長期的目標方向。目前情況能夠選擇的領域比較多,比如、雲計算(分布式存儲、分布式計算、虛擬機等),機器學習(數據挖掘、模式識別等,應用到統計、個性化推薦),自然語言處理(中文分詞等),搜索引擎技術、圖形圖像、語音識別等等。除了這些高大上的,也有很多偏工程方面可以學習的地方,比如高性能系統、移動開發(Android/IOS)、計算機安全、嵌入式系統、硬體等方向。

    10. 系統設計

    系統設計在第二階段的基礎之上,能夠應用掌握的經驗技能,設計出比較復雜的中大型系統,能夠解決大部分線上的各種復雜系統的問題,完成類似 瀏覽器 -> CDN -> 負載均衡 ->接入層 -> Nginx+PHP -> 業務緩存 -> 資料庫 -> 各路復雜後端RPC交互(存儲後端、邏輯後端、反作弊後端、外部服務) -> 更多後端 醬紫的復雜業務;能夠支撐每天數千萬到數億流量網站的正常開發維護工作。

『柒』 fastdfs 與 為什麼要搭建nginx

上次搭建FastDFS使用的版本是v4.05
http://blog.itpub.net/29254281/viewspace-1283539/
這個版本已經比較舊了

最新的版本是v5.04,由於作者重構了代碼,所以安裝過程還是有一些不一致.
最新版本下載地址:
http://sourceforge.net/projects/fastdfs/files/
安裝可以參考壓縮包內的INSTALL文件。

實驗還是搭建一個FastDFS環境,並增加Nginx模塊
所用軟體:
FastDFS_v5.04.tar.gz
libfastcommon-master.zip
fastdfs-nginx-mole_v1.16.tar.gz
nginx-1.6.2.tar.gz

與之前版本不同的是,v5.04首先需要安裝libfastcommon
下載地址:
https://github.com/happyfish100/libfastcommon.git

1.安裝libfastcommon
在每一台伺服器上,解壓libfastcommon,進入libfastcommon-master目錄執行
./make.sh
./make.sh install

可以看到libfastcommon.so安裝到了/usr/lib64/libfastcommon.so

但是FastDFS主程序設置的lib目錄是/usr/local/lib
所以需要創建軟鏈接.
ln -s /usr/lib64/libfastcommon.so /usr/local/lib/libfastcommon.so
ln -s /usr/lib64/libfastcommon.so /usr/lib/libfastcommon.so
ln -s /usr/lib64/libfdfsclient.so /usr/local/lib/libfdfsclient.so
ln -s /usr/lib64/libfdfsclient.so /usr/lib/libfdfsclient.so

2.安裝FastDFS主程序
這個版本似乎已經不需要libevent依賴
在每台伺服器,解壓縮FastDFS_v5.04.tar.gz,進入FastDFS目錄
執行
./make.sh
./make.sh install
如果上步的軟鏈接創建成功,就應該會非常順利。

配置Tracker伺服器(192.168.1.70)
vim /etc/fdfs/tracker.conf文件,修改如下內容
base_path=/tracker
然後執行命令
fdfs_trackerd tracker.conf

配置Storage伺服器(192.168.1.80,192.168.1.30)
vim /etc/fdfs/storage.conf
group_name=group1
base_path=/storage
store_path0=/storage
tracker_server=192.168.1.70:22122
然後執行命令
fdfs_storaged storage.conf

執行測試,修改Tracker伺服器192.168.1.70的配置文件/etc/fdfs/client.conf
tracker_server=192.168.1.170:22122
執行命令
[root@mysql1 fdfs]# fdfs_upload_file client.conf /home/nginx/FastDFS_v5.04.tar.gz
group1/M00/00/00/wKgBHlQvrQGARrS6AAU9tcFAzok.tar.gz

3.解壓fastdfs-nginx-mole
FastDFS通過Tracker伺服器,將文件放在Storage伺服器存儲,
但是同組之間的伺服器需要復制文件,有延遲的問題.
假設Tracker伺服器將文件上傳到了192.168.1.80,文件ID已經返回客戶端,
這時,後台會將這個文件復制到192.168.1.30,如果復制沒有完成,客戶端就用這個ID在192.168.1.30取文件,肯定會出現錯誤
這個fastdfs-nginx-mole可以重定向連接到源伺服器取文件,避免客戶端由於復制延遲的問題,出現錯誤。

修改fastdfs-nginx-mole的config文件
原來的內容是
CORE_INCS="$CORE_INCS /usr/local/include/fastdfs /usr/local/include/fastcommon/"

vim /home/nginx/fastdfs-nginx-mole/src/config,修改為
CORE_INCS="$CORE_INCS /usr/include/fastdfs /usr/include/fastcommon"

各個版本的位置並不統一.所以需要根據自己的版本修改位置。

4.安裝nginx
在每個Storage伺服器上安裝Nginx
http://blog.itpub.net/29254281/viewspace-1283760/

yum -y install gcc automake autoconf libtool make gcc-c++ pcre* zlib openssl openssl-devel

增加fastdfs-nginx-mole模塊
./configure \
--prefix=/home/nginx/nginx-1.6.2 \
--sbin-path=/home/nginx/nginx-1.6.2/nginx \
--conf-path=/home/nginx/nginx-1.6.2/nginx.conf \
--pid-path=/home/nginx/nginx-1.6.2/nginx.pid \
--with-http_ssl_mole \
--add-mole=/home/nginx/fastdfs-nginx-mole/src

make -j `cat /proc/cpuinfo | grep processor| wc -l` && make install

復制fastdfs-nginx-mole源碼中的配置文件到/etc/fdfs
cp /home/nginx/fastdfs-nginx-mole/src/mod_fastdfs.conf /etc/fdfs
修改該配置文件
group_name=group1
tracker_server=192.168.1.70:22122
store_path0=/storage
base_path=/storage

復制FastDFS的配置到/etc/fdfs

修改Nginx配置文件
location /M00 {
root /storage;
ngx_fastdfs_mole;
}
在/storage目錄下創建軟連接,將其鏈接到實際存放數據的目錄,
[root@mysql2 storage]# pwd
/storage
[root@mysql2 storage]# ln -s data/ M00

創建軟鏈接的好處是方便多目錄的管理

啟動Nginx,就可以使用HTTP下載了.

注意事項:
1.FastDFS各個版本安裝方式有差別,需要閱讀INSTALL文件
2.FastDFS各個組件的默認位置可能不同,需要按照版本創建相應的軟鏈接

『捌』 C++ int i[233];我直接這樣寫代表了什麼意思

C++ int i[233];直接這樣寫代表了,定義了一個整形的數組,共有233個整形元素,數組的名字叫做i。

『玖』 FastAPI 源碼閱讀 (一) ASGI應用

本章開啟 FastAPI 的源碼閱讀,FastAPI是當下python web中一顆新星,是一個劃時代的框架。從誕生便是以快速和簡潔為核心理念。
它繼承於 Starlette ,是在其基礎上的完善與擴展。詳細內容可以翻看我之前的源碼閱讀。

openapi() 與 setup() 是在初始化階段,對 OpenAPI 文檔進行初始化的函數。
而 add_api_route() 一直到 trace() ,是關於路由的函數,它們都是直接對 router 的方法傳參引用。所以這些放在解讀 routing.py 時一並進行。

除了 Starlette 原生的參數,大量參數都是和API文檔相關。
而路由從 Starlette 的 Router 換成了新式的 APIRouter

這兩個概念查詢了大量文檔才搞明白。他們主要是關於文檔與反向代理的參數。當使用了Nginx時等反向代理時,從Uvicorn直接訪問,和從Nginx代理訪問,路徑可能出現不一致。比如Nginx中的Fastapi根目錄是 127.0.0.1/api/ ,而Uvicorn角度看是 127.0.0.1:8000/ 。對於API介面來說,其實這個是沒有影響的,因為伺服器會自動幫我們解決這個問題。但對於API文檔來說,就會出現問題。

因為當我們打開/docs時,網頁會尋找 openapi.json 。他的是寫在html內部的,而不是變化的。這會導致什麼問題?

例如當我們從Uvicorn訪問 127.0.0.1:8000/docs 時,他會尋找 /openapi.json 即去訪問 127.0.0.1:8000/openapi.json (了解前端的應該知道)

但是假如我們這時,從Nginx外來訪問文檔,假設我們這樣設置Nginx:

我們需要訪問 127.0.0.1/api/docs ,才能從代理外部訪問。而打開docs時,我們會尋找 openapi.json

所以這時,它應該在 127.0.0.1/api/openapi 這個位置存在。
但我們的瀏覽器不知道這些,他會按照 /openapi.json ,會去尋 127.0.0.1/openapi.json 這個位置。所以他不可能找到 openapi.json ,自然會啟動失敗。

這其實是openapi文檔前端的問題。

root_path ,是用來解決這個問題的。既然 /openapi.json 找不到,那我自己改成 /api/openapi.json 不就成了么。
root_path 即是這個 /api ,這個是在定義時手動設置的參數。為了告訴FastAPI,它處在整個主機中的哪個位置。即告知 所在根目錄 。這樣,FastAPI就有了"自知之明",乖乖把這個 前綴 加上。來找到正確的 openapi.json

加上了 root_path openapi.json 的位置變成了 /api/openapi.json 。當你想重新用Uvicorn提供的地址從代理內訪問時,他會去尋找哪?沒錯 127.0.0.1:8000/api/openapi.json ,但我們從代理內部訪問,並不需要這個 前綴 ,但它還是 「善意」 的幫我們加上了,所以這時候內部的訪問失靈了。

雖然我們不大可能需要從兩個位置訪問這個完全一樣的api文檔。但這點一定要注意。

我在翻官方文檔時,看到他們把 root_path 吹得天花亂墜,甚至棄用了 openapi_prefix 參數。但最後是把我弄得暈頭轉向。

這樣要提到 servers 這個參數,官方首先給了這么段示例,稍作修改。

當我們打開API文檔時

我們可以切換這個 Servers 時,底下測試介面的發送鏈接也會變成相應的。

但是記住,切換這個server,下面的介面不會發送變化,只是發送的host會改變。
這代表,雖然可以通過文檔,測試多個其他主機的介面。但是這些主機和自己之間,需要擁有一致的介面。這種情況通常像在 線上/開發伺服器 或者 伺服器集群 中可能出現。雖然不要求完全一致,但為了這樣做有實際意義,最好大體上是一致的。

但是我們看到,這是在代理外打開的,如果我們想從代理內打開,需要去掉 root_path 。會發生什麼?
我們將 root_path 注釋掉:

如果想解決這個問題,只需要將自身手動加入到 Servers 中。

關於 root_path_in_servers ,當 root_path servers 都存在時, root_path 會自動將自己加入到 servers 中。但如果這個置為False,就不會自動加入。(默認為True)

API文檔實際上以字元串方式,在FastAPI內部拼接的。實際上就是傳統的 模板(Templates) ,這個相信大家都很熟悉了。優點是生成時靈活,但缺點是不容易二次開發。fastapi提供了好幾種文檔插件,也可以自己添加需要的。

這么長一大串,實際上就一句話 self.router.add_api_route() ,其他剩下的那些我暫且省略的,其實基本都是這樣的。就是調用router的一個功能。下面我用省略方式將它們列出。

可以看到有些在這里就做了閉包,實際上除了這里的'add_api_route()'他們最終都是要做閉包的。只是過程放在里router里。它們最終的指向都是 router.add_api_route() ,這是一個添加真正將 endpoint 加入到路由中的方法。
FastAPI 添加路由的方式,在starlette的傳統路由列表方式上做了改進,變成了裝飾器式。

其實就是通過這些方法作為裝飾器,將自身作為 endpoint 傳入生成 route 節點,加入到 routes 中。

FastAPI的入口沒有太大的變化,借用starlette的 await self.middleware_stack(scope, receive, send) 直接進入中間件堆棧。

『拾』 《Nginx模塊開發指南使用 C++11和 Boost程序庫》txt下載在線閱讀全文,求百度雲資源

《Nginx模塊開發指南》(羅劍鋒)電子書網盤下載免費在線閱讀

鏈接: https://pan..com/s/19HN5knWwFEO8-M78kQc-6w

提取碼: ri89

書名:Nginx模塊開發指南

作者:羅劍鋒

出版社:電子工業出版社

出版年份:2015-10

頁數:372

內容簡介:

Nginx 是由俄羅斯工程師Igor Sysoev 開發的一個高性能Web 伺服器,運行效率遠超傳統的Apache、Tomcat,是世界第二大Web 伺服器,被國內外諸多頂級互聯網公司採用。

Nginx 的一個突出特點是其靈活優秀的模塊化架構,可以在不修改核心的前提下增加任意功能,自2004 年發布至今,已經擁有百餘個官方及非官方的功能模塊(如fastcgi、memcached、mysql 等),使得Nginx 成長為了一個近乎「全能」的伺服器軟體。

Nginx 以純C 語言實現,開發擴展功能模塊也大多使用C 語言,但由於C 語言固有的過程式特性,編寫、調試代碼都較麻煩——特別是對於Nginx 的初學者。《Nginx 模塊開發指南:使用C++11 和Boost 程序庫》深入源碼,詳細解析了模塊體系、配置指令、HTTP 框架等Nginx 核心運行機制,並在此基礎上講解如何使用C++和Boost 程序庫來開發Nginx 模塊,充分利用現代C++里的大量新特性和庫組件,讓Nginx 的模塊開發變得更加便捷、輕松和愉快。

《Nginx 模塊開發指南:使用C++11 和Boost 程序庫》結構嚴謹、脈絡清晰、論述精確、詳略得當,值得廣大軟體開發工程師、系統運維工程師和編程愛好者擁有。

熱點內容
什麼電腦單機游戲好玩又免費配置低 發布:2025-07-04 22:51:27 瀏覽:417
真香配置有哪些 發布:2025-07-04 22:49:05 瀏覽:207
安卓在哪裡找游戲 發布:2025-07-04 22:15:25 瀏覽:243
路由器訪問光貓 發布:2025-07-04 22:07:47 瀏覽:898
資料庫顯示語句 發布:2025-07-04 22:04:30 瀏覽:741
編程課道具 發布:2025-07-04 22:04:02 瀏覽:845
華為手機不是安卓什麼時候可以更新米加小鎮 發布:2025-07-04 22:01:37 瀏覽:786
飢荒伺服器搭建視頻 發布:2025-07-04 21:48:38 瀏覽:524
github上傳文件夾 發布:2025-07-04 21:29:22 瀏覽:1003
php課程學習中心 發布:2025-07-04 21:29:16 瀏覽:298