連載:C# 4入門

第7回 DynamicObjectを継承したダイナミックなクラス

株式会社ピーデー 川俣 晶
2011/01/14
Page1 Page2 Page3

クラウド・クラウド・クラウド

 今回は話のまくらにクラウドの話を書いてみよう。

 現在、筆者は、Windows Azure(以下、Azure)のアプリケーション開発を行っている。従って、一応、Azure開発のプロ……といいたいが、実はそうもいいきれない。なぜなら、Azure開発の対象になっているのは、自社用にほそぼそと開発しているアプリだけで、仕事としてAzureに関連した話が来たことはほとんどないからだ。

 日本国内の状況を見てもAzureに関する注目がほとんどないことは、愕然(がくぜん)とするばかりだ。Azure開発をやっていて頼りになるのも英語の情報ばかりだ。日本語で質問しても、返ってくる参考URLは英語だったりする。

 実際のAzure開発の証拠として、私が書いている「Cloud3」というソフトのテスト版は以下で公開されている。

 このオータムガマジンは(“マガジン”ではないことに注意)、Cloud3そのものの開発記録を書いていて、そこにはAzure開発で役立つヒントもかなり入っているはずだが、ほとんど読まれていない。Cloud3も、タダでブログ作成を行う機能が(不完全ながら)公開されているのだが、それを実際に使って「話題のAzureとはどの程度のものか」と興味を持つ人もほとんどいない。

 「これでいいのか」と思うが、現状はそんなものである。公開していると課金されるから、本当はテストのときだけ動かして、後は止めておく方が安上がりだが、それでは読む人の都合が悪いと思って常時動かしているのだが、それもあまり生かされていない感じだ。「Windows Azureに関して原稿を書いてください」という依頼もほとんど来ない。

 しかし、その理由の一端が秋ごろからやっと分かってきた。

 以前に、とあるクラウド関連のイベントに行っておどろいたことがあった。クラウドのイベントなのに「SaaSゾーン」という一角があったのだ。

 筆者がなぜ驚くのか分からない読者も多いはずなので説明しよう。「クラウド」とは比較的新しい言葉である。新しい言葉である以上、何か新しいことを主張しているはずだと思っていた。決して、それ以前からある内容ではないと思っていた。一方で、SaaSとは「Software as a Service」の略で、機能をソフトではなくサービスとして提供する形で利用するものだ。つまり、パッケージを買ってインストールする代わりに、Webアプリケーションを利用する形態だ。え? そんなの知っている? 私も知っている。みんな知っている。当たり前だ。このSaaSという形態はかなり古くからある。何ら目新しくない。従来のASP(アプリケーション・サービス・プロバイダ)と区別する場合もあるが、本質的にはASPとSaaSに違いはない。とすれば、それが「新しい何かを示す言葉であるはずのクラウド」の一部であるはずがないと漠然と思い込んでいた。しかし、実際はそうではなかったのである。うかつなことに、Azure開発に専念して周囲を見ていなかった筆者は、そのイベントに行くまで気付かなかったのである。

 以前、@IT上でWeb関連の注目サイトを毎月紹介するAjaxの連載を書いていたことがある。そこでの目玉の1つが、要するにSaaSだった。つまり、とっくに終わった連載のかつての目玉に過ぎない、あまりにも当たり前の話が大きな比率を持って取り上げられていたのである。もちろん、「クラウドをプラットフォームにしてSaaSを開発する」という話はあり得るが、既存のSaaSと違ってクラウド(PaaS/IaaS)を使うメリットが強調されているという雰囲気ではなかった。というよりも、プラットフォームが既存のサーバ環境かクラウドかの区別もほとんど主張されていなかった。

 つまり、「クラウドでも何でもないものを何でもクラウドと呼ぶ」という批判をよくネットで見かけていたが、その批判は、ある意味で間違いではなかったわけだ。逆にいえば、「SaaSもクラウドのうち」とするなら、クラウドは何ら目新しくないということになり、誰も注目しない。

 しかし、誰も注目しないというのは、恐らく誤りだろう。

 それはなぜか。

 Azure開発に体当たりで取り組んで見えてきたのは、既存技術では対応できない新しい領域に踏み出す入り口が確かにあるということだ。日本のクラウドの世界では見えてこない状況かもしれないが、確かにそれはあるし、それは必要だ。

 では、その新しい領域とは何か。

 ずばり「スケールアウト」である。インスタンスの契約数を増やすだけで、即座に設備を増強できる能力だ。

 しかし、これはいうほど楽ではない。静的なWebページをサービスするだけなら難しくはない。単にサービスを行う仮想マシンをコピーして複製も立ち上げるだけでよい。これで、ロード・バランサが適切にリクエストを旧仮想マシンと新仮想マシンに振り分けてくれる。いくらでも簡単に台数を増やせる。

 しかし、動的なWebアプリとなると、これほど話は簡単ではない。なぜなら、別仮想マシンがいくつ立ち上がっても協調して動かねばならないからだ。この問題は、サービスを小さく立ち上げて大きく育てる場合には、大きな問題として立ちふさがる。同じアプリでは処理をさばけなくなるからだ。台数を増やしてもすぐ全体の整合性が崩れてしまう。次にアクセスしたとき、ロード・バランサがそのアクセスを別のサーバに割り当て、前回書き込んだはずのデータが見つからなければ、ユーザーはデータが消えたと怒るだろう。だから最初から、「スケールアウト」を意識したプログラムを書くことは重要なポイントになる。それ抜きではスムーズに小さなシステムを大きく育てられない。つまり、疎結合の分散処理を最初から意識して作る必要があるということである。

 では、その場合のポイントは何か。

 ずばりストレージである。

 メモリやファイルというストレージはあまり役に立たない。複数のインスタンス間で共有が難しいからである。RDB(リレーショナル・データベース)でも十分ではない。RDBのスケールアウト能力には限界があるからだ。

 従って、アメリカの情報を主に英語でたどっていくと、AzureもライバルのAmazon EC2などの多くも、すべて独自のストレージ機能を備えている。構造はそれぞれだが、Azureの場合、リレーショナルな構造ではない別の構造を持つそのものズバリの「テーブル・ストレージ」と呼ばれるキー/バリュー型のデータベースである(XMLデータベースでもない。ストレージには、テーブル・ストレージのほか、ファイル・システムに近いブロブ・ストレージや、キュー用のデータ構造に特化したキュー・ストレージがある。以降、これらをまとめて「ストレージ」と呼ぶ)。実は、このストレージをいかに活用するかが、ほぼクラウドのすべてといってよい。

 後はほとんど既存技術である。もしかしたら、デバッグが容易ではないリモートの世界が難しいと感じる読者もいるかもしれないが、デバッグ環境が貧弱なシステムなど昔から珍しくないので対処方法もある(ログを残す、printfデバッグを行う、リモートではなくローカルのエミュレーション環境で動作を追いかける、など)。やはり問題はストレージに絞られる感がある。

 しかし、クラウド開発を行っていれば誰でも「ストレージが鍵になること」が分かっているのかといえば、それも怪しい。Azureでは、(ベータ版ではあるが)VM(仮想マシン)ロールという強力な機能が最近利用可能になった(※申請しても利用可能になるまでには少し時間がかかる)。VMロールを使えば、そのロール・インスタンス内のローカル環境でSQL Serverやファイル・システムを自由に使える。実際にVMロールを使って最小の手間でアプリケーションを移行することもできるが、その場合、スケールアウト性能はどうしても低めになってしまうので、すぐに限界に達してしまう(かもしれない)。VMロールでクラウドを使ったと思うことはできるが、まだまだである。

 VMロールを安易に使うと、痛い目に遭う(可能性がある)。確かにVMロールを使えば何でもできるとはいえ、実はVMロール(のローカル環境)上で設定した内容や保存したファイルはロール・インスタンスが再起動されると消えてしまうのである。つまり「状態」は、個々のロールで持ってはいけないということだ。「状態」はすべてAzureのストレージ上に保存される必要がある。このあたりも単一のサーバで動かすASP.NETのWebアプリなどとは大きく異なる点である。

 そして、はたと気付いたのだが、以前C#の本に書いた「ステート集約プログラミング」と思想は同じである。「変化するステート(=状態)」を1カ所に集約すると、いろいろよいメリットがあるわけである。

 もう1つ、Azureのストレージにはスキーマが存在しない。ブロブとキューに何でも入るのはもちろんだが、テーブルにもスキーマがない(何でも自由に入れられるとはいえないが、昔からスキーマレスがデータベースとして必要で重要な資質という主張と相通じるものがあって、おどろいた)。

 そのようなわけで、あえていおう。

 日本でナンバーワンのクラウド使いという気はない(上には上がいるものである)。しかし、もしかしたら日本で1番クラウドと相性がよいのは筆者かもしれない(しかし、SaaSという古い話をクラウドだと思って納得している人たちには分からない話だろう)。

 さて、この話は今回のテーマとは無縁ではない。なぜなら、常に扱うデータが最初から分かっているわけではない、という話をするからだ。データベースでスキーマが最初から書けないことは珍しくないのと同じように、最初からクラスを書けないことも珍しくないからだ。

状態を保管する

 まず、あるC#プログラムで、A、B、Cという3つの情報を保存する必要があるとしよう。

 これを記述するのは簡単だ。適当なクラス(リスト1ではDataStore)を作成し、そこにデータを保存するメンバやプロパティ(A、B、C)を記述すればいい。このコードには含まれていないが、データを永続化したければ、クラスをシリアライズするだけでよい。

using System;

static class Work
{
  internal static void DoIt()
  {
    DataStore.A = DataStore.B + DataStore.C;
  }
}

static class DataStore
{
  internal static int A;
  internal static int B;
  internal static int C;
}

class Program
{
  static void Main(string[] args)
  {
    DataStore.B = 1;
    DataStore.C = 2;
    Work.DoIt();
    Console.WriteLine(DataStore.A);
  }
}
リスト1

3
リスト1の実行結果

 ところが、この話は1つの条件を付け加えると急に難しくなる。

  • DoItメソッドを別アセンブリに切り分けたい

 実行ファイルはDoItメソッドを含むアセンブリを参照したいが、DoItメソッドを含むアセンブリはDataStoreクラスを必要とするので、それを含むアセンブリを参照したい。解決できない循環参照が生じてしまうのである。

 これを解決する方法はいくつかある。

 一番簡単なのは、DataStoreクラスも切り出して、これも独立したアセンブリにしてしまうことだ。そして、実行ファイルとDoItメソッドを含むアセンブリの双方からこれを参照すればいい。

 しかし、そこまで行くと別の欲求が出てくる。

  • 単なるモジュール分割では終わりたくない。プラグインで拡張できるアーキテクチャとして整備したい

 そこで出てくるのは別の欲求である。

  • DataStoreクラスに任意の値を入れられるようにしたい(将来追加されるプラグインが扱う情報も入れられるように)

 これは難しくない。名前を付けてデータを格納するだけならDictionaryクラスを使えばよい。実際に書き直してみよう。

using System;
using System.Collections.Generic;

public static class Work
{
  public static void DoIt()
  {
    DataStore.Data["A"]
            = (int)DataStore.Data["B"] + (int)DataStore.Data["C"];
  }
}

public static class DataStore
{
  public static Dictionary<string, object> Data
                            = new Dictionary<string, object>();
}

class Program
{
  static void Main(string[] args)
  {
    DataStore.Data["B"] = 1;
    DataStore.Data["C"] = 2;
    Work.DoIt();

    Console.WriteLine(DataStore.Data["A"]);
  }
}
リスト2

 これで、めでたしめでたしであるが、このコードで納得できるかは別の問題である。DoItメソッドの中身を比較してみよう。

DataStore.A = DataStore.B + DataStore.C;
DataStore.Data["A"]
      = (int)DataStore.Data["B"] + (int)DataStore.Data["C"];

 これほど記述量が増え、しかもキャストまで入ってあんまりだ……と思うなら、これはよい書き換えとはいえない。もっとマシな書き換えはないのだろうか。

 

 INDEX
  C# 4入門
  第7回 DynamicObjectを継承したダイナミックなクラス
  1.クラウド・クラウド・クラウド/状態を保管する/状態を保管する
    2.ダイナミックに解決せよ!/この機能は便利か?/動的なメソッド
    3.動的な演算子/まとめ
 
インデックス・ページヘ  「C# 4入門」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間