linux服务器高并发处理,Linux高并发服务器的架构简介

Linux的服务器软件,大多是基于epoll事件机制的多进程架构。

其中有一个主进程master,负责监控其他进程的状态。如果其他进程异常退出,则把它从新启动起来。

其他的进程是工作进程worker,负责具体的业务处理。

worker进程的主函数是一个基于epoll事件机制的循环,监控它负责的socket连接的读写状态,读取数据并且调用相关的各个协议解析模块进行处理,应答用户的请求。

之所以用多进程结构,是个别worker进程的异常退出不会导致整个服务器程序的退出,而是会被master进程再次启动起来继续服务,比多线程的稳定性要高。

带来的问题之一就是,跨进程的数据通信比较繁琐。一般是关键信息通过AF_UNIX域的socketpair传递,如果是视频之类的大量数据则使用共享内存。

典型的Linux高并发服务器,就是著名的nginx,作者俄罗斯程序员Igor Syseov。

1,主进程master,

主进程在初始化结束之后的工作比较简单,主要是监控工作进程的状态,并及时重启异常退出的工作进程。

再就是接受用户的命令,例如reload,quit之类的操作。

Linux的子进程退出时会给父进程发送SIGCHILD信号,然后父进程可以使用wait系统调用获取子进程的退出状态。

信号的传递是异步的,需要用Linux的signal()函数挂载自定义的信号处理函数,然后就可以捕获子进程的SIGCHILD信号了。

主进程master发现子进程worker退出后,就可以用fork()系统调用再创建一个子进程,以维持worker进程的个数。

其他时间master只需要关注标准输入STDIN,及时获取用户的命令。

2,worker进程,

worker进程的主循环是一个死循环,不断的用epoll_wait()系统调用监控socket连接的状态。

如果有数据可读,则读取数据,并调用相关的模块进行处理。

如果有新连接要接入,则使用accept()函数获取连接的文件描述符,并为该用户创建连接的上下文,该上下文代表服务器上的一个用户。

同时这个主循环还要处理定时器问题,有时候给用户发送的数据需要控制带宽,就按定时器每多少毫秒发送多少字节,以保证带宽不会暴涨暴跌。

定时器需要频繁的添加、删除、查找,所以多是使用红黑树作为管理结构。

定时器的精度取决于epoll_wait的超时时间。

下图为Linux man手册关于epoll_wait的介绍,它的最后一个参数就是超时的毫秒数。

即使没有等到新数据到达,在超时之后也会返回,这时可以处理定时器。极端情况下,定时器的最粗精度取决于这个超时的时间。

690778e374b5601e28c8d2521b78eb35.png

工作进程的主循环代码大概这样:

while(!exit) { //如果没有退出标志则循环

int ret = epoll_wait(epfd, events, max events, timeout);

if (ret < 0) {

//出错处理

break;

}

//正常返回时的ret返回值是触发的事件个数

int i;

for (i =0; i < ret; i ++) {

//遍历处理所有的触发事件

}

//处理到时间的定时器事件

}

像http之类的具体协议的处理,都是在事件的回调函数里做的。

epoll_event的结构分两个部分,一个是具体的epoll事件的标志,例如EPOLLIN表示读,EPOLLOUT表示写。

另一部分是用户自定义的上下文数据的指针。各模块的数据结构可以放在这个指针里。

如下图,可以把自定义数据结构挂在e->data.ptr里,其中e为struct epoll_event类型的指针。

ab17602ceb212aeca23cb79596834bd4.png

假设自定义的连接上下文结构是:

struct connection {

int fd; // socket文件描述符

int (*read)(struct connnection* c);

//读数据的函数指针

int (*write)(struct connection* c);

//写数据的函数指针

uint8_t* readbuf; //读缓冲区

int readbuf_size; //当前读缓冲区大小

uint8_t* writebuf; //写缓冲区

int writebuf_size; //写缓冲区大小

void* data; //下一级模块的结构体

};

那么工作进程worker的主循环while()里,遍历触发的事件的for循环的处理就是:

for (i =0; i < ret; i++) {

struct epoll_event* e = &events[i];

struct connection * c = e->data.ptr;

int ret2 = c->read(c);

...

}

这只是示例代码,nginx的这部分代码还是比较复杂的。

最精简的事件框架记得是redis自带的libae,大概几百行。

libevent框架则特别的复杂。

a85774481cb81055b9765a77d747b3da.png

举报/反馈

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值