Uncategorized

Azure Cloud Service の WebRole で効率的な Timer 処理をする

こんにちは。

昨日は、Web アプリケーションのパフォーマンス改善 (スレッドの有効活用とテスト方法) のハンズオン・ラボにご参加いただき、ありがとうございました。そして、また、失敗してしまいました。申し訳ありません !

すご~く気になったので、今朝調査しました。(気になって、夜も眠れませんでした . . .)

まず、ハンズオン参加者の皆様が昨日アクセスした「重いサービス」は、多くの負荷に耐えられるよう、ASP.NET MVC 2 の手法を使って、下記の通り非同期で記述していました。(というか、前日 気になって、以下の通り書き換えました。そして、すみません、これが仇となりました。)

. . .using System.Threading;. . .public class HomeController : AsyncController{  [AsyncTimeout(45000)]  public void IndexAsync()  {    AsyncManager.OutstandingOperations.Increment();    Timer timer = new Timer(new TimerCallback((s) =>    {      AsyncManager.OutstandingOperations.Decrement();    }), null, 30000, Timeout.Infinite);  }  public ActionResult IndexCompleted()  {    return View();  }}. . .

30 秒待って結果を返すだけの意味のない Timer 処理 (ASP.NET MVC の Action) ですが、これが、Windows Azure Cloud Service の WebRole にホストすると問題を引き起こします。Visual Studio Load Test でテストをすると一目瞭然ですが、頻繁に Async の Timeout エラーが発生します。

Windows Server にホストすると、このようなエラーは発生しませんから、Windows Azure 固有のリソース管理に起因すると思われますが、すみません、明確な理由はよくわかりません。ただ、いろいろ調べてみると、Windows Azure 上における AsyncController と Timer の相性に起因している感じです。(時間がかかっているのではなく、そもそも、Async の Completion メソッドが呼ばれません。)
Windows Azure 上では、どうも、昨日紹介した Task による方法を使ったほうが良さそうです。

そこで、昨日 解説したように、Worker Thread の解放をおこなう方法 (Worker Thread をブロックしない方法) で、Task を使って書き換えます。
IO 処理なら、昨日紹介したように Completion Port による待機が発生するので問題ありませんが、今回は、単に待機 (Wait) する処理なので注意が必要です。例えば、下記のコードは悪い例です。(真似しないでください。これなら、同期で書いても同じです。) Completion Port などで待機をおこなう処理ではないため、結局、Thread は実行中にブロックされてしまいます。

. . .using System.Threading.Tasks;. . .public class HomeController : Controller{  public Task<ActionResult> Index()  {    Task<ActionResult> t =      Task.Factory.StartNew<ActionResult>(() =>      {        System.Threading.Thread.Sleep(30000);        return View();      });    return t;  }}. . .

Task を使って待機する場合は、下記の通り、Task が持っている待機メソッド (今回の場合、Delay) を使って記述します。この方法だと、Worker Thread のブロックはおこなわず、効率的に 30 秒の待機をおこないます。

. . .using System.Threading.Tasks;. . .public class HomeController : Controller{  public Task<ActionResult> Index()  {    Task<ActionResult> t =      Task.Delay(30000).ContinueWith<ActionResult>((t1) =>      {        return View();      });    return t;  }}. . .

昨日のハンズオン・ラボで使ったサイトは修正しましたので、ハンズオン参加者の方は、再度 お試しください。(お手数おかけし、すみません。)

なお、リソース増強に頼りたくないので、依然、Small Size の 1 インスタンス (1 コア) の強気で行かせて頂きます !

ある有名な作家がテレビで、「自分は作家だから、『言葉にできない』 とは表現したくない」と言ってましたが、プログラマーの私としても、「プログラミングこそが、問題を本質的に克服する」と信じつつ。。。
(実際、Cloud Service の Small インスタンスでも Available の Worker Thread が 32767 もあるので、今回のハンズオンは同期処理でも充分なんですけどね。私が納得できません。。。)

 

Categories: Uncategorized

Tagged as: ,

1 reply»

Leave a Reply