Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ZeroMQ share a context with all child processes in a forking server

Tags:

c++

fork

zeromq

ipc

I'm writing a forking chat server in C++, with each incoming client being its own process. The server-client interactions are done though normal sockets with ZeroMQ sockets handling message queuing and IPC. The basic problem is that there when the server forks to accommodate the new client, the client's process has a copy of the context (which is what fork does, right?), so when it binds sockets with the context, none of the other clients are aware of the socket. Long story short: how do I get each client thread to have the same context so that they can talk to each other through ZeroMQ?

I've looked at various ways to share the context between processes, and so far I'm found only this one. The problem with that is 1) it uses a thread pool, and from what I understand from what's written there, only 5 threads are created; this server is needs to support at least 256 and thus will have at least that many threads, and 2) it uses ZeroMQ for both talking to clients and for backend tasks; I'm limited to using ZeroMQ for backend only.

I've looked at the ZeroMQ mailing list and one message said that fork() is orthogonal to how ZeroMQ works. Does this mean that I can't share my context across forked child processes? If that's the case, how do I share the context across multiple processes while keeping in mind the requirement of supporting at least 256 clients and using ZeroMQ for only backend?

EDIT: Cleared up the thread/process confusion. Sorry about that.

EDIT2: The reason why I'm also favoring forking over threads is that I'm used to having a main process that accepts an incoming socket connection then forks, giving the new socket to the child. I'm not sure how to do that in a threading fashion (not really well-practiced, but not totally out of my league)

EDIT3: So, starting to rewrite this with threads. I guess this is the only way?

EDIT4: For further clarification, incoming connections to the server can be either TCP or UDP and I have to handle which type it is when the client connects, so I can't use a ZeroMQ socket to listen in.

like image 662
Dreamless Memory Avatar asked Jan 24 '26 20:01

Dreamless Memory


1 Answers

Context Sharing

The reason to share ZMQ context in the example code from your link is, that the server(main()) uses inproc socket to communicate with workers(worker_routine()). Inproc sockets cannot communicate with each other unless they are created from the same ZMQ context, even they settle in the same process. In your case, I think it's not necessary to share it since no inproc sockets are supposed to be used. So, your code might look like:

void *worker_routine (void *arg)
{
    // zmq::context_t *context = (zmq::context_t *) arg;    // it's not necessary for now.
    zmq::context_t context(1);    // it's just fine to create a new context

    zmq::socket_t socket (context, ZMQ_REP);
    // socket.connect ("inproc://workers");    // inproc socket is useless here.
    socket.connect("ipc:///tmp/workers");    // need some sockets who can cross process.

    // handling code omitted.
}

int main ()
{
    //  omitted...

    // workers.bind ("inproc://workers");    // inproc socket is useless here.
    workers.bind("ipc:///tmp/workers");

    //  Launch pool of worker processes
    for (int i = 0; i < 5; ++i) {
        if (fork() == 0) {
            // worker process runs here
            worker_routine(NULL);
            return 0;
        }
    }
    //  Connect work processes to client process via a queue
    zmq::proxy (clients, workers, NULL);
    return 0;
}

handling process per request

And now to talk about your requirement, one process per request. The last example code is just intended to illustrate the usage of zmq::proxy, which is provided to simplify the server code with ROUTER-DEALER pattern. But it can't fulfill your requirement. So, you have to implement it manually. It just looks like another example. The difference is that you need to invoke fork() when frontend socket is readable and put the while loop into sub process.

if (items[0].revents & ZMQ_POLLIN) {
    if (fork() == 0) {
        // sub process runs here
        while (1) {
            // forward frames here
        }
        // sub process ends here
        return 0;
    }
}

Suggestion

At the end, I have to say, it's too much heavy to create a process for one request exactly unless your scenario is really special. Please use thread, or consider the asynchronous IO like zmq::poll.

like image 112
Matt Yang Avatar answered Jan 27 '26 10:01

Matt Yang



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!