UnityVS - Unity開発におけるVisual Studio利用のすすめ

Unityで開発するにあたってエディタは何を使っていますか?といったら、勿論Microsoft Visual Studio!というわけで、VSとUnityを統合してコーディング&デバッグを可能にしてくれるUnityVSの紹介をしたいと思います。とにかく素晴らしいので、超オススメ。ちなみに最新のVS2013にも勿論対応していますよ。

ちょうどUnite Japan 2014で、UnityVS作者のJb Evain氏が「Unityゲーム開発へのVisual Studio導入」というセッションを行い、勿論喜んで聞きに行った!感動した!ので、その講演をベースに紹介したいと思います。講演聞く前からUnityVSは使っていたのですが、改めて超良いなー、と、むしろもっと利用者を増やさなければ!という義務感がですね、はい。

Unityのスクリプト開発環境

UnityScriptの、じゃなくて.csとか.jsを何で書くか、のお話。

  1. MonoDevelop

標準同梱、色々なプラットフォームで動くし、ちゃんとIDEなので決して悪くない。が、Unityについてくるバージョンは古い場合が多く、そのためバグが残っていたり。また、日本語の入力が大変問題が多く叫ばれていたりする。

  1. 外部のテキストエディタ

SublimeやVim、Emacsなど。特にSublimeはよく使われているよう。非常に軽快で悪くない、とはいえ、IDEの持つ多くの機能を当然持っていないわけで、機能としては劣るといえる。SublimeはSublimeSocketAssetを入れると補完(弱)とかエラー表示とかは少しまかなえるようですけれど、Vimとかもそうですね、頑張れはするのだけれど、最終的にIDEに及ぶかというと、まあ、頑張れる。頑張れはする。

  1. Unityエディタ上のスクリプトエディタ

UnIDEとか。カジュアルな編集にはベンリだけれど専用に使っていけるか、というと……。

  1. Visual Studio

Windows専用。有料、安くない。とはいえ機能は最強。

そんなわけでVisual Studioを選べるのなら選ぶべき!なのだけれど、単純にUnityのエディタとしてVSを使うと幾つかの問題がある。

VSはMicrosoft .NET用でありUnity向けではない
VSのプロジェクト構造はUnityとミスマッチがある
2つのツール(Unity-VS)間でやりとりが取れない
デバッガーが動かせない(動かしにくい)

と、いった問題をUnityVSは解決し、両者を完全に統合してくれる。

UnityVSの機能

  • Unity Project Explorer

勿論、VSのソリューションエクスプローラーも使えますが、このUnity Project ExplorerはUnityのProjectウィンドウと同じ見た目を提供してくれるので、こっちのほうが選びやすい場合はこっちが使えます。

  • C#/Boo/UnityScriptのシンタックスハイライト/入力補完。

C#だけじゃなくてBoo, UnityScriptにも対応。普段はC#使ってても、AssetStore経由でBooやUnityScriptを取得することもあるだろうし(Booはあるのかなあ?)、全対応は良いこと素晴らしい。日本語のコメントを使うことも出来るし、入力補完等はVisual Studioそのものなので完璧。

  • メソッド生成ウィンドウ

MonoBehaviourのOnMouseEnterなどの雛形が直ちに作れるので、ついつい忘れがち/ミスりがちな名前をリファレンスからコピペったりしなくても作れる。画像のような大きなウィンドウの他に、その場でテキストベース補完でササッと作れるQuick MonoBehaviours Windowもあり。

  • リファレンスの統合

クラス名やメソッド名を選択してヘルプ→Unity API Reference、もしくはショートカットキーでVS上でその場でリファレンスが引けます(VS内ウィンドウで開かれる)。ベンリベンリ。

  • デバッグ

F5押すだけでデバッガをUnityにアタッチできる、当然動いてる最中はVSのデバッグ機能がフルに利用可。ローカルウィンドウもウォッチウィンドウも、ステップ実行も全て。(ただしCoroutineの中では挙動がかなり怪しくなるのでそこだけは注意)。

  • 外部DLLサポート

外部DLLを参照した場合でも、デバッガでしっかりとステップ実行できてとても嬉しい。あと、Visual Studioで開発できることの嬉しさに、サーバーサイド(C#/ASP.NET)とUnityのプロジェクトを同じソリューションに突っ込めるところがあったりします。移動が簡単だし、通信用データや幾つかのロジックが共有できたりする。サーバーサイドはPHPで開発なんておかしいよ!全部C#で書くんだよもん。例えば以下のような構成を取ってみる。

私がCTOを務めるグラニは、現在のところウェブベースのソーシャルゲーム(神獄のヴァルハラゲート、今CMやってますん)を提供していて、それはC# 5.0 + Windows Server 2012(IIS 8.0) + ASP.NET MVC 5で動いていたりします。サーバーサイドをC#で開発するのは得意領域なので、そのままにクライアントサイドとC#で融和出来れば、開発効率相当良い……!といったことがUnityVSならシームレスに実現できて素晴らしい。

実際、そうしたサーバーAPIをC#で書いて、そのメタデータを元にUnityの通信クライアントを自動生成、送受信データはサーバー側とクライアント側で共有するの前提にしたWebAPIフレームワークLightNodeというのを作ってます。(ところで絶賛エンジニアも募集してます←求人←宣伝)。

共有用のクラスライブラリは、UnityVS入れるとUnity用プロファイルで作れるのも嬉しい。

ちなみに、見た目上はVisual Studioのソリューションに収まっているとはいえ、Unityのシステム的に、VSのプロジェクト参照は無効なのは注意(参照してもUnity側でリロードすると消えちゃうの)。ルール通り、Assets下にDLLを配置する必要があります。それに関してはUnityVSのドキュメントDLL Debuggingで触れられてますが、ビルド後にAssetsにDLLを配置するように仕込むと良いもよう。例えば以下のようなものをShare.csprojに足してやればOK。

<!-- 実際にはDebugビルドとRelaseビルド分けるのもいれよふね -->
<Target Name="AfterBuild">
    <ItemGroup>
        <CopySource Include="bin\Debug\*.*" />
    </ItemGroup>
    <Copy SourceFiles=" @(CopySource)" DestinationFolder="$(SolutionDir)\Assets\External\" SkipUnchangedFiles="true" OverwriteReadOnlyFiles="true" />
</Target>

利用感としては、まぁまぁ悪くないです。メソッドの参照に飛ぶとDLLのメタデータを参照しに行ってしまうのが残念ではありますが。あとは.slnの場所がUnityプロジェクト下になってしまって構成がいびつなのがちょっとだけ辛い、かな?その辺の生成はカスタマイズできる - Project File Generationっぽいんだけど、上手くいかなくて今のところ断念中。

ということで

Unityの開発って結構Macで行ってる人が多いのですね。実にイマドキ……。それでも、スクリプティング環境はVisual Studioがベストだと思うんだなあ。VMにWindows入れてでもなんでも使ったほうが圧倒的に捗る、はず。少なくともエディタで苦労するよりは百億倍。ちなみにRemote Debuggingもあるようですよ。

それでもVisual Studioは高い……?うーん、そもそもUnity Proのほうがずっと高いんですが(VS Proは単品をふつーに買って6万ぐらい、色々な購入モデルがあるので実質もっと安くはなるかな)、それはおいといて、私がBuild Insiderに寄稿した記事でスタートアップ企業にマイクロソフト製品の開発ライセンスが無償提供されるBizSparkプログラム活用のススメ(タイトルクソ長い!)で紹介しているBizSparkというプログラムでは、設立5年未満の企業ならVisual Studioを無償で利用することが可能です。実際BizSparkはとても良い、助かる、助かってた。学生さんならDreamSparkという同様の学生支援プログラムがあります。

C#自体、非常に良い言語なのですが、Visual Studioと合間れば相乗効果で数倍数十倍に更に良くなるので、(たとえUnityのC#が古いバージョンだったりiOSのせいでAOTで苦しんだりしつつも)、良きC#生活を満喫して欲しい/したいところですねー。

Comment (2)

Jb Evain : (04/11 11:04)

Hi!

Jb here from the UnityVS team. I saw your blog post because people were talking about it on Twitter. I’m afraid my Japanese it too limited to write this in Japanese.

I would like to thank you for writing such a comprehensive blog post about UnityVS. Thank you very much also for the kind words and for your help spreading the word!

どうもありがとうございました

neuecc : (04/11 15:37)

I saw your UnityVS session on Unite Japan 2014, I was impressed.
And of course I’m using UnityVS, it’s great product.
Thank you very much.

Our project is sharing common dll both ASP.NET and Unity.
Can move seamlessly in the same solution file is thanks to UnityVS.
But .sln file under Root/Unity/ is not good.
It is best to have a .sln file under Root.
It is a good to be able to easily customize the generation of. csproj or sln.
(sorry, I think that it can work with ProjectFilesGenerator.Project / SolutionFileGeneration maybe, but I can’t reference SyntaxTree.VisualStudio.Unity.Bridge…)

I look forward also v2.
Thank you for the great product!

Trackback(0)
Write Comment

Microsoft MVP for Visual C#を再々々受賞しました

今年も受賞できました。4年目です。去年の受賞した頃は謎社が始動し、タイトルもリリースされ、新しい人が入社しだす頃でした。その頃はまだPHPで実装されていたのですが、C#に移行するプロジェクトをちょうど始めた頃です。

C#による圧倒的な成果、C#だからこその強さ、というのを現実に示していく

というのを所信表明として掲げたわけですが、この一年で、成果は出すことができました。C#への移行は成功し、その強さ、体制は誇ることができると思っています。とはいえまだ、やっとスタート地点に立てたばかり。世間への認知は全然されていないでしょう。もっともっと強く、私にしかできないことを。

さて、個人として、MVPのありようというのはScott HanslemanのアナウンスChanges in the Microsoft MVP Program - MVPs for Open Source Contributionsのアナウンスの通りかな、と。ただのMicrosoftの広告塔だけじゃない貢献をしなければならない。そしてコードを現実に落としこむ馬力が求められるのではないか、と。

私はそうしてきたつもりですし、これからも、もっと、他の言語圏と較べても、より先進的に、そしてより実践的に。いつまでも.NETの、Microsoftのローカルな世界で閉じている場合ではない。C#こそが魅力的な選択であるということを、あらゆる角度から示し続けたいと思います。引き続き、今年もよろしくお願いします。

ForEachAsync - 非同期の列挙の方法 Part2

Part2って、Part1はあったのかというと、うーん、非同期時代のLINQ、かな……?さて、今回はForEachがテーマです。といってもそれってSelect+WhenAllでしょ!「Selectは非同期時代のForEach」って言ってたじゃない、というと、はい、言ってました。まだ他に言うことあるの?というと、例えば以下のシチュエーション。

var httpClient = new HttpClient();
var tasks = Enumerable.Range(1, 100000)
    .Select(async x =>
    {
        var str = await httpClient.GetStringAsync("http://hogehoge?q=" + x);
        Console.WriteLine(str);
    });
await Task.WhenAll(tasks);

別に動きはしますが、制御不能に10万件、同時リクエスト走ります。これはまぁいくないですよね。もはや途中で死んだりしますので動くとも言えない……。というわけで、元シーケンスが巨大な時は、Select+WhenAllはForEachになりえないのです。

さて、この事態に手抜きで対抗すると?

var httpClient = new HttpClient();
Parallel.ForEach(Enumerable.Range(1, 100000), x =>
{
    var str = httpClient.GetStringAsync("http://hogehoge?q=" + x).Result;
    Console.WriteLine(str);
});

みんな大好きParallel.ForEachです。CPUバウンドとかI/Oバウンドとか面倒くさいんですよ、動きゃあいいんですよ(ホジホジ。という楽さ。実際これは普通に機能します。ので、バッチとかはこんなんでもいーんじゃないでしょうか、マジで。でも、これ、序盤はじわじわと並列数が上がってくので、初速がイマイチに感じるかもしれません。最初はコア数分しか並列にならず、待ちが多いことを検出してからじわじわ上がっていくので。あと終盤の挙動をアレゲに感じたりするかもしれません。待ち時間が長いと、際限なく並列数が上がってっちゃうんですよ。でも別に極端に上がっても速くなるわけじゃなくて、逆にむしろ余計遅くなる。

※これは別に作り話じゃなくて、私はプロダクション環境で実際に数十万リクエストを叩くコードを走らせていて、常に同時並列数やスレッド消費量のモニタ取って、調整いれてます。

どう調整入れるか、というと……

// 最小スレッドプール数を最初に適当に伸ばしてやると初速に効く
// 設定は一回でいいので、アプリケーションスタートアップのところにでも置いときましょう
ThreadPool.SetMinThreads(200, 200);
 
// 無尽蔵に伸び続けるのもいくないのでMaxDegreeOfParallelismを設定
var httpClient = new HttpClient();
Parallel.ForEach(Enumerable.Range(1, 100000), new ParallelOptions { MaxDegreeOfParallelism = 200 }, x =>
{
    var str = httpClient.GetStringAsync("http://hogehoge?q=" + x).Result;
    Console.WriteLine(str);
});

SetMinThreadsとMaxDegreeOfParallelism、この2つはふとぅーに影響大きくて大事。なので適当に、とか書きましたがあんまり適当にやるのはよくない。

ForEachAsync

とはいえ、非同期は非同期として扱いたい!そりゃそーだ。で、つまり、ようするに、同時実行数を抑えながら非同期を走らせられればいい。それにうってつけのクラスがSemaphoreSlim。「リソースまたはリソースのプールに同時にアクセスできるスレッドの数を制限する Semaphore の軽量版です。SemaphoreSlim は、Windows カーネルのセマフォを使用しない、軽量セマフォ クラスを提供します。」。です。.NET 4.0からの登場。使うメソッドはWaitAsync(これは.NET 4.5から)とReleaseがほとんどかな。.NET 4.0の場合はWaitAsyncのかわりにWaitで。

内部にCountを持っていて、それをWaitAsyncで減らし、Releaseで増やします。Countが0に達すると、WaitAsyncは待機するようになります。これを用いてForEachAsyncを作ってみると?

public static class EnumerableExtensions
{
    public static async Task ForEachAsync<T>(this IEnumerable<T> source, Func<T, Task> action, int concurrency, CancellationToken cancellationToken = default(CancellationToken), bool configureAwait = false)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (action == null) throw new ArgumentNullException("action");
        if (concurrency <= 0) throw new ArgumentOutOfRangeException("concurrencyは1以上の必要があります");
 
        using (var semaphore = new SemaphoreSlim(initialCount: concurrency, maxCount: concurrency))
        {
            var exceptionCount = 0;
            var tasks = new List<Task>();
 
            foreach (var item in source)
            {
                if (exceptionCount > 0) break;
                cancellationToken.ThrowIfCancellationRequested();
 
                await semaphore.WaitAsync(cancellationToken).ConfigureAwait(configureAwait);
                var task = action(item).ContinueWith(t =>
                {
                    semaphore.Release();
 
                    if (t.IsFaulted)
                    {
                        Interlocked.Increment(ref exceptionCount);
                        throw t.Exception;
                    }
                });
                tasks.Add(task);
            }
 
            await Task.WhenAll(tasks.ToArray()).ConfigureAwait(configureAwait);
        }
    }
}

ほむ、わからん。ExceptionとかCancellationTokenとかでゴチャついてますが、よーわ、実行開始しようとするとWaitAsyncでカウントを減らして、実行完了したらReleaseでカウントを増やす。初期値の指定がそのまま並列実行数になる、って感じ。利用例を見ると

var httpClient = new HttpClient();
await Enumerable.Range(1, 100000)
    .ForEachAsync(async x =>
    {
        var str = await httpClient.GetStringAsync("http://hogehoge?q=" + x);
        Console.WriteLine(str);
    }, concurrency: 200);

実に簡単にひどぅーきなForEachができました。これは、Taskの実行開始はシーケンシャルです。これも何気に有難かったりしますねえ。実行完了のほうは順不同です。まあ、そりゃそうだ、って話ですね。

まとめ

SemaphoreSlimかわいい。

RespClient - PowerShell向けのRedisクライアント/コマンドレット

というものを作りました。

例によってインストールはNuGetで。PowerShellのコマンドレットを含んでいるのでSystem.Management.Automationがないと動きません(多分、よく知らない)。

RESPって?

REdis Serialization Protocolです。RespClientは、何かのRedisClientのラッパーではなくて、自前でプロトコルを解釈してSocket経由で叩いてます。といっても、RESPは非常にシンプルなプロトコロでして、そんなに難しくはありません。作ろうと思った発端は、プロトコルの定義を見てて、先頭の識別子がEnumで

public enum RespType : byte
{
    SimpleStrings = (byte)'+',
    Erorrs = (byte)'-',
    Integers = (byte)':',
    BulkStrings = (byte)'$',
    Arrays = (byte)'*'
}

こんな風に定義できて面白いなー、という、それがきっかけなのでした。ただ、せめて実用的なものを作りたいと思ったので、特にPowerShellに強くフォーカスするようにしています。

既存のクライアント、私が作ってるCloudStructuresなり、その元のBookSleeveなり、ServiceStack.Redisというのは、やはりC#から使うのが前提で、結構ヘヴィーだと思うのです、PowerShell的なコマンドラインインターフェイスで使うには。なので、Redis-Cli的な感覚で使える、Windowsネイティブのクライアントは、隙間産業的に、ちょうどなかったので良いかな、と。なので私にしては珍しくというか初めてコマンドレット作りましたし!

PowerShellコマンドレット

こんなふーに使います。

# モジュールはdllで提供されています。
Import-Module RespClient.dll
 
# RedisServerへのコネクションは、一度コネクションを張るとセッション中、ずっと維持されます。
# 他のパラメータは -Host, -Port, -Timeout があります。
Connect-RedisServer 127.0.0.1
 
# コマンドを送るのはSend-RedisCommandで。戻り値はUTF8Stringでデコードされます。
Send-RedisCommand "set test abcde"
 
# パイプラインモードもサポートしています。
Begin-RedisPipeline
Send-RedisCommand "set test fghijk"
Send-RedisCommand "incr testb"
Send-RedisCommand "incr testc"
Send-RedisCommand "get test"
Execute-RedisPipeline
 
# 明示的にコネクションを切るときはDisconnectしてください。
Disconnect-RedisServer

RespClient(.NET)

生の.NETクライアントのほうが、よりコマンドレット経由よりも高機能です。場合によってはPowerShellで使う場合も、コマンドレットではなくて、こちらを使ったほうがいいこともあるかもしれません。具体的な差は、バイナリセーフな値を投げることができるのと、戻り値のバイナリのデコード形式を自由に選べます。

using (var client = new Redis.Protocol.RespClient())
{
    // string command
    client.SendCommand("set a 1", Encoding.UTF8.GetString);
 
    // binary safe command
    client.SendCommand("set", new[] { Encoding.UTF8.GetBytes("test"), Encoding.UTF8.GetBytes("abcde") }, Encoding.UTF8.GetString);
 
    // use pipeline
    var results = client.UsePipeline()
        .QueueCommand("incr a")
        .QueueCommand("incrby b 10")
        .QueueCommand("get a", Encoding.UTF8.GetString)
        .Execute();
} // disconnect on dispose

まとめ

弊社でぎたぱそさんがたまに使ってます。(私は……そもそもあんま生で触る機会がなく、かな!?)

C#での動的なメソッド選択における定形高速化パターン

動的なメソッド選択、といってもなんのこっちゃというわけですが、身近な例だと、ようするにURLをルーティングして何のコントローラーの何のメソッドを呼ぶのか決めるって奴です、ASP.NET MVCとかの。ようするにLightNodeはいかにして速度を叩きだしているのか、のお話。自慢。嘘本当。

以前にExpression Treeのこね方・入門編 - 動的にデリゲートを生成してリフレクションを高速化という記事を書いたのですが(2011/04ですって!もうすっかり大昔!)、その実践編です。Real World メタプログラミング。

とあるController/Action

とあるControllerのとあるActionを呼び出したいとするじゃろ?あ、ちなみに別に例として手元にあるのがちょーどこれだったというだけで、別に同様なシチュエーション(動的にメソッドを選択する)では、うぇぶに限らず何にでも応用効きます。というわけでウェブ興味ね、という人も帰らないで!!!このサイトはC#のサイトですから!

// 何の変哲もない何か
public class HogeController
{
    public string HugaAction(int x, int y)
    {
        return (x + y).ToString();
    }
}
// コード上で静的に解決できるならこう書くに決まってるじゃろ?
var result = new HogeController().HugaAction(1, 10);

ただたんにnewして実行するならこうなります。が、これが動的にControllerやActionを選ばなきゃいけないシチュエーションではどうなりますん?ルーティング処理が済んで、呼び出すクラス名・メソッド名が確定できたというところから行きましょう。

// コントローラー名・アクション名が文字列で確定出来た場合
var controllerName = "ConsoleApplication.HogeController";
var actionName = "HugaAction";
 
var instance = Activator.CreateInstance(Type.GetType(controllerName));
var result = (string)Type.GetType(controllerName).GetMethod(actionName).Invoke(instance, new object[] { 1, 10 });

一番単純なやり方はこんなものでしょう。Activator.CreateInstanceでインスタンスを生成し、MethodInfoのInvokeを呼ぶことでメソッドを実行する。基本はこれ。何事も素直が一番良いですよ。おしまい。

動的コード生成事始め

あけましておめでとうございます。素直が一番、おしまい、で済まない昨今、リフレクションは実行速度がー、という亡霊の声が聞こえてくるのでshoganaiから対処しましょう。基本的にC#でリフレクションの速度を高める手段は一択です、デリゲート作ってキャッシュする。というわけでデリゲート作りましょう。

// ここから先のコードでは↓4つの変数は省略します
var controllerName = "ConsoleApplication.HogeController";
var actionName = "HugaAction";
var type = Type.GetType(controllerName);
var method = type.GetMethod(actionName);
 
// インスタンスが固定されちゃう
var instance = Activator.CreateInstance(type);
var methodDelegate = (Func<int, int, string>)Delegate.CreateDelegate(typeof(Func<int, int, string>), instance, method);

Delegate作ると言ったらDelegate.CreateDelegate。なのですが、静的メソッドならいいんですが、インスタンスメソッドだと、それも込み込みで作られちゃうので些かイケてない。今回は毎回インスタンスもnewしたいので、これはダメ。

が、Delegate.CreateDelegateの面白いところは、「オープンなインスタンス メソッド デリゲート (インスタンス メソッドの隠れた第 1 引数を明示的に指定するデリゲート) を作成することもできます」ことです。どういうことか、というと

// 第一引数にインスタンスの型が渡せる
var methodDelegate = (Func<HogeController, int, int, string>)Delegate.CreateDelegate(
    typeof(Func<HogeController, int, int, string>), method);
 
// だからこう呼べる(キャストしてたりしてActivator.CreateInstanceの意味がほげもげ)
var result = methodDelegate((HogeController)Activator.CreateInstance(type), 10, 20);

あら素敵。素敵ではあるのですが、面白いだけで今回では使い道はなさそうです、Activator.CreateInstance消せてないし、HogeControllerにキャストって、ほげほげですよ。

というわけで、ちょっと込み入った生成をしたい場合はExpressionTreeの出番です。new生成まで内包したものを、以下のように捏ね捏ねしましょう。

// インスタンスへのnewを行う部分まで生成する、つまり以下の様なラムダを作る
// (x, y) => new HogeController().HugaAction(x, y)
var x = Expression.Parameter(typeof(int), "x");
var y = Expression.Parameter(typeof(int), "y");
var lambda = Expression.Lambda<Func<int, int, string>>(
    Expression.Call( // .HugaAction(x, y)
        Expression.New(type), // new HogeController()
        method,
        x, y),
    x, y) // (x, y) => 
    .Compile();

ExpressionTreeの文法とかは3年前だか4年前だかのExpression Treeのこね方・入門編 - 動的にデリゲートを生成してリフレクションを高速化を参照してください、というわけでここでは解説はスルー。ExpressionTreeの登場はVS2008, .NET 3.5, C# 3.0から。もう5年以上前なのですねー。

さて、Compileは非常に重たい処理なので(カジュアルに呼びまくるようなコード例をたまに見ますが、マヂヤヴァイのでやめましょう)、作ったらキャッシュします。

// Compile済みのラムダ式はキャッシュするのが基本!
// 以下の様なものに詰めときゃあいいでしょう
// .NET 4以降はキャッシュ系はConcurrentDictionaryのGetOrAddに入れるだけで済んで超楽
var cache = new ConcurrentDictionary<Tuple<string, string>, Func<int, int, string>>();

ConcurrentDictionaryのKeyは場合によりけり。Typeの場合もあればStringの場合もあるし、今回のようなCotrollerName/ActionNameのペアの場合はTupleを使うと楽ちんです。Tupleは辞書のキーなどに使った場合、全値の一致を見てくれるので、適当に文字列連結して代用、なんかよりも正確で好ましい結果をもたらしてくれます。簡易的に作る場合は、私も、とても多用しています。

大文字小文字比較とかの比較まではやってくれないので、そこまでやりたければ、ちゃんとGetHashCode/Equalsを実装したクラスを作りましょう(LightNodeではそれらを実装したRequestPathというクラスを作っています)

そういえばPCLはWindows Phoneを対象に含めるとConcurrentDictionary使えなくてイラ壁なのでWindows Phoneは見なかったことにしてPCLの対象に含めないのが最善だと思います!どうせ端末出てないし!

: Delegate

ところでFunc<int, int, string>という型をLambdaに指定しているけれど、これもまた決め打ちで、動的じゃあない。んで、そもそもこの手の作る場合、メソッドの型って色々あるのね。

// メソッドの型は色々ある!
public class HogeController
{
    public string HugaAction(int x, int y)
    {
        return (x + y).ToString();
    }
 
    public double TakoAction(string s, float f)
    {
        return double.Parse(s) * f;
    }
}

こんな風にActionが増えたら、というか普通は増えるというか違うに決まってるだろという話なわけで、問題色々でてきちゃいます。 まずデリゲートの型が違うのでキャッシュ不可能。Func<int, int, string>のConcurrentDictionaryにFunc<string, float, double>は入りません。そして”HugaAction”や”TakoAction”という動的に来る文字列から、コンパイル時にデリゲートの型は決められない。ていうかそもそもパラメータのほうもxだのyだのって不明じゃないですかー?ゼロ引数かもしれないし10引数かもしれないし。

どーするか。型名指定ができないなら指定しなければいいじゃない。

// Expression.Lambdaに型名指定をやめ、CacheはDelegateを取ってみる
var cache = new ConcurrentDictionary<Tuple<string, string>, Delegate>();
var dynamicDelegate = cache.GetOrAdd(Tuple.Create(controllerName, actionName), _ =>
{
    // パラメータはMethodInfoから動的に作る
    var parameters = method.GetParameters().Select(x =>
            Expression.Parameter(x.ParameterType, x.Name))
        .ToArray();
 
    return Expression.Lambda(
            Expression.Call(Expression.New(type), method, parameters),
        parameters).Compile();
});

やった、大解決!

// 但しDelegateのキャッシュは呼び出しがDynamicInvokeでイミナイ。
// もちろん別に速くない。
var result = dynamicDelegate.DynamicInvoke(new object[] { 10, 20 });

はい、意味ありません。全然意味ないです。Funcなんちゃらの共通型としてDelegateで統一しちゃうと、DynamicInvokeしか手がなくて、ほんとほげもげ!

Everything is object

Cacheに突っ込むためには、あらゆるメソッドシグネチャの共通項を作らなきゃあならない。でもDelegateじゃあダメ。じゃあどうするか、というと、objectですよ!なんでもobjectに詰めればいいんです!

// 解決策・最も汎用的なFuncの型を作ること
// オブジェクトの配列という引数を受け取り、オブジェクトを返す関数である
// Func<object[], object>
// これを作ることによりDynamicInvokeを回避可能!(Boxingはあるけどそこは諦める)
 
// このキャッシュならなんでも入りそうでしょう
var cache = new ConcurrentDictionary<string, Func<object[], object>>();

良さそうな感じ、というわけで、object[]の配列を受け取りobjectを返すデリゲートを作っていきましょう。

// 作る形をイメージしよう、以下の様な形にするのを狙う
// (object[] args) => (object)new HogeController().HugaAction((int)object[0], (int)object[1])
 
// 引数はオブジェクトの配列
var args = Expression.Parameter(typeof(object[]), "args");
 
// メソッドに渡す引数はオブジェクト配列をインデクサでアクセス+キャスト => (cast)args[index]
var parameters = method.GetParameters()
    .Select((x, index) =>
        Expression.Convert(
            Expression.ArrayIndex(args, Expression.Constant(index)),
        x.ParameterType))
    .ToArray();
 
// あとは本体作るだけ、但し戻り値にもobjectでキャストを忘れず
var lambda = Expression.Lambda<Func<object[], object>>(
    Expression.Convert(
        Expression.Call(Expression.New(type), method, parameters),
        typeof(object)),
    args).Compile();
 
// これでふつーのInvokeで呼び出せるように!
var result = lambda.Invoke(new object[] { 1, 10 });

これは完璧!若干Boxingが気になりますが、そこは贅沢は敵ってものです。というか、実際はパラメータ作る前工程の都合でobjectになってたりするので、そこのコストはかかってないと考えて構わない話だったりします。ちなみにvoidなメソッドに関しては、戻り値だけちょっと弄った別デリゲートを作ればOKですことよろし。その辺の小さな分岐程度は管理するが吉。

Task & Task<T>

Extra Stage。今どきのフレームワークはTaskへの対応があるのが当たり前です。(いい加減ASP.NET MVCもフィルターのTask対応してください、LightNodeのフィルターは当然対応してるよ!)。というわけでTaskへの対応も考えていきましょう。

TaskだってobjectなのだからobjectでOK!ではないです。Taskなメソッドに求めることって、awaitすることなのです。Taskで受け取って、awaitするまでが、戻り値を取り出す工程に含まれる。

var result = new Hoge().HugaAsync();
await result; // ここで実行完了される、的なイメージ

といってもTaskは簡単です、ようは戻り値をTaskに変えればいいだけで、↑のと変わらないです。Func<object[], Task>ということです。Taskに変えるといっても、元から戻り値がTaskのもののデリゲートを作るわけですから、ようするところObjectへのConvertを省くだけ。簡単。

// もちろん、実際にはキャッシュしてね
var lambda = Expression.Lambda<Func<object[], Task>>(
        Expression.Call(Expression.New(type), method, parameters),
    args).Compile();
 
var task = lambda.Invoke(new object[] { 1, 10 }); // 戻り値はTask
await task; // 待機

そう、Taskはいいんです、Taskは。簡単です。でもTask<T>が問題。というかTが。Tってなんだよ、という話になる。例によってTは静的に決まらないのですねえ……。こいつはストレートには解決できま、せん。

少しだけ周りっくどい道を通ります。まず、メソッド呼び出しのためのデリゲートはTaskと共通にします。Task<T>はTaskでもあるから、これはそのままで大丈夫。で、取り出せるTaskから、更にTを取り出します。どういうこっちゃ?というと、.Resultですよ、Result!

Task task = new Hoge().HugaAsync(); // Taskとして受け取る
await task; // ここで実行完了される
var result = ((Task<T>)task).Result; // ↑で実行完了しているので、Resultで取り出せる

こういう風なコードが作れればOK。イメージつきました?

// (Task task) => (object)((Task<>).Result)
// キャッシュする際は、キーはTypeでTを取って、ValueにFunc<Task, object>が省エネ
var taskParameter = Expression.Parameter(typeof(Task), "task");
var extractor = Expression.Lambda<Func<Task, object>>(
    Expression.Convert(
        Expression.Property(
            Expression.Convert(taskParameter, method.ReturnType), // method.ReturnType = Task<T>
            "Result"),
        typeof(object)),
    taskParameter).Compile();
 
// これで以下のように値が取れる!
await task;
var result = extractor(task);

ここまでやれば、非同期にも対応した、モダンな俺々フレームワーク基盤が作れるってものです。

C# is not LightWeight in Meta Programming

そんなわけでこれらの手法は、特にOwinで俺々フレームワークを作る時などは覚えておくと良いかもです。そして、定形高速化パターンと書いたように、この手法は別に全然珍しくなくて、実のところASP.NET MVCやASP.NET Web APIの中身はこれやってます。ほとんど全くこのとーりです。(べ、べつに中身見る前からコード書いてたしその後に答え合わせしただけなんだから!!!)。まぁ、このぐらいやるのが最低水準ってことですね。

で、しかし、簡単ではありません。ExpressionTreeの取り扱いとか、ただのパターンなので慣れてしまえばそう難しくもないのですけれど、しかし、簡単とはいいません。この辺がね、C#ってメタプログラミングにおいてLightWeightじゃないよねっ、ていう事実。最初の例のようにActivator.CreateInstanceで済ませたり、dynamicだけで済む範囲ならそうでもないんですが、そこを超えてやろうとするとねーっ、ていう。

ExpressionTreeの登場のお陰で、今どきだとIL弄りの必要はほぼほぼなく(とはいえゼロじゃあないですけどね)、楽になったとはいえ、もっと、もっとじゃもん、と思わないこともない。そこでRoslynなら文字列でソースコードベタベタ書いてEvalでDelegateが生成できて楽ちんぽん!な未来は間違いなくありそうです。ですがまぁ、あと1~2年であったり、あとPortable Class Libraryに落ちてくるのはいつかな?とかっていった事情を鑑みる、まだまだExpressionTree弄りスキルの重要性は落ちなさそうなので、学ぶならイマノウチ!損はしません!

メタプログラミング.NETは、満遍なく手法が紹介された良書だと思いますので、読んだことない人は是非是非読むといいかと思います。参考資料へのリンクが充実してたりする(ILのOpCodeとか)のも嬉しい点でした。

まとめ

ExpresionTreeは優秀なIL Builder(Code as Dataとしての側面は残念ながらあまり活用できそうにもないですが、こちらの側面で活躍しているのでいいじゃないですか、いいの、か???)なんでも生成頑張ればいいってものではなく頻度によりけり。頻度少なけりゃDynamicで全然OK。ん……?

ちゃぶ台返しますと、ウェブのリクエスト処理的な、リクエストの度に1回しか呼ばれない場合だと、別にここがDynamicInvokeで実行されるかどーかなんて、ハイパー誤差範囲なんですねぇ、実は!クライアントアプリとか、O/R Mapperとかシリアライザとか、テンプレートエンジンのプロパティ評価とか、凄く呼び出されまくるとかじゃなければ、割とどうでもいい範囲になってしまいます。0.01msが0.001msになって10倍高速!なのは事実ですが、そもそもウェブは1回叩いて10msかかったりするわけで誤差範囲としか言い様がない次元になってしまう。

でも、ちゃんと頑張ったほうが格好はつくので、頑張ってみると良いです、みんなもやってるし!こんなのチキンレースですから。LightNodeの速さの秘訣はこれだけ、ではないですが、大事な一端には変わりないです。

C# ASP.NETのRESTフレームワークパフォーマンス比較大全

LightNode(という私の作ってるOwinで動くMicro REST Framework)の0.2出しました。でも皆さんあんま興味ないと思うので(!)、先にベンチマークの話をしましょふ。0.1を出した時にもグラフを出したのですが、よく見るまでもなく詐欺グラフで非常に良くなかったので載せ直し&NancyとかWCF RESTとか他のフレームワークも追加しました。そして今回測りなおしてみると、そもそも前回のものは致命的に計測のための環境作りにミスッていたので、まるっきりナシでした、すびばせん。

パフォーマンステスト

各ソースコードはLightNode/Performanceに置いてあるので再現できます。数字はrequest per secondで、Apache Benchで叩いているだけです。実行環境は「Windows 8.1/CPU Core i7-3770K(3.5GHz)/Memory 32GB」という、私の開発環境のデスクトップPC上で動かしてます。ホスト先はIIS ExpressじゃなくてローカルIIS。

オレンジと緑はふつーのIISでホストしてるもの。グレーはちょっと別枠ということで色分けてます。

結果に納得いきます?イメージとちょっと違う結果に?まず、WCFのRestが一番遅いです。これはどうでもいいですはい。次点がASP.NET MVC、そこからちょっと僅差でASP.NET Web API。これはJSONシリアライザの問題もあるかなってところ(MVCのデフォルトシリアライザはJavaScriptSerializerで、あれは遅い)かどうかは知りませんけれど、ともかくWeb APIは言われるほど遅い遅いなんてことはない、ってとこでしょーか。安心して使っていきましょう。そこから別に大きく差がついてってことなくNancy、ちゃんと定評通りにLightweightで速そうです、偉い。ServiceStackは、一応謳い文句どおりちゃんとFastestでした。

生OwinHandler(Async)とHttpTaskAsyncHandlerはほとんど変わらない。といったところから、Owin(Katana)でラップされることは、そんなにパフォーマンスロスは発生しない、と考えられるでしょう。この事実はかなり安心できて嬉しい。で、LightNodeがそれらよりもグッと高速。そう、LightNodeはちゃんと最速フレームワークなのです。で、しかし生OwinHandler(Sync)と差がちょっと付いちゃってるので、ここはもう少し縮められないかなあ、といったところ(実際のところ、LightNodeのデフォルトは出力をバッファするようになってて、そのオプションをオフにすればもっと迫れますが、実用的にはオンにせざるを得ないので、ここはオン時で)。そして最速はSyncのHttpHandler。生ハンドラ強い。

SelfHostとHeliosはちょっと別枠。SelfHostは一節によるとSloooooooooowって話もあったのですけれど、手元でやると普通に速いのですよねえ。そのSloooowってコード見たら、なんかそもそも他のテストと出力物が全然違ったので、その人のミスなのかなぁ?って思ってますがどうなのでしょうね。ともあれ、私の環境でやってみた限りではSelfHostはかなり速いです。

Helios(Microsoft.Owin.Host.IIS)は、System.Webを通さずにIISネイティブをペシペシ叩くことで超最速を引き出すとかいう代物で、まだ0.1.2-preでプロダクション環境に使える代物ではないとはいえ、とにかく速い。すぎょい。これだけ違うとニヨニヨしちゃうねぇ。

Sync vs Async

ベンチ結果の突っ込みどころは二点ほどあるかな、と。ひとつはHttpHandler(Async)がHttpHandler(Sync)に比べて険しく遅いこと。もう一つは、RawOwinHandler(ASync)とRawOwinHandler(Sync)ってなんだよハゲ、と。RawOwinHandlerはこんなコードになっています。

app.Run(context =>
{
    // 中略
 
    if (context.Request.Query.Get("sync") == "true")
    {
        context.Response.Body.Write(enc, 0, enc.Length);
        return EmptyTask; // Task.FromResult<object>(null)
    }
    else
    {
        return context.Response.Body.WriteAsync(enc, 0, enc.Length);
    }
}

違いはWriteしてるかWriteAsyncしてるか。で、これだけで1000rpsも変わってしまうんですねえ、お、おぅ……。HttpHandler(Async)とHttpHandler(Sync)も同じ話です。これねえ、困った話です。しかも、Heliosだと遅くならなかったりするので、原因はSystem.Webのネットワークストリームに対するWriteAsyncに何らかの欠陥があるのかなあ、と。それ以外にとりあえず考えつくのは、そもそもレスポンスサイズが小さいとかlocalhost同士だから、とかなくもないので(所詮マイクロベンチですから)、厳密にどーこうというのは言いづらくてまだ要調査ってところ。でもHeliosだと遅くならなかったりするのでもうHeliosでいいよ(投げやり)

まぁネットワーク離したりサイズ大きくしたりとかは、そのうちやりませうか。そのうち。多分やらない(面倒くさいの!)

ベンチ実行環境の注意

Windows Defenderのリアルタイム保護が有効ならば、無効にしましょう。これ、有効か無効かでめちゃくちゃ結果変わります。3000rpsぐらい変わるしHeliosにしても別に大して差が出ないとか、もう根源的に結果が変わります。Defenderがオンの時に測った結果とかクソの役にも立たないゴミデータなので投げ捨てましょう。ネットにある計測しました、とかってのもそれの可能性があるので見なかったことにしましょう。ファイアウォールとかもとりあえず切っといたほうがいいんじゃないでしょーか。

IISかIIS Expressかは、傾向としてそこまで大きく違うってこともないですけれど、IISのほうが成績は良好なので、一応測るのだったらIISでやったほうが良いかと思います。以前にやってた時は横着してIIS Expressでやってたのですけれど、反省ということで。そこまでやるならWindows Serverでー、とか無限に要求は加速しますが、フレームワーク同士の相対的な比較なので、そこまでやる必要はないかな?

LightNode 0.2

ここからタイトル詐欺の抱合せ商法。じゃなくて、最初はこっち本題で書いてたんですが思ったよりパフォーマンス比較が厚くなったので上に持ってきただけなんですよ……。ということでLightNode 0.2出しました。LightNode自体は0.1の解説LightNode - Owinで構築するMicro RPC/REST Frameworkを読んで欲しいのですけれど、0.1はOwinへの理解度が足りなかった成果、安定性に難があったり、細部が詰められてなかったりしました。けれど、今回はかなり完成度上がってます。このバージョンからは普通に投下しちゃって問題ないレベルに達しているかな、と。ベンチマークを細かく取って検証しているとおり、パフォーマンスについてもよりシビアに詰めています。

変更点は割といっぱいあります。

Enumバインディングの高速化
Enumパースの厳密化
T4クライアントコード生成内容の変更、アセンブリロック回避
ContentFormatterのコンストラクタ修正
IContentFormatterにEncodingインターフェイス
Extensionを|区切りで受け付けるように
void/Task時は204を返す
ReturnStatusCodeExceptionを投げることで任意のステータスコードで返せる
Optionでstringはデフォルトではnull非許可に
IgnoreOperationAttribute追加
フィルター追加

こんなとこで。あとGitHub Pages -LightNode立てたのでトップページがちょっとオサレに。

Enum高速化・判定厳密化

C#のEnum自体は凄く好きなんですよ、プリミティブとほとんど変わらない、という、それがいい。Javaみたいにゴテゴテついてると逆に使い勝手悪かったりパフォーマンス上の問題で使われない(Android!)とかって羽目になってたりしますし、これはこれで良いかな、って思ってます。拡張メソッドによってちょっとしたメソッドは足すことが可能になりましたしね。ただ、静的メソッドが頂けない。リフレクションの塊なので速くない、なんか色々使い勝手悪い、などなどビミョー感半端ない。

しょうがないので、LightNodeではEnum専用のインフラ層を構築して回避しました。高速化しただけじゃなくて、値の判定を厳密化しています。Enumの値が1,10,100の時に5を突っ込んだらダメ、って感じです。更にビットフラグに対しても厳密な判定がされるように加えているので([Flags]属性がついてるかどうかを見ます)、1,2,4,8の時に100が来たら死亡、7ならOK、って感じですにぇ。ASP.NET MVCなどでも、Enumでゆるふわな値が渡ってくるのはかなり嫌だったので、良いんじゃないかと思います。他、デフォルトでstringもnull非許可、配列の場合は空配列になるので、デフォではnullや範囲外の値というのは完全排除しています。

なお、このEnumインフラストラクチャは、後日、もう少し機能を追加して専用ライブラリとして切り出そうと思っています。使い道はかなり多いんじゃないかなー、と思いますのでお楽しみに。

例外によるステータスコード変更

ASP.NET Web API 2 で追加された機能について見てて、いいですよねー、ということで。実際、戻り値の型をシンプルなオブジェクトで指定した場合って、例外でグローバルに吹っ飛ばすしか手段ないですしね。

public class Hoge : LightNodeContract
{
    public int HugaHuga()
    {
        throw new ReturnStatusCodeException(HttpStatusCode.NotImplemented);
    }
}

単純明快でいいと思います。IHttpActionResultなんて作りゃあそりゃ最大の柔軟性ですがResponseType属性とかは、まぁ、やっぱあんまりだな、って思いますよ、ほんと……。

さて、実際のとこWeb APIはやっぱり一番参考にしていて、機能眺めながら、どうするか考えてます。ルーティングや認証は他のMiddlewareがやればいい、Request Batchingは入れたい、ODataはOData自体がイラネ、フィルターオーバライドはうーん?とか。色々。色々。

Middleware vs Filter

そして、フィルター入れました。最初から当然入れる気ではあったのですが実装時間的に0.1では間に合わずで。まぁ、あと、0.1の時点ではミドルウェアパイプラインとフィルターパイプラインの違いを言語化出来なかったり、実装方法というかインターフェイスの提供方法についても全然考えが固まっていなかったので、無理ではあった。今はそれらはしっかり固まってます。

というわけで、こんなインターフェイス。

public class SampleFilterAttribute : LightNodeFilterAttribute
{
    public override async Task Invoke(OperationContext operationContext, Func<Task> next)
    {
        try
        {
            // OnBeforeAction
 
            await next(); // next filter or operation handler
 
            // OnAfterAction
        }
        catch
        {
            // OnExeception
        }
        finally
        {
            // OnFinally
        }
    }
}

ASP.NET MVCとかの提供するOnActionExecuting/Executedとか、アレ、私は「大嫌い」でした。挙動が不明だから。Resultに突っ込むと何が起こるの?Executingで投げた例外はExecutedに届くの?(届かない)、などなど、分かりやすいとは言い難くて、LightNodeではその方式は採用したくなかった。

かわりにシンプルなパイプラインを採用しています。OWINのInvokeパイプラインとほぼ同等の。

// app.Use
Func<IOwinContext, Func<Task>, Task> handler
// LightNode Filter
Func<OperationContext, Func<Task>, Task> invoke
// インターフェイスでは
public abstract Task Invoke(OperationContext operationContext, Func<Task> next);

というか一緒です。見た目もやってることも一緒なミドルウェアとフィルターですが、違いは当然幾つかあります。第一に、ミドルウェアだとほとんどグローバルに適用されますが、フィルターはグローバル・クラス・メソッド単位の3つが選べます。そして、最大の違いは実行コンテキストを知っていること。フィルターが実行されるのはパラメータバインディングの後なので、実行されるメソッドが何かを知っています。どのAttributeが適用されているかを知っています。ここが、大きな違い。

フィルタはGlobal/Contract/Operation単位で設定可能ですが、順番は全て設定されたOrderに従います。AuthenticationとかActionとかの順序パイプラインはなし。だって、あの細かい分け方、必要です?認証先にしたけりゃOrderを-int.MaxValueにでもすりゃあいいんです。というわけで、そのへんは取っ払って、Orderだけ、ただしOrderのレンジは-int.MaxValueからint.MaxValueまで、かつデフォルトはint.MaxValue(一番最後に発火される)。です。ASP.NET MVCのデフォが-1で最優先されるってOrderも意味不明で好きじゃないんですよねえ……。

OperationContextはIsAttributeDefinedなど、属性のチェックや取り出しが容易になるメソッドが幾つか用意されてますにゃ。もし実行をキャンセルしたければ、nextを呼ばなければいい。その上で大きく結果を変えたければ、Owin Environmentを弄れば好きなようにStatusCodeでもResponseでもなんでも設定できます。十分十二分。

まとめ

パフォーマンスは、まぁ自分で測らないと納得しにゃいところもきっとあると思いますので自分で測るのが一番いーですね、そりゃそうかそりゃそうだ。生ハンドラに比べるとロスは少なくないなぁというのは現実かもしれませんねぇ(LightNodeは除く)。Owinを被せることのロスは少なめってのが確認できたのは安心できて良いですね、さぁ積極的に使っていきましょう。

LightNodeは超簡単。超速い。十分なカスタマイズ性。というわけで、真面目に実用的です!使うべき!さぁ今すぐ!あと、意図的に既存のフレームワークの常識と崩しているところも含めて、全ての挙動の裏には多くの考えが含まれています。全ての挙動は明確に選択しています。そーいうのも読み取ってもらえると嬉しいですね。

また、ちょっとした縁があって2014/2/8に北海道のCLR/Hにてセッションを一つ持ちます。北海道!あんまり東京から出ない私なのですが、こないだは大阪に行きましたし、ちょっとだけたまにはお外にも出ていこうかな、なんて思っていなくもないです。北陸とかにもそのうち行きたいですね、色々なのと重ならなければ……。

セッションタイトルは「LightNode Demystified - How to Make Extreme Fast Owin Framework」を予定しています。ネタは色々あるんですが、LINQはこないだ大阪でやったし、Real World Hyper Performance ASP.NET Architecture(Internal謎社)は、もう少し後でいいかなぁ(?)だし、前2つがうぇぶけー(Windows Azure 最新アップデート/最新Web アプリケーションパターンと .NET)なので、合わせて見るのが良いかにゃ、と。

LightNode Demystified、ですけれど、LightNodeを作ることを通してOWINとはどのようなものなか、どのような未来が開けるのか、というのを伝えられれば良いと思っています。私自身、実際に作ることによる発見がかなり多かったし、作ってみないと分からないことというのはかなり多いと思いますので、そうして開けた視野をシェアできれば嬉しいですね。

また、1/17には弊社で開催するめとべや東京#3にてLTでデモをするので、そちらのほうも都合がつく方は是非是非どうぞ。

OWINのパイプラインとMiddleware作成ガイド

あけおめました。振り返る~系の記事はこっ恥ずかしいのでいつまでも先頭に出ていると嫌なので、割と流したくてshoganaiので、記事をでっち上げます。実際切実。記事あげてる場合じゃなくても、これはこれでsetsujitsuなので許してあげてほしいのね。

Node.jsでKoaというフレームワークが盛り上がっているらすぃ。で、新しいWebフレームワーク Koa についてを見てて、あー、まんまKatana - Microsoft.Owinで置き換えられるなぁと思ったので、書いてみました。

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        // KoaとOwinを比較して
        // http://blog.kazupon.jp/post/71041135220/koa
 
        // 3. Response Middleware
        app.Use(async (context, next) =>
        {
            Console.WriteLine(">> one");
            await next();
            Console.WriteLine("<< one");
        });
 
        app.Use(async (context, next) =>
        {
            Console.WriteLine(">> two");
            await context.Response.WriteAsync("two");
            await next();
            Console.WriteLine("<< two");
        });
 
        app.Use(async (context, next) =>
        {
            Console.WriteLine(">> three");
            await next();
            Console.WriteLine("<< three");
        });
    }
}

比較すると、そっくりそのまま。Koaはフレームワークというか小さなツールキット、Connect/Koa = Katana, Express = ASP.NET MVC/Web API、みたいな図式で捉えればいいのでしょうね。

OWINの成り立ちについては、OWIN - Open Web Interface for .NET を使うで解説されていますが、Ruby- Rack/Python - WSGI/Perl- PSGIと同じようなものと捉えられます。OWINとKatanaに関しては、PerlのPSGIとPlackの関係性を見れば、そのまま当てはめることが可能です。

よって、OWINの基本的なことは、OWIN関連を漁るよりも、Plack Handbookを読んだほうがピッと理解できそうです。GitHubのリポジトリには日本語の生Markdown原稿もあるので、目を通しておくと、理解がとっても進みます。

Pluggable Pipe Dream

OWINがASP.NETにもたらしたものは2つ。一つはバックエンドの自由、IISでもSelfHostでもー、という側面。もう一つはプラガブルなMiddleware。そしてこれは、パイプラインになっているのですね、こちらのほうが開発者にとって注目に値する、影響力の大きなものです。

最初の例で書いた app.Use(async (context, next) => はMicrosoft.Owinによるもので、ラムダ式でその場でMiddlewareを定義していることに等しい(AnonymousMiddleware!)わけですが、まずはこっちから書いてったほうが、わかりやすいかな、と。(ちなみにRunはnextのないバージョン、つまりMiddlewareの終点)

app.Use(async (context, next) =>
{
    try
    {
        // 実行前の処理が書ける
 
        await next(); // 次のミドルウェアの実行(これを呼ばないことでパイプラインのキャンセルも可能)
 
        // 正常実行後の処理が書ける
    }
    catch
    {
        // 例外時の処理が書ける
    }
    finally
    {
        // 後処理が書ける
    }
});

こういったパイプラインは.NETには偏在しています。HttpClientのDelegatingHandler - HttpClient詳解、或いは非同期の落とし穴についてや、LINQ to Objects - An Internal of LINQ to Objectsの中身と変わらない話です、特にDelegatingHandlerは近いイメージ持ってもらうと良いかな、と。図にすればこんな感じ。

next()を呼び出すことで、円の中央、次のパイプラインに進む。きっと、一番最後、中心のMiddlewareはフレームワークとしての役割を担うでしょう(ResponseStreamに書いたりなど処理をかなり進めてしまうので、フレームワークが後続のMiddleware読んでも無意味というか逆に死んだりするので、フレームワーク部分では意図的にnext呼ばない方がいい←だから実際app.RunもNext呼ばないしね)、とはいえ構造上ではFrameworkとMiddlewareに別に区別はないです。処理が終わったら、今度は円の内側から外側に向かって処理が進んでいきます。Nextを呼ばなければ、途中で終了。1-2-3-4-5-4-3-2-1を、3で止めれば、1-2-3-2-1になる、といった感じです。これはASP.NET MVCのfilterでResultに小細工したり、Exceptionを投げたりして中断するようなものです。

HttpModuleだってHTTPパイプラインぢゃーん、というツッコミもあっていいですけれど、それよりもずっと単純明快な仕組みだというのがとても良いところ。こういった薄さであったり単純さであったり、をひっくるめたLightweightさって、とっても大事です。

Mapping

LightNode - Owinで構築するMicro RPC/REST FrameworkではURLは決め打ち!と言いましたが、むしろそもそも、URLのルーティングはLightNodeが面倒見るものではなくて、他のMiddlewareが面倒見るものなのです。例えば、APIのバージョン管理でv1とv2とで分けたい、というケースがあったとしましょう。その場合、MapWhen(Katanaに定義されてます)を使うと、条件指定で利用するMiddlewareのスタックを弄ることができます。

// Conditional Use
app.MapWhen(x => x.Request.Path.Value.StartsWith("/v1/"), ap =>
{
   // Trim Version Path
   ap.Use((context, next) =>
   {
        context.Request.Path = new Microsoft.Owin.PathString(
            Regex.Replace(context.Request.Path.Value, @"^/v[1-9]/", "/"));
        return next();
   });
 
    ap.UseLightNode(new LightNodeOptions(AcceptVerbs.Post, new JsonNetContentFormatter()),
        typeof(v1Contract).Assembly);
});
 
app.MapWhen(x => x.Request.Path.Value.StartsWith("/v2/"), ap =>
{
   // 手抜きなのでコピペ:)
   ap.Use((context, next) =>
   {
        context.Request.Path = new Microsoft.Owin.PathString(
            Regex.Replace(context.Request.Path.Value, @"^/v[1-9]/", "/"));
        return next();
   });
 
   ap.UseLightNode(new LightNodeOptions(AcceptVerbs.Post, new JsonNetContentFormatter()),
    typeof(v2Contract).Assembly);
});

LightNodeはサービスの記述された読み込むアセンブリを指定できるので、v1用アセンブリとv2用アセンブリを分けて貰って、Request.Pathを書き換えてもらえれば(/v1/部分の除去)動きます。これは単純な例ですが、複雑なルーティングだって頑張れば出来るでしょう。きっと。

OWINにはSuperscribeというグラフベースルーティング(何だそりゃ)とかもありますし、そういうのと組み合わせれば、実際LightNodeでうまく使えるかどうかは知りませんが、まぁ、そういうことです。やりたければ外側で好きにやればいいのです。プラガブル!

Headerが送信されるタイミング

話は突然変わって、Middleware実装上のお話。表題のことなのですけれど、原則的には「最初にWriteされた時」です。原則的には、ね。最初にFlushされた時かもしれないし、そもそもされないかもしれないこともあるかもですが、とはいえ原則的には最初にWriteされた時です。どーいうことか、というと

app.Run(async (context) =>
{
    try
    {
        // Writeしてから
        await context.Response.WriteAsync("hogehoge");
        context.Response.Body.Flush();
 
        // StatusCodeやHeaderを書き換えると
        context.Response.StatusCode = 404;
    }
    catch (Exception ex)
    {
        // 例外出る
        Debug.WriteLine(ex.ToString());
    }
});

これをMicrosoft.Owin.Host.SystemWebでホストすると、「HTTP ヘッダーの送信後は、サーバーで状態を設定できません。」というお馴染みのような例外を喰らいます。ちなみにHttpListenerによるSelfHostでは無反応という、裏側のホストするものによって挙動は若干違うのだけは注意。とはいえ、どちらも共通して、ヘッダーが送信された後には幾らStatusCodeやHeaderを書き換えても無意味です。上の例だと、404にならないで絶対200になっちゃうとか、そういう。

当たり前といえば当たり前なのですが、生OWIN、生Katanaだけで色々構築すると、Middlewareの順序によっては、そーなってしまうことも起きてしまいがちかもしれません。

なお、Katanaのソースコード読む時はHttpListenerのほうを中心に追ったほうが分かりやすいですね。System.Webのほうは、つなぎ込みがややこしかったり、すぐにブラックボックスに行っちゃったりで読みにくいので。若干の挙動の差異はあるとはいえ、概ね流れや処理は同じですから、まずは読みやすいほう見たほうが楽でしょう。

バッファリングミドルウェア

さて、そんな、Writeが前後して厄介というのを防ぐためのMiddlewareを作ってみましょう。解決策は単純で、上流のパイプラインでバッファリングしてやればいいわけです。

app.Use(async (context, next) =>
{
    var originalStream = context.Response.Body;
    using (var bufferStream = new MemoryStream())
    {
        context.Response.Body = bufferStream; // 差し替えて
        await next(); // 実行させて
        context.Response.Body = originalStream;  // 戻す
 
        if (context.Response.StatusCode != 204) // NoContents
        {
            context.Response.ContentLength = bufferStream.Length;
            bufferStream.Position = 0;
            await bufferStream.CopyToAsync(originalStream); // で、コピー
        }
    }
});

単純簡単ですね!そう、Middlewareとか別にあんまり構える必要はなくて、Global.asax.csに書いていたのと同じようなノリでちょろちょろっと書いてやればいいわけです。そして、それが膨らみ始めたり、汎用的に切り離せそうだったら、独立したMiddlewareのクラスを立ててやれば再利用可能。これはIHttpModuleを作るのと同じ話ですけれど、Middlewareは、それよりもずっとカジュアルに作れます。

さて、上のコード、しかしこれだとMemoryStreamが中で使ってる奴にCloseされちゃったりするとCopyToAsyncでコケてしまいます。いや、誰がCloseするんだ?という話はありますが、でも、例えばStreamWriterを使って、usingして囲んでStreamに書いたりすると、内包するStreamまでCloseされちゃうんですねぇ。

usingしないように注意する、というのも、パイプラインに続くMiddleware全てで保証なんて出来ないので、ここもまた上流で防いでやるのがいいでしょう。LightNodeではUnclosableStream.csというものでラップしています。どういうものかというと

internal class UnclosableStream : Stream
{
    readonly Stream baseStream;
 
    public UnclosableStream(Stream baseStream)
    {
        if (baseStream == null) throw new ArgumentNullException("baseStream");
 
        this.baseStream = baseStream;
    }
 
    // 以下ひたすらStreamを移譲
 
    // そしてCloseとDisposeは空白
 
    public override void Close()
    {
    }
 
    protected override void Dispose(bool disposing)
    {
    }
}

という単純なもの。これを、ついでに独立したMiddlewareにしてみますか、すると、

public class BufferingMiddleware : Microsoft.Owin.OwinMiddleware
{
    public BufferingMiddleware(OwinMiddleware next)
        : base(next)
    {
 
    }
 
    public override async Task Invoke(Microsoft.Owin.IOwinContext context)
    {
        var originalStream = context.Response.Body;
        using (var bufferStream = new MemoryStream())
        {
            context.Response.Body = new UnclosableStream(bufferStream); // Unclosableにラップする
            await this.Next.Invoke(context); // Microsoft.Owin.OwinMiddleware使うとthis.Nextが次のMiddleware
            context.Response.Body = originalStream;
 
            if (context.Response.StatusCode != 204)
            {
                context.Response.ContentLength = bufferStream.Length;
                bufferStream.Position = 0;
                await bufferStream.CopyToAsync(originalStream);
            }
        }
    }
}

フレームワークレベルのものを作る時は、このレベルまで気を使ってあげたほうが間違いなくいいかと思います。

Owin vs Microsoft.Owin

Middleware作るのにMicrosoft.Owin.OwinMiddlewareを実装する必要はありません、InvokeとTaskと、などなどといったシグネチャさえあってればOKです。同様にIOwinContextはKatanaで定義してあるものであり、Owin自体はIDictionary<string, object>が本体です。

Katana(Microsoft.Owin)は便利メソッドの集合体です。Dictionaryから文字列Keyで引っ張ってResponseStream取り出すより、context.Response.WriteAsyncと書けたほうが当然楽でしょふ。他にも、Cookieだったりヘッダだったり、Middlewareの定義用ベースクラスだったり、などの基本的な、基本的な面倒事を全てやってくれる薄いツールキットがKatanaです。冒頭の、Node.jsのKoaみたいなものであり、PerlのPlackに相当するようなもの、と捉えればいいんじゃないでしょーか。

LightNodeはMicrosoft.Owinを参照していません。これは、依存性を最小限に抑えたかったからです。その分だけ、面倒事もあるので、楽したかったり社内用Middlewareを少し作るぐらいだったら、Katana使っちゃっていいと思いますですね。リファレンス実装、でありますが、どうせ事実上の標準として収まるでしょうし。フレームワークレベルでがっつし作ってみたいという時に、依存するかしないか、どちらを選ぶかは、まぁお好みで。依存したって全然構わないし、依存しないようにするのもそれはそれでアリだと思いますし。

HTMLを書き換えるMiddlewareを作る

というわけで、応用編行くよー。mayuki先生の作られているCarteletというHTMLパーサー/フィルターライブラリがあるのですが(某謎社で使われているらしいですよ)、それをOwinに適用してみましょう。Carteletのできることは

HTMLのそれなりに高速でそれなりなパース 出力時にCSSセレクターで要素に対してマッチして属性や出力フィルター処理 フィルターしない部分は極力非破壊 ASP.NET MVCのViewEngine対応 CSSのstyle属性への展開 (Cartelet.StylesheetExpander)

だそうです。

例として、class=”center”という属性を、style=”text-align:center”に展開するというショッパイ決め打ちな例を作ってみます。こんなMiddlewareを作ります。

// Cartelet Filter Middleware
app.Use(async (context, next) =>
{
    var originalStream = context.Response.Body;
    using (var bufferStream = new MemoryStream())
    {
        context.Response.Body = bufferStream; // 差し替えて
        await next(); // 実行させて
        context.Response.Body = originalStream;  // 戻す
 
        if (context.Response.StatusCode != 204) // NoContents
        {
            // Carteletによるフィルタリングもげもげ
            var content = Encoding.UTF8.GetString(bufferStream.ToArray());
 
            var htmlFilter = new HtmlFilter();
            htmlFilter.AddHandler(".center", (ctx, nodeInfo) =>
            {
                nodeInfo.Attributes.Remove("class");
                nodeInfo.Attributes["style"] = "text-align:center";
                return true;
            });
 
            var node = HtmlParser.Parse(content);
 
            var sw = new StreamWriter(context.Response.Body); // usingしない、stream閉じないために(leaveOpenオプションもあるのでそちらのほうが望ましいけど横着した)
            var cartelet = new CarteletContext(content, sw);
 
            htmlFilter.Execute(cartelet, node);
 
            await sw.FlushAsync(); // usingしない時はFlushも忘れないように。。。
        }
    }
});

Carteletの受け取るのがStringなので、全パイプラインが完了するまではバッファリングします。で、それで手に入れたStringをCarteletに流し込んで、本来のBodyに流し込む。(Content-Lengthの設定を省いてるので直に流し込んでますが、設定が必要なら再再バッファリングががが、まぁ、どうせ更に上流でgzipとかするだろうから、ここでContent-Length入れる必要はあんまにゃいかな!)

実際に結果を見てみると、

// これを実行すると
app.Run(async context =>
{
    context.Response.StatusCode = 200;
    context.Response.ContentType = "text/html;charset=utf-8";
    await context.Response.WriteAsync(@"
            <html><body>
            <div class=""center"">
               ほげほげ!
            </div>
            </body></html>");
});
<html><body>
<div style="text-align:center">
   ほげほげ!
</div>
</body></html>

というHTMLが出力されます。へーへーへー。色々応用効きそうですね!

OWINはパイプラインの夢を見るか?

色々出来る、しかも色々簡単!素晴らしい素晴らしい!プラガブル!はたして本当に?実際、フレームワーク書いたりミドルウェア書いたりしてると、ふつふつふつと疑問が湧いてきます。そういう時は先行事例を見ればいい、というわけでPythonのWSGIでは以下の様な話が。

(翻訳) WSGIは死んだ: WSGI Liteバンザイ!

すべてがプラガブルで、モノリシックなアプリケーションフレームワークを持つ理由がもはや一つもないような未来を思い描いていました。すべてライブラリ、ミドルウェア、デコレータでまかなえるからです。 悲しいことに、そんな理想的な未来はやってきませんでした。

OWINでも、一個のでっかいフレームワークを持つ必要なんてない!と言える時が来るか、というと、さすがにそれはないでしょうねえー。また、たとえ分離可能なコンポーネントであっても、フレームワークの提供するシステム(フィルターやプラグイン)から離れられるかというと、必ずしもそうではないのかな、って。

Middlewareのパイプラインは、ASP.NET MVC/Web APIとかのフィルターのパイプラインとも同じようなものです。だったらフィルターで作るよりMiddlewareで作ったほうが、フレームワークという制限から離れられて良さそう。でも、Middlewareの欠点は、後続のパイプラインのコンテキストを知らないことです。認証を入れるにしても、[AllowAnonymous]属性が適用されているかなんてしらないから、全部適用するかしないか、ぐらいにしか出来ない。filterContext.ActionDescriptorのようなもの、というのは、フレームワークの内側のシステムしか持ち得ないのです。でも、そうしてフィルターとして実装すれば、フレームワークに深く依存することになる。

そんな悩ましさを抱えつつも、それは、あんま無理せずに、コンテキスト不要なら最大限独立性の高いOwin Middlewareとして。そうでないならアプリケーションのプラグイン(フィルター)として。でいいかな、って思ってます。今のところ。何れにせよIHttpModuleなんかよりは遥かに作りやすいし、その手の話だって今に始まったことじゃあないのよね?HttpModuleだってHTTPパイプラインぢゃーん、って。はい。

2014/1/18(土)に開催されるめとべや東京#3(開催場所は謎社です)では、LTで5分でサービスAPIをOwin/LightNodeを使って作って実際にAzure Web Sitesにホストするまで、デモしようと思ってますので、OWIN知りたい、どう動かすのか見てみたい、って人もどうぞ。めととは。

2013年を振り返る

振り返るシリーズ第三弾。毎年、30日に振り返っているので、今年も30日で。ちなみに12/30は私の誕生日でして、ついに30歳を迎えてしまった……。20代さようなら、いや、別にいいんですが、C#er若くない人サイドに入ったな!という感じなわけでして、新陳代謝がほげもげとか。

2011年はMVP受賞、2012年はgloopsへの入社と退社(在籍期間たったの10ヶ月だった!)、というわけですが、では今年のメイントピックは、やはり当然グラニ a.k.a.謎社の始動です。2012年末の段階では

今はニート。ではなく、謎社にいます。謎社ってなんだよというか伏せる意味は特にないんですが、まぁまだ伏せておきます。実際のとこ出来たばかりの会社でして、だいたいほぼほぼ創立メンバーとして働いてます。そして現在のところPHPが95%でC#が5%といったところですが(私もPHP書いてますよ!毎日吐き気が!)、直近の目標はC#比率を高めることです(笑)

来年は変化というよりは進化、↑で書いたとおりにゲームを、じゃあなくて会社を前身させるのに全力で突き進む、というわっかりやすい目標があるんで、そのとーりに邁進しましょう。C#といったら謎社!みたいな、C#を使う人が憧れるぐらいな立ち位置の会社にできればいいなと思っています。

という話を立てていたわけですが、どうでしょう?かなりの部分で達成出来たのではないかと思います。会社は信じられないぐらいの成功を果たしていますし(※別に私の力ではなくてメンバー全員の力の賜物です)、当初PHPで書かれていたプログラムは、100%、C#へのリプレイスを果たしました。ボロ一軒家(リリース前は会社=一軒家にすし詰めで開発してた)でPHP書いてる時から、こういったヴィジョンを描いていたし、1年のうちに実現しきったのは、相当やった方だと年の末ぐらいは自画自賛させてくださいな。

といっても、まだ「C#を使う人が憧れるぐらいな立ち位置の会社」になれているかといったら、知名度であったり、そしてまだまだ実績も足りていないので、全然、目指す水準には満たしていないです。まだ、やっと0→1に、スタート地点に立ったばかり。ここからは1→100にしていかなければならない。また、外向きだけではなく、内側もまた全然整備しきれてないので、働く人がここで働くことに満足できる状態を作れなきゃとか、やることは山積み。

C#

今年のブログ内容は一気に非同期に傾いています。というのも会社で本格的にasyncを導入して使いまくり始めたこともあって、実践的に地雷を踏みまくってノウハウが溜まったからです。こういう実践的な話は、リファレンス情報だけではどうしても足りないわけで、両方が必要なのです。よく、C#はMSDNに情報がいっぱいあって、こんなに充実している言語、他にないよ!何が不満なの!?というけれど、半分合ってて、実態としては全然あってない。あくまで実践的な情報が重要度では第一、リファレンスは補完するもの。だから不満に感じるのは当然です。Microsoftとしては、そういう不足はコードレシピで補いたいようだけれど、それじゃ補えないというか、結局こういうのってリファレンス側に近い情報であって、欠落を埋められるわけがない。

じゃあ誰が埋めるのか、埋められるのかって、それは私達自身だけでしょう。自らの知見から来る情報がネットに溢れるといいな、と思っていますし、そのためにもまず自分たちがやっていきます。特に.NET界隈は実地的な話がなくて。海外とのレベル格差も酷い。圧倒的に日本はレベルが低い、ように見えてしまう。実際は優れた人は表に出てこないとか、絶対量が違うからとか、幾らでも言い分もあるし、確かにそうなのでしょう。でも、やっぱり、見えなければ意味がないし、その結果が、これ。C#という言語の他言語に比べた地位の低さ。例えばですよ、AWSは沢山の実地的な話が溢れてる。かたやAzureは、ただのリファレンス情報が垂れ流されているだけ。こういうの地味にきっついし差として現れるんだよね。C#も同じようなものですよ、現状。残念ながら。

というわけで超積極的に、情報は出していきたいのですにぇ。

さて、個人的には相変わらず小さなライブラリは作りまくってました。NuGetの登録数も36になりました。そういえばAsyncOAuthも今年からですね、おかげ様でOAuthライブラリといったらこれだよね!ぐらいに受け入れてもらえたようで、色々なところで使われているようです。謎社自身でもがしがし使ってます。

今年もう一つ、注力していたのはCloudStructuresというRedisクライアントですね。C# + Redis、しかもC# 5.0推奨というハードルの高さすぎてあまり使われてる感はないですが、これは謎社でハイパー使ってます。

AsyncOAuthやCloudStructuresは、謎社でのPHP→C#移行で絶対必要になるパーツという目算だったので先行して仕上げていました。その路線に乗っかって、来年育てていくのはLightNodeですね。こういう技術、必要になってから調べ始めているのでは遅く、でもあまり長いスパンで見ていてもしょうがない。研究開発機関ではないのだから。というわけで、今のところ、半歩先ぐらいを見据えて動いています。

ハイパー放置状態のlinq.jsを毎年何とかするする詐欺は、来年こそ、は……。

会社

今では当たり前のようにC#の企業ですが、当初はPHPでした。6/8時点で講演した.NET最先端技術によるハイパフォーマンスウェブアプリケーションは、はてブ300以上、現時点でViewsが33500と、.NET系にしてはクリティカルヒットを飛ばしましたが、スライドの冒頭で紹介したとおり、この時点ではPHPだったのです。実際にリプレースが完了したのは7/16日。深夜メンテナンス時間を挟んで朝に一気に切り替え。若干のトラブルはあったものの無事完了。

今年最大のハイライトであり、というか私自身も今のところ生きてて一番のハイライトですね。出来てまもない会社だから、体力も人員もない、でも凄い上り調子だからアプリを止めるとかありえないどころか育てなければならない。そんな中でリソース分散してPHPとC#を並走させて、統合、そして切り替え。まず、やるって決定が常識的に考えてありえないほど無茶苦茶だし、実際にやりきったのも凄いなぁと自画自賛Part2。やれるはずっていう机上の空論と、実際にやりきるってのは、違いはないけど必要な体力と精神力が全然違いますね、もうほんとヤバかった、特に精神的に……。

これに関しては、メンバーに恵まれました。出来たばかりの先行き不明な会社の、しょぼいホームページに、たった二行で求人情報が書かれててmailtoで送れ、というような状況で、素晴らしい人が次々と来てくれたのは信じられない話で。ほんと感謝の念に尽きません。

逆に今のほうが求人に苦戦しているのがアレ。あ、そんなわけでWe’re Awaitingということで積極的に採用はしているのでいつでも歓迎ですよ - 求人ページ ← 未だにサイトしょぼい

職種はCTOなのですけれど、CTO論をぶったりは、しません特に。色々なところにボーダーがあって、そこを超えそうになったら発言するようにしているのと、つまり超えない時は何もやってないように見えて実際何もやってない!こたぁない。って感じですかね(テキトー)。まぁ、かくあるべきみたいなものはあります、私の中で、ちゃんと。どんぐらい全うしてるかというと、うーん、70点?

技術的な方向性はシンプルに最先端のC#、なわけですけれど、それが良いことなのかどーか。勿論、良いことです。でも必ずしも良いことと言えるわけじゃあない。じゃあ良いことに変えればいいだけです。こんなの単純な話で、よーは会社として保守的にならないことがバリューを産み出せるようになりゃあいいだけです。逆に、何も産み出せないならNGでしょうね。そうならないように何が出来るかを行動していくべきでしょう。

ゲーム

Super Hexagon最高!これに尽きる。マジ最高。これはヤヴァい。Windows Phoneの最大の欠点はSuper Hexagonがプレイできないことと断言していい。絶対無理ー、と思ったHYPER HEXAGONESTをクリアした瞬間とか、ゲームの達成感の全てが詰まってた。超興奮。数年ぶりにゲームの面白さを思い出させてくれたマスターピース。

というわけで、今年は圧倒的に超えられない壁にSuper Hexagonが存在しているのですが、次点としてはGAMELOFTのモバイル用レースゲーム、Asphalt 8かな。グラフィックのケレン味が実にモバイル向けで、スマートフォン向けゲームとしては現時点で最高のグラフィック品質。ゲームのほうも大味、じゃなくてダイナミックで楽しい。通信対戦もあるしね。

そしてWindows Store App版もある!しかもWindows Phone 8版もある!しかもWindows Store App - Windows Phone 8間での通信対戦が可能!(iPhone-AndroidとかiPhone-WP8とかは対戦不可)。そんなわけで社内Windowsクラスタで話題騒然、Surface持っているならマストバイ、とか微妙な盛り上がりを見せました。ていうか社内で普通にWindows Phone 8の実機が集まるところがまずアレ。

本とか映画とか漫画とかアニメとか

見てない!ゲームと同じく、この辺りも年々見なくなっていってますが今年は特に一番見てない気がする、忙しさのせいかな(言い訳)。引っ越しして、実家に預けていた本とか漫画を全部回収したのだけど、あー、学生の頃は、いっぱい読んでるわけでもないけれどまぁまぁ読んでたなぁ、とか寂しくなったりはしたりして。

来年

ここ数年は、毎年ジェットコースター状態で目まぐるしく変化していて。けれど、大きな目標からはブレないで、年々近づけている気がします。一番最初に若くない人サイドに入ったとか、新陳代謝とか言いましたが、来年はそういうことが起こる状態を作っていきたいですね。C#が、若い人がこぞって使うような言語になってればいい、と。そのためにできること。人がすぐに思い浮かべられる、メジャーなアプリケーションの創出と、C#による圧倒的な成果、C#だからこその強さ、というのを現実に示していくこと。雇用の創出、の連鎖。

というわけで、来年も引き続きご期待くださいだし、よろしくお願いします。

LightNode - Owinで構築するMicro RPC/REST Framework

LightNodeというMicro RPC/REST FrameworkをOwinで作りました。というわけで、LightNodeについて……の前に、そもそもOwinって何?という感じだと思いますので、作成物を通してOwinが開くC#によるウェブ開発の未来について、もしくはOne ASP.NETというヴィジョンが見せる世界についてお伝えしようかな、と。これはOne ASP.NET Advent Calendar 2013への記事ですしね!ちなみに副題は「OWINでハイパー俺々フレームワーク作成」。きゃうん。

LightNode

バージョンはまだ0.1です。急ぎで作ったので、そう完成度高くないです。とはいえ十分動きますし、これは来年育てていきたいと思っているフレームワークです。やる気は、かなりあります。半年後ぐらいには実用になってるかなあ、と。ソースコードとか課題管理はGitHubで。

例によってインストールはNuGetから。

細かいパッケージが実はいっぱいあったりして……。

LightNodeが提供するのはサーバーサイドフレームワーク(競合はASP.NET Web APIです)と、クライアントサイドのAPIアクセスコード自動生成(WCFがやっているような!)、両方です。クライアントサイドの生成は、Unity3Dへのコード生成が最初のターゲットだったはずなんですが時間的な都合上、今はPCLだけ、です。まあ近いうちにはUnityのは出します、あとTypeScript用のも。

目標はクライアントサイドからサーバーサイドまで全てC#で統一されることによる生産性の超拡張を具現化すること。クライアントがUnityでサーバーがOwinで全部C#、みたいな、ね。両方C#で作り上げられることによるメリットを最大限引き出すことを目指しています。また、JSONオンリーではなくMessagePackやProtocol Buffersでのやり取りも可能なように、パフォーマンスを最大限追求します。また、そのうえで他言語との通信も捨てない、というわけでHTTPでRESTなでほげもげは捨てず、他言語からもサーバーへは自由にアクセス可能です。

逆にRESTfulでビューティフォーなURL設計とかは優先度ゼロなので完全に捨てています。

Lightweight as a Server

LightNodeは超絶Lightweightなフレームワークです。何がLightweightかというと、パフォーマンスと実装の簡単さ、両方を指して言ってます。特に実装の手間はほとんどないぐらい非常に軽量です、ASP.NET Web APIとか超重量級ですからね(それはさすがにいいすぎ)。

サーバーはOwin上に構築されていますので、まずOwinMiddlewareのセットアップが必要です。コンフィグだけは少し書いて下さい。SelfHostでもIISでもいいので、どちらかのOwinホストパッケージをNuGetで引っ張ってきて、スタートアップクラスでUseLightNodeする。

// OwinのStartup
public class Startup
{
    public void Configuration(Owin.IAppBuilder app)
    {
        // 受けつけるVerbを決めたりデフォのTypeFormatter(複数も当然できる)設定したり
        app.UseLightNode(new LightNodeOptions(
            AcceptVerbs.Get | AcceptVerbs.Post, 
            new JavaScriptContentTypeFormatter()));
    }
}

準備はこれだけ。で、実際にAPIはどうやって作るかというと、LightNodeContractを継承したクラスのパブリックメソッドが、自動的にAPIとして公開されます。

// LightNodeContractを実装すると全てのpublicメソッドがAPIになる
// URLは {ClassName}/{MethodName} で固定
// この場合だと例えば http://localhost/My/Echo?x=test
public class My : LightNodeContract
{
    // 戻り値は↑で設定したContentTypeFormatterでシリアライズされて渡る    
    public string Echo(string x)
    {
        return x;
    }
 
    // 今時なのでasyncもサポートしてるよ!戻り値はvoid, T, Task, Task<T>が使えます、ようは全部。
    // パラメータのほうは配列、Nullable、オプション引数あたりはOK
    public Task<int> Sum(int x, int? y, int z = 1000)
    {
        return Task.Run(() => x + y.Value + z);
    }
}

これで、「http://localhost/My/Echo?str=hoge」で叩けるってことになります。URLは {ClassName}/{MethodName} の形式で完全に統一されて、カスタマイズの余地はありません。

サーバー側は基本的にこれだけです。単純!地味!

必要最小限のラインってどこかなぁ、というのを考えた時、ここになるかな、と。ルーティングやパラメータのバインディング、レスポンスへの戻り値の書き込みなどはフレームワークがやってくれなきゃ死ぬけれど、それ以上はない。これだけでも割と十分便利に使える、の限界ラインを狙って、極力、機能を削ぎ落とす形で取捨選択しています。ちょっと不便、なぐらいで存外良かったりするのですよ、ちょっと便利、のために色々なものが引っ張られるより100倍良いでしょう?

あと私は「設定より規約」って嫌いなんですよね。別にXML Hellがいいとは言わないですが、あのやり方はLL向けかなあ、という気が相当してまして、C#でそれをやっても嬉しいところってあんまないんじゃないかって思います。属性とか型をどういう活かすか、のほうがいいとオモイマス。

Lightweight as a Client

純粋(?)なRESTって、C#でも、他のどの言語でも、決して扱いやすいわけじゃない。だからラップしたHogeClientを作りますよね。そして、そうした特化したRestClientの作成って、結構難しい。使いやすいClientって中々作れるものじゃあないです。手間がかかるうえに使いにくいものが出来上がるなら、絶望的です。だからサーバーAPIとクライアント、自分たちで両方を作る時、もんのすごく苦労してしまう。どこもLightweightじゃない。こんなことならSOAPでVisual Studioで自動生成してくれてるののほうが100億倍Lightweightだったよー、とかね、それはそれで事実です。

そこでLightNodeは真のLightweightを提供します。自動生成するからコストゼロで完璧なClient SDKが手渡されます。

// 中身はHttpClientなので当然全部async
// メソッドは全て
// client.{ClassName}.{MethodName}Async({parameter}) で生成されます
 
var client = new LightNodeClient("http://localhost");
 
await client.Me.EchoAsync("test");
var sum = await client.Me.SumAsync(1, 10, 100);

C#クライアントにとって、自然な操作感でサーバーサイドへとアクセスし、戻り値を受け取ることが出来ます(複雑なオブジェクトは内部のシリアライザを通して自動変換されます)。クライアント側にとってはRPCのように、サーバーを意識せず透過的にやり取り可能なこと、を目指しました。

この自動生成コードは、HttpClientを使ったRestClientとしては、割とイイ感じに出力するので、そういったのの参考にもどうぞ多分。REST APIはこういった形にラップされてるのが使いやすいと思ってるんですね、私は。インターフェイスの明示的実装の活用例。手作業だと面倒でサボッてしまいがちなCancellationTokenも受け取り可能になってたり、その辺は機械生成ならではの徹底さです。

ちなみに現状は実装時間的都合でまだPOSTにしか対応してない(次のアップデートでGETにも対応させます……)。

Micro RPC/REST Framework

Micro RPC FrameworkないしMicro REST Frameworkというのは造語です。ググッてもさして検索結果には出てきません。とはいえ、言わんとすることは分かるのではないでしょうかしらん。ヘヴィ級ORMのEntity Frameworkに対する、機能最小限でコンパクトなDapper。みたいなものです。徹底的に削ぎ落としたREST Framework。対極にあるのはUltra Super HeavyなFramework、って何?というと、ASP.NET Web APIかな。そう、ASP.NET Web APIって、別にLightweightじゃないよね?と、ずっと思っていて。ずっとしっくりこなくて。

というか既存のRESTなフレームワークってどれもLightweightに思えない。何が自分の求めているものなのかなあってずっと考えていたのだけれど(その間、会う人会う人にWeb APIってしっくりこないんです!と吹っかけて回ってた、どうもご迷惑おかけしました)、RPCだ!って至りまして。一周回ってRPC、これはアリだ、と。

REST vs SOAP, REST vs RPC, REST vs WCF

そもそも対立軸がオカシイ。そして、その結果、orになるんだよね、どちらを選びますか?って。それ以外がないの。なんでそう極端な対立になってしまうの?でも、しかし、それはある意味正しい。だって何かを作るには、この世にあるものから選ぶしかないのだから。ヘヴィなSOAPが嫌ならRESTしかなく、ヘヴィなRPCが嫌ならRESTしかなく、ヘヴィなWCFが嫌ならREST(ASP.NET Web API)しかない。

でも、本来は選択肢もっとあって良かったはずなんだよね。どうして中間がなかったんだろうね。そんなにRESTfulは素晴らしく輝かしい未来だったのかな。あまりにも、SOAPが、WCFが辛すぎて反動で極端に振れるしかなかったのかな。

RESTful

どうでもいい。だからLightNodeはGETとPOSTしかありません。

XML/JSON/XXX-RPC

doudemoii。入/出力がフォーマットに固定されるのが世の中的に厳しい。XMLは今どきアリエナイといわれてもshoganai感じになってきてしまっているし、その他のバイナリ形式もJavaScriptで扱いにくくなったりして絶望感ある。JSON最強はありますけど、それはそれで、一部クライアントとはMsgPackとかProtobufとかで高速省スペースな通信したいって欲求には応えられない。仕様もあってないようなものだし、それらに従っていいこと、あまりない。

Language Interoperability

LightNodeはかなりC#に依存というか、むしろ尻尾から先頭までC#で一気通貫して通せることをメリットの一つとしています。とはいえ(広義の)RESTなので、HTTPでGETかPOSTでアドレス叩けば結果帰ってきます。他の言語からも叩けるって物凄く大事なので、いくら一気通貫、C#で大統一理論を正義にしていても、大事にしてあげたいです。JavaScript無視するとか自殺行為ですしね(TypeScriptコードの生成は将来的に作りたいものの一つです)。

仕様は、URLは{ClassName}/{MethodName}、パラメータはGETはクエリストリング、POSTはx-www-form-urlencodedで送ります。そのためということもあって、基本的にパラメータの型には制限があって、基本型(intとかstringとかDateTimeとか)のnullableとarray、それとオプション引数までにしか対応していません。複雑な型はダメ。

ダメな理由としては、あと、それ許可するとメソッドや引数がAPIドキュメントの代わりにならないんですよね。何を渡すことが許されるているのか、のシンプルさが消える。せっかくC#側で作ることの良さ、型があること、を消してしまうほうがmottainai、トレードオフとしてナシという判断です。そしてそのほうが言語間Interoperabilityにも有利ですし。

レスポンスのほうは自由です。何でもありです。基本的にbodyに書かれるだけなので、シリアライズ可能なものならなんでもOK。シリアライザも自由に選べます。こういった形式が自由なのは、パフォーマンスのためです。C#でガリガリに速くしたいなら、やっぱProtobufやMsgPackだろう、と(バイナリだから単純に高速省スペースとかいうのはただの幻想なのでWCFをそういう目では見ないようにしましょう)。でもJSONで吐けないのはそれはそれでありえないわけで、自由に選べる、かつ共存できるように(拡張子やContent-Typeで識別します)しています。

RPC風であり、REST風な中間点がこれかなあ、と。これなら俺々仕様っぽさは特になくRESTといって納得できるレベルに収まってるかと。そのうえで、クライアント側的にはRPC風に使えるのでシームレス感が相当ある。APIの構造がC#に引っ張られて、他言語からキモチワルイ感を醸しだしてしまう可能性はあるのですが(但しメソッド名のcamel,Pascalは自由でどちらでも通るようになってます)、こればっかりはshoganaiかなあ。

そもそもREST的な公開されてるほげもげって各言語、どの言語でも決して使いやすくはないような。だからSDKでラップしたものを使うでしょう?言語中立で万歳、みたいな理想世界がない以上は、プライマリの言語での使いやすさ+セカンダリ以降でも可能な限り使いやすさを維持できる構造、にするのがベターかなあ、って。思ってます。

Why Code Generation? Why not Dynamic Proxy?

今のクライアントコードは、T4によるソースコード生成になっています。正直ダサい。クライアント側はソースコード生成よりも、共通のインターフェイスに対して動的コード生成でProxy作ってやるほうが手軽に扱えていいのよね。どういうイメージかと言いますと、例えば

// こういうインターフェイスがサーバー側とクライアント側が共に参照するDLLに定義してあって
public interface IHoge
{
    int Sum(int x, int y);
}
 
// サーバー側は↑のインターフェイスを実装する
public class HogeContract : IHoge
{
    public int Sum(int x, int y)
    {
        return x + y;
    }
}
 
// クライアント側は↓のような形で使える
// Createの戻り値がIHogeになってて、その実装は動的生成されたもの、という感じ
var sum = LightNodeClient.Create<IHoge>("http://localhost").Sum(10, 20);

実にスッキリしていいですね!クライアントサイドのIHogeの実装は、動的コード生成により実行時に挿入されるので一切、手を加える必要はありません。ちなみに実装方法はAssemblyBuilderを使ってひたすらILゴリゴリです。ExpressionTreeのCompileToMethodは静的メソッドしか作れないので、↑のイメージのようなインスタンスメソッドへの生成は気合入れて書くしかないのですねえ、やれやれ……。

でも、今回はソースコード生成にしました。それはIL書くのが面倒だから、ではなくて(実際面倒だからってのはちょっとありますが!)、理由はそれなりに幾つかあります。

まず、インターフェイスの戻り値=クライアントにとっての戻り値、じゃあなくなってます。具体的にはTaskです。非同期以降の世界ではクライアント側の型はTask以外はありえないんです。ここで、じゃあインターフェイス側もTaskを強要すればいい、ってのは、それは不便なのでナシですしねえ。クライアント側のメソッド名はXxxAsyncにしたいとかってのもありますし、やっぱ、現代においてはインターフェイスをきっちり一致させるというのは難しい。

あと、Unity。まあ、何度か名前↑で出しているようにUnityはかなりターゲットなわけですが、UnityのC#ってバージョン古いのですよね、Taskなんてないんですよ……。そんなわけで各プラットフォーム毎に全然違う生成したほうがいいってことになってしまいますよねえ、と。C#以外にTypeScriptなんかもターゲットにしたいですしね。

そして最後に、AssemblyBuilderはフル.NET Frameworkにしかない。WinRTやPhone、当然PCLにはない。ないないないないなので、手間隙かけてIL書いてもあんま嬉しくなれない。

そんなわけで、ソースコード生成を手法に選んでいます。

とはいえ、提供手段がT4であることが良いかどうかはビミョイところですね。こういうの自体は、別に割とあるパターンではあるのですけど、例えばPetaPocoやORM LiteなどMicro ORM系はEFなどのヘヴィーなデザイナの代わりとしてT4を用いているし、 T4 MVCとかもあるし、……、うーん、そのぐらいか。あんまないね。

あと今の実装はdllをロードしてそれを解析するんですが、ロードしたあとそのまんまアセンブリ掴みっぱなしで解放されないから、解放するにはVS再起動しないといけないとかいうクソ仕様とかも残ってるので、何とかしなきゃ度は相当高いです。誰か解決策教えてください。

Performance

機能面では最小な上に(劣る、とは言いません)、わざわざ新しく作る以上、パフォーマンスで負けていたら馬鹿みたいな話です。というわけで結果。

OWIN上のWeb API、OWIN上のLightNode、OWIN上の生app.Run、あとふつーにIISでホストする生HttpHandlerの4つでテキトーに測ってみました。Nancyは加えようと思ったんですがちょっと動かなくて調べる時間がなかったので(この記事はAdvent Calendar的にギリギリで書き上げているのです!)いったんナシ。

んで、速いです。というかほっとんど生HttpHandlerと変わらない速度出せてます。そりゃ機能少ないんだから当たり前……、ではないです。機能が少ない=速い、に直接結びつくほど世の中、甘くはありません!この手のものを作るにあたって速度を稼ぐポイントは幾つかあって、しっかりポイント抑えたコード生成(&キャッシュ)をしつつ、余計な要素を足さないことで最速になります。そりゃそーだ。ともあれ、これ以上は速くならないという限界ラインを突いてます。これより先はどう頑張っても誤差範囲は超えないでしょう、というか生Handler近辺の時点で、もう大して変えられんです。

その辺の実装のコツのお話はまた次回にでも。(ただEnum周りのマッピング処理が現在ゴミなのでEnum入れると遅いです、これは次回までに改善します)

Owin

ASP.NET Web APIがOwin対応とか、そういうのどーでもいーんだよね。だってIISにホストするでしょ?SelfHostとか別になくてもいいレベルでしょ?プロダクション環境では使わないでしょ?というわけで、あるものを使うという点では、別に今はOwin対応とかドウデモイイレベルの話です。皆が今Owinにさして興味持てなかったり使い道に想像沸かないとしても、そりゃそうだ、です。だってIISでいいんですもの。

Owinの利点はMiddlewareを組み合わせられること。けれど現状は、多様なMiddlewareは、特にはない。できたてほやほやみたいなものだから。むしろASP.NET Web APIやASP.NET MVCレベルでのコンポーネントのほうがあるし、将来的にもきっとそうでしょう。つまり、Middlewareも利点だー!と声高に言ってもshoganaiところがある。

でも、それでも、そこに未来はある。Owinは誰もが簡単にMiddlewareを作れる。小さなちょっとしたユーティリティから、大きいフレームワークまで。ついに始まった自由の世界。多様なMiddlewareは、今は、特にはない。でも、作ればいい、必ず彩り豊かになる。そうなればASP.NET Web APIのOwin対応なども、意味がでてくる。

そしてパフォーマンスですら手に入る。ああ、パフォーマンスは大事だ、そう、本当は大事でなかったとしても、とにかくキャッチーだからね。今までのASP.NETコアランタイム、System.Webがヘヴィだとしたら、それを完全にバイパスして直繋ぎしたら。発表されたHelios IIS Owin Web Server Hostは驚異的なパフォーマンスを見せている。なるほど、すごく魅力的に見える。なにより、Microsoftは本気なんだなって気がする。Helios自体はまだαだけど、今はSystem.Webにホストしてもらって、Heliosが完成したらそっちでホストすればいい。そこが選べるのもOwinのいいところだ。ああ!素晴らしいじゃないか、Owin!

Create Your Own Framework

俺々フレームワークは悪。常識です。常識。かといって、何もかも作らないわけにはいきません。何を作り、作らないか、その見極めが戦略として非常に大事。自分の戦略でもそうだし会社だったらなお大事。

さて、今回は作ったわけですけれど、その理由は単純にないから。ないものは作る。当たり前だよにぇ。といっても何もかもを満たすものなんて存在しないので、妥協できるかどうかのラインを見定めるってことではあるのだけれど。妥協ラインですが、C#の場合って、Microsoftで完結するものなら凄く整ってるんですよね、妥協OKというかむしろ完璧すぎるぐらいに。でも、今回の需要はMicrosoftの外側、Unityとか他のクライアント系のとか、それらと一気通貫に繋がって欲しいって需要なのです。Microsoftの中で完結してそれ以外とは疎結合、じゃなくて、繋がれる範囲は可能な限り全開に密結合して欲しいってのがリクエスト。そういうのって、未来永劫Microsoftから出てくることはない。絶対に。だから、作るって結論になる。

あともう一つはどのぐらいのクオリティで作れるか。作ったはいいけどクソクオリティだったら不幸になるだけだからね!そして、C#の場合はVisual Studioとの統合具合もかなり大事。だから、MVCフレームワークなどだと、単純に作業量が超絶多くて全体のクオリティを保つのは非常に大変なうえに、ASP.NET MVCはVS統合が進んでてサクサクViewとControllerを相互に移動出来たりコンパイルエラーがくっついてたり、そういうところまで面倒見るのは不可能に近い。だから、部分的に良い物を作れたとしても全体的には超えるのって凄く難しいから、俺々フレームワークは、あまり良い選択肢にはなれなさそう(でもNancyとか頑張って欲しい!)。

Service系のフレームワークだとViewとかとの面倒みなくていいしVS統合もそんなに気を配らなくていい(WCFぐらいパーフェクトな統合があればそりゃ素敵だけど、WCFは統合されてはいても他に問題だらけなので除外)、最小限の機能のラインが見えていて、かなり満たしやすい。性能だって少し頑張れば既存のものを抜くのも簡単。そんなわけで作るのはアリだ、のラインに個人的には達しました。

Owin EcoSystem

Service系ならば、そもそもHTTPに乗らなくてもいいじゃない?特にパフォーマンス優先なら!という選択もありますね。それを選ばないのは、エコシステム。サーバー側には沢山のノウハウやシステムがあり、何もしなくても最高のInteroperabilityがある。通信関連ではHTTPったら最強ね。っていうのは揺るがない。よほどパフォーマンス優先な根幹的な何かを作るのでなければ。

そして、Owinもまた理由になります。今までの俺々フレームワークの最大の欠点は、全て自前で作るしかなかったことです。でもOwinがあれば違う。認証?他のMiddlewareで。パフォーマンスモニタ系?例えばGlimpseは最高のモニタライブラリだけど、俺々フレームワークで、こういうのが一切使えなくなるって、痛手というか、それだけでありえないレベルになりますよね。でも、Owinならば、GlimpseがOwinに対応すればそれだけで乗っかることが出来る(そして実際、現在対応作業中のようです)。New Relicのような監視ツールなどもそう、俺々フレームワークであっても、そういうのにフルに乗っかっていけるってのが、今までと違うところだし、だから、作ってもOKの許容ラインに達しやすくなったと思いますですよ。

私も、LightNodeのようなフレームワークレベルのものだけじゃなく、他のフレームワークで使える小さなMiddlewareをこっそり作って公開してたりします。一つはOwinRequestScopeContextで、HttpContext.CurrentのようなものをOwin上で使えるようにするもの。もう一つはRedisSessionで、その名の通り、裏側がRedisのセッションストアです。RedisのHash構造に格納していて、リクエスト開始時に全部のデータを読み込み、リクエスト実行中のアクセスは全てインメモリで完結さえ、リクエスト終了時に変更があったもの差分だけを書き出す(RedisのHash構造だからこそ可能)ようにしています。実はこれの原型は既に謎社で実稼働していて、沢山のアクセスを捌いている実績アリだったりして。

今後RubyのRackにある便利Middlewareが移植されたりとかもするんじゃないでしょうか、むしろ良さそーな発想のものは自分達で移植してみるのもいいかもしれません。Owinが出たことで、自分達で作ることが、独善じゃなく発展の道になった。

One ASP.NET。You。使うだけじゃなく作る。それがこれからのASP.NETの未来だと思います。

Related Works

WCF。なんのかんのいってWCF。は偉いねえ、壮大だねえ、とか。LightNodeはWCFのABCからBindingを抜いたようなイメージでいいですよ。で、やっぱWCFとかの、その手の抽象化は辛い!何か被せて共通化して出来た気がするのは誰も満足させられないパターン。

rpcoder。Aimingさんの、独自IDL(Interface Definition Language)からUnity用のC#コードとかを吐き出すもの。LightNodeとの違いは、IDLかどうか、かしらん。LightNodeはIDLじゃなくてサーバーサイドの実装そのものが定義になるので、そういった外部定義不要なので、手間削減と、実装との乖離が絶対にないってとこかしらん。

似たようなというか定義という点ではRAMLとかね、まぁRAMLは最悪かなぁって思うのですけれど。RESTfulの呪縛に囚われて極北まで行くとそうなるのかねえ。どうぞ素敵なモデリングをしてください。ほんとdoudemoii。

Google Cloud Endpoints。サーバーの実装があって、そこからiOSやAndroid用のコードを生成するってもの。いいですねー、これですよこれ。Cloud Endpointsの正式リリースはついこないだですが、(特に)モバイル向けのバックエンドはこういうのがベストだと本当に思いますし、RPCの時代というかそういったようなものの時代への揺り戻しというか、再び多様性の時代が来たかな、と、健全で素敵です。

ServiceStack。これは、WCF Alternativeの中では一番メジャーな選択肢、ではあるのだけど、正直、なんか、この人のAPIセンスは……。辛い。正直ナシです。ちなみにv4から有料化しました。

Finagle。Twitter製の、Scalaでできた非同期でプラガブルなRPCフレームワーク。非同期なので全部Future(C#のTaskみたいなもの)。Relatedといったけど特に直接的な影響はないけど、オサレでモダンなフレームワークがRPC、というところだけちょっと強調とか。

DuoVia.Http。Owinで動くLightweightのService Libraryということで、LightNodeに一番近い先行実装ですね!クライアント側はプロキシによる動的生成なので非同期なし。サーバー側がrefやoutに対応させたりとか多機能を狙いすぎて、実行速度が引っ張られてたりとか、ちょっと違うかな、と。

ASP.NET Web API。まぁ、散々腐しましたけれど、実際ふつーに選ぶのならASP.NET Web APIが最初の選択肢だと思います。悪くないですよむしろイイですよ。そもそもLightNodeの実装にあたっては50分で掴み取る ASP.NET Web API パターン&テクニックとかOne ASP.NET, OWIN & Katanaとかガン見してたので味噌先生には頭が上がらないのでWeb APIいいんじゃないでしょうか(適当)。真面目な話、ASP.NET Web APIが一番参考にしてるのは間違いないですので、話の流れ(?)で色々腐しましたが、良いと思いますよ、本当。

Conclusion

One ASP.NETと言いつつも別にフィーチャーされないYou!の部分を推してみました。人昔前は、こういった俺々フレームワークが乱立しないのが.NETの良さ、と言われていた、こともありました。ありました。過去の話です。世界の進化は速く、Microsoftだけが一手に全ての需要を引き受けられるわけがない。それぞれの需要に合わせて、時に組み合わせて、時に自分で作り上げることができる。そういった世界の幕開けがOwinです。まだまだMiddlewareは足りていないので、「組み立てる」にはならないでしょう、けれどそれを解決するためにも、自分達で作り、公開していきましょう?それがOpenな世界だし、これからのC#コミュニティのあるべき姿だと思っています。

(いつもやるやる詐欺で毎回言ってる気がしますが)LightNodeはコンセプトだけじゃなく、真面目に育てていきたいと思っています。そもそも、会社として、この辺の通信が来年は重要課題になってくるなあ、というのがあって考えてたものなので、諸々色々で半年後ぐらいには十分な完成度で掲示できるかなあ、って思いますですよ。勿論、皆さん今から使ってくれたら嬉しいですにぇ。

また、コンセプト語るには実装がなきゃ、と相当思っていまして。かつて人々は「パターン」「契約による設計」などアイデアに名前をつけて論じたけれど、 このごろの新しいアイデアはフレームワークやプログラミング言語、データベースエンジンなどを通じて表現されるようになった。 今は書籍ではなく実装が思想を表現する手段になっていると、Eric Evans(DDD本の人)は語った。そんなわけで、というわけではないですけれど、私は私の思想はコードで表現していきたいと思っているし、そもそもそうしてきた。linq.js(LINQが言語を超えることを)もChaining Assertion(流れるようなインターフェイスや英語的なるものの馬鹿らしさを)もReactiveProperty(全てが繋がるイメージを)もそうです。ライブラリは思想の塊なのです、言葉に出されていなければそこに思想はない?そんなことはなく、ずっと流暢に語ってくれるはず。

そしてC#の強さの証明は、会社の結果で表現していきます。実証されなければ何の意味もないし、何の説得力もない。誰に?というと、日本に、世界に。というわけで、引き続き来年の諸々にもご期待ください!

Comment (4)

ufcpp : (12/24 01:04)

実は今のrpcoderは全部C#とT4で書きなおして、C#クラス→C#ソースコード生成になってたり。ついかっとなって自分が書き替えた。型の定義は静的なクラスと属性のある言語でやるのがいいです。

たいへー : (12/24 02:27)

>アセンブリ掴みっぱなしで解放されない
AppDomainのシャドウコピー使えばよいとおもいますです。

NyaRuRu : (12/24 08:13)

RPC と聞くとどうしても morita-san のこれを思い出して笑ってしまうという病気に.未読でしたら是非.
http://dodgson.org/omo/t/?date=20080724

そういえば protobuf のオープンソース化からまだ 5 年ちょっとなんですねぇ.
protobuf RPC は社内だとお世話になりまくりっす.

neuecc : (12/24 17:06)

> 全部C#とT4で書きなおして、C#クラス→C#ソースコード生成になってたり

Oh!イイですねー。あとは是非、サーバー側もC#を。

> AppDomainのシャドウコピー

ほええ、21世紀にAppDomainなんて触りたくないよぅ。
とばかりも言ってられないですがが、shoganai。

> REST vs RPC

何年経っても無限にREST vs RPCは繰り返す!!!
面白すぎて笑ってしまいました、きゃふん。

外から見たカンジだとprotobufだとまだ5年(おお!そうなんですね!)で、
MsgPackだともう5年(動き出したのはその翌年ぐらいだとしても)って見えますねえ。
種々諸々としてはMsgPackのほうがイケてる……
と思いつつただのバイナリフォーマットとして激しくお世話に。

Trackback(0)
Write Comment

An Internal of LINQ to Objects

というタイトルで、大阪で開催された第3回 LINQ勉強会で発表してきました。

An Internal of LINQ to Objects from Yoshifumi Kawai

大阪は初めてですね!というか、東京以外で発表しに行くのは初めてです。大阪遠い。

レベル感は、まぁもうLINQも初出から10年も経つわけだし(経ってない)、もはや初心者向けもないだろうということで、LINQ to ObjectsのDeep Diveなネタを突っ込んでおきました。こんなにまとまってる資料は世界にもないですよ!なんで知ってるかというと、linq.jsの実装で延々と何回も書いてるからです、はい。いいことです。そのぐらいにはパーフェクトな実装ということで。ver.3の完成は、も、もう少し、ま、まだ……。ごめんなさい。近いうちには、またベータ出すよ!←いい加減完成させろ

口頭で捕捉した内容としては、yield returnで書くメソッドは引数チェック用のと分離させよう、というところ。これ、メンドーくさかったらやらなくていいです。実際メンドウクサイですしね。コアライブラリっぽい位置づけのものだったらがっつしやるのも良いとは思いますが、普段からやるのはカッタルイでしょう。と、いっても、LINQ以降はあまり生でyield return使うこともないでしょうけれど。

イテレータの話とかは、実際doudemoiiんですが、気になる人は、これはそもそもC#の言語仕様書に書いてあります。言語仕様書はVSインストールディレクトリの

C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC#\Specifications\1041\CSharp Language Specification.docx

にあるので(日本語訳されてるよ!)、読んだことない人は眺めてみると楽しいんではかと思います。

Comment (4)

toshi : (12/16 23:50)

いつもここのブログで勉強させていただいています。
このブログでC#に興味を持ち、C#プログラマを目指して現在必死に勉強中です。

ご質問させていただきたいのですが
P18で
「runnning状態ではMoveNextの挙動は未定義なので、ThreadSafeではないけど、Enumerator自体はThreadSafe」の部分がよく理解できなかったのですが
MoveNextをrunning状態に連続で呼ばれると駄目ということなのでしょうか?
Enumerator自体はThreadSafeなので結果的に問題ないということですか?

もしよろしければご返事頂けると嬉しいです。

neuecc : (12/19 05:36)

おっと、返信遅れて失礼しました&どもです。

あー、ここは日本語が悪いですね、すみません、自分でも読み返して意味不明感あるというか、
このまま取ると全然正しくないですね……。
正しくはIEnumerableのGetEnumeratorメソッドがスレッドセーフで
IEnumeratorのMoveNextメソッドがスレッドセーフではない、です。

hoge : (12/19 10:58)

IEnumerableのGetEnumeratorをマルチスレッドで同時に呼んでも良いけ
ど、一度取得したIEnumeratorをマルチスレッドでMoveNextするのはダメよ
(ThreadSafeでない)ということでしょうか。

neuecc : (12/19 15:04)

はい、その通りです!

Trackback(0)
Write Comment

非同期時代のLINQ

この記事はC# Advent Calendar 2013の4日目となります。2012年はMemcachedTranscoder - C#のMemcached用シリアライザライブラリというクソニッチな記事で誰得でした(しかもその後、私自身もMemcached使ってないし)。その前、2011年はModern C# Programming Style Guide、うーん、もう2年前ですかぁ、Modernじゃないですねえ。2011年の時点ではC# 5.0はCTPでしたが、もう2013年、当然のようにC# 5.0 async/awaitを使いまくる時代です。変化は非常に大きくプログラミングスタイルも大きく変わりますが、特にコレクションの、LINQの取り扱いに癖があります。今回は、非同期時代においてLINQをどう使いこなしていくかを見ていきましょう。

Selectは非同期時代のForEach

これ超大事。これさえ掴んでもらえれば十二分です。さて、まず単純に、Selectで値を取り出す場合。

// こんな同期版と非同期版のメソッドがあるとする
static string GetName(int id)
{
    return "HogeHoge:" + id;
}
 
static async Task<string> GetNameAsync(int id)
{
    await Task.Delay(TimeSpan.FromMilliseconds(100)); // 適当に待機
    return "HogeHoge:" + id;
}
 
// 以後idsと出てきたらこれのこと指してるとします
var ids = Enumerable.Range(1, 10);
 
// 同期バージョン
var names1 = ids.Select(x => new { Id = x, Name = GetName(x) }).ToArray();
 
// 非同期バージョン
var names2 = await Task.WhenAll(ids.Select(async x => new { Id = x, Name = await GetNameAsync(x) }));

ラムダ内でasyncを書き、結果はIEnumerable<Task<T>>となるので、配列に戻してやるためにTask.WhenAllとセットで使っていくのが基本となります。Task.WhenAllで包むのはあまりにも頻出なので、以下の様な拡張メソッドを定義するといいでしょう。

// こういう拡張メソッドを定義しておけば
public static class TaskEnumerableExtensions
{
    public static Task WhenAll(this IEnumerable<Task> tasks)
    {
        return Task.WhenAll(tasks);
    }
 
    public static Task<T[]> WhenAll<T>(this IEnumerable<Task<T>> tasks)
    {
        return Task.WhenAll(tasks);
    }
}
 
// スッキリ書ける
var names2 = await ids.Select(async x => new { Id = x, Name = await GetNameAsync(x) }).WhenAll();

では、foreachは?

// 同期
foreach (var id in ids)
{
    Console.WriteLine(GetName(id));
}
 
// 非同期
foreach (var id in ids)
{
    Console.WriteLine(await GetNameAsync(id));
}

そりゃそーだ。……。おっと、しかしせっかく非同期なのに毎回待機してループしてたらMottaiなくない?GetNameAsyncは一回100ミリ秒かかっているから、100*10で1秒もかかってしまうんだ!ではどうするか、そこでSelectです。

// 同期(idsがList<int>だとする)
ids.ForEach(id =>
{
    Console.WriteLine(GetName(id));
});
 
// 非同期
await ids.Select(async id =>
{
    Console.WriteLine(await GetNameAsync(id));
})
.WhenAll();

ForEachの位置にSelect。ラムダ式中では戻り値を返していませんが、asyncなので、Taskを返していることになります(Task<T>ではなく)。同期ではvoidとなりLINQで扱えませんが、非同期におけるvoidのTaskは、Selectを通ります。あとはWhenAllで待機してやれば出来上がり。これは全て同時に走るので100msで完了します。10倍の高速化!

ただし、この場合処理順序は保証されません、同時に走っているので。例えばとある時はこうなりました。

HogeHoge:1
HogeHoge:10
HogeHoge:8
HogeHoge:7
HogeHoge:4
HogeHoge:2
HogeHoge:6
HogeHoge:3
HogeHoge:9
HogeHoge:5

処理順序を保証したいなら?WhenAll後に処理ループを回せばいいぢゃない。

// こうすれば全て並列でデータを取得したあと、取得順のままループを回せる
var data = await ids.Select(async id => new { Id = id, Name = await GetNameAsync(id) }).WhenAll();
foreach (var item in data)
{
    Console.WriteLine(item.Name);
}

一旦、一気に詰めた(100ms)後に、再度回す(0ms)。これはアリです。そんなわけで、非同期時代のデータの処理方法は三択です。逐次await, ForEach代わりのSelect, 一気に配列に詰める。どれがイイということはないです、場合によって選べばいいでしょう。

ただ言えるのは、超大事なのは、Selectがキーであるということ、ForEachのような役割を担うこと。しっかり覚えてください。

非同期とLINQ、そしてプリロードについて

さて、SelectだけではただのForEachでLINQじゃない。LINQといったらWhereしてGroupByして、ほげ、もげ……。そんなわけでWhereしてみましょう?

// 非同期の ラムダ式 をデリゲート型 'System.Func<int,int,bool>' に変換できません。
// 非同期の ラムダ式 は void、Task、または Task<T> を返しますが、
// いずれも 'System.Func<int,int,bool>' に変換することができません。
ids.Where(async x =>
{
    var name = await GetNameAsync(x);
    return name.StartsWith("Hoge");
});

おお、コンパイルエラー!無慈悲なんでなんで?というのも、asyncを使うと何をどうやってもTask<bool>しか返せなくて、つまりFunc<T,Task<bool>>となってしまい、Whereの求めるFunc<T,bool>に合致させることは、できま、せん。

Whereだけじゃありません。ラムダ式を求めるものは、みんな詰みます。また、Selectで一度Task<T>が流れると、以降のパイプラインは全てasyncが強いられ、結果として……

// asyncでSelect後はTask<T>になるので以降ラムダ式は全てasyncが強いられる
// これはコンパイル通ってしまいますがkeySelectorにTaskを渡していることになるので
// 実行時エラーで死にます
ids.Select(async id => new { Id = id, Name = await GetNameAsync(id) })
   .OrderBy(async x => (await x).Id)
   .ToArray();

Selectがパイプラインにならず、むしろ出口(ForEach)になっている。自由はない。

ではどうするか。ここは、一度、配列に詰めましょう。

// とある非同期メソッドのあるClassがあるとして
var models = Enumerable.Range(1, 10).Select(x => new ToaruClass());
 
// 以降の処理で使う非同期系のメソッドなり何かを、全てawaitで実体化して匿名型に詰める
var preload = await models
    .Select(async model => new
    {
        model,
        a = await model.GetAsyncA(),
        b = await model.GetAsyncB(),
        c = await model.GetAsyncC()
    })
    .WhenAll();
 
// そうして読み取ったもので処理して、(必要なら)最後に戻す
preload.Where(x => x.a == 100 && x.b == 20).Select(x => x.model);

概念的にはプリロード。というのが近いと思います。最初に非同期なデータを全て取得しまえば、扱えるし、ちゃんと並列でデータ取ってこれる。LINQの美徳である無限リストが取り扱えるような遅延実行の性質は消えてしまいますが、それはshoganai。それに、LINQにも完全な遅延実行と、非ストリーミングな遅延実行の二種類があります。非ストリーミングとは、例えばOrderBy。これは並び替えのために、実行された瞬間に全要素を一度蓄えます。例えばGroupBy。これもグルーピングのために、実行された瞬間に全要素を舐めます。非同期LINQもまた、それらと同種だと思えば、少しは納得いきませんか?現実的な妥協としては、このラインはアリだと私は思っています。分かりやすいしパフォーマンスもいい。

AsyncEnumerableの幻想、或いはRxとの邂逅

それでも妥協したくないならば、次へ行きましょう。まだ手はあります、良いかどうかは別としてね。注:ここから先は上級トピックなので適当に読み飛ばしていいです

そう、例えばWhereAsyncのようにして、Func<T,bool>じゃなくFunc<T,Task<bool>>を受け入れてくれるオーバーロードがあれば、いいんじゃない?って思ってみたり。こんな風な?

public static class AsyncEnumerable
{
    // エラー:asyncとyield returnは併用できないよ
    public static async IEnumerable<T> WhereAsync<T>(this IEnumerable<T> source, Func<T, Task<bool>> predicate)
    {
        using (var e = source.GetEnumerator())
        {
            while (e.MoveNext())
            {
                if (await predicate(e.Current))
                {
                    yield return e.Current;
                }
            }
        }
    }
}

ただ、問題の本質はそんなことじゃあない。別にyield returnが使えなければ手書きで作ればいいわけで。そして作ってみれば、本質的な問題がどこにあるのか気づくことができます。

class WhereAsyncEnumerable<T> : IEnumerable<T>, IEnumerator<T>
{
    IEnumerable<T> source;
    Func<T, Task<bool>> predicate;
    T current = default(T);
    IEnumerator<T> enumerator;
 
    public WhereAsyncEnumerable(IEnumerable<T> source, Func<T, Task<bool>> predicate)
    {
        this.source = source;
        this.predicate = predicate;
    }
 
    public IEnumerator<T> GetEnumerator()
    {
        return this;
    }
 
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
 
    public T Current
    {
        get { return current; }
    }
 
    object System.Collections.IEnumerator.Current
    {
        get { return Current; }
    }
 
    public void Reset()
    {
        throw new NotSupportedException();
    }
 
    public void Dispose()
    {
 
    }
 
    // ↑まではdoudemoii
    // MoveNextが本題
 
    public bool MoveNext()
    {
        if (enumerator == null) enumerator = source.GetEnumerator();
 
        while (enumerator.MoveNext())
        {
            // MoveNextはasyncじゃないのでawaitできないからコンパイルエラー
            if (await predicate(enumerator.Current))
            {
                current = enumerator.Current;
                return true;
            }
        }
        return false;
    }
}

MoveNextだけ見てもらえればいいのですが、predicateを使うのはMoveNextなわけです。ここがasyncじゃないと、AsyncなLINQは成立しません。さて、もしMoveNextがasyncだと?

public async Task<bool> MoveNext()
{
    // ここで取得するenumeratorのMoveNextも
    // 全て同一のインターフェイスであることが前提条件なのでTask<bool>とする
    if (enumerator == null) enumerator = source.GetEnumerator();
 
    while (await enumerator.MoveNext())
    {
        if (await predicate(enumerator.Current))
        {
            current = enumerator.Current;
            return true;
        }
    }
    return false;
}

これは機能します。MoveNextをasyncにするということは連鎖的に全てのMoveNextがasync。それが上から下まで統一されれば、このLINQは機能します。ただ、それってつまり、IEnumerator<T>を捨てるということ。MoveNextがasyncなのは、似て非なるものにすぎない。当然LINQっぽい何かもまた、全て、このasyncなMoveNextを前提にしたものが別途用意されなければならない。そして、それが、Ix-Async

Ix-Asyncのインターフェイスは、上で出したasyncなMoveNextを持ちます。

public interface IAsyncEnumerable<out T>
{
    IAsyncEnumerator<T> GetEnumerator();
}
 
public interface IAsyncEnumerator<out T> : IDisposable
{
    T Current { get; }
    Task<bool> MoveNext(CancellationToken cancellationToken);
}

そして当然、各演算子はIAsyncEnumerableを求めます。

public static IAsyncEnumerable<TSource> Where<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, bool> predicate);

これの何が便利?IEnumerable<T>からIAsyncEnumerable<T>へはToAsyncEnumerableで変換できはするけれど……、求めているのはIEnumerable<Task<T>>の取り扱いであったりpredicateにTaskを投げ込めたりすることであり、何だかどうにもなく、これじゃない感が否めない。

そもそも、LINQ to Objectsから完全に逸脱した新しいものなら、既にあるじゃない?非同期をLINQで扱うなら、Reactive Extensionsが。

Reactive Extensionsと非同期LINQ

ではRxで扱ってみましょう。の前に、まず、predicateにTaskは投げ込めません。なのでその前処理でロードするのは変わりません。ただ、そのまま続けてLINQ的に処理可能なのが違うところです。

await ids.ToObservable()
    .SelectMany(async x => new
    {
        Id = x,
        Name = await GetNameAsync(x)
    })
    .Where(x => x.Name.StartsWith("Hoge"))
    .ForEachAsync(x =>
    {
        Console.WriteLine(x);
    });

おお、LINQだ?勿論、Where以外にも何でもアリです。RxならLINQ to Objects以上の山のようなメソッドを繋げまわることが可能です。ところで、ここで出てきているのはSelectMany。LINQ to ObjectsでのSelectの役割を、Rxの場合はSelectManyが担っています。asyncにおいてForEachはSelectでRxでSelectはSelectMany……。混乱してきました?

なお、これの結果は順不同です。もしシーケンスの順序どおりにしたい場合はSelect + Concatを代わりに使います。

await ids.ToObservable()
    .Select(async x => new
    {
        Id = x,
        Name = await GetNameAsync(x)
    })
    .Concat()
    .Where(x => x.Name.StartsWith("Hoge"))
    .ForEachAsync(x =>
    {
        Console.WriteLine(x);
    });

ソーナンダー?ちなみにSelectManyはSelect + Mergeに等しい。

await ids.ToObservable()
    .Select(async x => new
    {
        Id = x,
        Name = await GetNameAsync(x)
    })
    .Merge()
    .Where(x => x.Name.StartsWith("Hoge"))
    .ForEachAsync(x =>
    {
        Console.WriteLine(x);
    });

この辺のことがしっくりくればRxマスター。つまり、やっぱRxムズカシイデスネ。とはいえ、見たとおり、Rx(2.0)からは、asyncとかなり統合されて、シームレスに取り扱うことが可能になっています。対立じゃなくて協調。自然に共存できます。ただし、単品でもわけわからないものが合わさって更なるカオス!強烈強力!

まとめ

後半のAsyncEnumerableだのIx-AsyncだのRxだのは、割とdoudemoii話です、覚えなくていいです。特にIx-Asyncはただの思考実験なだけで実用性ゼロなので本気でdoudemoiiです。Rxは便利なので覚えてくれてもいいのですが……。

大事なのは、async + Selectです。SelectはForEachなんだー、というのがティンとくれば、勝ったも同然。そして、プリロード的な使い方。そこさえ覚えれば非同期でシーケンス処理も大丈夫。

asyncって新しいので、今まで出来たことが意外と出来なくてはまったりします。でも、それも、どういう障壁があって、どう対処すればいいのか分かっていればなんてことはない話です。乗り越えた先には、間違いなく素晴らしい未来が待っているので、是非C# 5.0の非同期、使いこなしてください。

Comment (2)

biac : (12/04 14:33)

「// こうすれば全て並列でデータを取得したあと、取得順のままループを回せる」のところ、PLINQでもまぁ同じことができるっぽい。やっぱり、ちょっとオーバーヘッドが大きいかな。でも、Taskは10本生成されるし、CPUが空いてれば10本並行で流れるし。

var tasks = ids.AsParallel().AsOrdered().Select(id => new { Id = id, NameTask = GetNameAsync(id) });
foreach (var t in tasks)
{
Console.WriteLine(await t.NameTask);
}

neuecc : (12/04 15:57)

ええと、それもうメチャクチャなので何の意味もなしてません。

Trackback(1)
Write Comment

GlimpseによるRedis入出力の可視化とタイムライン表示

空前のGlimpseブーム!Glimpse最高!これ入れてないASP.NET開発はレガシー!というわけで、グラニ a.k.a. 謎社でも激しく使用を開始しています。さて、ではGlimpseとは何ぞやか、という話は今回は、しません(!)。それは誰かがしてくれます(チラッ。今回は本題のRedis周りのGlimpse拡張について。

CloudStructures 0.6

グラニではRedisライブラリとしてBookSleeve + CloudStructuresを利用しています。RedisやBookSleeveについては、C#のRedisライブラリ「BookSleeve」の利用法を読んでね!何故BookSleeveだけじゃないのかというと、BookSleeveだけだと使いづらいからです。例えるなら、BookSleeveはRedisドライバであり、ADO.NETみたいなもの。CloudStructuresはO/R(Object/Redis)マッパーであり、Dapperのようなもの。といった関係性で捉えてもらえればあってます。で、CloudStructuresは私謹製です。

今回Glimpseと連携させて使いやすくログ吐きするのに若干足りてなかったので、出力用のインターフェイス ICommandTracer を破壊的変更かけてがっつし変えちゃってます。というわけでバージョン0.6に更新。破壊的変更、ま、まだ0.xだし……。0.xとはいえ、CloudStructuresはグラニのプロダクション環境下で激しく使われてます。もんのすごい量のメッセージを捌いている(BookSleeve開発元のStackOverflowよりも遥かに捌いているはず)、秒間で数万とか数十万とかのクエリ数を、なので実績は十二分にあると言えるでしょう。

今回からリポジトリにCloudStructures.Demo.Mvcというプロジェクトを入れているので、実環境でどういう風に使えばいいのかとか、Glimpseでどう表示されるのかが分かるようになってますので、使う時は参考にどうぞ。

Glimpseによる表示

今回からCloudStructuresによるRedisアクセスを可視化できるように、Glimpseプラグインが別途インストールできるようになりました。NuGet上で Glimpse.CloudStructures.Redisから入れればおk。インストール後、Glimpseを有効にし、ICommandTracerにGlimpse.CloudStructures.Redis.RedisProfilerを渡してやると(Web.configからの設定の仕方などはCloudStructures.Demo.Mvcを見て下さい)、以下のようになります。

Redisタブが追加され、Redisで、どのコマンドでどのキーで実行したか、何を送って何を受け取れたか、かかった時間。それらが一覧表示されます。また、重複コマンド・キーで発行した場合は警告表記になります(一番下のオレンジの)

とにかく、通信が全部見える。中身含めて。圧倒的に捗ります。これがないなんて、盲目で歩いているに等しかった。ありえない。もう、戻れない。見えるっては、いいことですねぇ、ほんと。

更に、Glimpse標準のTimelineタブに統合されていて、これが超絶最高です。

5つ連なってる部分、これは非同期に並列アクセスされています。コードは

var ids = new[] { 12, 3124, 51, 636, 6714 };
var rand = new Random();
 
await Task.WhenAll(ids.Select(async x =>
{
    await RedisGroups.Demo.String<int>("TestInc.Id." + x).Increment(rand.Next(1, 10));
}).ToArray());

のようになっているわけですが、これ、もし同期アクセスだったら、同時に動くことなく、この分だけタイムラインが右に長くなるわけです。可視化されることで全て非同期!全てパイプライン!効率的に、高速に、というのがとてもわかりやすく見えます。実際、グラニではこういった一気に叩いてWhenAllというパターンを多用して高速なレスポンスを実現しています。ここまでASP.NET MVCの非同期コントローラーを死ぬほど使い倒している企業は、世界でも稀なのではないか、というレベルで非同期祭りやってます。(そして時に地雷踏みつつ前進してます)。

Glimpseについてもうちょっとだけ

実際のところGlimpse、ただ入れただけだとあんまり嬉しいことはないでしょう。色々なサーバーの情報が見えると言ったって、そんなの一度だけ見れば十分だしー、で終わってしまったり。ただ情報がありゃあいいってものじゃなくて、何の情報が必要か、が大事。でも、Glimpseってカスタマイズが容易なんですね。だから、その会社にとって、そのアプリケーションにとって大事な情報を流しこむことができる。大事な情報を流しこむようにすることで「頻繁に使うものではない」から「頻繁に使うもの」に変える。そうすることで、手放せないものとなります。

これはグラニで実際に使っているタブですが、Log, PlatformAPI, Redis, SQL Explainは弊社が独自に作成したタブです。以前にHttp, SQL, Redisのロギングと分析・可視化についてという記事を書きましたが、そこで、外部へのアクセスをフックする仕組みは作ってあったので、それぞれでGlimpseにデータ流すように変えただけです。これによりRedis, Http, SQLの全てがタブで全て見えるし、Timelineにも出てくるのでボトルネックが一目瞭然に。過度な通信なども一発で分かる。

SQL Explainはリクエスト中で発行された全てのSQLに対してexplainを発行し結果を表示しています(うちはMySQLを使っているのでexplainです、SQL Serverならそれ用のコマンド打てば同様以上のことが当然可能です)。これにより、将来ボトルネックになるクエリが早期発見できます。

explainとか面倒だから全てに律儀に打ったりしないし、*ms以上遅いクエリは警告するとかやったって、開発環境のデータ量だと無意味だったりするでしょう。だから、全てのクエリに対して自動でexplainをかけてやって、怪しそうなもの(using filesortとか)は最初から警告してあげる。これで、クソクエリの早期撲滅が可能となります。

ちなみにMiniProfilerとの使い分けですが、うちではむしろもうMiniProfilerイラナクね?の方向で進んでます。ちょっとまだGlimpse、足りてないところもあるのですが、割と直ぐに解決しそうだし、むしろMiniProfiler動かしてるとHistoryが汚れるのでGlimpseにとって邪魔なのですよね。

まとめ

といったように、グラニではより開発しやすい環境を作ることに全力を注いでいます。なんと今だとアプリケーションエンジニア・インフラエンジニア・フロントエンドエンジニアを募集中だそうですよ!←求人広告記事ダッタノカー、イヤソンナコトナイデスケドネ。なんか敷居高杉と思われてるかもですが、アプリエンジニアに関しては割とそんなことなく、基準は最低限LINQ to Objects知ってる程度からなので気楽にどうぞ。最近、オフィスを六本木ヒルズに移転しまして、ハードウェア環境も充実しているので、そちらの面でも開発しやすいかな、と。モニタも27インチ(2560×1440)をトリプルですしドリンク無料飲み放題ですし。

ソフトウェア側も良さそうならどんどんバシバシ変えてます。Glimpseも最初は導入されてませんでしたが、後から入れていますしね。ASP.NET MVCも早速5へ。この辺の姿勢は初期の文化が大事だと思っているので、絶対緩めないのを信条にしています。

なお、Glimpseの導入や独自タブの作成、ならびにGlimpse.CloudStructures.Redisの作成は、私がやったわけではありません!私がやったのはSQL Explainタブの作成とICommandTracerの改変、Glimpse.CloudStructures.Redisを社外に出せるよう依存部分の除去しただけでして。そんなわけで皆で一岩となって改革してるんですよー、とっても刺激的でいい職場です。私にとっても。←ヤッパリ求人広告記事ダッタノカー、イヤソンナコトナイデスヨ。

.NETのコレクション概要とImmutable Collectionsについて

先週の土曜日に、「プログラミング .NET Framework 第4版 」座談会でOverview of the .NET Collection Framework and Immutable Collectionsとして、コレクションフレームワークとImmutable Collectionsについて話してきました。

Overview of the .Net Collection Framework and Immutable Collections from Yoshifumi Kawai

案外コレクションについてまとまった話って、ない(or .NET 4.5からReadOnly系が入ってきて、話が更新されているもの)ので、資料として役に立つのではないかと思います。

Collection Framework

前半部分ですが、これのジューヨーなところはILinqable<T>、じゃなくて(スライド資料では出てないのでナンノコッチャですが)、ReadOnly系の取り扱いですね。MutableとReadOnlyが枝分かれしている理由とか対処方法とか、が伝えたかった点です。いやあ、コレクション作る時は両方実装しよう!とかしょうもないですねえ、shoganaiのですねぇ……。

IEnumerable<T>とIReadOnlyCollection<T>の差異は実体化されていない「可能性がある」かどうか。で、なのでメソッドの引数などで内部で実体化されてるのを前提にほげもげしたい場合は、IReadOnlyCollection<T>を受け取るほうが望ましいといえば望ましいのですが、汎用的にIEnumerableのままで……という場合は、以下のようなメソッドを用意しとくといいでしょう。

/// <summary>
/// sourceが遅延状態の場合、実体化して返し、既に実体化されている場合は何もせずそれ自身を返します。
/// </summary>
/// <param name="source">対象のシーケンス。</param>
/// <param name="nullToEmpty">trueの場合、sourceがnull時は空シーケンスを返します。falseの場合はArgumentNullExceptionを吐きます。</param>
public static IEnumerable<T> Materialize<T>(this IEnumerable<T> source, bool nullToEmpty = true)
{
    if (nullToEmpty && source == null)
    {
        return Enumerable.Empty<T>();
    }
    else
    {
        if (source == null) throw new ArgumentNullException("sourceがnullです");
    }
 
    if (source is ICollection<T>)
    {
        return source;
    }
    if (source is IReadOnlyCollection<T>)
    {
        return source;
    }
 
    return source.ToArray();
}

こんなのを作って、冒頭で呼べば、二度読みなどもOKに。

public static void Hoge<T>(IEnumerable<T> source)
{
    source = source.Materialize(); // ここで実体化する
 
    // あとは好きに書けばいいのではないでせうか
}

どうでしょ。また、二度読みなら列挙したらキャッシュして、再度読む時はそっから読んでくれればいいのに!というリクエストあるかと思います。それは一般的にはメモ化(Memoization)といいます。というわけで、シーケンスに実装してみましょう。

public static IEnumerable<T> Memoize<T>(this IEnumerable<T> source)
{
    if (source == null) throw new ArgumentNullException("sourceがnull");
    return new MemoizedEnumerable<T>(source);
}
 
class MemoizedEnumerable<T> : IEnumerable<T>, IDisposable
{
    readonly IEnumerable<T> source;
    readonly List<T> cache = new List<T>();
    bool cacheComplete = false;
    IEnumerator<T> enumerator;
 
    public MemoizedEnumerable(IEnumerable<T> source)
    {
        this.source = source;
    }
 
    public IEnumerator<T> GetEnumerator()
    {
        if (enumerator == null) enumerator = source.GetEnumerator();
        return new Enumerator(this);
    }
 
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
 
    public void Dispose()
    {
        if (enumerator != null) enumerator.Dispose();
    }
 
    class Enumerator : IEnumerator<T>
    {
        readonly MemoizedEnumerable<T> enumerable;
        int index = 0;
 
        public Enumerator(MemoizedEnumerable<T> enumerable)
        {
            this.enumerable = enumerable;
        }
 
        public T Current { get; private set; }
 
        public void Dispose()
        {
 
        }
 
        object System.Collections.IEnumerator.Current
        {
            get { return Current; }
        }
 
        public bool MoveNext()
        {
            if (index < enumerable.cache.Count)
            {
                Current = enumerable.cache[index];
                index++;
                return true;
            }
 
            if (enumerable.cacheComplete) return false;
 
            if (enumerable.enumerator.MoveNext())
            {
                Current = enumerable.enumerator.Current;
                enumerable.cache.Add(Current);
                index++;
                return true;
            }
 
            enumerable.cacheComplete = true;
            enumerable.enumerator.Dispose();
            return false;
        }
 
        public void Reset()
        {
            throw new NotSupportedException("Resetは産廃");
        }
    }
}

こうしておけば、

// hoge:xが出力されるのは1回だけ
var seq = Enumerable.Range(1, 5)
    .Select(x =>
    {
        Console.WriteLine("hoge:" + x);
        return x;
    })
    .Memoize();
 
// なんど
foreach (var item in seq.Zip(seq, (x, y) => new { x, y }).Take(4))
{
    Console.WriteLine(item);
}
 
// ぐるぐるしても一度だけ
foreach (var item in seq.Zip(seq, (x, y) => new { x, y }))
{
    Console.WriteLine(item);
}

といった感じ。Materializeより合理的といえば合理的だし、そうでないといえばそうでない感じです。私はMaterializeのほうが好み。というのもMemoizeは完了していないEnumeratorを保持しなければいけない関係上、Disposeの扱いがビミョーなんですよ、そこが結構引っかかるので。

あと、IEnumerable<T>ですが、スレッドセーフではない。そう、IEnumerable<T>にはスレッドセーフの保証は実はない。というのを逆手に取ってる(まぁ、それはあんまりなので気になる人はlockかけたりしましょう)。ちなみにReadOnlyCollectionだってラップ元のシーケンスが変更されたらスレッドセーフじゃない。そして、スレッドセーフ性が完璧に保証されているのがImmutable Collections。という話につながったりつながらなかったり。

Immutable Collections

Immutable Collectionsは実装状況が.NET Framework Blogで随時触れられていて、リリース時のImmutable collections ready for prime timeを読めば、なんなのかっては分かるのではかと。その上で私が今回で割と酸っぱく言いたかったのは、ReadOnly「ではない」ってことです。そして結論はアリキタリに使い分けよう、という話でした。

セッション後の話とかTwitterで、バージョニングされたコレクションって捉えるといいんじゃないの?と意見頂いたのですが、なるほどしっくりきそうです。

スピーカー予定

今後ですが、大阪です!12/14、第3回 LINQ勉強会で発表する予定なので、関西圏の人は是非是非どうぞ。セッションタイトルは「An Internal of LINQ to Objects」を予定しています。これを聞けばLINQ to ObjectsのDeep Diveの部分は全部OK、といった内容にするつもりです。もう初心者向けってこともないので、完全に上級者がターゲットで。

asyncの落とし穴Part3, async voidを避けるべき100億の理由

だいぶ前から時間経ってしまいましたが、非同期の落とし穴シリーズPart3。ちなみにまだ沢山ネタはあるんだから!どこいっても非同期は死にますからね!

async void vs async Task

自分で書く場合は、必ずasync Taskで書くべき、というのは非同期のベストプラクティスで散々言われていることなのですけれど、理由としては、まず、voidだと、終了を待てないから。voidだと、その中の処理が軽かろうと重かろうと、終了を感知できない。例外が発生しても分からない。投げっぱなし。これがTaskになっていれば、awaitで終了待ちできる。例外を受け取ることができる。await Task.WhenAllで複数同時に走らせたのを待つことができる。はい、async Taskで書かない理由のほうがない。

んじゃあ何でasync voidが存在するかというと、イベントがvoidだから。はい。button_clickとか非同期対応させるにはvoidしかない。それだけです。なので、自分で書く時は必ずasync Taskで。async voidにするのはイベントだけ。これ絶対。

ASP.NET + async voidで死ぬ

それでもasync voidをうっかり使っちゃうとどうなるでしょう?終了を待てないだけとか、そんなんならいいんですよ、でも、ASP.NETでasync void使うと死にます。文字通りに死にます。アプリケーションが。じゃあ、ASP.NET MVCで試してみましょうか。WebForms?しらね。

public async void Sinu()
{
    await Task.Delay(TimeSpan.FromSeconds(1));
}
 
public ActionResult Index()
{
    Sinu();
 
    return View();
}

死にました!警告一切ないのに!って、ああ、そうですね、async=”true”にしないとですね、まぁそれはないのですけれど、はい、Task<ActionResult>を返しましょう。そうすればいいんでしょ?

public async void Sinu()
{
    await Task.Delay(TimeSpan.FromSeconds(1));
}
 
public async Task<ActionResult> Index()
{
    Sinu();
 
    return View();
}

はい、死にました!非同期操作が保留中に非同期のモジュールとハンドラーが完了しちゃいましたか、しょーがないですね。しょーがない、のか……?

で、これの性質の悪いところは、メソッド呼び出しの中に一個でもasync voidがあると詰みます。

// こんなクソクラスがあるとして
public class KusoClass
{
    public async void Sinu()
    {
        await Task.Delay(TimeSpan.FromSeconds(1)); // 1じゃなく5ね。
    }
}
 
// この一見大丈夫そうなメソッドを
public async Task Suteki()
{
    // ここでは大丈夫
    await Task.Delay(TimeSpan.FromSeconds(1));
 
    // これを実行したことにより……
    new KusoClass().Sinu();
 
    // ここも実行されるし
    await Task.Delay(TimeSpan.FromSeconds(1));
}
 
// このアクションから呼び出してみると
public async Task<ActionResult> Index()
{
    // これを呼び出してちゃんと待機して
    await Suteki();
 
    // ここも実行されるのだけれど
    await Task.Delay(TimeSpan.FromSeconds(1));
 
    return View();
}

死にます。ただし、上で5秒待機を1秒待機に変えれば、動きます。なぜかというと、KusoClass.Sinuを実行のあとに2秒待機があってViewを返してるので、Viewを返すまでの間にKusoClass.Sinuの実行が完了するから。そう、View返すまでに完了してればセーフ。してなければ死亡。まあ、ようするに、死ぬってことですね結局やっぱり。何故かたまに死ぬ、とかいう状況に陥ると、むしろ検出しづらくて厄介極まりないので、死ぬなら潔く死ねって感じ。検出されずそのまま本番環境に投下されてしまったら……!あ、やった人がいるとかいないとかいるらしい気がしますが気のせい。

呼び出し階層の奥底にasync voidが眠ってたら死ぬとか、どーせいという話です。どーにもならんです。なので、共通ライブラリとか絶対async void使っちゃダメ。あるだけで死んでしまうのですから。

FireAndForget

さて、投げっぱなしの非同期メソッドを使いたい場合、どうすればいいんでしょう?

public async Task ToaruAsyncMethod()
{
    await Task.Delay(TimeSpan.FromSeconds(1));
    Debug.WriteLine("hoge");
}
 
public async Task<ActionResult> Index()
{
    // 待機することなく投げっぱなしにしたいのだけど警告が出る!
    ToaruAsyncMethod();
 
    return View();
}

あー、警告ウザす警告ウザす。その場合、しょうがなく変数で受けたりします。

public async Task<ActionResult> Index()
{
    // 警告抑制のため
    var _ = ToaruAsyncMethod();
 
    return View();
}

はたしてそれでいーのか。いやよくない。それに、やっぱこれだと例外発生した時に捉えられないですしね。TaskScheduler.UnobservedTaskExceptionに登録しておけば大丈夫ですけれど(&これは登録必須ですが!)。というわけで、以下の様なものを用意しましょう。

// こんな拡張メソッドを用意すると
public static class TaskEx
{
    // ロガーはここではNLogを使うとします
    private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
 
    /// <summary>
    /// 投げっぱなしにする場合は、これを呼ぶことでコンパイラの警告の抑制と、例外発生時のロギングを行います。
    /// </summary>
    public static void FireAndForget(this Task task)
    {
        task.ContinueWith(x => 
        {
            logger.ErrorException("TaskUnhandled", x.Exception);
        }, TaskContinuationOptions.OnlyOnFaulted);
    }
}
 
// こんな投げっぱなしにしたい非同期メソッドを呼んでも
public async Task ToaruAsyncMethod()
{
    await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
    Debug.WriteLine("hoge");
    throw new Exception();
}
 
public ActionResult Index()
{
    ToaruAsyncMethod().FireAndForget(); // こうすれば警告抑制&例外ロギングができる
 
    return View();
}

いいんじゃないでしょうか?

ところで、ToaruAsyncMethodに.ConfigureAwait(false)をつけてるのは理由があって、これつけないと死にます。理由は、覚えてますか?asyncの落とし穴Part2, SynchronizationContextの向こう側に書きましたが、リクエストが終了してHttpContextが消滅した状態でawaitが完了するとNullReferenceExceptionが発生するためです。

そして、これで発生するNullReferenceExceptionは、FireAndForget拡張メソッドを「通りません」。こうなると、例外が発生したことはUnobservedTaskExceptionでしか観測できないし、しかも、そうなるとスタックトレースも死んでいるため、どこで発生したのか全く分かりません。Oh…。

たとえFireAndForgetで警告が抑制できたとしても、非同期の投げっぱなしは細心の注意を払って、呼び出しているメソッド階層の奥底まで大丈夫であるという状態が確認できていて、ようやく使うことができるのです。うげぇぇ。それを考えると、ちゃんと警告してくれるのはありがたいね、って思うのでした。

まとめ

voidはまぢで死ぬ。投げっぱなしも基本死ぬ。

では何故、我々は非同期を多用するのか。それはハイパフォーマンスの実現には欠かせないからです。それだけじゃなく、asyncでしか実現できないイディオムも山のようにあるので。いや、こんなの全然マシですよ、大袈裟に書きましたがasync void使わないとか当たり前なのでそこ守れば大丈夫なんですよ(棒)。じゃあ何でasync voidなんてあるんだよとか言われると、イベント機構があるからしょうがないじゃん(ボソボソ)、とか悲しい顔にならざるを得ない。

というわけで、弊社は非同期でゴリゴリ地雷踏みたいエンジニアを大募集してます。ほんと。

Comment (1)

Mike He : (02/02 14:27)

Nice one. It’s only useful to handle events.

Trackback(0)
Write Comment

謎社が一周年を迎えました。

まあ、迎えたのは9/19なので、10日以上経っちゃってるんですが。ほげ。というわけかで、謎社あらため株式会社グラニは、設立一年を迎えました。前職を退職したのが10/20なので、私にとっては一年、まだ経ってません。はい。今の役職は取締役CTOなのですが、実は設立時には居なかったんですねえ。ジョインしたのは若干遅れてます。その間シンガポールにいたりほげもげ……。

ともあれ今は、当面は地下に潜伏していますが、必ず浮上しますのでしばしお待ちくだしあ。
gloopsを退職しました。 - 2012/10/20

なんで謎社かというと、退職後から表に出るまでは、ちょっとだけ内緒ということで、その間Twitterでずっと謎社って言ってたのが残ってるだけですね。Twilog @neuecc/謎社 古い順。googleで謎社で検索しても一位がグラニになってたりするので、それはそれで何となく定着してるのでヨシとしませうか。

一年の成果

謎々潜伏期間中に思い描いてたこと、あります。

C#といったら謎社!みたいな、C#を使う人が憧れるぐらいな立ち位置の会社にできればいいなと思っています
2012年を振り返る。 - 2012/12/30

これには、まず、会社が成功してなきゃダメです。その点でいうと、最初のタイトル(1/25に出しました)神獄のヴァルハラゲートは大躍進を遂げ、3度のテレビCM(今も放送してます)、GREE Platform Award2013年上半期総合大賞受賞など、業界では2013年を代表するタイトルとなれたと思います。

というわけで、会社は成功した(勿論、まだまだこれから更に発展していきますよ!)。技術的にはどうでしょう。実は最初のリリース時はPHPだったのですが、これは7月にC#に完全リプレースしていて、今は100% C#, ASP.NET MVCで動いています。技術に関しては、一部はリリース前にBuild Insider Offlineというイベントで.NET最先端技術によるハイパフォーマンスウェブアプリケーションとして発表しましたが(.NET系にしては珍しくはてブ300超えて割とヒット)、使用テクノロジ・アーキテクチャに関しては、間違いなく最先端を走っていると思います。エクストリームWebFormsやエクストリームDataSetに比べると、ちゃんと技術を外に語れるのがいいですにぇ。

また、.NETでのフルAWS環境で超高トラフィックを捌いているのですが、これは結構珍しいところかもです。.NETというだけじゃなく、この業界だと、データベースはFusion-ioのようなハイパーなドライブを詰んだオンプレミス環境であることも多いのですが、Fusion-ioは甘え、クラウドでも十分やれる。むしろこれからはそれがスタンダード。完全クラウドでやれる、という証明をしていく、というわけでAWS ゲーム業界事例 株式会社グラニ様などでも紹介されています。

NewRelicSumo Logicなど、日本では(特に.NETでは)マイナーなサービスでも、良いと思ったら柔軟にガンガン導入していっています。特にSumoLogicはWindows+日本語環境だと文字化けとかもありましたが、弊社からのフィードバックで解消していっているなど(つまりうち以外誰も使ってないのかいな……)我々が次代のスタンダードを作っていく、という気概でやっていってます。

と、たった一年の企業にしては相当やったと思うのですが、しかし、「憧れるぐらいな立ち位置」には、まだまだ全然。土台は出来たと思うので、ここからはしっかり発展させていかなきゃな、と。

We’re Hiring

というわけで、何を言いたいかというとコレです(笑)。超採用中です。グラニ/採用情報が、非常に古臭いページで、しかもmailtoでしか応募できないというハードルの高さでアレなのですが、かなり!真面目に!募集してます。ページはそのうちまともになるので、むしろ応募人数が少ないmailtoのうちのほうが採用確立高いかもですよ!?

現在どのぐらい人数がいるかというと、会社全体で既に50人ぐらい、エンジニアも20人弱います。小規模な会社、というフェーズは超えてます。会社自体も↑のように割と成功しているので、色々とは安心してください。

開発環境はかなり充実していて、トリプルディスプレイが出力できない開発PCなんて許さん!とかショボい椅子は嫌だ!とかWindows 8じゃなきゃ嫌だ!とか当然VS2012!Fakesの使えないVisual StudioなんてありえないからPremium以上!とか、こんなにやれてる会社は中々ないでしょう。

コードは、つい7月にリリースしたものがソースコードの全てで過去の遺産が一切ない状態なので、100%、C# 5.0 + .NET 4.5 + ASP.NET MVC 4という、最先端のフレームワークが存分に利用できます。これは、常にアップデートしていく、という意思が固いので、今後も古いもので書かなきゃいけない……みたいな状況は絶対作りません。これはもう宣言。誓って。

技術的にも凄まじいasync祭り(Webでここまでやってるのは世界でも稀でしょう)とか、良くも悪くも先端を突っ走るし地雷は自分で踏んで自分で処理して、「我々が道を作る」覚悟で、技術的に業界をリードする会社であろうとしています。そうじゃなきゃ「C#を使う人が憧れるぐらいな立ち位置」にはなれませんから。なので、技術的な発信に関しては、私に限らず、皆がアクティブに行っていきたいと思っています。なお、私含めてMicrosoft MVPは3人在籍しています。

C#といったら謎社にする。といった気概のある方は、是非とも応募してみて下さい。らんぷの巣窟にC#で殴りこみをかけれるとか謎社にしか出来ない面白いポジションですし、.NET世界に篭もらずに、C#を業界のスタンダードへと導けるのは我々だけ!というぐらいな勢いがありますよ。

(注意:但し、我々はサービスを提供している会社です。技術あってのサービス、サービスあっての技術。両輪なので、多少の偏りはいいんですが、片方がゼロの場合は良い物は作れないので、お断るかもしれません)

とかなんとかだと、ハードル高すぎ、な感がするかもですが、そんなにそんなでもないので、気になるなぁと思った人は現時点での何らかの懸念(技術的に、とかスキルセットが合わないかも、とか)は抜きにして、来てもらえると嬉しいですね。ウェブ系以外でも全然OKですし。C#が全てに通用することを現実世界での成功でもって証明する!ことも掲げているので、ウェブ以外であっても、アリアリなのです。

Comment (1)

misaxi : (01/31 15:01)

Hi neuecc,

Congrats to your new venture. DIdn’t know that you quit the previous job. ;) Looks like you enjoyed the new position very much. So I just stop by to say hi.

Trackback(0)
Write Comment

Prev |

Search/Archive

Category

Profile


Yoshifumi Kawai
Microsoft MVP for Visual C#

April 2011
|
March 2015

Twitter:@neuecc