从 PHP 向 Node.js 发送消息

Posted

技术标签:

【中文标题】从 PHP 向 Node.js 发送消息【英文标题】:Sending messages from PHP to Node.js 【发布时间】:2012-04-20 09:29:56 【问题描述】:

如何将消息从 php 发送到 node.js?我有一个运行 php 和 node.js 的 linux 服务器。

当用户完成交易(通过 php)时,我想从 php 向 node.js 发送一条消息。然后节点将通过套接字连接更新客户端。

在不影响node.js性能的情况下,从php向node.js发送少量数据有什么好方法?

【问题讨论】:

【参考方案1】:

建议似乎是通过 HTTP 接口与节点通信,就像任何其他客户端一样。您可以在 php 中使用 cURL 通过 HTTP 与节点通信

见:http://groups.google.com/group/socket_io/browse_thread/thread/74a76896d2b72ccc/216933a076ac2595?pli=1

特别是,请参阅 Matt Pardee 的这篇文章

我遇到了类似的问题,希望让用户了解新的 注释添加到错误中,以及类似的通知确实可以 只能有效地从 PHP 发送到我的节点服务器。我做了什么 如下(抱歉,如果这一切都是乱码和未格式化的 发送,如果是,我很乐意将代码粘贴到其他地方): 首先,您需要使用 PHP 中的 cURL。我为我写了一个函数 像这样的类:

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(这是我的节点服务器的端口 正在运行,以及 socket.io 服务器连接到的端口)。这 发送一个带有 post 字段编码的 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 中设置 'Expect:' 标头——而我 当它终于奏效时非常兴奋。让我知道你是否需要任何 其他帮助。

最好的,

马特·帕迪

【讨论】:

不错的链接!非常感谢您的帮助。 虽然理论上可以回答这个问题,但我们希望您在回答中包含链接文章的基本部分,并提供link for reference。不这样做会使答案面临链接失效的风险。 同意。重要信息现在存在于答案中。不再有腐烂的风险。【参考方案2】:

有点晚了,但是您可以使用 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 不是排队系统,这意味着如果您的节点进程宕机,所有在它宕机时发送的消息都将丢失。

    如果您有超过 1 个发布者的订阅者,他们都会收到相同的消息并处理它,如果您有多个节点进程监听同一个 redis db 处理您的实时,请注意这一点逻辑(虽然有一些简单的方法可以解决这个问题)

这个系统的好处是你不需要向现有的基础设施添加任何东西,并且可以立即开始,它非常快,它的行为就像一个 HTTP 服务器。

以下是更多可扩展选项的替代方案:

    使用自托管的快速消息队列服务器(ActiveMQ、RabbitMQ、beanstalkd ...)服务器来处理 php 和节点之间的消息逻辑,这些往往很快,但随着负载的增加,您会损失一些性能,并且必须维护/扩展您的消息服务器,并处理跨区域的重复,这不是一件容易和愉快的事情(取决于您喜欢做什么)。 使用托管消息队列服务器(IronMQ、SQS...)其中一些(IronMQ)速度非常快,非常适合您的用例,但会给您的代码库带来一些(次要)复杂性。 使用 Redis 和集群节点服务器构建消息队列:https://davidmarquis.wordpress.com/2013/01/03/reliable-delivery-message-queues-with-redis/ 在 *** 中使用 HTTP 与节点服务器通信。看到流量激增后,您只需对节点服务器进行负载平衡,并根据需要添加尽可能多的无状态服务器,并将 POST 消息发送到该负载平衡器。

这个冗长编辑的要点是,没有神奇的可扩展解决方案,您需要权衡您的选择,看看哪一个最适合您的用例。 在我看来,如果你现在开始构建你的第一个迭代,选择任何你喜欢的选项,编写非常干净的代码,当你开始扩展时,它会很容易改变,这就是我所做的:)

【讨论】:

高流量应用怎么样。这个机制好卖吗?我想在我的实时应用程序中使用 redis 和 laravel。请让我知道它是否成功。 请问purchase_listener.on('message'函数有什么用?和socket.on类似吗? 是的,它类似于 socket.on,但是它不是监听 websockets,而是监听 Redis Pubs。每次调用redis publish,都会调用匿名函数(channel, message) => // do something,使用Redis Publish发送的数据。更多信息在这里:redis.io/commands/publish【参考方案3】:

我发现这样的问题可以通过使用 Express 框架来解决。 假设 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 脚本可以向特定聊天室的用户发送消息。

https://github.com/lteu/chat


我对 Redis 方法的个人印象:繁琐。您需要同时运行 Apache、nodeJS 和 Redis,三台服务器一起运行。而且PubSub机制和socket.io的emit有很大的不同,所以你需要看看它是否和你现有的代码兼容。

【讨论】:

【参考方案4】:

我一直在寻找一种非常简单的方法来让 PHP 向客户端发送 socket.io 消息。

这不需要任何额外的 PHP 库 - 它只使用套接字。

与其像许多其他解决方案那样尝试连接到 websocket 接口,只需连接到 node.js 服务器并使用.on('data') 接收消息。

然后,socket.io 可以将其转发给客户。

在 Node.js 中检测来自 PHP 服务器的连接,如下所示:

//You might have something like this - just included to show object setup
var app = express();
var server = http.createServer(app);
var io = require('socket.io').listen(server);

server.on("connection", function(s) 
    //If connection is from our server (localhost)
    if(s.remoteAddress == "::ffff:127.0.0.1") 
        s.on('data', function(buf) 
            var js = JSON.parse(buf);
            io.emit(js.msg,js.data); //Send the msg to socket.io clients
        );
    
);

这是非常简单的 php 代码 - 我将它封装在一个函数中 - 你可能会想出更好的东西。

请注意,8080 是我的 Node.js 服务器的端口 - 您可能需要更改。

function sio_message($message, $data) 
    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    $result = socket_connect($socket, '127.0.0.1', 8080);
    if(!$result) 
        die('cannot connect '.socket_strerror(socket_last_error()).PHP_EOL);
    
    $bytes = socket_write($socket, json_encode(Array("msg" => $message, "data" => $data)));
    socket_close($socket);

你可以这样使用它:

sio_message("chat message","Hello from PHP!");

您还可以发送转换为 json 并传递给客户端的数组。

sio_message("DataUpdate",Array("Data1" =&gt; "something", "Data2" =&gt; "something else"));

这是一种“信任”您的客户端从服务器获取合法消息的有用方法。

您还可以让 PHP 传递数据库更新,而无需数百个客户端查询数据库。

我希望我能早点找到这个 - 希望这会有所帮助! ?

【讨论】:

【参考方案5】:

我们通过使用消息队列来做到这一点。有很多解决方案,例如 radis (https://github.com/mranney/node_redis) 或 0mq (http://zeromq.org/)。它允许向订阅者发送消息(例如从 php 到 nodejs)。

【讨论】:

【参考方案6】:

步骤 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 );
);

【讨论】:

以上是关于从 PHP 向 Node.js 发送消息的主要内容,如果未能解决你的问题,请参考以下文章

向 Gmail API 发送消息的 Node.js POST 请求

SocketIO 通过 API 路由向客户端发送消息

从 PHP 向 Node.js 发送请求

如何在 websocket 服务器上向特定用户发送消息

我写的websocket推送例子,每隔5秒服务器向客户端浏览器发送消息(node.js和浏览器)

反序列化从 node.js (azure sdk) 发送的 Azure ServiceBus 队列消息时出错