首页 文章

如何在PHP客户端和C守护程序服务器之间进行IPC?

提问于
浏览
22

并感谢您看一下这个问题 .

The background
我有几台机器可以在很短的时间内连续生成多个(最多300个)PHP控制台脚本 . 这些脚本快速运行(不到一秒钟)然后退出 . 所有这些脚本都需要对大型trie结构的只读访问权限,每次运行每个脚本时加载到内存中都非常昂贵 . 服务器运行Linux .

My solution
创建一个C守护进程,将trie结构保留在内存中并接收来自PHP客户端的请求 . 它将收到来自每个PHP客户端的请求,对内存结构执行查找并回答答案,从而避免PHP脚本执行该工作 . 请求和响应都是短字符串(不超过20个字符)

My problem
我是C守护进程和进程间通信的新手 . 经过大量研究,我已将选择范围缩小到Message Queues和Unix域套接字 . 消息队列似乎已经足够了,因为我认为(我可能错了)他们排队守护进程的所有请求以连续回答它们 . 但是,Unix域套接字似乎更容易使用 . 但是,我有各种各样的问题,我无法找到答案:

  • PHP脚本如何发送和接收消息或使用UNIX套接字与守护进程通信?相反,C守护进程如何跟踪它发送回复的PHP进程?

  • 我见过的守护进程的大多数例子都使用了一个带有睡眠条件的无限while循环 . 我的守护进程需要为任何时候可能出现的许多连接提供服务,并且响应延迟至关重要 . 如果PHP脚本在睡眠时发送请求,守护程序将如何反应?我读过有关民意调查和epoll的信息,这是等待收到邮件的正确方法吗?

  • 每个PHP进程将始终发送一个请求,然后等待接收响应 . 我需要确保如果守护程序关闭/不可用,PHP进程将等待设置的最大时间响应,如果没有收到回答将继续,而不是挂起 . 可以这样做吗?

数据结构的实际查找非常快,我不需要任何复杂的多线程或类似解决方案,因为我认为以FIFO方式处理请求就足够了 . 我还需要保持简单的愚蠢,因为这是一项关键任务服务,而且我对这类程序还是比较陌生的 . (我知道,但我真的无法解决这个问题,学习经验会很棒)

我真的很感激代码片段,它为我所具有的具体问题提供了一些亮点 . 指南和指针的链接也将受到欢迎,这些指南将进一步了解这个低级别IPC的阴暗世界 .

谢谢你的帮助!


更新

现在比我在提出这个问题时所知道的要多得多,我只是想向任何有兴趣的人指出Thrift框架和ZeroMQ在抽象硬件,套接字级编程方面做得非常出色 . Thrift甚至可以免费为您提供服务器的脚手架!

事实上,不要去构建网络服务器的所有艰苦工作,而是考虑使用已经为您解决问题的良好异步服务器编写应用程序服务器代码 . 当然,使用异步IO的服务器非常适合不需要密集CPU处理的网络应用程序(或者事件循环块) .

python的示例:Twistedgevent . 我更喜欢gevent,而且我不包括龙卷风,因为它专注于HTTP服务器端 .

Ruby的例子:EventMachine

当然,Node.js基本上是现在异步服务器的默认选择 .

如果你想更深入,请阅读C10k ProblemUnix Network Programing .

7 回答

  • 0

    虽然我从来没有尝试过,memcached以及适当的PHP extension应该消除大部分咕噜咕噜的工作 .

    澄清:我隐含地假设如果你这样做,你会使用扁平键将trie的单个叶子放入memcache中,放弃trie . 当然,这种方法的可行性和可取性取决于许多因素,首先是数据来源 .

  • 2

    这是一个工作示例,其中php脚本向C守护程序发送请求,然后等待响应 . 它在数据报模式下使用Unix域套接字,因此它快速而简单 .

    client.php

    <?php
    
    do {
      $file = sys_get_temp_dir() . '/' . uniqid('client', true) . '.sock';
    } while (file_exists($file));
    
    $socket = socket_create(AF_UNIX, SOCK_DGRAM, 0);
    
    if (socket_bind($socket, $file) === false) {
      echo "bind failed";
    }
    
    socket_sendto($socket, "Hello World!", 12, 0, "/tmp/myserver.sock", 0);
    
    if (socket_recvfrom($socket, $buf, 64 * 1024, 0, $source) === false) {
      echo "recv_from failed";
    }
    echo "received: [" . $buf . "]   from: [" . $source . "]\n";
    
    socket_close($socket);
    unlink($file);
    ?>
    

    server.c

    #include <stdio.h>
    #include <sys/un.h>
    #include <sys/socket.h>
    
    #define SOCKET_FILE "/tmp/myserver.sock"
    #define BUF_SIZE    64 * 1024
    
    int main() {
      struct sockaddr_un server_address = {AF_UNIX, SOCKET_FILE};
    
      int sock = socket(AF_UNIX, SOCK_DGRAM, 0);
      if (sock <= 0) {
          perror("socket creation failed");
          return 1;
      }
    
      unlink(SOCKET_FILE);
    
      if (bind(sock, (const struct sockaddr *) &server_address, sizeof(server_address)) < 0) {
          perror("bind failed");
          close(sock);
          return 1;
      }
    
      while (1) {
        struct sockaddr_un client_address;
        int i, numBytes, len = sizeof(struct sockaddr_un);
        char buf[BUF_SIZE];
    
        numBytes = recvfrom(sock, buf, BUF_SIZE, 0, (struct sockaddr *) &client_address, &len);
        if (numBytes == -1) {
          puts("recvfrom failed");
          return 1;
        }
    
        printf("Server received %d bytes from %s\n", numBytes, client_address.sun_path);
    
        for (i = 0; i < numBytes; i++)
          buf[i] = toupper((unsigned char) buf[i]);
    
        if (sendto(sock, buf, numBytes, 0, (struct sockaddr *) &client_address, len) != numBytes)
          puts("sendto failed");
      }
    
    }
    
  • 1

    “问题”(可能不是?)是SysV MQ上肯定会有很多消费者/ 生产环境 者 . 如果您不一定需要 生产环境 者:消费者对资源模型,那么您可以完全实现您正在做的事情,这里有一个请求/响应模型 .

    您可以使用SysV MQ获得一些奇怪的挂断 .

    首先,你确定INET插座不够快吗?使用unix域套接字的快速PHP示例位于http://us.php.net/socket-create-pair(正如代码示例当然,对PHP endpoints 使用socket_create()) .

  • 4

    使用Pipes可以很容易地完成脚本之间的IPC . 这使得实现起来非常简单 .

  • 0

    您还可以使用PHP的共享内存函数http://www.php.net/manual/en/book.shmop.php将数据结构加载到共享内存中 .

    哦,从文档来看并不明显,但协调变量是shmop_open中的$ key . 需要访问共享内存的每个进程都应该具有相同的$ key . 因此,一个进程使用$ key创建共享内存 . 然后,如果其他进程使用相同的$ key,则可以访问该共享内存 . 我相信你可以为$ key选择你喜欢的任何东西 .

  • 0

    我怀疑Thrift是你想要的 . 你必须写一些胶水代码来做PHP <-thrift-> C < - > C,但这可能比滚动你自己更强大 .

  • 0

    nanomsg用纯C编码,所以我猜它比用C编码的Thrift和ZeroMQ更适合你的需要 .

    对于包括PHP在内的多种语言,它有wrappers .

    以下是使用 NN_PAIR 协议的工作示例:(您也可以使用 NN_REQREP

    client.php

    <?php
    
    $sock = new Nanomsg(NanoMsg::AF_SP, NanoMsg::NN_PAIR);
    
    $sock->connect('ipc:///tmp/myserver.ipc');
    
    $sock->send('Hello World!', 0);
    
    $sock->setOption(NanoMsg::NN_SOL_SOCKET, NanoMsg::NN_RCVTIMEO, 1000);
    
    $data = $sock->recv(0, 0);
    
    echo "received: " . $data . "\n";
    
    ?>
    

    server.c

    #include <stdio.h>
    #include <string.h>
    #include <nanomsg/nn.h>
    #include <nanomsg/pair.h>
    
    #define address "ipc:///tmp/myserver.ipc"
    
    int main() {
      unsigned char *buf = NULL;
      int result;
      int sock = nn_socket(AF_SP, NN_PAIR);
      if (sock < 0) puts("nn_socket failed");
    
      if (nn_bind(sock, address) < 0) puts("bind failed");
    
      while ((result = nn_recv(sock, &buf, NN_MSG, 0)) > 0) {
        int i, size = strlen(buf) + 1;  // includes null terminator
        printf("RECEIVED \"%s\"\n", buf);
        for (i = 0; buf[i] != 0; i++)
          buf[i] = toupper(buf[i]);
        nn_send(sock, buf, size, 0);
        nn_freemsg(buf);
      }
      nn_shutdown(sock, 0);
      return result;
    }
    

相关问题