對于廣大C語言開發者來說,缺乏類似C++ STL和Boost的庫會讓開發受制于基礎庫的匱乏,也因此導致了開發效率的驟降。這也使得例如libevent這類事件庫(基礎組件庫)一時間大紅大紫。
今天,碼哥給大家帶來一款基礎庫,這套庫不僅僅提供了常用的數據結構、算法,如紅黑樹、斐波那契堆、隊列、KMP算法、RSA算法、各類哈希算法、數據恢復算法等等,還提供了多進程框架、多線程框架、跨平臺高性能事件等實用內容。注意:這是一款不依賴第三方的庫。
除此以外,它也是筆者之前文章(Melang腳本語言)中的核心庫。這也就意味著,使用該庫,不僅可以快速獲得上述內容,還可以讓開發者所構建的系統很方便地引入腳本語言的功能。
它就是——Melon
下面,碼哥便帶諸位一覽這個庫的功能。
數據結構
Melon中包含如下數據結構的實現:
- 雙向鏈表
- 斐波那契堆
- 哈希表
- 隊列
- 紅黑樹
其中:
- 雙向鏈表使用宏實現,可以通過兩行宏函數即可完成雙向隊列插入和刪除操作的聲明和定義。
- 斐波那契堆是一個最小堆,在庫中的事件功能中用于實現定時器的維護管理,當然,也可以單獨使用。
以上結構幾乎均可在其對應名稱的頭文件中找到數據結構定義以及函數定義。
一般情況下,數據結構的使用都是函數調用形式,因此也盡可能降低了不同組件間的耦合度。
算法
Melon中包含的算法如下:
- 加密算法:AES 、DES 、3DES 、RC4 、RSA
- 哈希算法:MD5 、SHA1 、SHA256
- Base64
- 大數計算
- FEC
- JSON
- 矩陣運算
- 里德所羅門編碼
- 正則匹配算法
- KMP
- Cron格式解析器
如上算法基本都在其各自頭文件中可以找到對應的函數聲明以及必要的數據結構定義。
其中,FEC與里德所羅門編碼均屬于糾錯碼,FEC常用于RTP中做數據修復,而里德所羅門編碼既可以用于實時語音中丟包恢復,也可以用于冗余陣列(RAID)和其他UDP丟包恢復的場景。關于里德所羅門編碼,感興趣的讀者可以閱讀碼哥之前的文章:神奇的數據恢復算法。
其他組件
前面的都是常規操作,這里才是重頭戲。
Melon中還包括如下實用組件:
- 錯誤碼管理
- 內存池
- 線程池
- I/O線程模型
- 數據鏈
- TCP 封裝
- 事件機制
- 文件緩存
- HTTP 處理
- 腳本語言
- 詞法分析器
- 語法解析器生成器
- websocket
因Melon作者Nginx中毒較深,所以Melon中部分機制與Nginx較為相似。
內存池:這里內存池不僅支持對從堆中分配的內存進行管理,還支持對共享內存的管理。
數據鏈與TCP封裝:TCP封裝中包含了阻塞與非阻塞下的收發邏輯,并利用數據鏈結構來存放發送數據與接收數據。
事件機制:事件機制中不僅支持epoll、select,還支持Kqueue,庫在編譯前會自行檢測平臺支持情況。事件包含了:
- 句柄(文件描述符)事件:讀、寫、出錯事件,以及超時事件(主要用于超時斷開鏈接);
- 定時事件(與句柄超時是兩碼事);
- 信號處理事件:這里的信號處理并非一個信號只能有一個處理事件,而是設置多少個處理函數就會執行多少個;
文件緩存:參考Nginx文件緩存,避免對同一文件的重復打開浪費文件描述符資源。
HTTP:包含了HTTP的接收解析和發送,該套接口依賴于數據鏈結構來進行處理,因此可配合TCP封裝一同使用。
腳本語言:內容較多,可另行參考:Melang腳本語言。
詞法分析器:之所以這個單獨算一個功能組件,是因為在Melon中,配置文件解析就是使用該詞法分析器處理的。僅通過三行C代碼就可以實現一個最最基礎的詞法分析器,這也歸功于C語言宏的強大。
websocket:該部分依賴于HTTP組件。
多進程:多進程采用一主多從模式,主進程做管理,從進程處理實際業務。主進程與從進程之間由socketpair相連,因此從進程異常退出,主進程會立刻拉起一個新的子進程,同時主子進程也可以通過該socketpair進行數據通信。除了自身子進程可以管理,也可以通過配置文件配置來拉起其他程序作為自己的子進程來管理,有些類似于supervisord。
多線程:多線程分為三類,一類是常規的線程池,一類是IO線程,還有一類是模塊化的線程。后者也是一主多從模型,主與子之間是通過socketpair進行通信,而每一個子線程都有其入口函數(類似main函數),每一個子線程通常都是處理一類單一事務。
使用舉例
上面說了那么多,下面就來看一個多進程的例子。
首先,我們要先安裝Melon:
$ git clone https://github.com/Water-Melon/Melon.git$ ./configure$ make$ sudo make install$ sudo echo "/usr/local/melon/lib/" >> /etc/ld.so.conf$ sudo ldconfig
安裝好后,Melon會被安裝在/usr/local/melon下。
接著,我們創建一個名為hello.c的源文件來完成我們期望的功能:
#include#include "mln_core.h"#include "mln_log.h"#include "mln_event.h"char text[1024];static int global_init(void);static void worker_process(mln_event_t *ev);static void print_handler(mln_event_t *ev, void *data);int main(int argc, char *argv[])struct mln_core_attr cattr;cattr.argc = argc;cattr.argv = argv;cattr.global_init = global_init;cattr.worker_process = worker_process;return mln_core_init(&cattr);static int global_init(void)//global variable init functionint n = snprintf(text, sizeof(text)-1, "hello worldn");text[n] = 0;return 0;static void worker_process(mln_event_t *ev)//we can set event handler here//let's set a timermln_event_set_timer(ev, 1000, text, print_handler);static void print_handler(mln_event_t *ev, void *data)mln_log(debug, "%sn", (char *)data);mln_event_set_timer(ev, 1000, data, print_handler);
這段代碼主要是初始化了一個全局變量,然后給每一個子進程創建了一個定時事件,即每一秒中輸出一個hello world。
我們先進行編譯鏈接生成可執行程序:
$ gcc -o hello hello.c -I /usr/local/melon/include/ -L /usr/local/melon/lib/ -lmelon
然后,我們需要先修改Melon庫的配置文件:
$ sudo vim /usr/local/melon/conf/melon.conflog_level "none";//user "root";daemon off;core_file_size "unlimited";//max_nofile 1024;worker_proc 1;thread_mode off;framework off;log_path "/usr/local/melon/logs/melon.log";* Configurations in the 'exec_proc' are the* processes which are customized by user.* Here is an example to show you how to* spawn a program.* keepalive "/tmp/a.out" ["arg1" "arg2" ...]* The command in this example is 'keepalive' that* indicate master process to supervise this* process. If process is killed, master process* would restart this program.* If you don't want master to restart it, you can* default "/tmp/a.out" ["arg1" "arg2" ...]* But you should know that there is another* arugment after the last argument you write here.* That is the file descriptor which is used to* communicate with master process.exec_proc {// keepalive "/tmp/a";thread_exec {// restart "hello" "hello" "world";// default "haha";
我們做如下修改:
- framework off; --> framework on;
- worker_proc 1; --> worker_proc 3;
這樣,多進程框架將被啟用,且會產生三個子進程。
程序啟動后如下:
$ ./helloStart up worker process No.1Start up worker process No.2Start up worker process No.302/08/2021 09:34:46 GMT DEBUG: hello.c:print_handler:39: PID:25322 hello world02/08/2021 09:34:46 GMT DEBUG: hello.c:print_handler:39: PID:25323 hello world02/08/2021 09:34:46 GMT DEBUG: hello.c:print_handler:39: PID:25324 hello world02/08/2021 09:34:47 GMT DEBUG: hello.c:print_handler:39: PID:25322 hello world02/08/2021 09:34:47 GMT DEBUG: hello.c:print_handler:39: PID:25323 hello world02/08/2021 09:34:47 GMT DEBUG: hello.c:print_handler:39: PID:25324 hello world
這時,可以ps看一下,一共存在四個hello進程,一個為主,其余三個為子進程。
小結
事實上,Melon并不會有過多條條框框需要開發者小心謹慎怕踩坑。與Sk.NET類似,Melon(repo: Water-Melon/Melon)提供的絕大多數內容都可獨立使用,而不必一定與多進程多線程框架結合。因此,這也給了使用者極大的自由度。
感謝閱讀,歡迎各位在評論區留言評論。