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

时间:2009-11-17 02:23:02

标签: php c ipc daemon

并感谢您查看这个问题。

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

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

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

  1. PHP脚本如何发送和接收消息或使用UNIX套接字与守护进程通信?相反,C守护进程如何跟踪它必须向哪个PHP进程发送回复?
  2. 我见过的守护进程的大多数例子都使用了一个带有睡眠条件的无限while循环。我的守护进程需要为任何时候可能出现的许多连接提供服务,并且响应延迟至关重要。如果PHP脚本在睡眠时发送请求,守护程序将如何反应?我已经阅读过有关民意调查和epoll的内容,这是等待收到邮件的正确方法吗?
  3. 每个PHP进程将始终发送一个请求,然后等待接收响应。我需要确保如果守护程序关闭/不可用,PHP进程将等待设置的最大时间响应,如果没有收到回答将继续,而不是挂起。可以这样做吗?
  4. 数据结构的实际查找非常快,我不需要任何复杂的多线程或类似解决方案,因为我认为以FIFO方式处理请求就足够了。我还需要保持简单的愚蠢,因为这是一项关键任务服务,而且我对这类程序还是比较陌生的。 (我知道,但我真的无法解决这个问题,而且学习经验会很棒)

    我真的很感谢代码片段,这些代码片段为我所拥有的具体问题提供了一些亮点。我还欢迎指导和指导的链接,以进一步了解这个低级别IPC的阴暗世界。

    感谢您的帮助!


    更新

    现在比我提出这个问题时了解得多,我只是想向任何有兴趣的人指出Thrift框架和ZeroMQ做了很好的抽象工作。硬件,套接字级编程。 Thrift甚至可以免费为您提供服务器的脚手架!

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

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

    Ruby的例子:EventMachine

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

    如果您想深入了解,请阅读C10k ProblemUnix Network Programing

7 个答案:

答案 0 :(得分:5)

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

答案 1 :(得分:2)

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

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

答案 2 :(得分:2)

nanomsg以普通C编码,所以我猜它比用C ++编码的Thrift和ZeroMQ更适合你的需要。

对于许多语言都有wrappers,包括PHP。

以下是使用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;
}

答案 3 :(得分: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");
  }

}

答案 4 :(得分:1)

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

你可以使用SysV MQ获得一些奇怪的挂断。

首先,你确定INET套接字不够快吗?使用unix域套接字的快速PHP示例位于http://us.php.net/socket-create-pair(当然,就像代码示例一样,对PHP端点使用socket_create())。

答案 5 :(得分:0)

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

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

答案 6 :(得分:0)

脚本之间的IPC可以使用Pipes轻松完成。这使得实现起来非常简单。