Repository navigation
EventBase::loop(): kevent: Bad file descriptor #280
Description
Activity
@clue @WyriHaximus Can anyone pay attention to this issue, tks
@slince We can expose the raw event base, but why not start the server after forking. Or use
react/child-processto spawn more server processes?hello
why not start the server after forking
If so, then we need to enable the
so_reuseportoption of the socket. After my local test, usingreuseportis not as efficient as using the child process fork directlyuse react/child-process to spawn more server processes?
forkis more convenient thanexecwhen ipc; in fact, I also implementedexecin my project, but this is the second option, andexecwill only be used whenforkis not available.In fact, i have an idea. i can create an socket server importing an existing socket stream . before this, i have a discusssion #220
// create raw socket befor fork. $rawSocket = stream_socket_server('xxxx'); const WORKER_NUM = 3; for ($i = 0; $i <= WORKER_NUM; $i++) { $pid = pcntl_fork(); if (-1 === $pid) { exit('fork error'); } elseif ($pid) { } else { // create a server using raw socket $loop = \React\EventLoop\Loop::get(); $server = new \React\Socket\SocketServer($rawSocket, $loop); $loop->run(); exit; } } pcntl_wait($status);
@slince Thanks for bringing this up, excellent question!
Whether
SO_REUSEPORTscales better or not indeed depends on the protocol and application workload, but from my experiments I would say this is usually preferable. As an alternative, I would highly recommend using the ChildProcess component and/or duplicating the file descriptor as recently implemented in #269.PHP's
pcntl_fork()is quite low-level and unfortunately PHP only provides somewhat limited support for low-level OS operations, so this may open up a can of worms due to the PHP's limited access to low-level APIs. PHP provides higher-level APIs for process creation as used in our ChildProcess component. These APIs internally use the underlyingfork()andexec()calls but avoid many of their shortcoming. Once PHP offers more low-level APIs in the future (close(),dup()etc. come to mind), I agree it might make sense to revisit this low-level API, but I don't see this anywhere in the foreseeable future at least. This refs reactphp/event-loop#184 and others.The underlying problem is that libev and others rely on epoll/kevent which use a file descriptor which will be inherited to any child processes when forking the parent. As a consequence, any child process may affect events received by the parent process and vice versa. The underlying libev and others implement low-level APIs to essentially re-create this file descriptor in the child process, which is not currently exposed in our EventLoop API because it is somewhat specific to the platform and/or extension in use. For more background, see the above ticket and https://metacpan.org/dist/EV/view/libev/ev.pod#The-special-problem-of-fork
I believe this has been answered, so I'm closing this for now. Please come back with more details if this problem persists and we can always reopen this 👍
PHP's pcntl_fork() is quite low-level and unfortunately PHP only provides somewhat limited support for low-level OS operations, so this may open up a can of worms due to the PHP's limited access to low-level APIs.
@clue hi I think fork is worthy using, you can refer to workerman, it has been working very well and has been trusted by everyone;
@slince Forking works just fine, also in ReactPHP. Like I said, using the
pcntl_fork()function is very low-level and easy to get wrong, because the defaults (while making sense from a historical perspective) don't really fit into long-running, event-driven applications, so you have to do a lot of legwork (which I'd rather avoid). If this sounds confusing, it's because it is, and I would recommend looking into using the ChildProcess component as an easier way to use forks.If you really want to use the low-level forking functions in PHP to create a shared file descriptor referencing a socket server, here's how you can do this:
<?php require __DIR__ . '/vendor/autoload.php'; // create raw socket befor fork. $rawSocketFd = 3; // TODO: low-level stuff not exposed in PHP, but safe to assume in here if you don't have any other file descriptors except stdio $rawSocket = stream_socket_server('xxxx'); const WORKER_NUM = 3; for ($i = 0; $i <= WORKER_NUM; $i++) { $pid = pcntl_fork(); if (-1 === $pid) { exit('fork error'); } elseif ($pid) { } else { // create a server using raw socket $loop = \React\EventLoop\Loop::get(); $server = new \React\Socket\SocketServer('php://fd/' . $rawSocketFd, $loop); $loop->run(); exit; } } pcntl_wait($status);
The above example should work just fine with ReactPHP, but again I would not recommend using this unless you have a very good understanding of how lower-level file descriptors work. In particular, you may find that PHP does not expose file descriptor numbers at all, does not allow you to get a FD number for a resource or even
closean FD (see #269, https://github.com/clue/fd and others).I hope this helps 👍
hello,
I'm writing forking TCP server with master and forked worker processes. the
ExtEventLoopinstance in child process will raise some error like this:Sample code:
environment:
Reference https://stackoverflow.com/questions/33735302/reactphp-libevent-and-socket-pair-throws-error
Is it possible to add a method
getEventBase()in https://github.com/reactphp/event-loop/blob/master/src/ExtEventLoop.php to expose the raweventBaseobject?