mongoの負荷監視でちょっとつまづいた。

mongodbの監視でmongostatとmongotopコマンドを使いたかったのですが、

いくらコマンド打ってもlockedの値が表示されなくて詰んでました。

 

で、ドキュメント読み直した。

mongostat — MongoDB Manual 2.6.4

 

以下の記述があった。

The mongostat utility provides a quick overview of the status of a currently running mongod or mongosinstance. mongostat is functionally similar to the UNIX/Linux file system utility vmstat, but provides data regarding mongod and mongos instances.

 

で、読んでたけどよくわからん。

で、言われた。

「お前、mongosのステータス見てね?」

 

 

mongostatはデフォルトで27017番ポート見に行くんだけど、

私の環境では

mongos -> 27017

mongod -> 27018

で動いてた。

 

つまり、私はずっとmongosのステータスを見て

「lockが見えない!ムキー!」

って、なってたんですね。愚か。。。

 

お恥ずかしや。

pythonの軽量フレームワーク「Bottle」でpymongoを試してみました。

ちょっと仕事でpython使う機会あったので、なんか試してみたくてやってみた。

 

Bottle: Python Web Framework — Bottle 0.13-dev documentation

PyMongo 2.7.2 Documentation — PyMongo 2.7.2 documentation

 

で、やりたいこと

・webアプリ(webサーバ?)立ち上げる

・mongoにアクセス

ユニットテスト書く

インストールはググるとたくさん出てくるので省略します。

pythonのバージョンは 2.7.5

bottleは手で落として、pymongoはhomebrewで導入しました。 

 

・アプリの立ち上げ

8080番portでアクセスできるようにします。

1 ダウンロードしてきた「bottle.py」と同じディレクトリに「hello.py」を設置

hello.pyは以下の実装(bottle.pyのチュートリアルから引用)

from bottle import route, run, template

@route('/hello/<name>')
def index(name):
    return template('<b>Hello {{name}}</b>!', name=name)

run(host='localhost', port=8080)

 

2 「python hello.py」を実行

Bottle v0.11.4 server starting up (using WSGIRefServer())...

Listening on http://localhost:8080/

Hit Ctrl-C to quit.

 

3 「http://localhost:8080/hello/hogemoge

たぶんこんな感じの画面でる。

f:id:Twintaro:20140814015600p:plain

OK!

 

・mongodbにアクセス

/hello/<name>で受け取ったnameをmongodbに保存します。

1 「pymongo」をインポート

import pymongo

 

2 受け取ったnameをmongoに保存する

コレクションを作成して、そこに詰めます

conn = pymongo.Connection()

db = conn.test

db.names

db.names.save({"name": name})

 

3 保存されたかどうか確認もできるように

現在保存されているnameの件数を画面に返しましょう

count = db.names.count()

return template('<b>Hello {{name}}</b>!<br/><br/>--- Access count ---<br/>{{count}}', name=name,count=count)

 

4 最終的にこんな感じの実装にしました

from bottle import route, run, template

import pymongo

 

@route('/hello/<name>')

def index(name):

conn = pymongo.Connection()

db = conn.test

db.names

db.names.save({"name": name})

count = db.names.count()

return template('<b>Hello {{name}}</b>!<br/><br/>--- Access count ---<br/>{{count}}', name=name,count=count)

 

run(host='localhost', port=8080)

 

5 画面はこんな感じ

f:id:Twintaro:20140814172521p:plain

 アクセスするとAccess countが増えていきます。

 

ユニットテスト書く

テスト書いてコンソール上から実行できるように

1 testクラス作成する

import hello

 

def test_hello_index():

    assert hello.index("testman").find('Hello testman')

    print "test close."

 

2 実行

nosetestで実行します。

----------------------------------------------------------------------

Ran 1 test in 6.213s

 

OK

 

と、こんなところで一旦終了。

bottleはすごい軽くて、pymongoも特に難しいこともなく使えて、

簡単なWEBアプリ組むにはもってこいな感じです。

ユニットテストも組めるし、設定さえ外の出せれば、それなりに大きいアプリなんかも、これで作れるんじゃないですかね!

作ったゲームのバグを直した話


コインゲッチュ★ユニティちゃん

 
以前公開したこちらのゲーム、公開して暫く経ってからバグ報告を頂きました。
「取ったコインが消えないんだけど?」
と。
 
確認してみました。
using System.Collections;
using UnityEngine;

public class CoinController : MonoBehaviour
{

    private Vector3 posArray;

    private Vector3 pos1 = new Vector3(-6,26,0);
    private Vector3 pos2 = new Vector3(-6,14,0);
    private Vector3 pos3 = new Vector3(-6,4,0);
    private Vector3 pos4 = new Vector3(0,20,0);
    private Vector3 pos5 = new Vector3(0,9,0);
    private Vector3 pos6 = new Vector3(6,26,0);
    private Vector3 pos7 = new Vector3(6,14,0);
    private Vector3 pos8 = new Vector3(6,4,0);

    private Vector3 currentPos;

    public AudioClip getCoin;

    void Start() {
        posArray = new Vector3{pos1,pos2,pos3,pos4,pos5,pos6,pos7,pos8};

    }

    void OnTriggerEnter2D(Collider2D other)
    {
        if (other.tag == "Player")
        {
            AudioSourceController.instance.PlayOneShot(getCoin);
            ChangePos();
        }
    }

    void ChangePos() {
        int i = (int)(Random.value * 7);
        if (posArray[i].x == currentPos.x && posArray[i].y == currentPos.yChangePos ();
        gameObject.transform.position = posArray[i];
        currentPos = posArray [i];
    }
}
 
あれ、一応対策してるつもりだったんだけど。。。
if (posArray[i].x == currentPos.x && posArray[i].y == currentPos.yChangePos ();
ここで。
コイン取得したら同じ場所に出現しないようにしてる(つもり)
 
ただ、実際にプレイして検証してみると、確かにバグってる。

f:id:Twintaro:20140805224554p:plain

 

なんでだろう。。。

で、ログ仕込んで確認したところ、、、最初は原因がよくわかりませんでした。

とりあえず、以下のようにしたら直りました。

    void ChangePos() {
        int i = (int)(Random.value * 7);
        if (posArray[i].x == currentPos.x && posArray[i].y == currentPos.y) {
            ChangePos ();
        } else {
            gameObject.transform.position = posArray[i];
            currentPos = posArray [i];
        }
    }

 

で、再度考察したら原因がわかりました。コード的には前の状態でも問題ないと思っていたのですが、、、お恥ずかしい。

 

changePos(コインの位置を移動させる関数)はランダムで生成した変数から移動先の位置を算出するのですが、もし移動先の位置と現在のコインの位置が同じであれば、再度changePosが呼ばれるようになっています。これが原因でした。

 

どういうことかといいますと、、、順を追って説明します。

1 ユニティちゃんとコインが接触したタイミングでchangePosが呼ばれる(呼び出し①)

2 ランダム変数から移動先の位置を取得(posArray[A])

3 posArray[A]が現在のコインの位置と同じだったので再度changePosを呼び出す(呼び出し②)

4 再度ランダム変数から移動先(posArray[B])を取得し、現在のコインのポジションと別のポジションだったので、コインをその位置に移動(呼び出し②が完了)

5 呼び出し②が終わったタイミングで、呼び出し①の処理に戻ってしまう

6 ①がメソッド内部から②を呼び出している、かつ、呼び出し後の処理が分岐されていないためそのまま次の処理を継続してしまう

7 最終的に①の処理の最初で取得した移動先(posArray[A])に移動してしまう

8 posArray[A]は現在ユニティちゃんと同じ位置のため、一見すると同じ位置から移動していないように見えてしまう

 

と、いったことが原因でした。

再帰呼び出しとか、やるもんじゃないっすね。。。

ゆえに、if else で処理を分岐してあげることで、解決したというわけです。

 

いやあ、、、我ながらひどいコードでした。

ただ、リファクタけっこう好きなんで、けっこう楽しかったです。

 

やっぱりコード書いて、直して、書いて、直して、、、っていうのは楽しいですね。

過去の自分との対話みたいで。

 

このゲームではそんなにコード書いてないので、次作るやつは、もっとガッツリとロジック書けるようなゲームを考えたいですね。

画面上に表示したオブジェクトを再利用する


空から降る一億のウニ

 

以前公開したこちらのゲームで、工夫した点について書き残しておこうと思い立ったので、備忘録的に。
 
工夫した点は
・画面上に一度表示したオブジェクトを再利用する
と、いう点です。
 
上のゲームでは空からウニが落ちてくるのですが、
よくあるパターンとしては
ウニ落ちてくる -> 床かユニティちゃんに当たる -> オブジェクト消して再生成
かと思いますが、そういう感じだと削除と生成のオーバーヘッドが発生するんじゃないかと思って、「オブジェクト再利用(?)すればオーバーヘッドないし、メモリ使用量も変わらないんじゃないか?」と思って、
床かユニティちゃんに当たった瞬間に位置を初期の場所に移動させる
というスクリプトをあてました。
 
実際には以下のようなスクリプトをあてています。
using UnityEngine;
using System.Collections;

public class uni : MonoBehaviour {

    private State state = State.Normal;
    private GameMaster gm;
    
    void Start()
    {
        gm = GameObject.Find("Root").GetComponent<GameMaster>();
    }

    void Update()
    {
        if(state == State.Inited)
        {
            state = State.Normal;
            gameObject.rigidbody2D.isKinematic = false;
        }
    }

    void OnTriggerEnter2D(Collider2D obj)
    {
        if (obj.name == "Ground" )
        {
            float f = Random.value * 8;
            if (Random.value > 0.5f)
            {
                f = f * -1.0f;
            }

            gameObject.transform.position = new Vector3(f,6.0f,gameObject.transform.position.z);
            gameObject.rigidbody2D.isKinematic = true;
            state = State.Inited;
        }
        else if(obj.name == "UnityChan2D")
        {
            gm.substructLifeCount();
        }
    }

    enum State
    {
        Normal,
        Inited
    }
}
 
初期位置(y)を決めておく必要があります。
xはRandomで適当な数字とってきて、そこから実際に配置する場所を毎回変えてあげています。
 
今回貼付けたウニの動きに関して言うと、単純に位置を上に戻すだけだと、働いている慣性力が消えないため、ゲーム開始と同時にウニの落下速度が際限なく増えていってしまいます。
そのため位置を初期位置に戻すタイミングで一旦isKinematicを有効にしてあげることで、働いていた慣性力をゼロに戻してあげています。
そのあとisKinematicをfalseに戻してあげれば、再度重力がかかって、ちょどいい(?)速度で降ってくるようになります。
 
こうしてあげると、オブジェクトを毎回削除 -> 生成する必要はなくなり、決められたオブジェクトのみを使い回すことができます。
 
今回私が作成したような、生成にそれほど時間のかからないオブジェクトであればこういった工夫は必要ないかもしれませんが、生成時に負荷がかかるオブジェクトを削除 -> 生成して使用している場合は、使い回すということも選択肢にいれてあげると、もしかしたらゲームの処理速度なんかがはやくなるかもしれませんね。
 
注意点としましては、使い回す必要のあるオブジェクトを最初の段階ですべて生成しておかないといけないため、初回表示の際の負荷があがる、ということでしょうか。
 
ただ、最初のタイミングが時間かかるか、プレイ中に負荷がかかってもっさりしちゃうか、どちらをとるかといえば私は前者だと思うので、このやり方が使える場面では、使っていこうと思います。

ゲームサーバ勉強会に参加しました。

第二回ゲームサーバ勉強会

 

こちらに参加してきました。

 

先日Unityの記事を書いたりもしましたが、ゲーム関連は大変興味があったのと、
今の仕事に活かせる物がなにかしらあるんじゃないかな?と思い参加してきましたので、
そのあたりのまとめです。
 
■データベースの再入門
講演者:@nsegaさま

■データベースを使った使用事例
講演者:株式会社gumi 清水さま
 
■MMOのサーバについて「剣と魔法のログレス 〜いにしえの女神〜」での実装例
講演者:株式会社Aiming 山藤 智之様
 
 
で、以下は各セッションの要約と感想です。
 
 
■データベースの再入門
・DBについて
DML,DDL,DCL
=>このへんの役割ちゃんと理解しときたい
=>何かいい本とかあるかな?
DB使う意味
=>大量データをメモリ空間に展開するときになんでDBの方がいいの?
インデックス
=>インデックスってどういう風につくられるのか?
=>マスタとスレーブ構成
=>スレーブ間のデータ整合性とかってどうやって担保してる?
 
RDBとNoSQL
RDBの得意なこと
=>トランザクションがある(整合性担保できる)
=>枯れてる
=>テーブル結合とかできる
RDBだと厳しい
=>大量データの書き込み
=>インデックスとかテーブル耕造の変化に弱い
=>トランザクションがある(遅い)
NoSQL
=>キーバリュー、ドキュメントベース、カラムファミリーとか。いろいろある
=>データを分散させやすい
=>大量データの書き込みがらく
=>スケールしやすい
=>なぜ???
NoSQLだとだめなとこ
=>CAPの中の、三つ全部担保できない
RDBとNoSQL
=>戦わせる必要はない
=>お互いの得意なところをカバーする
ログインセッションのキャッシュ
タイムライン形式のアプリ
リアルタイム処理
ページごとのアクセス数
 
・テーブル設計
=>概念、論理、物理の順でモデリング
=>データの正規化ってけっこう難しいけど、どうやってる?
分類
=>マスタ系とトランザクション系で分類してる
=>基本的にマスタ系はセレクトしかされない感じ?
=>このへんのデータの性質によってDB変えたりする?
レビュー観点
=>命名規則おかしくないか
=>カラムの型の精度
=>データ量は考慮されているか
=>テーブルのCRUDを説明できるか
=>主キーは適切か
=>ある程度汎用的な構成になっているか
現場でのノウハウ
=>実行コストもちゃんと見る
=>ORマッパーのデメリットも考慮する
=>パフォーマンステストでもわかるけど、先につぶせるとこはつぶす
 
=>DBを分割する(垂直、水平)
=>性能、管理しやすさはあがるが、設計で考慮しないといけない内容が増える
=>JOINしないといけなくなったり、最悪2回引かないといけなくなる
 
=>担当をクライアントサーバでわけない?(タスクの分割のやりかた?)
=>個人の技術力による
 
まとめ
=>クライアントだけでなくサーバも理解しておけば、開発効率がよくなるよ!
 
・質問
PostgresとMongoDBを採用した理由
=>Postgresは最初からあった
=>Mongoは採用当時(2年くらい前)に一番情報の公開が活発であった
逆に今から選び直すとしたら
=>正直なところわからない、その時に状況に寄る
=>そんなにキャッチアップしてない?
レビューできる人がいない、時間がない、はじめてやるって場合はどうしたらいい?
=>とりあえずやってみればいいのに
RDBMSとNoSQLへのデータ移行は?
=>自分でスクリプト組めばいい
 
・感想
DBとは?というところから、若干駆け足であったが実際に運用のノウハウまでの話
目新しい内容はなかったが、実践ノウハウが聞けたのが面白かった
また、Postgres -> mongoの流れがうちと同じで親近感がわいた
NoSQLはまだまだ枯れてない技術なので、これからもリサーチは必須かなと思う
個人的にはKV型以外のやつを使ってみたいので、Cassandraとかつかってみて、Mongoとどのくらいパフォーマンスに差が出るのか調べてみたい
 
 

■データベースを使った使用事例
分割と整合性をがんばる話
radis
rabbitsq
使ってる
 
負荷対策の話
=>サービスがヒット
=>更新処理が限界に
=>どのくらいアクセスきたの?
=>DBを垂直分割して、負荷を分散させた
=>いっきにやったのではなくて、性能の限界に打ち当たるたびに少しずつ分割していった
=>1機能に負荷が集中すると結局だめ
=>また、同時に使う機能は分割しても意味ない
=>ユーザ数に比例して水平分割して解決させた
=>スケールにアプリケーション側が対応できてなかっただけ?
=>水平分割する際には、主キーの扱いに気をつける
 
=>不整合の問題が残っている
=>100万ユーザ対応できるような設計を求められた
=>負荷は水平分割で対応
=>XA Transaction(複数テーブルにまたがったトランザクション
=>prepareはcommitの成功を保証する?
=>ロックによる排他制御
=>複数ユーザ(テーブル)にまたがる処理に関しては適当な分割しない
=>マスタデータはjsonファイル化?
=>自動回復系のステータスもトランザクション管理するようにした(ユーザの資産に直結するため)
=>RDBにもたせて、実装でカバーした
=>データの正規化をすごくがんばった
=>インデックスの貼り忘れで若干事故った
 
=>インデックス貼ってないとこに対してロックかけると、ロック範囲が全体になったりする
=>分割したDB間のデッドロックが検知できなくて死んでた
=>ロックする順番を決めることで解決させた
=>大きめにロックをとるのも手
=>参照に更新が混じっているとけっこう危ない
 
まとめ
=>ちゃんとしてあげればRDBだけでもなんとかできる
=>NewRelicとJetProfilerはなかなかいい
=>適切なインデックスを貼る
=>SQLアンチパターンはいい本
 
感想
使っていたのがMySQLだったが、負荷の問題に関しては共感できた
で、それに対する解決策が水平分割でありそれなりに成果が出ていたようなので、社内で共有してみたいと思った
逆に、現在社内でそれができていない理由が知りたくなった
予算の問題か、そもそも設計上それが不可能なのか?
また、XA Transactionに関して(というかTransactionそのものに関しても)はまだまだ知らないことが多いので調べてみる
 
 
 
■MMOのサーバについて「剣と魔法のログレス 〜いにしえの女神〜」での実装例
サーバ構成
=>階層わけて冗長化、横のつながりはない
=>フロント:独自実装の通信方式と、HTTPを使い分けている
=>バックエンド:ゲームサーバと常時接続
=>ぜんぶC++で書いてる
=>Socket:サーバからのプッシュが必要な場合、レスポンス速度重視、常時接続、差分更新
=>上記でない場合はHTTP
=>Socketは毎回コネクションを貼り直す必要がないし、一回の送受信量が少なくてすむ
=>しかし、バッテリ消費多くなったり回線切れに弱いし、スケールしにくい
 
実装
=>通信範囲、という考え方
=>一定の範囲内に入っているユーザ同士でしか、データのやりとりをする必要がない
=>移動のデータフロー
=>キャラクタ移動の際、サーバ側との通信を待たないで先に移動させる(通信待ちがストレスになるから)
=>その後サーバ側で移動先などを確認し、他のクライアントへ通知する
=>チャット
=>キャラクタ上に表示されているメッセージはSocketで、チャットログはHTTPで実装されている
=>過去の発言でも見れるように、HTTPになっている
 
問題と対応
=>クライアントが接続してるサーバが切り替わった場合の、サーバ側のコストが高かった
=>サーバ側得をスケールさせることで解決
 
質問
WebSocket使わなかったのなんで?
=>オンラインの通信関連に強い人が多かった
=>新しいものを導入するのが大変だった
=>よくわからないもの使うよりかは、隅から隅まで全部わかるものを自分で作る方がいいんじゃないか、となった
=>仮に今からやり直すのであれば、十分選択肢には入ってくる
 
感想
あんまり深い内容はなかったが、だいたい、どういう風に実現しているのかがわかった
特にチャットのやり取りで、ログはHTTP、メッセージはSocketでやっているっていうのは、適材適所な感じでいいなと思う
 
 
 
最近サーバサイドのことあんまりやっていなかったので、とても楽しかったです。
DBも楽しいですが、こんどはApacheやnginxとかのミドルウェアの話を聞いてみたいと思いました。
大量のアクセスをさばいたり、レスポンスをコンマ一秒でも早くするためには、みたいなノウハウを聞いてみたいです。
まあそのためにも、まずは自分でやってみないと、ですね。
とりあえずAmazonEC2借りて、サーバ立てて遊んでみるべしです。
 
あとはサーバサイドのプログラミング言語で、Java以外の何かを勉強してみよう。
この勉強会で「Pythonいいよ!かわいいよ!」って話を聞いたので、それも楽しそうですし、あとは会社で使ってるscalaをちゃんと勉強したいなと思います。
 
ま、ちょっとずつ楽しくやってこう。
Enjoy。

AssetStoreの素材を始めて使った。

AssetStoreの素材を始めて使いました。

AssetStore自体はちゃんと知っていましたが、それを使ってゲームを作るのは初めてでした。

 

クジラがエサを食べるだけ

 

Hosted by UnityRoom.com
 
<ルール>
前からエサ(?)が飛んでくるので、クジラを上下左右に動かして食べるだけ
15個食べたらクリア
 
 
AssetStoreはすごいですね。
まさか無料でここまでの素材が手に入るなんて。。
自前でモデリングできない私としては本当にありがたいです。
ただ、いつかは自分で作りたいと思います。

ぷよぷよ的なものを作ってみました。

ぷよぷよ的な何か

 

 

&amp;lt;div align="center"&amp;gt; This content requires the Unity Web Player&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt; &amp;lt;a href="<a href="http://www.unity3d.com/unity-web-player-2.x">http://www.unity3d.com/unity-web-player-2.x</a>" data-mce-href="<a href="http://www.unity3d.com/unity-web-player-2.x">http://www.unity3d.com/unity-web-player-2.x</a>"&amp;gt;Install the Unity Web Player today!&amp;lt;/a&amp;gt; &amp;lt;/div&amp;gt;

Hosted by UnityRoom.com

 

ぷよぷよの中ってどうなってんだろ?

と思って、実際に作ってみようと思い、手を動かしてみた第一弾です。

 

仕組みとしては

・ブロックを配置できる位置を8×10に限定

・すべての位置で、現在ブロックが置かれているか否かを記憶

・ブロックが下に落ちるごとに置いてあるすべてのブロックに対して、隣または上にあるブロックと同じ色であるか?、を判定

・同じであれば削除

 

ってな感じです。

たぶんもっとシンプルなやり方があるかとは思いますが、

第一弾なので、まあいいかなと。

 

今回は久々にユニティちゃんを使わないでゲームを作成してみたのですが、

ゼロから何かを作るって言うのは、開放感があっていいですね。

あれがこうなっているから、こうしないといけないっていうのがないので。(もちろん、Unity使ってるので、Unityの制約みたいのには引っかかりますが)

 

ただ、それと同時にアセットストアの素晴らしさも実感しました。

ゼロから何かしようと思うとそれなりに壁にもぶつかるし、時間も掛かるので、

車輪の再発明しないで、本当に時間をかけるべきところに時間を使おう!

っていうスタンスを感じました。

 

ただ、自分以外の誰かに頼りっきりになって、

「なんでこれができないんだよ、むきー!」みたいになるのは愚かなので、

まあ、頼り過ぎず、一人でがんばり過ぎず、バランスが大切だなって思います。

 

それにしてもUnity、8割くらいまでできあがるのは早いですね。

しかしそこから細かい調整をするのが結構めんどくさいですね。

なんか、いろいろな所に手が届くのはすごくいいのですが、

本当にかゆい一点にはぎりぎりで届かない感じが出てます。

 

ただ、それを考慮しても

「なんとなく浮かんだことをパッと見せられる」 

っていうののスピードがすばらしいです。

実際に見てみたらちょっと違うからこうしてみよう、みたいな変化のスピードにも、

十分対応できるんじゃないかと思います。

 

いやはや、Unity様々でございます(はーと)