Help us understand the problem. What is going on with this article?

ブロックチェーンをソケット通信でモニターする

More than 1 year has passed since last update.

はじめに

ブロックチェーンとソケット通信はトレンドですが、少しニッチに(?)、以下のような構成でモニターします。
ブロックチェーン: Stellar
クライアントサイド: socket.io
サーバーサイド: PHP(Phpws)

もう少し細かく、以下が登場人物になります。
OS: Ubuntu 16.04LTS
Stellar: 9.2.0
WEBサーバ: Nginx 1.10.1
PHP: 7.3.0 (phpenv)
Phpws: c2eee39 (13 Aug 2018)
ソケットサーバ: localhost:11180
サーバーエージェント: server.php
モニター画面: https://socket.example.com
ソケットURI: wss://wss.example.com
htmlクライアント: index.html

概要として、
常駐するサーバーエージェントがソケットのポートを開いて待ち受け、ここにhtmlクライアントからNginx経由でプロキシされたソケットURIに対してStellarの状態を一定間隔毎に返します。

実習

さっそく構築方法です。

default.conf
server {
    listen xxx.xxx.xxx.xxx:443 ssl;
    ssl_certificate /etc/letsencrypt/live/socket.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/socket.example.com/privkey.pem;
    server_name  socket.example.com;

    root /home/you/monitor;

    location / {
        index  index.php index.html index.htm;
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        try_files $uri = 404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass   unix:/var/run/php-fpm.sock;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}

upstream websocket {
    server localhost:11180;
}

server {
    listen xxx.xxx.xxx.xxx:443 ssl;
    ssl_certificate /etc/letsencrypt/live/wss.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/wss.example.com/privkey.pem;
    server_name  wss.example.com;
    location / {
        proxy_pass http://websocket;
        proxy_redirect http:// https://;
    }
}

"upstream websocket"と"proxy_redirect"がポイントです。
ここで、httpsなインバウントアクセスをインターナルではhttp扱いでプロキシにつないでいます(SSL化はマストではないですが)。

次にサーバーエージェントです。
まず、PHPでソケットサーバーを立てるためのライブラリとして利用するPhpwsをcomposerでインストールておきます。

mkdir ~/monitor/
cd ~/monitor/
vi composer.json
    {
        "repositories": [
            {
                "type": "vcs",
                "url": "https://github.com/Devristo/phpws"
            }
        ],
        "require": {
            "devristo/phpws": "dev-master"
        }
    }

composer install

ここに以下を用意します。

server.php
<?php
require_once("vendor/autoload.php");
use Devristo\Phpws\Server\WebSocketServer;

$loop = \React\EventLoop\Factory::create();
$server = new WebSocketServer("tcp://localhost:11180", $loop);

foreach (['core-01', 'core-02', 'core-03', 'core-04'] as $core) {
    $loop->addPeriodicTimer(2, function() use($server, $core){
        foreach($server->getConnections() as $client) {
            list ($num, $state) = check_core($core);
            $client->sendString(json_encode([$core => compact('num', 'state')]));
        }
    });
}

$server->bind();
$loop->run();

function check_core($core) {
    exec('curl -s ' . $core . ':11626/info', $res);
    $num = null;
    $state = null;
    if (!empty($res)) {
        foreach ($res as $row) {
            strpos($row, '"num"') !== false and $num = trim(explode(':', $row, 2)[1]);
            strpos($row, '"state"') !== false and $state = trim(str_replace(['"', ','], '', explode(':', $row, 2)[1]));
            if (!is_null($num) && !is_null($state)) break;
        }
    }
    return [$num, $state];
}

addPeriodicTimer()で、2秒毎にcheck_core()した結果を$client->sendString()で渡しています。
addPeriodicTimer()はcore-**毎にセットされて、それぞれ非同期でcheck_core()がコールバックされるようになっています。

core-**は、/etc/hosts内であらかじめ定義されたコンテナ(DockerやLXC)内に構築された複数台構成の分散型ブロックチェーンサーバです。
11626ポートはStellarがデフォルトで公開しているHTTPなエンドポイントなのですが、これをそのまま利用します。
Stellar以外を使う場合は、このあたりをそれぞれのブロックチェーンのインターフェースにあわせて置き換えればよいかと思います。

最後にhtmlクライアントです。

index.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Core monitor</title>
<link rel="stylesheet" href="index.css">
<script src="//code.jquery.com/jquery-1.11.1.js"></script>
<script>
$(function () {
  function core_processor(core, data) {
    var num = data['num'];
    var state =  data['state'];
    if ($('#' + core + ' dd.state span').text() === '') {
      $('#' + core + ' dd.state span').text('');
    }

    switch (state) {
      case 'Synced!':
        $('#' + core + ' dd.state span').removeClass().addClass('green');
        break;
      case 'Catching up':
        $('#' + core + ' dd.state span').removeClass().addClass('red');
        break;
      case 'Joining SCP':
        $('#' + core + ' dd.state span').removeClass().addClass('black');
        break;
      default:
        $('#' + core + ' dd.state span').removeClass().addClass('gray');
    }
    if (state === 'Synced!' && num !== $('#' + core + ' dd.num span')) {
      $('#' + core + ' dd.num span').text(num);
    } else {
      $('#' + core + ' dd.num span').text('');
    }
    $('#initializer').hide();
  }

  var socket = new WebSocket("wss://wss.ketoha.xyz");
  socket.onmessage = function(msg) {
    data = JSON.parse(msg.data);
    if (!data) return;

    if (!!data['core-01']) core_processor('core-01', data['core-01']);
    if (!!data['core-02']) core_processor('core-02', data['core-02']);
    if (!!data['core-03']) core_processor('core-03', data['core-03']);
    if (!!data['core-04']) core_processor('core-04', data['core-04']);
  };

});
</script>
</head>
<body>
  <div id="initializer"></div>
  <dl id="core-01" class="core">
    <dt>core-01</dt>
    <dd class="state"><span></span></dd>
    <dd class="num"><span></span></dd>
  </dl>
  <dl id="core-02" class="core">
    <dt>core-02</dt>
    <dd class="state"><span></span></dd>
    <dd class="num"><span></span></dd>
  </dl>
  <dl id="core-03" class="core">
    <dt>core-03</dt>
    <dd class="state"><span></span></dd>
    <dd class="num"><span></span></dd>
  </dl>
  <dl id="core-04" class="core">
    <dt>core-04</dt>
    <dd class="state"><span></span></dd>
    <dd class="num"><span></span></dd>
  </dl>
</body>
</html>

socket.onmessageで、サーバーエージェントから$client->sendString()を受け取る度に、それぞれの監視対象のブロックチェーンサーバに応じたDOMを更新させています。
index.cssは省略します。

ここまで来たら、いよいよ動かしてみましょう。

php -q server.php

ブラウザから https://socket.example.com にアクセスすると、以下のような画面になります。

ss.png
2秒毎にブロックチェーンサーバの監視結果、ここでは同期状態とレジャ番号が画面でウォッチできるようなっています。

実際にはここでトレードや送金の状態を追加することで、実用的な運用をおこなうことができます。

最後に

最後に予断ですが、なぜこんな少しニッチ(?)な構成にしているかというと―――

Stellar
分散サーバ型のブロックチェーンなので、サーバーサイドで完結でき(プライベートチェーンとして使い勝手がよい!)、かつトランザクションもSCPという仕組みを使っているため、ビットコインやイーサリウムと比べて格段に早く安定していて、最高だからです。

PHP
同じものをnodejsで動かしてはいるのですが、サーバーサイドのコーディングはまだPHPの方が生産性が高いひとも多いのではないか(つまり自分...)、そういった意味で、まだあまり情報の少なそうなこちらの構成で書いてみました。

yos_hirata
請負システム開発の合同会社ケトハで代表社員をやってます。完全リモート開発で日本のフリーランスの方やベトナムのオフショア開発会社と一緒に日々楽しく痺れる開発業務を行ってます。分散サーバとかKVSとかXLC(Docker)とかブロックチェーンのノード構築とかのインフラ側が得意(プログラミングも大好きですが)。設計に命をかければプログラマーは手戻りや短納期で泣かずに済むというウォーターフォール信者です。
http://ketoha.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした