1. 程式人生 > >Linux + C + Epoll實現高併發伺服器(執行緒池 + 資料庫連線池)

Linux + C + Epoll實現高併發伺服器(執行緒池 + 資料庫連線池)

一, 背景

       先說下我要實現的功能,server端一直在linux平臺下面跑,當客戶端有請求過來的時候server端接受到請求,拿到客戶端的資料,根據拿到的資料做出相應的處理,得到處理的結果直接把結果資料傳送給客戶端。這樣一個連線的請求結束,我的不是長連線的情況,不會一直保持客戶端的連線。來一個處理一個處理完了就結束了。

二,原始碼下載(包括客戶端測試程式碼)

  1. 我把邏輯處理部分簡單化了,如果這份程式碼對你們有用的話,可以自己實現邏輯處理部分。
  2. 程式碼是要傳入引數的,埠
  3. 程式碼我已經在Linux下面編譯過了,也測試號了,因為程式碼中用到了資料庫,如果你沒有編譯過可能要在Linux下裝Mysql資料庫。

三,程式碼的簡單介紹

  • socket接受執行緒:C語言為了高併發所以選擇了epoll。當程式啟動的時候(g_net_update.c檔案中main函式,會啟動一個thread見函式create_accept_task)這個thread就處理一件事情,只管接收客戶端的連線,當有連線進來的時候 通過epoll_ctl函式,把socket fd 加入到epoll裡面去,epoll設定監聽事件EPOLLIN | EPOLLET; 主要是監聽的是加入到epoll中的socket是否可讀(因為我的需求是客戶端連上了server就會馬上向server傳送一份資料的)。其它的部分在主執行緒中處理。

  • 主執行緒:是一個無線迴圈,epoll_wait 函式相當於把客戶端的連線從epoll中拿出來(因為我們監聽的是EPOLLIN | EPOLLET)說明這個時候客戶端有資料傳送過來)。再通過recv_buffer_from_fd 函式把客戶端傳送過來的資料讀出來。然後其他的一切就拋給執行緒池去處理。

  • 執行緒池:(程式碼中我會在池裡面建立15個執行緒) 雙向連結串列。加入執行緒就是在連結串列後面加一個連結串列項,連結串列的前面會一個一個被拿出來處理。主要是malloc 函式free函式,sem_wait函式sem_post的處理(sem_wait 會阻塞當值大於0是會減一,sem_post是值加一)。typedef void* (FUNC)(void

    arg, int index);是我們自定義的執行緒的邏輯處理部分,arg是引數,index是第幾個執行緒處理(我們隱形的給每個執行緒都標了號),例如程式碼中的respons_stb_info,更加具體可以看看程式碼裡面是怎麼實現的。聰明的你也可以改掉這塊的內容改成動態執行緒池,當某個時刻的處理比較多的時候能夠動態的增加執行緒,而不像我程式碼裡面的是固定的。

  • 資料庫連線池:按照我的需求在處理客戶端請求資料的時候是要訪問資料庫的。就是一下子創建出一堆的資料連線。要訪問資料庫的時候先去資料庫連線池中找出空閒的連線,具體可以看下程式碼。使用的時候可以參考下database_process.c檔案(程式碼中資料庫連線池和執行緒池中的個數是一樣的)。這裡我想說下get_db_connect_from_pool這個函式,我用了隨機數,我是為了不想每次都從0開始去判斷哪個連線沒有用到。為了資料庫連線池中的每個連結都能等概率的使用到,具體的還是可以看下程式碼的實現。

  • log檔案,程式碼中是可以自動儲存log資訊到檔案中去的,具體可以看下程式碼。

四,碰到的一些問題和解決辦法

  • 最初的時候server程式跑起來佔掉了linux 90%多的使用率,因為是我們在create_accept_task 中socket沒有設定成阻塞的。

  • server經常碰到一些莫名其妙的宕機,沒辦法用了core dump 去抓宕機的堆疊資訊看在哪個函式宕機的。

  • 在處理資料庫的時候有的資料會自動的斷掉(說是說8個小時) 後來採用的辦法是每次都先mysql_ping一次讓他重新連線上。

    就說幾點吧,其實還有好多其他的就不說了。

五,在Linux下面用到的幾個命令

  • ./server程式名 & //加&後臺執行。

  • killall server程式名 // 停掉server的執行。要在server目錄下面執行

  • netstat -antp|grep :埠號 // 檢視埠下的socket狀態

  • ps -eaf | grep server程式名 // 檢查程式是否在執行,不過我一般是netstat -antp|grep :埠號 來看程式是否在執行。