Как отправлять сообщения с php на node.js? У меня есть сервер linux, на котором запущены php и node.js.
Когда пользователь завершает транзакцию (через php), я бы хотел отправить сообщение с php на node.js. Затем узел обновит клиент через соединение сокета.
Каким образом можно отправить небольшой объем данных с php на node.js, не нарушая производительность node.js?
Предположение, похоже, заключается в том, чтобы разговаривать с узлом через HTTP-интерфейс, как это делает любой другой клиент. Вы можете поговорить с узлом через HTTP, используя cURL в php
В частности, см. Этот пост от Мэтта Парди
Я столкнулся с аналогичной проблемой, желая информировать пользователей о новой заметке, добавленной к ошибке, и подобных уведомлениях, которые действительно могут быть эффективно отправлены только с PHP на мой сервер Node. То, что я сделал (извинения, если это получило все искаженные и неформатированные при отправке, если это произойдет, я был бы счастлив вставить код в другое место): во-первых, вам нужно будет использовать cURL из PHP. Я написал функцию для своего класса следующим образом:
function notifyNode($type, $project_id, $from_user, $data) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'http://127.0.0.1'); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:')); curl_setopt($ch, CURLOPT_PORT, 8001); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 2); curl_setopt($ch, CURLOPT_POST, true); $pf = array('f' => $type, 'pid' => $project_id, 'user_from' => $from_user, 'data' => array()); foreach($data as $k => $v) { $pf['data'][$k] = $v; } curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($pf)); curl_exec($ch); curl_close($ch); }
Вы заметите, что я отправляю запрос cURL на тот же сервер, поскольку на нем работают PHP и NodeJS, ваш пробег может отличаться. Порт, к которому я установил этот код для подключения, – 8001 (это порт, на котором запущен мой сервер Node, и порт, к которому подключается сервер socket.io). Это отправляет HTTP-запрос POST с полем после поля. Это все довольно стандартное средство cURL.
В приложении Node у вас есть что-то вроде:
var server = http.createServer(function(req, res) {}); server.listen(8001); var io = io.listen(server, { transports: ['websocket', 'flashsocket', 'xhr-polling'] }); ...
хорошо, что мы будем делать здесь, расширяется на части http.createServer, чтобы прослушивать соединения, поступающие с нашего локального хоста («127.0.0.1»). Затем создается код createServer:
var server = http.createServer(function(req, res) { // Check for notices from PHP if(res.socket.remoteAddress == '127.0.0.1') { if(req.method == 'POST') { // The server is trying to send us an activity message var form = new formidable.IncomingForm(); form.parse(req, function(err, fields, files) { res.writeHead(200, [[ "Content-Type", "text/plain"] , ["Content-Length", 0] ]); res.write(''); res.end(); //sys.puts(sys.inspect({fields: fields}, true, 4)); handleServerNotice(fields); }); } } });
Оттуда вы можете реализовать свою функцию handleServerNotice.
function handleServerNotice(data) { ... }
и т. д. Я не тестировал это некоторое время, и на самом деле этот блок кода был прокомментирован на моем узловом сервере, поэтому я надеюсь, что то, что я вставил здесь, работает – в целом эта концепция доказана, и я думаю, что это будет работа для вас. Во всяком случае, просто хотел убедиться, что вы знали, что прошло несколько месяцев, поэтому я не совсем понимаю, почему я прокомментировал это. Код, который я написал, занял немного исследований – например, настройка заголовка «Ожидание:» в cURL – и я был очень взволнован, когда он наконец-то работал. Дайте мне знать, если вам нужна дополнительная помощь.
Лучший,
Мэтт Парди
Немного поздно, но вы можете общаться с вашим клиентом узла, используя механизм Redis Pub / Sub очень простым и эффективным способом. Все, что вам нужно сделать, это установить redis на свой сервер.
На стороне php инициализируйте Redis, затем опубликуйте сообщение
$purchase_info = json_encode(array('user_id' =>$user_id, 'purchase_information'=>array('item'=>'book','price'=>'2$')); $this->redis->publish('transaction_completed', $purchase_info);
На стороне node.js
var redis = require('redis'); var purchase_listener = redis.createClient(); purchase_listener.subscribe('transaction_completed'); purchase_listener.on('message', function(channel, message){ var purchase_data = JSON.parse(message); user_id = purchase_data.user_id; purchase_info = purchase_data.purchase_information; // Process the data // And send confirmation to your client via a socket connection })
Является ли это масштабируемым? (В ответ на @ mohan-singh)
Говоря о масштабируемости, вам нужно подумать о архитектуре вашей инфраструктуры и ваших конкретных потребностях, но вот быстрый ответ: я использовал вариант этого механизма в приложении с высоким трафиком в реальном времени без проблем, но вот что вы должны быть осторожны :
Redis PUB / SUB не является системой очередей, это означает, что если ваш узел обрабатывает все сообщения, которые были отправлены WHILE, он будет потерян.
Если у вас более одного подписчика издателя, все они получат одно и то же сообщение и обработают его, будьте осторожны, если у вас есть более чем процесс узла, который прослушивает один и тот же redis db, обрабатывающий вашу логику в реальном времени (есть простые способы обойти это, хотя)
Приятная вещь в этой системе заключается в том, что вам не нужно добавлять что-либо к существующей инфраструктуре, и ее можно начать немедленно, она очень быстрая и ведет себя точно так же, как HTTP-сервер.
Вот ваши альтернативы для более масштабируемых параметров:
Точка этого длинного редактирования заключается в том, что нет такой вещи, как волшебное масштабируемое решение, вам нужно взвесить ваши варианты и посмотреть, какой из них лучше всего подходит для вашего случая использования. На мой взгляд, если вы сейчас начинаете строить свою первую итерацию, выберите любой вариант, с которым вам удобно, напишите очень чистый код, и когда вы начнете масштабирование, это будет очень легко изменить, вот что я сделал 🙂
Шаг 1. Получить PHP-эмиттер: https://github.com/rase-/socket.io-php-emitter
$redis = new \Redis(); // Using the Redis extension provided client $redis->connect('127.0.0.1', '6379'); $emitter = new SocketIO\Emitter($redis); $emitter->emit('new question', '<b>h<br/>tml</b>');
добавьте это в свой index.js:
var redis = require('socket.io-redis'); io.adapter(redis({ host: 'localhost', port: 6379 })); io.on('connection', function(socket){ socket.on('new question', function(msg) { io.emit('new question', msg); }); });
добавьте что-то подобное в ваш index.html
socket.on('new question', function(msg) { $('body').append( msg ); });
Я нашел, что такая проблема может быть решена просто с помощью Express Framework. Предположим, что php отправляет json-сообщение на сервер узлов, и сервер отвечает с помощью ok.
В app.js
var app = require('express')(); var http = require('http').Server(app); var io = require('socket.io')(http); var bodyParser = require('body-parser') app.use(bodyParser.json()); app.post('/phpcallback', function(req, res) { var content = req.body; console.log('message received from php: ' + content.msg); //to-do: forward the message to the connected nodes. res.end('ok'); }); http.listen(8080, function(){ var addr = http.address(); console.log('app listening on ' + addr.address + ':' + addr.port); });
В test.php
<?php $data = array("name" => "Robot", "msg" => "Hi guys, I'm a PHP bot !"); $data_string = json_encode($data); $ch = curl_init('http://localhost:8080/phpcallback'); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST"); curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json', 'Content-Length: ' . strlen($data_string)) ); echo curl_exec($ch)."\n"; curl_close($ch); ?>
Здесь у нас также есть более подробный пример, где php-скрипт может отправить сообщение пользователям определенной комнаты чата.
Мое личное впечатление о подходе Редиса: громоздко. Вам нужно запустить Apache, nodeJS и Redis, три сервера вместе. Механизм PubSub сильно отличается от источника socket.io, поэтому вам нужно убедиться, что он совместим с вашим существующим кодом.
Мы делаем это, используя очередь сообщений. Существует множество решений, таких как radis ( https://github.com/mranney/node_redis ) или 0mq ( http://zeromq.org/ ). Он позволяет отправлять сообщения подписчикам (например, от php до nodejs).