想了解下数据是如何分发到子进程的

万宝

关闭端口复用时:在子进程分支之前开始监听
打开端口复用时:在子进程分支之后开始监听
这两种方式有什么区别呢
还有就是无论哪种,在分支之后相当于每个进程都在监听同一个端口,那数据是怎么分发到一个进程去处理而不是所有进程都进行处理呢

5384 3 3
3个回答

walkor

1、不使用端口复用(SO_REUSEPORT),要想让多个进程监听同一个端口,就需要先让主进程监听端口,然后fork出子进程,让子进程继承主进程的监听socket句柄。

这种情况下当一个tcp链接到来时,首先tcp链接进入系统内核一个队列里,等待进程去接受(accept系统调用),如果子进程都是以io复用(select/poll/epoll等)的方式监听socket句柄(workerman就是这样),那么就会有多个进程被内核唤醒同时去争抢(accept)这个队列里的tcp链接,但是只有一个进程能成功,所以有些进程被白白唤醒,造成一些cpu浪费,这就是所谓的惊群效应。

2、打开端口复用时,主进程不创建监听句柄,每个子进程会自己创建监听句柄(不开端口复用无法做到这一点)

这种情况下当一个tcp链接到来时,首先tcp链接进入系统内核一个队列里,这时候系统内核会自动将tcp链接分配给某一个监听句柄的进程,这样只有一个进程被唤醒去接受(accept),所以没有惊群效应。也就是说开启端口复用后内核会自动做负载均衡,在短链接应用中性能会提升一些(长链接应用没有明显效果)。

其它相关链接:http://wenda.workerman.net/?/question/179

  • 暂无评论
万宝

我编辑Worker.php,在acceptConnection中加入调试内容

$new_socket = @stream_socket_accept($socket, 0, $remote_address);
if (!$new_socket) {
    echo 'my pid is '.posix_getpid().', accept failed.'.PHP_EOL;
    return;
}
echo 'my pid is '.posix_getpid().', accept success.'.PHP_EOL;

开了4个子进程,然后用客户端连接端口,发现每次惊群唤起的进程数量并不固定呢,这是为什么

my pid is 2388, accept failed.
my pid is 2390, accept failed.
my pid is 2389, accept failed.
my pid is 2387, accept success.

my pid is 2388, accept success.
my pid is 2390, accept failed.
my pid is 2387, accept failed.

my pid is 2390, accept success.
my pid is 2387, accept failed.
my pid is 2389, accept failed.
my pid is 2388, accept failed.

my pid is 2389, accept failed.
my pid is 2387, accept failed.
my pid is 2390, accept success.

my pid is 2390, accept success.
my pid is 2388, accept failed.

my pid is 2390, accept success.
my pid is 2387, accept failed.

my pid is 2390, accept success.
my pid is 2389, accept failed.
my pid is 2387, accept failed.

my pid is 2390, accept success.
my pid is 2388, accept failed.
  • dignfei 2020-03-13

    牛!!!!!!!!!!!!!!!研究的这么深入!

walkor

是的,并不是每次都唤醒所有进程。
唤醒数一般和cpu核数以及各个进程繁忙程度有关。

1、如果进程都空闲,那么唤醒的进程数一般等于cpu核数
2、如果有些进程正在处理某些请求,这些进程就不会得到io复用(select/poll/epoll等)关于有新tcp链接的通知,那么唤醒的进程数一般小于cpu核数。

极端的情况的一个例子,如果只有一个进程空闲(阻塞在IO复用系统调用监听链接事件),那么只有它能立刻监听到有链接事件,它accept领走链接后,系统内核链接队列已经是空了,等其它进程从繁忙转到空闲时已经没有链接可以accept,所以其它进程不会再被通知有新链接而被唤醒。这种情况等于没发生惊群效应。

我没研究过linux源码,上面都是一些经验之谈,不一定十分准确。

  • 暂无评论
年代过于久远,无法发表回答
🔝