かばちんのエンジニアブログ

日々の経験の中で培った内容を備忘録も兼ねて記録していくブログです。少しでも誰かの役に立つために頑張って続けていけたらなと思います。

「ぬこしょうぎ」におけるリアルタイム通信

f:id:kabatin:20160907142256p:plain

リアルタイム将棋「ぬこしょうぎ」をリリース

先日こんなアプリをリリースさせて頂きました。
ぬこしょうぎ - ヤルキマントッキーズ株式会社


将棋をモチーフにコマの動きは将棋そのままで、将棋からターン制をバッサリと取り除いたゲームです。
ルールはシンプルなので将棋を知らない方でも楽しめる仕様になっています。

(不思議なことにターン制を取り除いてもやっぱり将棋を知っている人が若干有利になるようです!)


通信をどのような構成で、何を使ってやっているか

◆サーバサイド(コネクションサーバ)

クライアントからの WebSocket 接続を受け付けるサーバ。
知人が作成した nginx-luajit-websocket-pubsuber を利用して WebSocket 接続を受け付けている。
github.com


簡単に説明すると、nginx で接続を受け付けて WebSocket コネクションを維持し、
送られてきたデータを Disque に格納する仕組みです。(←この部分を lua が担っている)

※Disqueとは
Redis の開発者が作成した、オンメモリで動く Queue システムです。
github.com


◆サーバサイド(ゲームコンテキスト)

ゲームコンテキスト側ではとにかく Disque からデータが取得さえ出来ればあとは自由に処理ができます。
そこで利用したライブラリが、同じく知人が作成した Disquuun という C# で作成された Disque ライブラリです。
これにより、コネクションごとのデータを自由に出し入れできるようになります。
github.com


◆クライアントサイド(Unity 5.3.5p8)

クライアントからコネクションサーバに対して WebSocket 接続を行う必要があります。
利用したライブラリは、これまた同じく知人が作成した WebuSocket という C# で作成されたライブラリです。
github.com


全体の構成図

f:id:kabatin:20160907144618p:plain


この構成の最大の特徴1

何と言ってもこの構造で素晴らしい点、それは全てが非同期で行われているということです。
基本的に WebSocket 接続だと、何か送ったらそれをトリガーにしてサーバが処理を行い結果を返すと思います。
そうなってしまうと、サーバ側の処理が遅延した場合に後続の処理がすべて引きずられてしまいます。


この仕組みでは、クライアントから来た通信を元にまず nginx-lua が Disque にデータを格納します。
この時点でクライアントが送ったデータの処理は完了します。


その後、ゲームコンテキストがゲームループ周期のタイミングで Disque からデータを吸い上げて
結果を返すために必要な処理を行い、クライアントへ送るデータを Disque にまた書き出します。


そのデータを nginx-lua が Disque から吸い上げ、クライアントへデータを Push します。


この構成の最大の特徴2

サーバサイドの負荷の低さ、サーバコストの安さです。
IDCF の最も安価なサーバである S1 サーバ(月額200円+ディスク代300円=500円/月)で同時接続500人に耐えられます。


ぬこしょうぎでは、プレイタイムがだいたい1分〜2分前後であるのにも関わらず、
リリースして間もない時期に同時接続が70人を上回る時がありましたが、全く問題なく S1 サーバ1台で処理しています。


サーバのスペックをそれなりのものに変更すると、なんと20,000接続以上も耐えることができます。
もちろん1台でとにかく多くさばけばいいというものでもないので、リスクとのトレードオフがありますが
負荷の軽さについてはだいたい想像がついたのではないでしょうか。


UnityEditorとの相性の良さ

サーバサイド(ゲームコンテキスト)が C# で書かれているため、なんとUnity上でも動きます。
サーバサイドであるゲームコンテキストが Unity 上で動くので、個人端末でクライアントとサーバサイドの
両方を同時に開発することが可能になります。


世界初の DotNetCore 実用サービス

あくまで現時点での想定ではありますが、DotNetCore を実際に使用してリリースされた世界初のサービスです。
すでにお気付きの方もいらっしゃったかもしれませんが、サーバサイドを C# で書いているので Windows サーバだと
思った方も多いかもしれません。


が、しかし。
ぬこしょうぎは Ubuntu 14.04 LTS を利用してリアルタイム通信を実現しています。


そうです。
CoreCLR を利用して Linux上で C# のコードを動かしています。
Download .NET (Linux, macOS, and Windows)


まとめ

知人すごい。これに尽きる。

まだ大規模な実用で使われたことはない構成ではあるが、個人的にはものすごく理にかなった仕組みだと思っている。
開発のしやすさ、スケールのしやすさ、運用コストの低さ、負荷の低さ、どれを見ても群を抜いている気がする。

また UnityEditor 上でも動作する仕様上、開発のしやすさも兼ねておりいずれ標準的なモノになるのではないだろうか。
今後もこの仕組みを利用したアプリをたくさん出していきたい。






CoreCLRでNewtonsoft.Jsonを試してみた

前回こちらの記事でCoreCLRでNugetからライブラリを取得する方法をご紹介しました。
kabatin.hateblo.jp


実際にNugetからライブラリを取得し、使用できるのかを試してみましたので共有したいと思います。

サンプルソース

using System;
using Newtonsoft.Json;

namespace ConsoleApplication
{
    public class Person
    {
      public string name { get; set; }
      public int age { get; set; }
    }

    public class Program
    {
      public static void Main(string[] args)
      {
        try
        {
          var person = new Person {
            name = "sample",
            age = 30
          };

          var json = JsonConvert.SerializeObject(person);
          Console.WriteLine(json);
        }
        catch (Exception e)
        {
          Console.WriteLine(e.Message);
        }
      }
    }
}

実行結果

$ dotnet run
Project JsonTest (.NETCoreApp,Version=v1.0) was previously compiled. Skipping compilation.
{"name":"sample","age":30}

結論

動いた!!

CoreCLRでNugetからライブラリを取り込む方法

CoreCLRを使うことでLinux上で.NETプロジェクトを動かすことができるようになりました。
空のプロジェクトからシステムを組んでいくにあたりどうしてもNugetからライブラリが欲しかったりすると思います。


もしくは、Windowsで作っていたプロジェクトを移植するにあたりNugetのライブラリを既に使っていた場合も同様に、
Nuget経由でのライブラリ取得をしたい場合が出てくると思います。


CoreCLRの情報がまだまだ少ないので、自分で試してみたNuget経由でのライブラリ取得方法をご紹介したいと思います。
紹介と言っても、やっているので設定ファイルを変更してコマンドをひとつ叩くだけですが。。

前提条件

・CoreCLRがどんなものか知っている
dotnet new や dotnet restore コマンドでプロジェクトを作成できることを知っている


設定ファイルの変更

CoreCLRの設定ファイルといえば project.json ファイルです。
このファイルはCoreCLRプロジェクトに関わる設定が書かれています。


CoreCLRバージョンの指定や、依存関係、フレームワークなどを指定できます。
今回は試しに Newtonsoft.JSON の 9.0.1 をNugetで取得する記述を追加してみます。

変更前

{
  "version": "1.0.0-*",
  "buildOptions": {
    "emitEntryPoint": true
  },
  "dependencies": {
    "Microsoft.NETCore.App": {
      "type": "platform",
      "version": "1.0.0-rc2-3002702"
    }
  },
  "frameworks": {
    "netcoreapp1.0": {
      "imports": "dnxcore50"
    }
  }
}

変更後

{
  "version": "1.0.0-*",
  "buildOptions": {
    "emitEntryPoint": true
  },
  "dependencies": {
    "Microsoft.NETCore.App": {
      "type": "platform",
      "version": "1.0.0-rc2-3002702"
    },
    "Newtonsoft.Json": "9.0.1"  // この行を追加
  },
  "frameworks": {
    "netcoreapp1.0": {
      "imports": "dnxcore50"
    }
  }
}

Nugetからの取得

実際にライブラリを取得するには、プロジェクト作成時にも行ったであろう
dotnet restore コマンドを再度実行します。
すると下記のような感じで取り込まれます。

$ dotnet restore
log  : Restoring packages for /root/project.json...
info :   GET https://api.nuget.org/v3-flatcontainer/microsoft.netcore.dotnethostresolver/index.json
info :   GET https://api.nuget.org/v3-flatcontainer/newtonsoft.json/index.json
info :   OK https://api.nuget.org/v3-flatcontainer/microsoft.netcore.dotnethostresolver/index.json 673ms
info :   OK https://api.nuget.org/v3-flatcontainer/newtonsoft.json/index.json 678ms  // Newtonsoft
info :   GET https://api.nuget.org/v3-flatcontainer/microsoft.netcore.dotnethost/index.json
info :   GET https://api.nuget.org/v3-flatcontainer/newtonsoft.json/9.0.1/newtonsoft.json.9.0.1.nupkg
info :   OK https://api.nuget.org/v3-flatcontainer/newtonsoft.json/9.0.1/newtonsoft.json.9.0.1.nupkg 172ms
info :   OK https://api.nuget.org/v3-flatcontainer/microsoft.netcore.dotnethost/index.json 664ms
log  : Installing Newtonsoft.Json 9.0.1.  // Newtonsoft
info : Committing restore...
log  : Writing lock file to disk. Path: /root/project.lock.json
log  : /root/project.json
log  : Restore completed in 2970ms.

NuGet Config files used:
    /root/.nuget/NuGet/NuGet.Config

Feeds used:
    https://api.nuget.org/v3/index.json

Installed:
    1 package(s) to /root/project.json

最後に

まだ実際に使えるかどうかまでは試していませんが、ひとまず取り込むことはできました。
これがうまく使えるようになるといろいろなライブラリが自由に使えるようになるので、
スムーズな開発を行うことができるようになりそうです。


※追記
実際に動くかどうか、簡単ですが試してみました。
kabatin.hateblo.jp

UnityでRedisを使いたい場合に便利なTeamDev.Redis

Unity to Redis

果たして需要があるのか分かりませんが、、、
UnityからRedisに対してアクセスする方法を残したいと思います。


今回僕がUnityからRedisに接続するために利用したライブラリはTeamDev.Redisというものです。
TeamDev C# Redis Client - Home


基本的にはnugetで利用されるものだとは思うのですが、Unityで使うにはDLLさえ手に入ればいいので
ひとまずこのnupkgファイルをダウンロードして、nupkgファイルはただのzipファイルなので拡張子を書き換えて
強制的に解凍して、中に入っているTeamDev.Redis.dllを抜き出しました。


解凍して出来たファイル群の中にlibフォルダがあり、その中にnet20とnet40というフォルダがあります。
Unityは.NET3.5ベースで作られているので、net20フォルダに入っているTeamDev.Redis.dllを利用します。


Unityでの使い方

このDLLをどのように使うかですが、とても簡単です。
UnityのAssetsフォルダ配下にどこでもいいのでとりあえずDLLファイルを入れます。


そしたら下記のようなスクリプトを作ってカメラにでもアタッチすれば動いてくれます。
(もちろんRedisサーバが別に存在している前提です)

using UnityEngine;
using TeamDev.Redis;

public class RedisTest : MonoBehaviour
{
	void Start ()
	{
		var redis = new RedisDataAccessProvider ();

		redis.Configuration.Host = "192.168.***.***";
		redis.Configuration.Port = 6379;
		redis.Connect ();

		redis.SendCommand (RedisCommand.SET, "key", "value");
		redis.WaitComplete ();

		redis.SendCommand (RedisCommand.GET, "key");
		var result = redis.ReadString ();
		Debug.Log("result = " + result);
	}
}


Redisサーバの指定やポートの指定などは、Configurationクラスが保持されているようです。


Redisへのアクセスは基本的にはSendCommandメソッドで、
なんのコマンドを実行するのかをEnumで指定し、
そのあとに文字列パラメータを指定します。


値をSETする時はなんとなく分かりやすくて使いやすいんですが、
値をGETする時はSendCommandでGETを送ったあと、ReadString()メソッドで値を取得するという手順のようです。


なんとなくしっくりこないですね。


LISTの扱い方

SendCommand以外にもアクセスする方法はいくつか用意されています。
例えばRedisといえばLIST。


LISTをキーを指定して初期化する

redis.List["key"].Clear();


LISTにキーを指定して追加する

redis.List["key"].Append("value");


LISTのキーを指定して件数を取得する

var count = redis.List["key"].Count;


LISTからキーを指定して1要素を取得する

var value = redis.List["key"].LeftPop();


LISTをキーを指定して全要素を取得する

var values = redis.List["key"].Values;

HASHの扱い方

もちろんHASHだって使えます。


HASHにハッシュキーを指定して、キーと値をセットする

redis.Hash["hashKey"].Set("key", "value");


HASHにキーと値をセットする(すでにキーが存在する場合)

redis.Hash["hashKey"]["key"] = "value";


HASHからハッシュキーとキーを指定して値を取得する

var value = redis.Hash["hashKey"]["key"];

または

var value = redis.Hash["hashKey"].Get("key");


HASHからハッシュキーを指定してキーをすべて取得する

redis.Hash["hashKey"].Keys;


HASHからハッシュキーを指定して値をすべて取得する

redis.Hash["hashKey"].Values;


HASHからハッシュキーを指定してキーと値のペアをすべて取得する

redis.Hash["hashKey"].Items;


HASHにハッシュキーとキーを指定して存在するか調べる

redis.Hash["hashKey"].ContainsKey("key");

などなど、いろいろな使い方ができそうです。


最後に

UnityからRedisを使う場面はほとんどないとは思いますが、
もし簡単に利用できるとしたらデバッグ機能なんかに役立つかもしれません。

CoreCLRのインストールとビルド、実行まで

最近必要に駆られてCoreCLRを触っております。


CoreCLRとはMicrosoft社が開発しているオープンソースで、
LinuxOS上で.NET Coreを動かすことができるフレームワークです。


簡単に説明すると、C#で書いたプログラムがそのままLinuxOS上で実行できるという代物です。
LinuxOS上ということなので、Linuxベースで作られているMacOSでももちろん実行することができます。


なんとCoreCLRはdockerにも対応しているので、インストールするのが面倒でかつ
dockerに慣れている方はdocker版を導入してみると良いかもしれません

docker run -it microsoft/dotnet:latest


今回はUbuntuでのインストール手順を説明したいと思います。


インストール

インストールはCoreCLRの公式サイトに分かりやすく手順が書いてありますので、
そちらを参照して頂いても大丈夫ですが、こちらにも記載したいと思います。
.NET - Powerful Open Source Development


Ubuntu 14.04 の場合
sudo sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet/ trusty main" > /etc/apt/sources.list.d/dotnetdev.list'
sudo apt-key adv --keyserver apt-mo.trafficmanager.net --recv-keys 417A0893
sudo apt-get update
Ubuntu 16.04 の場合
sudo sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet/ xenial main" > /etc/apt/sources.list.d/dotnetdev.list'
sudo apt-key adv --keyserver apt-mo.trafficmanager.net --recv-keys 417A0893
sudo apt-get update

上記コマンドを実行してapt-getリストをアップデートしたら、実際にCoreCLRをインストールします。

sudo apt-get install dotnet-dev-1.0.0-preview2-003121


これでCoreCLRのインストールが完了しました!
とても簡単ですね。


ビルド準備

CoreCLRでビルドするには、まず下記のようにプロジェクトの初期化を行います。

$> mkdir sample
$> cd sample

$> dotnet new
Created new C# project in /root/sample.

$> dotnet restore
log  : Restoring packages for /root/sample/project.json...
log  : Writing lock file to disk. Path: /root/sample/project.lock.json
log  : /root/sample/project.json
log  : Restore completed in 1335ms.

$> dotnet run
Project sample (.NETCoreApp,Version=v1.0) will be compiled because expected outputs are missing
Compiling sample for .NETCoreApp,Version=v1.0

Compilation succeeded.
    0 Warning(s)
    0 Error(s)

Time elapsed 00:00:01.9024996
 

Hello World!

ひとつずつ説明していきます。


まずは、mkdirでプロジェクトが格納されるフォルダを作成し、cdでその中に入ります。
ここは特に問題ないと思います。



次に dotnet new コマンド。
このコマンドを実行することにより、デフォルトのProgram.csファイルとproject.jsonファイルが作成されます。
Program.csとproject.jsonファイルの中身は下記のようになっています。

// Program.cs
using System;

namespace ConsoleApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

// project.json
{
  "version": "1.0.0-*",
  "buildOptions": {
    "debugType": "portable",
    "emitEntryPoint": true
  },
  "dependencies": {},
  "frameworks": {
    "netcoreapp1.0": {
      "dependencies": {
        "Microsoft.NETCore.App": {
          "type": "platform",
          "version": "1.0.0"
        }
      },
      "imports": "dnxcore50"
    }
  }
}

Program.csはコンソールに「Hello World!」と出力するだけのシンプルなものになっています。
project.jsonはなにやらいろいろと設定されていますが、CoreCLRを動かすのに最低限必要なもののようです。
必要なフレームワークなどがある場合はこのファイルを編集して対応することができそうです。


次に dotnet restore コマンド。
このコマンドを実行するとproject.jsonファイルの設定に基づいて必要なファイルなどをダウンロードしてきます。
それと同時にproject.lock.jsonという中身の多い設定ファイルが作成されます。


最後に dotnet run コマンド。
このコマンドは読んで字の如くプログラムを実行してくれます。
ソースコード変更後の初回実行時に限りビルドも同時に行ってくれます。

ビルドのみを行いたい場合は下記コマンドにて行うことが可能です。

dotnet build

実行結果を見るとたしかに最下部のほうに「Hello World!」と表示されていますね!


最後に

今回はインストール手順とビルド、実行までの説明でした。


今後CoreCLRを使ってプロジェクトを進めていくにあたりいろいろと壁にぶつかることもあると思いますが、
これからCoreCLRを触る機会のある方たちに有用な情報として残していけたらなと思っています。


CoreCLRは2016年6月に1.0版がリリースされたので、今後ますます開発が進められて実用レベルに達する日も
遠くはないんじゃないかなぁと、期待も込めて思っています!


それでは。

Laravel5をLinuxで使う時にハマるポイント

f:id:kabatin:20160510153411p:plain

LinuxサーバでLaravel5を利用する時にだいたいの人がつまずくであろうポイントをまとめてみた。

ハマるポイント1

これを忘れてvendorフォルダが作成されておらず動かない。

composer install

ちなみに composer もインストールしなければならないので下記コマンドでインストールする。
インストールすると composer.phar というファイルになるので mv するなりして composer にするとよい。

curl -s http://getcomposer.org/installer | php

さらに、PHPもインストールされていない場合は下記コマンドでインストール。

sudo apt-get install php5-cli
ハマるポイント2

bootstrap、storageフォルダに対して777権限を付与していなくてエラーになる。
とにかくフォルダもファイルも777にしておけば動く。(セキュリティ面は自己責任で!)

chmod 777 -R bootstrap
chmod 777 -R storage
ハマるポイント3

.envファイルを作成していなくてエラーになる。
作成した後は中身も適切に変更すること。

cp -p .env_example .env
php artisan key:generate
ハマるポイント4

ページに「Whoops, looks like something went wrong.」が表示されて困る。
config/app.php のDEBUGモードをオンにすれば通常のスタックトレースが表示されるようになる。

vi config/app.php
まとめ

ほとんどのことはLaravel5のドキュメントを読めば解決するのでドキュメントはしっかり読みましょう。
(Laravelのドキュメントは英語しかなかった気がしますが、頑張れば読めるレベルだと思います。)