ZeroMQ与分叉服务器中的所有子进程共享上下文

时间:2013-04-19 23:37:13

标签: c++ fork ipc zeromq

我正在用C ++编写一个分叉的聊天服务器,每个传入的客户端都是自己的进程。服务器 - 客户端交互是通过普通套接字完成的,ZeroMQ套接字处理消息队列和IPC。基本的问题是,当服务器分叉容纳新客户端时,客户端的进程有一个上下文的副本(这是fork做的,对吧?),所以当它用上下文绑定套接字时,没有其他客户端知道插座。长话短说:如何让每个客户端线程具有相同的上下文,以便它们可以通过ZeroMQ相互通信?

我已经研究了在进程之间共享上下文的各种方法,到目前为止我只找到this one。这个问题是1)它使用了一个线程池,根据我的理解,从那里写的,只创建了5个线程;这个服务器需要至少支持256个,因此至少需要多个线程; 2)它使用ZeroMQ来与客户端和后端任务进行通信;我仅限于使用ZeroMQ作为后端。

我查看了ZeroMQ邮件列表,一条消息说fork()与ZeroMQ的工作方式正交。这是否意味着我无法在分叉子进程中共享我的上下文?如果是这种情况,我如何在多个进程之间共享上下文,同时牢记支持至少256个客户端和仅使用ZeroMQ作为后端的要求?

编辑:清除了线程/进程的混乱。对不起。

EDIT2:我之所以喜欢分叉线程的原因是我习惯于拥有一个接受传入套接字连接的主进程然后分叉,给孩子提供新的套接字。我不确定如何以线程方式做到这一点(不是很好的练习,但并非完全脱离我的联盟)

EDIT3:所以,开始用线程重写它。我想这是唯一的方法吗?

EDIT4:为了进一步说明,到服务器的传入连接可以是TCP或UDP,我必须处理客户端连接时的类型,所以我不能使用ZeroMQ套接字来监听。

1 个答案:

答案 0 :(得分:5)

上下文共享

在链接的示例代码中共享ZMQ上下文的原因是,服务器(main())使用inproc套接字与工作者(worker_routine())进行通信。 Inproc套接字不能相互通信,除非它们是从同一个ZMQ上下文创建的,即使它们在同一个进程中结算。在您的情况下,我认为没有必要共享它,因为不应该使用inproc套接字。因此,您的代码可能如下所示:

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;
}

每个请求的处理流程

现在谈谈你的要求,每个请求一个流程。最后一个示例代码仅用于说明zmq::proxy的用法,它是为了使用ROUTER-DEALER模式简化服务器代码而提供的。但它无法满足您的要求。所以,你必须手动实现它。它看起来像another example。不同之处在于,当前端套接字可读时,需要调用fork()并将while循环放入子进程。

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

建议

最后,我不得不说,除非您的方案非常特殊,否则为一个请求创建一个进程太沉重了。请使用线程,或者考虑像zmq::poll这样的异步IO。