对于广大C语言开发者来说,缺乏类似C++ STL和Boost的库会让开发受制于基础库的匮乏,也因此导致了开发效率的骤降。这也使得例如libevent这类事件库(基础组件库)一时间大红大紫。
今天,码哥给大家带来一款基础库,这套库不仅仅提供了常用的数据结构、算法,如红黑树、斐波那契堆、队列、KMP算法、RSA算法、各类哈希算法、数据恢复算法等等,还提供了多进程框架、多线程框架、跨平台高性能事件等实用内容。注意:这是一款不依赖第三方的库。
除此以外,它也是笔者之前文章(Melang脚本语言)中的核心库。这也就意味着,使用该库,不仅可以快速获得上述内容,还可以让开发者所构建的系统很方便地引入脚本语言的功能。
它就是——Melon
下面,码哥便带诸位一览这个库的功能。
数据结构
Melon中包含如下数据结构的实现:
其中:
以上结构几乎均可在其对应名称的头文件中找到数据结构定义以及函数定义。
一般情况下,数据结构的使用都是函数调用形式,因此也尽可能降低了不同组件间的耦合度。
算法
Melon中包含的算法如下:
如上算法基本都在其各自头文件中可以找到对应的函数声明以及必要的数据结构定义。
其中,FEC与里德所罗门编码均属于纠错码,FEC常用于RTP中做数据修复,而里德所罗门编码既可以用于实时语音中丢包恢复,也可以用于冗余阵列(RAID)和其他UDP丢包恢复的场景。关于里德所罗门编码,感兴趣的读者可以阅读码哥之前的文章:神奇的数据恢复算法。
其他组件
前面的都是常规操作,这里才是重头戏。
Melon中还包括如下实用组件:
因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";
我们做如下修改:
这样,多进程框架将被启用,且会产生三个子进程。
程序启动后如下:
$ ./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)提供的绝大多数内容都可独立使用,而不必一定与多进程多线程框架结合。因此,这也给了使用者极大的自由度。
感谢阅读,欢迎各位在评论区留言评论。