Как выполнить IPC между клиентами PHP и сервером C Daemon? - PullRequest
23 голосов
/ 17 ноября 2009

и спасибо, что взглянули на вопрос.

Фон
У меня есть несколько машин, которые непрерывно порождают несколько (до 300) консольных сценариев PHP в очень короткие сроки. Эти сценарии запускаются быстро (менее секунды) и затем завершаются. Все эти сценарии должны иметь доступ только для чтения к большой структуре trie , которую было бы очень дорого загружать в память каждый раз при запуске каждого из сценариев. Сервер работает под управлением Linux.

Мое решение
Создайте демон C, который хранит структуру trie в памяти и получает запросы от клиентов PHP. Он получит запрос от каждого из клиентов PHP, выполнит поиск структуры памяти и ответит ответом, спасая сценарии PHP от выполнения этой работы. И запросы, и ответы являются короткими строками (не более 20 символов)

Моя проблема
Я очень плохо знаком с демонами C и межпроцессным взаимодействием. После долгих исследований я сузил выбор до очереди сообщений и доменных сокетов Unix. Очереди сообщений кажутся адекватными, потому что я думаю (я могу ошибаться), что они ставят в очередь все запросы для демона, чтобы ответить на них последовательно. Доменные сокеты Unix, похоже, проще в использовании. Однако у меня есть различные вопросы, на которые я не смог найти ответы:

  1. Как PHP-скрипт может отправлять и получать сообщения или использовать сокет UNIX для связи с демоном? И наоборот, как демон C отслеживает, на какой процесс PHP он должен отправить ответ?
  2. Большинство примеров демонов, которые я видел, используют бесконечный цикл while с состоянием сна внутри. Мой демон должен обслуживать много соединений, которые могут быть установлены в любое время, и задержка ответа является критической. Как отреагирует демон, если PHP-скрипт отправляет запрос во время сна? Я читал о опросе и опросе, будет ли это правильный способ ожидания полученного сообщения?
  3. Каждый процесс PHP всегда отправляет один запрос, а затем будет ожидать ответа. Мне нужно убедиться, что если демон не работает или недоступен, процесс PHP будет ожидать ответа в течение заданного максимального времени, и если ответ не будет получен, он будет продолжаться независимо от зависания. Можно ли это сделать?

Фактический поиск структуры данных выполняется очень быстро, мне не нужно какое-либо сложное многопоточное или подобное решение, так как я считаю, что обработки запросов способом FIFO будет достаточно. Мне также нужно, чтобы это было просто глупо, поскольку это критически важный сервис, и я довольно новичок в этом типе программ. (Я знаю, но я действительно не могу обойти это, и опыт обучения будет отличным)

Я был бы очень признателен за фрагменты кода, которые проливают свет на конкретные вопросы, которые у меня есть. Также приветствуются ссылки на руководства и указатели, которые помогут мне лучше понять этот мрачный мир IPC низкого уровня.

Спасибо за вашу помощь!


Обновление

Теперь, зная гораздо больше, чем я задавал этот вопрос, я просто хотел показать всем, кто заинтересован в том, что и инфраструктура Thrift и ZeroMQ делают фантастическую работу абстрагироваться от жесткого программирования на уровне сокетов. Thrift даже бесплатно предоставляет вам леса для сервера!

На самом деле, вместо того, чтобы выполнять всю тяжелую работу по созданию сетевого сервера, рассмотрите возможность написания кода сервера приложений, используя хороший асинхронный сервер, который уже решил эту проблему для вас. Конечно, серверы, использующие асинхронный ввод-вывод, отлично подходят для сетевых приложений, которые не требуют интенсивной обработки ЦП (или же блоков цикла событий).

Примеры для python: Twisted , gevent . Я предпочитаю gevent, и я не включаю торнадо, потому что он ориентирован на сторону HTTP-сервера.

Примеры для Ruby: EventMachine

Конечно, Node.js в настоящее время является по умолчанию выбором для асинхронного сервера.

Если вы хотите углубиться, прочитайте C10k Problem и Сетевое программирование Unix .

Ответы [ 7 ]

5 голосов
/ 17 ноября 2009

Я подозреваю, Экономия - это то, что вы хотите. Вы должны написать небольшой склеивающий код для PHP <-thrift-> C ++ <-> C, но это, вероятно, будет более надежным, чем ваш собственный.

2 голосов
/ 15 апреля 2017

Вот рабочий пример, когда скрипт 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");
  }

}
2 голосов
/ 14 апреля 2017

nanomsg написано на простом C, поэтому я думаю, что оно лучше подходит для ваших нужд, чем Thrift и ZeroMQ, которые написаны на C ++.

Имеется оболочек для многих языков, включая 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;
}
2 голосов
/ 17 ноября 2009

Вы также можете загрузить структуру данных в разделяемую память, используя функции разделяемой памяти PHP http://www.php.net/manual/en/book.shmop.php.

О, это не очевидно из документации, но координирующей переменной является $ key в shmop_open. Каждый процесс, которому требуется доступ к общей памяти, должен иметь один и тот же ключ $. Итак, один процесс создает общую память с ключом $. Затем другие процессы могут получить доступ к этой общей памяти, если они используют тот же ключ $. Я верю, что вы можете выбрать все, что вам нравится, за ключ $.

1 голос
/ 17 ноября 2009

«Проблема» (возможно, нет?) Заключается в том, что на MQ SysV наверняка может быть много потребителей / производителей. Хотя это вполне возможно для того, что вы делаете, если вам не обязательно нужна m: n для модели «производитель: потребитель к ресурсам», у вас есть модель запрос / ответ.

Вы можете получить некоторые странные зависания с SysV MQ, как есть.

Во-первых, вы уверены, что сокеты INET недостаточно быстры для вас? Быстрый пример PHP с использованием доменных сокетов unix находится на http://us.php.net/socket-create-pair (так же, как пример кода, конечно, используйте socket_create () для конечной точки PHP).

0 голосов
/ 17 июля 2014

IPC между скриптами можно сделать намного проще, используя Pipes. Что делает очень простую реализацию.

0 голосов
/ 17 ноября 2009

Хотя я никогда не пробовал, memcached вместе с соответствующим PHP-расширением должны устранить большую часть тяжелой работы.

Разъяснение: я неявно предполагал, что если вы сделаете это, вы поместите отдельные листы дерева в memcache, используя сплющенные ключи, бросая дерево. Разумеется, целесообразность и желательность такого подхода зависит от многих факторов, в первую очередь от источника данных.

...