Ratchetを利用してPHPでWebソケット 2 デプロイ編

Javascript

以前に
Ratchetを利用してPHPでWebソケット 1
ということで、Ratchetを使ってWebソケットを実装しようというのをやりましたが、それから半年以上たって、やっとデプロイするときがやってきました。(゜∀゜)
時間空きすぎて、Ratchetのことをほとんど覚えていなかった…。
しかし、Ratchetを利用してPHPでWebソケット 1の記事が役に立ったことは言うまでもありません。

下記のサイトに親切にデプロイのやり方が書いてあるので、盲目的にまずやってみます。
http://socketo.me/docs/deploy

まず、HAProxyというものを導入します。
http://www.haproxy.org/

ググるとロードバランサーとしてご利用の方が多そうですが、これをつまりWebsocketと別のポートで接続しなくてもよいようにするために利用します。

これだけで、それなりのボリュームになりそうなので、
Linux初心者がHAProxyを導入する
という記事に書いておきます。

次に、Supervisorというプロセス管理ツールを入れます。
これは、Ratchetを永遠にw生かしておくために入れます。
Linux初心者がSupervisorを導入する

で、デプロイのコードを簡潔に書こうと思います。

構造は下記の通りです。

 Ratchet
   |  
   ---bin
   |   --chat-server,php
   |   --push-server.php
   |
   ---src
       --MyApp
          --Chat.php
          --entry.php
          --Pusher.php

ちなみに、HAProxyによって、Webソケットはポート番号1337ですので、各コードの中で1337を指定しています。
詳しくはここを見てくれってばよ→Linux初心者がHAProxyを導入する

上から順に行きます。

chat-server.php

 <?php
 use Ratchet\Server\IoServer;
 use MyApp\Chat;
 
    require dirname(__DIR__) . '/vendor/autoload.php';
 
    $server = IoServer::factory(
        new Chat(),
        1337
    );
 
    $server->run();
 ?>

push-server.php

 <?php
    require dirname(__DIR__) . '/vendor/autoload.php';
 
    $loop   = React\EventLoop\Factory::create();
    $pusher = new MyApp\Pusher;
 
    // Listen for the web server to make a ZeroMQ push after an ajax request
    $context = new React\ZMQ\Context($loop);
    $pull = $context->getSocket(ZMQ::SOCKET_PULL);
    $pull->bind('tcp://127.0.0.1:5555'); // Binding to 127.0.0.1 means the only client that can connect is itself
    $pull->on('message', array($pusher, 'onBlogEntry'));
 
    // Set up our WebSocket server for clients wanting real-time updates
    $webSock = new React\Socket\Server($loop);
    $webSock->listen(1337, '0.0.0.0'); // Binding to 0.0.0.0 means remotes can connect
    $webServer = new Ratchet\Server\IoServer(
        new Ratchet\Http\HttpServer(
            new Ratchet\WebSocket\WsServer(
                new Ratchet\Wamp\WampServer(
                    $pusher
                )
            )
        ),
        $webSock
    );
 
    $loop->run();

Chat.php

 <?php
 namespace MyApp;
 use Ratchet\MessageComponentInterface;
 use Ratchet\ConnectionInterface;
 
 class Chat implements MessageComponentInterface {
    protected $clients;
 
    public function __construct() {
        $this->clients = new \SplObjectStorage;
    }
 
    public function onOpen(ConnectionInterface $conn) {
        // Store the new connection to send messages to later
        $this->clients->attach($conn);
 
        echo "New connection! ({$conn->resourceId})\n";
    }
 
    public function onMessage(ConnectionInterface $from, $msg) {
        $numRecv = count($this->clients) - 1;
        echo sprintf('Connection %d sending message "%s" to %d other connection%s' . "\n"
            , $from->resourceId, $msg, $numRecv, $numRecv == 1 ? '' : 's');
 
        foreach ($this->clients as $client) {
            if ($from !== $client) {
                // The sender is not the receiver, send to each client connected
                $client->send($msg);
            }
        }
    }
 
    public function onClose(ConnectionInterface $conn) {
        // The connection is closed, remove it, as we can no longer send it messages
        $this->clients->detach($conn);
 
        echo "Connection {$conn->resourceId} has disconnected\n";
    }
 
    public function onError(ConnectionInterface $conn, \Exception $e) {
        echo "An error has occurred: {$e->getMessage()}\n";
 
        $conn->close();
    }
 }

entry.php …今回、本体のWebアプリはCakePHP3で作っていますが本体と連動しているのがこいつです。

 <?php
 //CakePHPからincludeで呼び出しされて利用される
 
 ini_set( 'display_errors', 1 ); 
 
    $entryData = array(
        'category' => "id".$shipper_id
      , 'title'    => $title
      , 'article'  => 'hogeraccho'
      , 'when'     => time()
    );
    
    
    // This is our new stuff
    $context = new ZMQContext();
    $socket = $context->getSocket(ZMQ::SOCKET_PUSH, 'my pusher');
    $socket->connect("tcp://localhost:5555");
 
    $socket->send(json_encode($entryData));
    

Pusher.php

 <?php
 namespace MyApp;
 use Ratchet\ConnectionInterface;
 use Ratchet\Wamp\WampServerInterface; 
 
 class Pusher implements WampServerInterface {
 
    /**
     * A lookup of all the topics clients have subscribed to
     */
    protected $subscribedTopics = array();
 
    public function onSubscribe(ConnectionInterface $conn, $topic) {
        $this->subscribedTopics[$topic->getId()] = $topic;
    }
 
    /**
     * @param string JSON'ified string we'll receive from ZeroMQ
     */
    public function onBlogEntry($entry) {
        $entryData = json_decode($entry, true);
 
        // If the lookup topic object isn't set there is no one to publish to
        if (!array_key_exists($entryData['category'], $this->subscribedTopics)) {
            return;
        }
 
        $topic = $this->subscribedTopics[$entryData['category']];
 
        // re-send the data to all the clients subscribed to that category
        $topic->broadcast($entryData);
    }
 
    public function onUnSubscribe(ConnectionInterface $conn, $topic) {
    }
    public function onOpen(ConnectionInterface $conn) {
    }
    public function onClose(ConnectionInterface $conn) {
    }
    public function onCall(ConnectionInterface $conn, $id, $topic, array $params) {
        // In this application if clients send data it's because the user hacked around in console
        $conn->callError($id, $topic, 'You are not allowed to make calls')->close();
    }
    public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude, array $eligible) {
        // In this application if clients send data it's because the user hacked around in console
        $conn->close();
    }
    public function onError(ConnectionInterface $conn, \Exception $e) {
    }
 }

bin/push-server.php
をSupervisorでの設定により、起動させることによって、Ratchetが常時動作します。
詳しくは→Linux初心者がSupervisorを導入する

Ratchetを利用してPHPでWebソケット 1

PHP

一つの課題がありまして、PHPでもうすでにできあがっているプログラムがあり、でもWebsocketでリアルタイム通信をしたいという課題です。

弊社では、今までWebsocketでリアルタイム通信をするとき、node.jsをよく利用していました。
が、すでにあるプログラム群に追加するのはいろいろと面倒だったのです。

そこで、Ratchet WebSockets for PHPなるものがあることがわかりました。
http://socketo.me/

ほほー
これは、私が求めているものに近い!!
しかも、解説が親切!

というわけで、とりあえず、最初のチュートリアルからやってみます。

“hello world”やってみよう、という公式サイトを初心者目線で解説してみます。
http://socketo.me/docs/hello-world

とりあえず、例として、サーバーの下記のパスにratchetを入れてみます。

 /var/www/html/test/ratchet/

まず、PSR-0というライブラリとか、Ratchetのライブラリを入手します。
こういうのは、今時はもうcomposerでさくっとやっちゃうのです⊂(^-^)⊃

composerの例文がありますね。

 {
    "autoload": {
        "psr-0": {
            "MyApp": "src"
        }
    },
    "require": {
        "cboden/ratchet": "0.3.*"
    }
 }

ちなみに、PSR-0のサイトへ行くと、PSR-4を使ってくれよーん(2016年5月2日現在)と書いてあるので、気を利かしてpsr-4とやっても、ratchetさんのほうで対応していないので、動きません!

上記をテキストファイルに書いて、composer.jsonという名前でサーバーにアップロードします。

 $ composer update

とやります。

あとはですね、/var/www/html/test/ratchet/の中に、src/MyAppというディレクトリを作って、その中にChat.phpという名前で次のPHPファイルを保存します。

 <?php
 namespace MyApp;
 use Ratchet\MessageComponentInterface;
 use Ratchet\ConnectionInterface;
 
 class Chat implements MessageComponentInterface {
    protected $clients;
 
    public function __construct() {
        $this->clients = new \SplObjectStorage;
    }
 
    public function onOpen(ConnectionInterface $conn) {
        // Store the new connection to send messages to later
        $this->clients->attach($conn);
 
        echo "New connection! ({$conn->resourceId})\n";
    }
 
    public function onMessage(ConnectionInterface $from, $msg) {
        $numRecv = count($this->clients) - 1;
        echo sprintf('Connection %d sending message "%s" to %d other connection%s' . "\n"
            , $from->resourceId, $msg, $numRecv, $numRecv == 1 ? '' : 's');
 
        foreach ($this->clients as $client) {
            if ($from !== $client) {
                // The sender is not the receiver, send to each client connected
                $client->send($msg);
            }
        }
    }
 
    public function onClose(ConnectionInterface $conn) {
        // The connection is closed, remove it, as we can no longer send it messages
        $this->clients->detach($conn);
 
         echo "Connection {$conn->resourceId} has disconnected\n";
    }
 
    public function onError(ConnectionInterface $conn, \Exception $e) {
         echo "An error has occurred: {$e->getMessage()}\n";
 
        $conn->close();
    }
 }

次に、/var/www/html/test/ratchet/の中に、binというディレクトリを作って、その中にchat-server.phpという名前で次のPHPファイルを保存します。

 <?php
 use Ratchet\Server\IoServer;
 use MyApp\Chat;
 
    require dirname(__DIR__) . '/vendor/autoload.php';
 
    $server = IoServer::factory(
        new Chat(),
        8080
    );
 
    $server->run();

サーバーで、次のようにやります。

 $ php bin/chat-server.php

Windowsで、コマンドプロンプトを立ち上げます。
hogehoge.netというサーバーに上記の設定をしたとすると、

 telnet hogehoge.net 8080

とやります。
ちなみに、デフォルトでWindows10にTelnetは無効化されてますので、下記の記事を参考に有効化してください。
Windows10 Telnetを使う

もう一つ、コマンドプロンプトを立ち上げます。

 telnet hogehoge.net 8080

とやります。

すると、一方の画面で文字を打つと…
もう一方の画面にでてくるんですね!!
すごい!
感動ひとしお!!!

  • aaaaa — bbbbb {2018-01-07 (日) 08:24:57}

RSS Feedが更新されたらTwitterに自動でツイートする

twitter

以前はこのサイトの投稿を自動でTwitterで配信するために
ブログなどの最新投稿を自動的にTwitterでつぶやく
で紹介したように、Twitter Feedというサービスを利用していましたが、サービスが終了してしまいました [sad]

で、dlvr.it という後継サービスを利用していましたが、余計なパラメーターが生成されて、URLにくっつくので、dlvr.it経由でTwitterで配信すると、リンク切れという悲しい事態になってしまっていました。

なので、今回IFTTTというサービスで、
「RSS Feedが更新されたらTwitterに自動でツイートする 」
ということをやってみましたので、やり方を紹介しておきます。

IFTTTは有名なサービスなので、みなさんご存知かもしれませんが、サービスが有名になった時より、今パワーアップした感ありますね。

https://ifttt.com/discover

家についたらWifiをONにする、とか、寝る時にはサイレントにする、とかお役立ち機能が実装されています。

家についたらWifiをONにする
というのは、もともと作ってある機能(アプレットと呼ぶらしい)なのでONにしたり、OFFにしたりするだけですが、
「RSS Feedが更新されたらTwitterに自動でツイートする 」
は、自分でアプレットを作らないといけません。

  1. アカウントを作って、ログインしたら、New Appletというボタンをクリックします。
  2. 下記の図の、+thisとある、青い字をクリック

ifttt.png

  1. Feedのオレンジのアイコンをクリック
  2. New Feed Itemをクリック
  3. Feed URLに反映させたいRSS FeedのURLを入力
  4. あとは、Twitterに投稿させたい内容を入力します。

しゃれたUIで、操作も楽しいですね![smile]