數據結構:緩存
成員:數據結構
1.一個隊列m_queue.其中存放Task,也就是任務,生產者線程,也就是主線程,往這個隊列中push;消費者,也就是工做線程,不停地從其中拿走Task去工做。app
關於Task,原型爲boost::function<void()> Task;socket
2.一個表示線程數量的變量m_threadNum函數
3.一個表示隊列,即流水線的最大長度變量m_maxSizethis
4.一個互斥量m_mutex,用於條件變量判斷時對隊列的狀態進行保護url
5.一個條件變量m_notFull,用於通知喚醒消費者線程線程
6.一個條件變量m_notEmpty,用於通知喚醒生產者線程設計
函數:指針
1.線程池構造函數ThreadPool(int threadNum,int maxSize)
threadNum爲線程數量,maxSize爲隊列最大大小
在構造函數中建立threadNum個工做線程
2.靜態函數startThread
工做線程實際上的工做內容,建立的工做線程從startThread開始工做。
首先pthread_detach,分離後線程的資源自動回收,不用join
startThread中傳入線程池的this指針,這樣靜態函數startThread能夠經過這個this指針調用線程池中的Work函數,Work纔是真正的線程工做的主體
3.Work函數
Work內是一個for循環f(;;),循環內調用函數Take()從線程池的隊列中取走任務,若是取來的任務可用,則執行這個任務
4.Take函數
典型的生產者消費者模型,首先用m_mutex加鎖,加鎖保護當前隊列狀態的原子性,用while(m_queue.empty())判斷流水線中是否有工做,若是沒有則wait陷入沉睡,直到生產者喚醒
若是有工做,則拿走,並返回這個Task;Work函數拿到這個Task後執行這個任務
5.append函數
參數傳入bind的function
典型的生產者消費者模型,用於生產者往流水線中添加任務。依然是mutex加鎖保護隊列的狀態,若是非滿則將這個function加入到隊列中
緩存Cache:
數據成員:
一個哈希表unordered_map:key爲頁面名,做爲key;value爲一個shared_ptr指針,指向FileInfo,FileInfo爲封裝了頁面mmap到內存的類
一個互斥鎖mutex,用於緩存滿了刪除時保證原子性
函數:
1.getFile用於在頁面緩存中尋找請求的資源。
首先加鎖,防止請求資源時該緩存被淘汰而出現問題。而後在哈希表中用文件名去尋找,若是未找到,則將這個頁面加入緩存。若是此時緩存已滿,則淘汰掉訪問次數較少的頁面,訪問次數的統計被封裝在FileInfo中。
2.清楚緩存的函數cleancache:
遍歷哈希表,若是訪問次數小於10的頁面均淘汰掉。
頁面封裝FileInfo:
數據成員:
1.存儲 經過mmap映射到內存的地址 的變量addr:所訪問的頁面都經過mmap映射到內存,而後記錄這個地址,封裝到FileInfo中,這樣每次打開就不用了從新IO打開文件,只要經過這個文件對應的FileInfo中的addr便可尋址到頁面對應的位置
2.size表示文件的大小
3.count用於統計頁面的訪問次數,當緩存滿時以此爲基準進行淘汰
方法:
1.構造函數:經過文件名打開對應文件,而後使用mmap將之映射到內存,mmap返回的結果賦予addr,用於尋址
2.析構函數:munmap來釋放映射到內存的內容。固然,析構的時間由shared_ptr來掌握
Http_Handle封裝:
數據成員:
1.文件描述符fd,用於表示對應的套接字
2.狀態變量state:
state爲Read,process轉入processRead進行處理,表示此時應該將對端的數據存入readBuf緩衝區,並分析readBuf緩衝區的內容,根據url找到對應的文件,打開並加入緩存,並寫好響應頭到writeBuf
state爲Write,process轉入processWrite進行處理,將writeBuf內的響應信息寫到對端,將請求的資源寫入到對端。
3.兩個緩存readBuf和writeBuf:readBuf用於存放對端發送過來的數據,好比Http頭的信息,將之存放在readBuf中;writeBuf用於存放響應信息,好比響應行
4.一個指向所請求資源的FileInfo封裝的shared_ptr指針,經過這個指針來找到資源內容,並寫道對端。
總體流程:
首先服務端進入被動打開:
建立一個Socket,將這個socket綁定到8080端口,listen後轉爲監聽套接字,用於監聽請求
建立epoll,設定監聽的文件描述符個數
將監聽套接字listenfd加入epoll的監聽範圍
初始化線程池,建立若干工做線程,設定隊列(流水線)大小
進入while(true)循環:
epoll_wait等待事件發生,當listenfd可讀,表明有請求,因爲使用的是ET模式,所以listenfd要一直accept到返回EAGAIN,每accept一個,返回套接字connfd,將這個套接字封裝成Http_Handle,此時狀態爲Read,及等待對端像這個socket發送數據並分析。將這個套接字connfd也加入到epoll的監聽裏,監聽時間爲EPOLLIN,也就是可讀。
當epoll_wait等待來的是其餘事件,將這個事件對應的fd,把它的process函數bind到function上,並將這個function加入到流水線
工做線程從流水線上拿到這個Task,進行執行:
此時這個Task至關於綁定到某個Http_Handle上的Http_Handle::process,消費者執行process時,根據Http_Handle中state的狀態來決定接下來的動做:
1.Read:表明讀取對端發過來的鏈接包到readBuf,並分析readBuf的內容,找到URL指定的資源,這是經過緩存來找到的。找到後將響應行和響應頭相關的信息寫入writeBuf(由於writeBuf原本就被初始化爲全0,因此不用在尾部加\0)。結束後將狀態設置爲Write,即只須要講這些信息以及文件寫入到對端就能夠了。修改本fd在epoll中的監聽事件,原本是監聽可讀,如今要寫了,所以監聽EPOLLOUT。
2.Write:表明此時應該向對端寫數據了,轉入執行processWrite:這個過程就是將writeBuf中的頭信息寫到對端,而後用Http_Handle中封裝的FileInfo的智能指針找到映射到內存裏的文件,將文件寫到對端。一切結束後表明這個Socket的任務已經完成,斷開鏈接。
這裏主線程至關於生產者,不斷的往隊列中append不一樣fd的process函數,而後消費者從隊列中拿走這些process函數, process函數會根據Http_Handle.中State的狀態不一樣執行processRead或processWrite.