まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

2015/09

243 ゲームをご存知でしょうか?
2015092901


2048 ゲームは(そこそこ)有名だと思いますが、そのクローンゲームです。2048 ゲームは以下のルールで遊ぶものです:
(1) 矢印キーを使って、画面内のタイルを4方向にスライドさせる
(2) 同じ数字のタイルをぶつけると、数字が倍の1つのタイルができる
(3) ぶつけて出来上がったタイルの数字が得点になる(2048 タイルを作ることが1つの目標)


243 は 2048 と異なり、3つの同じ数字を重ねることで1つにまとまります。2048 よりは難易度高くなっています。具体的なルールは以下になります:
(1) 矢印キーを使って、画面内のタイルを4方向にスライドさせる
(2) 同じ数字のタイルを3つぶつけると、数字が3倍の1つのタイルができる
(3) ぶつけて出来上がったタイルの数字が得点になる(243 タイルを作ることが1つの目標)



で、このゲームの HTML5 が Github 上で公開されていました。これを使って IBM Bluemix 上で 243 ゲーム環境を作ってみます(まあ、特にトリッキーなこともないんですが・・)。

まず Bluemix 上に 243 ゲームが動くランタイムを作成します。サーバーサイドで動くモジュールがないので、Github の staticfile ビルドパックを使っても動くと思いますが、Bluemix ダッシュボードから PHP ランタイムを選んでも(事実上 PHP を使わないので、かなり贅沢な環境ですが)動きます。今回は PHP ランタイムを使う方向でいきましょう:
2015092902


並行して 243 ゲームの zip モジュールを GitHub からダウンロードします:
2015092903


ダウンロードした zip モジュールを展開します。index.html が存在するディレクトリがドキュメントルートになります:
2015092904


ここまでできたら、cf コマンドラインツールを使って、このドキュメントルートを PHP ランタイムにプッシュするだけ、です:
# cd (展開した index.html があるディレクトリ) ドキュメントルートへ移動
# cf login -a https://api.ng.bluemix.net/    cf で Bluemix にログイン(米国DCの場合)
# cf push (PHP ランタイムの名前)        cf で Bluemix に 243 ゲームをプッシュ

プッシュが成功したら、PHP ランタイムの URL にブラウザからアクセスすると 243 ゲームが始まります:
2015092905


ぎゃー、惜しかったけど無理。難しい!!

IBM IoT Foundation が無料提供している Quickstart MQTT ブローカーを使ったアプリのサンプルを紹介します。今回紹介するのは IBM Bluemix各サービスの中で止まっているサービスがあったらそれを知らせる、という機能を Node-RED で作成するような内容にします。

このアプリを作成する上で必要なサービスの稼働状況確認は estado (エスタド)を使って調べます:
http://estado.ng.bluemix.net/  
(※この URL は北米データセンターのサービス状況確認用です。英国データセンター用は URL 内文字列の ng の代わりに eu-gb を指定してください)

2015092001


estado は Bluemix ランタイムやサービスの単位での稼働状況を教えてくれるサービスサイトです。問題なく稼働しているランタイム/サービスについては "up" と表示されますが、稼働が止まってたり、なんらかの障害を抱えていると判断されたサービスに関しては赤背景で "down" と表示されます。
2015092002


Bluemix の各機能の稼動状態は上記 URL にアクセスすることで確認することはできます。ただそれでは「上記サイトを見に行った人が稼働状況を確認できる」というだけです。情報そのものは便利なのですが、稼働ステータスをもっとインタラクティブにプッシュしたり、ユーザーフレンドリーな形で提供することを考えてみましょう。

具体的にはこのようなシステムを作ります。この図の緑の丸で描かれた「Java アプリ」部分を実装します:
2015092004


この Java(スタンドアロン)アプリケーションは定期的(この例では1分に1度)に北米データセンターの estado に HTTP GET リクエストを出して Bluemix サービスの稼動状態を確認します。確認した結果、停止中のサービスの一覧を IBM IoT Foundation の Quickstart MQTT ブローカー(quickstart.messaging.internetofthings.ibmcloud.com:1883)へランダムに生成した deviceId を指定して JSON でパブリッシュする、というものです。この時の deviceId はアプリケーション起動時に動的に作成し、コンソールへ出力します。英国データセンター用に作り変えたり、実行感覚を変更したい場合はソースコードの該当箇所を適宜変更してください。
 

estado を参照した結果は Quickstart MQTT ブローカーに格納されるので、パブリッシュ時と同じ deviceId を指定すれば MQTT サブスクライバーを使って取り出せます。特に IBM Bluemix 上の IoT Starter や NodeRED Starter ビルドパックからプロジェクトを作成した場合であれば、IBM IoT Input ノードを使えばこの deviceId だけで簡単にメッセージを取り出すことができるので、その結果をデバッグタブに出力したり、メールで通知したり、といった管理機能が簡単に実現できます。この辺りは Bluemix で NodeRED を使ったことがある方であれば、あまり難しくないと思っています。この辺りの詳しい説明はこちらのエントリを参照ください:
Bluemix の Node-RED サービスで IoT アプリを作る(1/2)


では話を Java アプリケーションの実装に戻します。今回は MQTT のオープンソース Java ライブラリである Paho と、Jakarta CommonsHTTP Client V3 を使ったサンプルを作成してみました。ソースコードのコンパイルや実行時には Paho の Java ライブラリ V3 の最新版(org.eclipse.paho.client.mqttv3-X.X.X.jar)が必要です。こちらからダウンロードしてください:
https://www.eclipse.org/paho/clients/java/


また HTTP Client の JAR ファイルも必要です。別途ダウンロードしておいてください。

なお、今回作成した Java アプリのソースコードは github で公開しました。必要であればこちらからダウンロードしておいてください:


公開したソースコードの内容は以下の様なものです:
import java.util.Calendar;
import java.util.TimeZone;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttTopic;

public class EstadoQuickstart implements MqttCallback {
  MqttClient myClient;
  MqttConnectOptions connOpt;

  static final String BROKER_URL = "tcp://quickstart.messaging.internetofthings.ibmcloud.com:1883"; //. IBM IoT Quickstart MQTT ブローカー	
  static final String dc = "ng"; //. 監視対象データセンター

  static String deviceId = "net.bluemix." + dc + ".estado.mqtt.publish.";
  static int interval = 60;

  @Override
  public void connectionLost(Throwable t) {
  }

  @Override
  public void deliveryComplete(IMqttDeliveryToken token) {
  }

  @Override
  public void messageArrived(String topic, MqttMessage message) throws Exception {
    // TODO Auto-generated method stub
    System.out.println("-------------------------------------------------");
    System.out.println("| Topic:" + topic);
    System.out.println("| Message: " + new String(message.getPayload()));
    System.out.println("-------------------------------------------------");
  }

  public static void main(String[] args) {
    try{
      if( args.length >; 0 ){
        interval = Integer.parseInt( args[0] );
      }

//. ランダムな deviceId の生成 String hex = "0123456789abcdef"; for( int i = 0; i < 8; i ++ ){ char c = hex.charAt( ( int )( Math.floor( Math.random() * 16 ) ) ); deviceId += c; } }catch( Exception e ){ } EstadoQuickstart eq = new EstadoQuickstart(); eq.runClient(); } public void runClient() { // TODO Auto-generated method stub String clientID = "d:quickstart:MyDevice:" + deviceId; System.out.println( "deviceId=" + deviceId ); connOpt = new MqttConnectOptions(); connOpt.setCleanSession( true ); connOpt.setKeepAliveInterval( 30 ); // Connect to Broker try{ myClient = new MqttClient( BROKER_URL, clientID ); myClient.setCallback( this ); myClient.connect( connOpt ); }catch( MqttException e ){ e.printStackTrace(); System.exit( -1 ); } String myTopic = "iot-2/evt/estado_" + dc + "/fmt/json"; //. MQTT トピック文字列を生成 MqttTopic topic = myClient.getTopic( myTopic ); while( true ){ try{
//. 日付文字列の生成 Calendar c = Calendar.getInstance(); TimeZone tz = TimeZone.getTimeZone( "Asia/Tokyo" ); c.setTimeZone( tz );; int y = c.get( Calendar.YEAR ); int m = c.get( Calendar.MONTH ) + 1; int d = c.get( Calendar.DAY_OF_MONTH ); int h = c.get( Calendar.HOUR_OF_DAY ); int n = c.get( Calendar.MINUTE ); int s = c.get( Calendar.SECOND ); String dt = y + "/" + ( m < 10 ? "0" : "" ) + m + "/" + ( d < 10 ? "0" : "" ) + d + "T" + ( h < 10 ? "0" : "" ) + h + ":" + ( n < 10 ? "0" : "" ) + n + ":" + ( s < 10 ? "0" : "" ) + s + "+09:00";
//. estado の HTML を取得 String out = ""; HttpClient client = new HttpClient(); GetMethod get = new GetMethod( "http://estado." + dc + ".bluemix.net/" ); int sc = client.executeMethod( get ); String html = get.getResponseBodyAsString();

//. HTML 内部を見て、サービスのステータスを確認 int x = html.indexOf( "<table" ); while( x >; -1 ){ int td1 = html.indexOf( "<td>;", x + 1 ); int td2 = html.indexOf( "</td>;", td1 + 4 ); int td3 = html.indexOf( "<td>;", td2 + 1 ); int td4 = html.indexOf( "</td>;", td3 + 4 ); if( td1 >; -1 && td2 >; -1 && td3 >; -1 && td4 >; -1 ){ String name = html.substring( td1 + 4, td2 ); String svalue = html.substring( td3 + 4, td4 ); if( svalue.equals( "down" ) ){ //. 止まっているサービスを探す String line = "\"" + name + "\""; //. 止まっているサービスの配列を返す if( out.length() >; 0 ){ line = "," + line; } out += line; } x = td4; } x = html.indexOf( "<tr ", x + 1 ); } out = "{\"datetime\":\"" + dt + "\",\"error_services\":[" + out + "]}"; //. MQTT Publish int pubQoS = 0; MqttMessage message = new MqttMessage( out.getBytes() ); message.setQos( pubQoS ); message.setRetained( false ); // Publish the message MqttDeliveryToken token = null; try{ // publish message to broker token = topic.publish( message ); // Wait until the message has been delivered to the broker token.waitForCompletion(); Thread.sleep( 1000 ); //. パブリッシュ後、とりあえず1秒待つ }catch( Exception e ){ e.printStackTrace(); } //. 次の実行タイミングを待つ Calendar c0 = Calendar.getInstance(); int s0 = ( c0.get( Calendar.SECOND ) % interval ); int w = 1000 * ( interval - s0 ); Thread.sleep( w ); }catch( Exception e ){ } } } }



Paho の Java ライブラリとソースコード(EstadoQuickstart.java)をダウンロードしてコンパイル&実行します。実行が成功すると、コンソール画面には次のように Quickstart MQTT ブローカーにパブリッシュした際の deviceId が表示されます。そして Ctrl+C などで終了するまでの間は(デフォルト状態であれば)60秒おきに estado をチェックしてその結果をパブリッシュする、という処理を繰り返します:
2015092101
 

IBM Bluemix のユーザーであれば、この結果を簡単に取り出すことができます。Bluemix 上に IoT Foundation Starter または NodeRED Starter のボイラープレートアプリケーションを作成して NodeRED 環境にアクセスします。そして IBM IoT Input ノードを1つ作成して、ノードの属性値に "Quickstart" と DeviceId に上述の Java アプリ実行時にコンソールに表示された deviceId 値と同じものを指定します:
2015092102


そしてとりあえず debug ノードを追加して IBM IoT ノードと線で結んでデプロイするだけで、このノードに60秒おきに停止している Bluemix サービスの情報が送られてくるようになります。
2015092003


このサンプルでは以下のような JSON フォーマットでメッセージを取得します。後はこのメッセージを Node-RED 側でハンドリングしてメールで通知したり、データベースに格納する、といった処理を実装することになります:
{
 "datetime": "2015/09/19T22:53:00+09:00",
 "error_services": [
  "account-usage", "meteringbackgroundprocess", "meteringdatabase", "runtime-usage-calculator", "dotnet", "alchemy_api [Free]", ・・・(止まっているサービス名の配列)・・・
 ] 
}



というわけで、後はこの Java アプリをどこかのクラウド上か、または(24時間電源ONが保証できる)ローカル環境で稼働させておけば、MQTT にステータスを送り続けてくれる環境が出来上がるので、このような Node-RED アプリを作って動かすことも可能になる、ということになります。

MQTT はデバイスを意識して作成された軽量なプロトコルですが、必ずしもデバイスでしか使えない、というものでもありません。今回のようなサーバーの死活情報を扱ってはいけない、というものではないし、現に某有名ソーシャルサイトのメッセージング機能で使われていたりもします。何をパブリッシュして、どこからサブスクライブするか、そしてサブスクライブした情報をどう扱うか(Node-RED はここが簡単にできる、というサービスです)、が重要だと思っています。みなさんも色んなアイデアを是非形にしてみてください。同様のアルゴリズムが実装できれば Java 以外の言語でも実現できると思っていますので、興味のある方は是非移植にも挑戦してみてください。



 

IBM Bluemix のサードパーティサービスの1つである Load Impact を紹介します:
2015092400


このサービスは単純にウェブサイト/ウェブサービスに負荷をかけて、その時のパフォーマンスを測定するサービスです。負荷をかける対象は Bluemix 上のランタイムでも、そうでなくても構いません。


Load Impact サービスは Bluemix 上では "DevOps" カテゴリに分類されています。このサービスは特定のランタイムにバインドして利用するものではないので、カタログページから直接選択して追加することで利用可能になります:
2015092401


Load Impact サービスの説明ページです。"Free"(無料)利用の条件が表示されているので確認して追加してください:
2015092402


Load Impact サービスを追加した後、ダッシュボード画面から追加した Load Impact サービスを選択すると、このような画面が表示されます。"OPEN LOAD IMPACT DASHBOAD" と書かれた箇所をクリックして、Load Impact のダッシュボード画面に移行します:
2015092403


サービスを利用するには最初にログインする必要があります。ここでは新規にアカウントを作成することもできますし、既にアカウントをお持ちであればそれを指定することもできますし、また Google や Github といったソーシャルサイトのアカウントをお持ちであればそれらとの OAuth を使ってログインすることもできます。お好きな方法でログインしてください:
2015092404


ログイン直後にこのような画面が表示されるはずです。この画面をスキップして詳細なテスト設定を行うこともできますが、まずはこの簡易機能を使ってテストしてみましょう:
2015092405


今回、テストする対象はこちらのサイトとします(皆さんがお作りになったサービスを指定しても構いませんが、他の方が作ったウェブサイトの URL を勝手に指定することはご迷惑をかけることになりかねないので控えてください):
http://fx.mybluemix.net/
2015092406


このサービスは先日、私が作って紹介したリアルタイム為替情報の REST API サービスです。IBM Bluemix 上で動いています。一般的には「ウェブサイト/サービスはリクエストからレスポンスまで2秒待つと、ユーザーは長く感じる」と言われているので、レスポンスタイムが2秒を確保できているかどうかをテストしてみたいと思います。また最大同時接続ユーザー数は30を想定します(つまり30ユーザーが同時にアクセスしてきても、このサービスは2秒以内にレスポンスを返せるか?をテストで計測する指標とします)。

では、このサービスの URL(http://fx.mybluemix.net/) を URL フィールドに入力します。同時に最大接続ユーザー数(Max VUs)を "30" に、Duration(mins) を "3" にします(つまり3分間で30ユーザーのテストをする、という指定をします)。最後に "Run test" と書かれたボタンをクリックして、テストを開始します。作業はこれだけ、簡単ですね:
2015092407


しばらくするとテストが始まります。最初に同時接続(仮想)ユーザーの準備を行います:
2015092401


テストが始まると、このサービスを提供している場所(バージニア州 Ashburn)から、目的のサーバー(下図ではテキサス州 Dallas)までの地図が表示されます。このレイテンシーがテスト結果に影響することを考慮してください:
2015092402


またテスト途中までの結果も順次表示されていきます。少しずつ接続ユーザー(青)を増やしながら、それぞれの回でどれくらいのレスポンスを実現できたのか(緑)が分かります:
2015092403


今回の設定ではテスト開始から3分後に最大接続ユーザーのテストが行われ、その後少しずつユーザーを減らしてテストが終了します。終了すると左上に "Finished" と表示されます:
2015092404


画面を下にスクロールすると、最終テスト結果がグラフ表示されています。一瞬危ういテスト回もありましたが、どうやらこのサービスは30ユーザー程度であれば、2秒どころか1秒以内にレスポンスを返せることがわかりました。リアルタイム性を重視するサービスなので、これは一応合格だと思います。さすが Bluemix 、という所?
2015092405


更に下にスクロールすると、更に細かなテスト結果も表示されています。ほぼ何の準備もせずに、URL と最大接続ユーザー数の指定をしただけで、このレベルの負荷テストが実現できた、ということがわかります:
2015092406


Load Impact はもっと細かなテスト項目の指定などもできますが、そこまでしなくてもある程度の負荷テストが実現できるツールです。Bluemix のようにアジャイルに機能を追加してアプリケーションを開発できる環境ですと、ついつい変更による負荷の影響を忘れがちになってしまいますが、そのようなことがないように Load Impact サービスを1つアクティブにしておくといいかもしれません。


連休中にふと気付いた、Bluemix / Watson デベロッパーとしては大きめのニュースです。

IBM Bluemix から提供されている Watson API 群の1つ、Text to Speech 。これは与えたテキストを自然な音声のオーディオデータにして返してくれる API サービスです。で、このサービスのカタログページ(https://console.ng.bluemix.net/catalog/text-to-speech/)をブラウザの言語設定を日本語にして参照すると、今(2015/Sep/23)も以前と特に変わらずこんな感じですが・・・
2015092301

 
ブラウザの言語設定を英語にして同ページにアクセスすると・・・
2015092302


こ、これはっ!
2015092303

 
念のため、Watson Developer 向けデモページ(http://text-to-speech-demo.mybluemix.net/)を開いてみると、言語サンプルに「日本語 - エミ(女性)」なる選択肢が含まれています!!サービスがいつの間にか日本語対応していたのか!?
2015092305


念のため、実際に API として利用できるかどうかを試してみました。Bluemix 上で Watson の Text to Speech サービスを追加し、接続情報(credentials 内の url と username と password)を参照します:
2015092307


この username と password の情報を使って、ウェブブラウザ(Chrome か FireFox)で以下の URL にアクセスします:
https://(usernameの値):(passwordの値)@stream.watsonplatform.net/text-to-speech/api/v1/synthesize?voice=ja-JP_EmiVoice&text=今日はいい天気ですね


するとこんな画面になって、女性の声で「今日はいい天気ですね」と聞こえてきます。データは audio/ogg で送られてくるので、この音声フォーマットを再生することのできるブラウザを使うか、或いは受け取る側でこの音声データを再生することができれば、その場で再生されるはずです:
2015092306


もちろん、これはあくまで API なので、実際のアプリケーションではそのまま再生する必要はなく、データベースに格納してもいいし、この音声データを別の API にポストしてもいいわけです。 ともあれ、いつの間にか Text to Speech が日本語対応していました!

なお、Text to Speech API のリファレンスはこちらを参照ください:
http://www.ibm.com/smarterplanet/us/en/ibmwatson/developercloud/apis/#!/text-to-speech/


で、「エミ」って誰??

IBM Notes の起動時に表示されるスプラッシュスクリーンをカスタマイズする方法を紹介します:
2015091801
 (↑これがスプラッシュスクリーン)


やり方は色々あって、「きちんと」対応しようとすると結構面倒なこともあるんですが、てっとり早く作るにはこれかなあ、という方法を紹介します。

まずはビットマップ(Windows BMP)形式のスクリーン画像を用意します。今回はこれを使います:
2015091802
(注 この画像↑自体は PNG 形式なので、保存してもそのままでは使えません。お手元の環境で BMP 形式に変換してから使ってください)

なお、ここで用意する画像のサイズがあまり小さすぎると、パスワードプロンプトメッセージがうまく表示できなかったりするので、適宜調整してください。ちなみに↑上記の画像のサイズは 640x480 です。

この BMP 画像ファイルを適当なフォルダに保存します(C:\IBM\Notes\shojoji.bmp に保存したとします)。そしていったんノーツを閉じて、notes.ini ファイルをテキストエディタで開き、最後に以下の2行を追加します:
  :
  :
SESPlashPath=C:\IBM\Notes\shojoji.bmp
HasNotesOverlay=1

この状態で notes.ini を保存して、改めてノーツを起動するとこんな感じになります。指定した画像がスプラッシュスクリーンになって、パスワード入力待ちになっていることが確認できます:
2015091803


スプラッシュスクリーンをよく見ると分かるのですが、画像左下にパスワードプロンプトメッセージが表示されていたりします。画像サイズによってはここが崩れてしまったり、(今回のように)見難い位置に表示されてしまったりする可能性があるので、そのあたり上手く調整する必要があります。


業務でノーツをお使いの皆様、仕事中の気分転換にどうぞ。

なお、「ちゃんとした方法」はこちらをどうぞ:
http://www-01.ibm.com/support/docview.wss?uid=swg21962056


このページのトップヘ