и спасибо, что посмотрели на вопрос.
Фон
У меня есть несколько машин, которые непрерывно генерируют несколько (до 300) скриптов в PHP за очень короткий промежуток времени. Эти скрипты выполняются быстро (менее секунды), а затем выходят. Все эти сценарии требуют доступа только для чтения к большой структуре trie, которая будет очень дорого загружаться в память каждый раз, когда запускается каждый из сценариев. Сервер работает под управлением Linux.
Мое решение
Создайте C-демона, который сохраняет структуру trie в памяти и получает запросы от клиентов PHP. Он получит запрос от каждого из клиентов PHP, выполнит поиск по структуре памяти и ответит на ответ, сохранив скрипты PHP от выполнения этой работы. Оба запроса и ответы – короткие строки (не более 20 символов)
Моя проблема
Я очень новичок в C-демонах и межпроцессном общении. После долгих исследований я сузил выбор до «Очередей сообщений» и сокетов домена Unix. Очереди сообщений кажутся адекватными, потому что я думаю (возможно, ошибаюсь), что они ставят в очередь все запросы к демону, чтобы они отвечали последовательно. Однако сокеты домена Unix кажутся более простыми в использовании. Тем не менее, у меня есть различные вопросы, на которые я не смог найти ответы:
Фактический поиск структуры данных очень быстрый, мне не нужно сложное многопоточное или подобное решение, так как я считаю, что обработка запросов в режиме FIFO будет достаточно. Мне также нужно, чтобы это было просто глупо, поскольку это критически важная услуга, и я довольно новичок в этом типе программы. (Я знаю, но у меня действительно нет никакого способа обойти это, и опыт обучения будет большим)
Я был бы очень признателен за фрагменты кода, которые светят какой-то свет в конкретные вопросы, которые у меня есть. Также приветствуются ссылки на руководства и указатели, которые будут способствовать моему пониманию в этом мрачном мире МПК низкого уровня.
Спасибо за вашу помощь!
Зная гораздо больше, чем когда-либо, когда я задавал этот вопрос, я просто хотел указать всем, кто заинтересован в том, что и инфраструктура Thrift, и ZeroMQ делают фантастическую работу по абстрагированию от жесткого программирования на уровне сокета. Thrift даже дает вам леса для сервера бесплатно!
На самом деле, вместо того, чтобы идти на все тяжелые работы по созданию сетевого сервера, подумайте о написании кода сервера приложений с помощью хорошего асинхронного сервера, который уже решил проблему для вас. Конечно, серверы, использующие асинхронный ввод-вывод, отлично подходят для сетевых приложений, которые не требуют интенсивной обработки процессора (или блоков цикла событий).
Примеры для python: Twisted , gevent . Я предпочитаю gevent, и я не включаю торнадо, потому что он сосредоточен на стороне сервера HTTP.
Примеры для Ruby: EventMachine
Конечно, Node.js в основном является выбором по умолчанию для асинхронного сервера в настоящее время.
Если вы хотите углубиться, прочитайте C10k Problem и Unix Network Programing .
Я подозреваю, что Thrift – это то, что вы хотите. Вам нужно написать небольшой код для создания PHP <-thrift-> C ++ <-> C, но это, вероятно, будет более надежным, чем ваш собственный.
Вы также можете загрузить структуру данных в общую память с помощью функций общей памяти PHP http://www.php.net/manual/en/book.shmop.php .
О, это не очевидно из документации, но координационная переменная – $ key в shmop_open. Каждый процесс, требующий доступа к общей памяти, должен иметь тот же ключ $. Таким образом, один процесс создает разделяемую память с помощью ключа $. Другие процессы затем могут получить доступ к этой общей памяти, если они используют один и тот же ключ $. Я считаю, что вы можете выбрать все, что угодно, для $ key.
«Проблема» (может быть, нет?) Заключается в том, что в MysSys MQ наверняка будет много потребителей / производителей. Хотя вполне возможно, что вы делаете, если у вас нет необходимости в m: n для модели производителя: потребитель к ресурсам, здесь у вас есть модель запроса / ответа.
Вы можете получить некоторые странные зависания с SysV MQ, как есть.
Во-первых, вы уверены, что гнезда INET для вас недостаточно быстры? Быстрый пример PHP с использованием сокетов домена unix находится по адресу http://us.php.net/socket-create-pair (как пример кода, конечно, используйте socket_create () для конечной точки PHP).
Хотя я никогда не пробовал это, memcached вместе с соответствующим расширением PHP должен устранить большую часть работы ворчания.
Уточнение: я неявно предполагал, что если вы это сделаете, вы поместите отдельные листья три в memcache, используя сплющенные ключи, отбрасывая trie. Возможность и желательность этого подхода, конечно, зависит от многих факторов, в первую очередь от источника данных.
IPC между сценарием можно легко сделать с помощью Pipes. Это делает очень простую реализацию.
Вот рабочий пример, где 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"); } }
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; }