こちらはすっかり冬景色の札幌からお送り致しますww
どうでも良い前フリ
http://www.flickr.com/photos/trasroid/3108074700/ |
クリスマスの季節には雪で真っ白な札幌に生まれ育った自分にとって、このサンタクロースはリアルに感じられておりました。
クリスマスが近づいても雪が少なかったりすると、そりが滑らないんじゃないか?とマジで心配したり、自宅の煙突はきちんと掃除されているか気になったり...(笑)
神奈川に住んで、東京で仕事を20年やってきましたけど、関東のクリスマスって雪も降らないし空っ風ピュ〜だから、子供達は皆どう思っているのか、長年の疑問でしたww
うちの息子もいったいどう思っていたのか・・?彼が大人になったら聞いてみたいと思います(笑)
ネタはSWFです
さて、このAWS Advent
Calendarは執筆陣が豪華なので、このメンツの中でいったい何を書けば良いのやら、結構悩みました。JAWS-UG札幌支部から代表?として書くからには、あまり下手な事はかけないし、さらに日程をAWSの荒木さんに譲って頂いたので、これはガッツリ「ディープな話題」で行くしか無いとwww先日のラスベガスで日本初のCDP(クラウドデザインパターン)を世界に対して発信されたAWSの片山さんが、すでにスベってるタイトルでSWFを取り上げるらしいのですが、楽しく正確な記事はそちらを見て頂くとして、私は以前書いたFlow Frameworkをさらに掘り下げることをやってみたいと思います。
なので、前回の記事を読んでいない方はそちらを先に読んでください!じゃないと、今から書く記事の意味が分からないと思いますwww
あと、コードはgithubに置いてありますので、そちらもどうぞ!
失敗を恐れるべからず
SWFで処理するワークフローはこのような感じでした。 - だれかがS3へ画像をアップロードする
- S3から画像をダウンロードする
- モノクロに変換する
- 処理完了をメールでお知らせする
ワークフローの途中で処理に失敗した場合、その先の処理は当然出来ないのできちんとワークフローを中断させる必要があります。
また、処理が失敗した事をログに書いたり、管理者にお知らせしたくなってますよね?
こういう面倒な事をスッキリ実現出来るクラスがFlow Frameworkに用意されています。では実際のコードを書いてみましょう。
話を簡単にする為に、S3からのアップロード、ダウンロードに失敗する事を想定してみます。もとのActivityのコードはこんな感じでした。 これらのメソッドに対して、きちんと例外をスローするように変更しましょう。
想定されるエラーを分析すべし!
もともとAWS SDKではAPIの呼び出しでエラーが発生した場合、AmazonClientExceptionのサブクラスが投げられます。これはRuntimeExceptionを継承したクラスなので、try/catch節を書かなくて済む、という利点がありますが、ついつい忘れがちなので注意が必要です。また、実装クラスの中ではローカルにテンポラリファイル作ったり、ストリームを読み書きしている部分があります。これもIOExceptionが投げられる可能性があります。この二種類の例外は性質が全く異なるものですので、一緒くたにせずにきちんと分けて処理する事にします。その為に、オレオレExceptionを定義しておきます。(これが後で効いてきますよ!)
この例外をスローするように、アクティビティの実装を改良してみましょう。
エラーは謙虚に受け止めた上で、雲になげてしまえ!
さて、WorkflowImplの方ですが、普通に考えたらスローされる例外をJavaのtry/catch/finally節で処理するでしょう。そのようなコードは一見問題なさそうに見えますが、忘れちゃならないのが「非同期実行される」という事実です。各アクティビティをわざわざメソッドに切り出して、@Asynchronousアノテーションを付けている事を忘れないでくださいwSDKではTryCatchFinallyクラスが用意されています。実装のやり方は簡単。Javaのtry/catch/finally節を書くのと同じ要領です。
doCatch()の中では、ログを書いて、管理者にメールを送って、例外を再スローしています。
doFinally()の中では今回は特に何もしていませんが、後始末などが必要であればここで行います。
ちなみに、スローされた例外を握りつぶしてしまう(再スローしない)と、SWFから見た場合そのアクティビティの実行がうまく行ったと判断されてしまいます。(タイムアウトなどを除いて)
例えば、アップロードに失敗したのに、ダウンロードがうまく行くはずがありませんが、前段のアクティビティがエラーになってしまった、という事実をSWFが把握出来れば、次のアクティビティの実行をきちんとキャンセルしてくれます。ですので、我々は例外に対して何も心配する必要はありません。
起きてしまったエラーはそれはそれとして心の中で事実として受け止め、大いに反省し、次のステップへの大いなる啓示として前向きにとらえた上で、えいやっ!とその例外をもう一度クラウドの中に放り投げましょう。
心広きSWFは、その邪悪なワークフローをしっかりと止める事が出来ます。
失敗したって、もう一度やってみればイイじゃないか。
さて、例外処理が出来て一安心ですが、実際に問題が起きた時に、そのリカバリーを誰がするのか?という事になります。SWFにはRe-Run(ワークフローの再実行)という機能があるので、それを手動(やそれに近い方法)で行う事も可能ですけど、今イチあか抜けないですよね。そういう貴方にFlow Frameworkが提供するのは、Exponential Retryです。つまり、自動リトライ機能です。早速これを実装してみましょう。Exponential Retryでは、「何秒後にリトライを開始して、それを何回やるか」を設定します。例えば以下のような事が簡単に実現出来ます。
- アクティビティが失敗したら、5秒後にリトライ
- 1回目のリトライに失敗したら、10秒後にリトライ
- 2回目のリトライも失敗したら、今度は20秒後にリトライ
- 3回リトライしてもダメだったら、そのワークフローは終了
簡単すぎて、このスゴさが伝わらないんじゃないか?と若干心配ではありますが(笑)、ここで一旦立ち止まって、深呼吸をして、胸に手を当てて、本当にコレで良いのかよ〜く考えてみてください。
・・・
・・・
・・・
・・・
・・・
オレはこんなにもSWFに頼っていいのか・・・いくらクラウドが無限にスケールする、って言っても、何でもかんでもリトライさせるのは賢くないよね・・・
賢くリトライするべし
download()の実装で考えられる例外は、大きく分けて二つあります。
- S3のAPIエラー
- ローカルファイルシステムのI/Oエラー
S3の例外は、先ほど作ったMyS3Exceptionです。オレオレExceptionをあえて作った理由はここにあります。「リトライするかしないか」と言う事を切り分けたかったからなんです。「あとで効いてきますよ!」というのは、ここの事です。
ちなみに、@ExponentialRetryアノテーションでは粒度が大きすぎる!なんて言う場合は、RetryDecoratorなんてのもあります。これはワークフローの実装の中で、Activityクライアントのリトライポリシーを動的制御したい、なんて時に有用そうです。 さらには、AsyncRetryingExecutorなんていうExecutorもあるようです。
え〜、両方とも使った事無いので、「ある」という事実だけお伝えし、コメントは控えておきますwwww
と言う訳で、複雑になりがちで、かつ信頼性の高いリトライ機能を組み込むのは非常に大変ですが、Flow Frameworkはこのように非常に簡単に組み込む事が可能です。
まとめ
以上、簡単にまとめてみましたが、自前でコレと同じ事を実装するとえらい大変な事になる事は皆さんお分かりでしょう。Flow Frameworkはこのように非常に実践的で、しかも簡単に作れるようになっています。
でも、一点、言っておきたいのが、設計をきちんとしておかなければ、このような有用な機能が全然活かせない、という点です。ここはある意味非常に難しい(笑)インターフェースをきちんと定義しておかないと、あとで「なまらわやくちゃ」になるので頑張りどころです。
これに関しては、中の人の名言がありますので、最後に引用させていただいて、JAWS-UG仙台の小泉さんにバトンを渡したいと思います!
@dateofrock @ar1 Flowは設計しないやつはいらねえ!って感じの漢なフレームワークです
— Shinpei Ohtaniさん (@shot6) 9月 19, 2012
0 件のコメント :
コメントを投稿