Подтвердить что ты не робот

Как IPC между клиентами PHP и сервером C Daemon?

и спасибо, что посмотрели на вопрос.

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

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

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

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

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

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

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


Update

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

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

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

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

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

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

4b9b3361

Ответ 1

Я подозреваю, что Thrift - это то, что вы хотите. Вам нужно будет написать небольшой код для создания PHP файлов, но это, вероятно, будет более надежным, чем ваш собственный.

Ответ 2

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

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

Ответ 3

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

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

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

Ответ 4

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

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

Ответ 5

IPC между script можно легко выполнить с помощью Pipes. Это делает очень простую реализацию.

Ответ 6

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

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

Ответ 7

Вот рабочий пример, где php script отправляет запрос на 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");
  }

}