在阻塞应用程序中使用反应式PHP

时间:2017-03-30 19:33:30

标签: php multithreading reactive-programming reactphp

我目前正在开发一个PHP应用程序,它将使用一些websocket连接与另一个服务进行通信。

要与这个websocket服务交谈,我们正在使用Ratchet - 这是一个基于PHP反应的PHP库。

这段代码需要发送并响应几个请求,之后,应该将信息返回给“主线程”。

示例流程:

HTTP请求 - >控制器 - >启动一个打开websocket客户端的服务 - > websocket客户端正在与服务器通信 - >一旦完成它应该将结果返回到控制器代码 - >控制器输出到用户

我遇到的问题是我不熟悉Reactive PHP并且不确定如何处理这个问题。

我试过了;

    $service = new WebsocketService();
    $startTimer = time();
    $service->getList(44);
    while($service->getResponse() == null) {
        usleep(500);
        if (time() > $startTimer + 10) {
            continue; //Timeout on 10 seconds
        }
    }
    var_dump($service->getResponse());

一旦完成,服务代码将其“响应”变量设置为除null之外的其他值。这显然失败了,因为sleep方法阻塞了线程。也没有,似乎while循环阻塞I / O并且反应代码失败。

解决方案是打开一个新线程并在那里运行websocket代码,但我对此不满意。

我觉得我需要围绕websocket流程实现某种“观察者”,但我不知道该怎么做。

我们的Websocket服务客户端代码如下所示;

private $response = null;

/**
 * @return null|object
 */
public function getResponse() {
    return $this->response;
}

public function getList($accountId) {
    $this->response = null;
    \Ratchet\Client\connect('ws://192.168.56.1:8080')->then(function(\Ratchet\Client\WebSocket $conn) use ($accountId) {
        $login = new \stdClass();
        $login->action = 'login';
        $conn->on('message', function($msg) use ($conn, $login, $accountId) {
            try {
                $response = json_decode($msg);
                if ($response->result_id == 100) {
                    //Succesfully logged in to websocket server

                    //Do our request now.
                    $message = new \stdClass();
                    $message->target = 'test';
                    $conn->send(json_encode($message));
                }

                if (isset($response->reply) && $response->reply == 'list') {
                    $this->response = $response; //This is the content I need returned in the controller
                    $conn->close(); //Dont need it anymore
                }

            } catch (\Exception $e) {
                echo 'response exception!';
                //Do nothing for now
            }
        });

        $conn->send(json_encode($login));
    }, function ($e) {
        echo "Could not connect: {$e->getMessage()}\n";
    });
}

运行这样的代码也行不通;

    $service = new WebsocketService();
    $service->getList(44);
    echo 'Test';
    var_dump($service->getResponse());

因为在我从websocket服务器得到响应之前就出现了“测试”回声。

拜托,赐教!我不确定该搜索什么。

2 个答案:

答案 0 :(得分:1)

PHP和websockets似乎仍处于试验阶段。不过,我在medium.com上找到了亚当·温尼帕斯(Adam Winnipass)撰写的精彩教程,该教程对于解决您的问题确实很有帮助:https://medium.com/@winni4eva/php-websockets-with-ratchet-5e76bacd7548

唯一的区别是,他们使用JavaScript而不是PHP来实现其websocket客户端。但是最后应该没有太大的区别,因为一旦我们打开了两端的Websocket连接,两个应用程序都必须发送并也要等待接收通知-这就是它们的显示方式:

Establishing a Web Socket connection

创建成功的Websocket连接的一种可能性似乎是扩展MessageComponentInterface

use Ratchet\MessageComponentInterface;

这也需要

use Ratchet\ConnectionInterface;

消息组件接口定义了以下方法:

  • onOpen
  • onMessage
  • onClose
  • onError

我认为这是Ratchet库实现它的方式。这就是他们最终启动服务器的方式:

use Ratchet\Server\IoServer;
use MyApp\MyCustomMessageComponentInterface;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
require dirname(__DIR__) . '/vendor/autoload.php';
$server = IoServer::factory(
            new HttpServer(
                new WsServer(
                    new MyCustomMessageComponentInterface()
                )
            ),
          8080
         );
$server->run();

使用这种体系结构,您已经可以接收(onMessage),也可以使用send()方法发送。

我无法解决您现有代码的确切问题。但是我想,如果您按预期使用库的预构建类和接口(并在此处进行了演示),则应该可以通过将代码添加到相应的方法中来实现所需的功能。< / p>

更多信息和示例可在文档中找到:

http://socketo.me/docs/server

http://socketo.me/api/namespace-Ratchet.html

答案 1 :(得分:-1)

您是否使用WsServer扩展类,如果您遇到致命错误,这可能是个问题。我不确定你是否会收到致命的错误或警告。另外我注意到公共函数onOpen()打开了一个连接。请尝试参阅此文档http://socketo.me/api/class-Ratchet.WebSocket.WsServer.html可能有用。