其实个人觉得采用多进程的模式开发服务器还是采用多线程的模式开发服务器都可以达到同样的性能,只是个人更熟悉哪种模式或者更擅长哪种模式罢了。
我个人更加喜欢多进程模式。这个貌似只与个人习惯有关啦
对于多进程的服务器开发模式,我比较喜欢master-worker模式,如下图所示:
但是这样一来,当一个连接到来时,每个worker进程都会收到连接请求的事件。但是只有一个worker进程的accept函数会调用成功,其它几个worker进程都会调用失败!那么对其它几个worker进程来说就是一种负担了,也就是不必要的惊醒。这就是linux高并发服务器中的惊群问题,那么如何解决呢?
这时就需要有个一全局的互斥锁,来保证每一时刻只有一个worker进程拥有互斥岁并且把监听套接字的时间加入到epoll事件模型中,而没有抢到互斥锁的进程则不用把监听套接字的相关事件添加到epoll中,只需要去处理自身的其它I/O事件。
那么,这样一来就解决了惊群问题,每一时刻当一个连接到来时就会只有一个worker进程被惊醒了。
至于互斥锁,则必须要用非阻塞的互斥锁,linux中可以用原子操作来实现,也可以用文件锁来实现。当一个worker进程去抢锁的时候,要么成功抢到锁,要么失败则需要立刻返回,而不能阻塞到抢到锁为止。否则将是资源的一种极大的浪费。
从这种设计模式可以看出,listen套接字是多个worker进程的共有资源,那么就必须要高效的利用起来。因为高效的工作模式和代码是服务器程序的核心,否则将不能把服务器的硬件资源充分发挥出来。
对于一个worker进程来说,尽可能少的时间占用互斥锁,才能提高服务器的处理能力,其它的worker进程也就有更多的机会处理新到来的连接。
这里可以设置两个缓存队列,当epoll_wait返回时,如果有事件需要处理,就把它们先缓存起来稍后处理,因为当务之急是使用完互斥锁释放掉。
这样就可以更高效的使用公共资源,在没有抢到互斥锁的时候再来处理缓存起来的这些事件!