MyCSS

ラベル SWF の投稿を表示しています。 すべての投稿を表示
ラベル SWF の投稿を表示しています。 すべての投稿を表示

2012/12/02

Amazon SWF Flow Frameworkのエラー処理について はてなブックマークに追加

AWS Advent Calendar 2日目担当の@dateofrockです。初日は横浜支部の吉田さんが書いておられます。なんでもラスベガスで開催されたAWSのグローバルイベントに参加された後、シアトルのホテルから記事をアップしたとか。うらやましー。

こちらはすっかり冬景色の札幌からお送り致します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で処理するワークフローはこのような感じでした。
  1. だれかがS3へ画像をアップロードする
  2. S3から画像をダウンロードする
  3. モノクロに変換する
  4. 処理完了をメールでお知らせする
これを見ると、危険な場所が沢山ありますよね。例えば、ファイルをアップロード・ダウンロードする訳ですから、ネットワーク障害などで失敗する可能性もあります。また、画像変換処理も危険な香りがしますよね。

ワークフローの途中で処理に失敗した場合、その先の処理は当然出来ないのできちんとワークフローを中断させる必要があります。
また、処理が失敗した事をログに書いたり、管理者にお知らせしたくなってますよね?

こういう面倒な事をスッキリ実現出来るクラスがFlow Frameworkに用意されています。では実際のコードを書いてみましょう。


話を簡単にする為に、S3からのアップロード、ダウンロードに失敗する事を想定してみます。もとのActivityのコードはこんな感じでした。 これらのメソッドに対して、きちんと例外をスローするように変更しましょう。


想定されるエラーを分析すべし!
もともとAWS SDKではAPIの呼び出しでエラーが発生した場合、AmazonClientExceptionのサブクラスが投げられます。これはRuntimeExceptionを継承したクラスなので、try/catch節を書かなくて済む、という利点がありますが、ついつい忘れがちなので注意が必要です。また、実装クラスの中ではローカルにテンポラリファイル作ったり、ストリームを読み書きしている部分があります。これもIOExceptionが投げられる可能性があります。

この二種類の例外は性質が全く異なるものですので、一緒くたにせずにきちんと分けて処理する事にします。その為に、オレオレExceptionを定義しておきます。(これが後で効いてきますよ!


この例外をスローするように、アクティビティの実装を改良してみましょう。



エラーは謙虚に受け止めた上で、雲になげてしまえ!
さて、WorkflowImplの方ですが、普通に考えたらスローされる例外をJavaのtry/catch/finally節で処理するでしょう。そのようなコードは一見問題なさそうに見えますが、忘れちゃならないのが「非同期実行される」という事実です。各アクティビティをわざわざメソッドに切り出して、@Asynchronousアノテーションを付けている事を忘れないでくださいw


SDKではTryCatchFinallyクラスが用意されています。実装のやり方は簡単。Javaのtry/catch/finally節を書くのと同じ要領です。


doCatch()の中では、ログを書いて、管理者にメールを送って、例外を再スローしています。
doFinally()の中では今回は特に何もしていませんが、後始末などが必要であればここで行います。

ちなみに、スローされた例外を握りつぶしてしまう(再スローしない)と、SWFから見た場合そのアクティビティの実行がうまく行ったと判断されてしまいます。(タイムアウトなどを除いて)

例えば、アップロードに失敗したのに、ダウンロードがうまく行くはずがありませんが、前段のアクティビティがエラーになってしまった、という事実をSWFが把握出来れば、次のアクティビティの実行をきちんとキャンセルしてくれます。ですので、我々は例外に対して何も心配する必要はありません。

起きてしまったエラーはそれはそれとして心の中で事実として受け止め、大いに反省し、次のステップへの大いなる啓示として前向きにとらえた上で、えいやっ!とその例外をもう一度クラウドの中に放り投げましょう。

心広きSWFは、その邪悪なワークフローをしっかりと止める事が出来ます。



失敗したって、もう一度やってみればイイじゃないか。
さて、例外処理が出来て一安心ですが、実際に問題が起きた時に、そのリカバリーを誰がするのか?という事になります。SWFにはRe-Run(ワークフローの再実行)という機能があるので、それを手動(やそれに近い方法)で行う事も可能ですけど、今イチあか抜けないですよね。そういう貴方にFlow Frameworkが提供するのは、Exponential Retryです。つまり、自動リトライ機能です。早速これを実装してみましょう。

Exponential Retryでは、「何秒後にリトライを開始して、それを何回やるか」を設定します。例えば以下のような事が簡単に実現出来ます。
  1. アクティビティが失敗したら、5秒後にリトライ
  2. 1回目のリトライに失敗したら、10秒後にリトライ
  3. 2回目のリトライも失敗したら、今度は20秒後にリトライ
  4. 3回リトライしてもダメだったら、そのワークフローは終了
リトライの間隔はExponential(指数的)に倍増します。 実装は非常に簡単。@ExponentialRetryアノテーションを自動リトライさせたいメソッドに加えるだけです。ここでは例として、S3Activitiesのdownload()メソッドに自動リトライをかけてみましょう。


簡単すぎて、このスゴさが伝わらないんじゃないか?と若干心配ではありますが(笑)、ここで一旦立ち止まって、深呼吸をして、胸に手を当てて、本当にコレで良いのかよ〜く考えてみてください。


・・・
・・・
・・・



オレはこんなにもSWFに頼っていいのか・・・いくらクラウドが無限にスケールする、って言っても、何でもかんでもリトライさせるのは賢くないよね・・・


賢くリトライするべし
download()の実装で考えられる例外は、大きく分けて二つあります。
  1. S3のAPIエラー
  2. ローカルファイルシステムのI/Oエラー
2番目の原因がディスク障害だったりすると、これはどう考えてもリトライさせない方が良いですよね。その場合は、S3のAPI例外がスローされた時だけリトライする、という設定をしましょう。exceptionsToRetryという属性を追加します。


S3の例外は、先ほど作ったMyS3Exceptionです。オレオレExceptionをあえて作った理由はここにあります。「リトライするかしないか」と言う事を切り分けたかったからなんです。「あとで効いてきますよ!」というのは、ここの事です。

ちなみに、@ExponentialRetryアノテーションでは粒度が大きすぎる!なんて言う場合は、RetryDecoratorなんてのもあります。これはワークフローの実装の中で、Activityクライアントのリトライポリシーを動的制御したい、なんて時に有用そうです。 さらには、AsyncRetryingExecutorなんていうExecutorもあるようです。

え〜、両方とも使った事無いので、「ある」という事実だけお伝えし、コメントは控えておきますwwww

と言う訳で、複雑になりがちで、かつ信頼性の高いリトライ機能を組み込むのは非常に大変ですが、Flow Frameworkはこのように非常に簡単に組み込む事が可能です。


まとめ
以上、簡単にまとめてみましたが、自前でコレと同じ事を実装するとえらい大変な事になる事は皆さんお分かりでしょう。Flow Frameworkはこのように非常に実践的で、しかも簡単に作れるようになっています。

でも、一点、言っておきたいのが、設計をきちんとしておかなければ、このような有用な機能が全然活かせない、という点です。ここはある意味非常に難しい(笑)インターフェースをきちんと定義しておかないと、あとで「なまらわやくちゃ」になるので頑張りどころです。

これに関しては、中の人の名言がありますので、最後に引用させていただいて、JAWS-UG仙台の小泉さんにバトンを渡したいと思います!




2012/03/15

Amazon SWFつかってみた(ながいよ) はてなブックマークに追加

それは突然にやってきた
AWS界隈ではDynamoDB祭りが開かれているまっただ中の2012年02月22日、いつものように何の前触れも無く、こっそりとManagement Consoleにタブが追加されました。Twitterで「何このタブは?」という情報が拡散してから公式ブログに発表になるという、最近おなじみのパターンですw
SWF?なにAWSでFlashが出来るの?などと一瞬思いましたが、もちろんそんなわけは無く、Simple Workflow Serviceの略でSWS、もとい、SWFでした。あれっw?

ワークフローと聞いて
自分が一番最初にイメージするのは、経費や稟議の申請処理なんかを思いつきますが、Amazon SWFはすでにNASAでも使われていると言う事で、火星への交通費の精算に使われたり、探査機が撮った膨大な写真を処理するのに使われているようです。 (一応言っておくと、前者は冗談です。)
SWFとはなんぞや?と言う事は、他でもイロイロ言われていると思いますので、詳細は割愛させて頂きます(汗)
※と言いつつ、現状AWSの公式ドキュメントくらいしか無いような気がする。

AWS Flow Framework
今回のJava SDKは気合いが入っています。DynamoDBの時もシャレオツなDataMapperがSDKに入っていて「おぉ!」と思ったけれども、SWFの場合はフレームワークが入っていて若干のけぞりました。その名もAWS Flow Frameworkです。

どれどれ、と思ってサンプルコードをダウンロードして手元のEclipseに取り込んだら、いきなりコンパイルできない。理由が、あるべきクラスが無いと言うもの。「これ、きっと中の人が入れ忘れたんじゃね(苦笑)?」などと思って調べてみたら、なんとAPT(Annotation Processing Tool)でクライアントコードが自動生成される事が判明。正直ビビりました(笑)

理解するまで結構時間かかりました(汗)
SWFはその他のAWSサービスと違って、結構複雑です。いや、API自体はシンプルなのは間違いないです。けれども、そもそもワークフローを扱うのですから、それなりに面倒な事になります。
※でも、そういうマンドクサイものを汎用的なAPIを仕立ててくるAWSはさすがです…。

まぁ、そうとはいえ、Flow Frameworkを使えばかなりスッキリしたコードが書ける事が分かりました。ただし、非同期な動きを良く理解しないと、振る舞いが意味不明な感じになります。java.util.concurrent.Future<V>を使ったプログラムを書いた事があれば、結構すんなり受け入れられるかな?とは思います。

そして、出来る事は沢山あります。自分もまだ全貌を把握しきれていません。とりあえず、最初の一発目を動かすのに結構苦労したので、自分がやってみた事を記事にしておきます。

間違い等あれば、ご指摘ください!!

サンプルワークフロー
実際に試すにあたり、こんなワークフローを考えてみました。
  1. だれかがS3へ画像をアップロードする
  2. S3から画像をダウンロードする
  3. モノクロに変換する
  4. 処理完了をメールでお知らせする
まぁ、AWSが提供しているサンプルに激しく近い感じですが、サンプルに適したよい例が思いつかなかったのでご了承くださいw

やったこと

1.まずはSWFドメインを作成する

ドメインが無い事には始まりませんので、Management Consoleから作成します。
Create a new Domainをクリックします。

以下のように設定します。Nameは何でもよいですが、プログラムから参照しますので分かりやすいものがよいでしょう。また、現状では一度登録したドメインが消せないようなので、注意が必要です。(Deprecate(廃止)する事は可能です)



2.SNSにトピックを作成する

今回はメール通知用にAmazon SNSを使う事にします。今回は新たに専用のトピックを作成してみます。Management ConsoleのSNSのタブに移動して、Create New Topicボタンをクリックします。
Topic Nameを入力。Display Nameは日本語が入らないんだなー。
 トピックが出来たら、メールの通知先を加えましょう。Create New Subscriptionをクリック。
 ProtocolにEmailを選択し、メールアドレスを入力。
Subscribeすると、AWSから確認メールが送られて来るので、Confirm subscriptionのURLをブラウザで開きます。
こんな感じで表示されればOKです。 
なお、TopicARNはプログラムから参照する必要があるので控えておきましょう。

3.Eclipseでプロジェクトを作る

Flow Frameworkを使う際、APTを使う設定をしないとどうにもなりませんので、ここはしっかりやる必要があります。

始めはMavenベースでやろうと思ったけど、aws-java-sdk-flow-build-tools.jarがリポジトリに見当たらなかったので、ここは素直にAWS Toolkit for Eclipseを使う事にしました。

Build PathにAWS SDK for Javaが入っている事を確認しましょう。


重要なのが、Annotation ProcessingがEnableになっている事です。

Eclipseのデフォルトでは.apt_generatedというディレクトリに生成されますが、自分の好きなものを指定してもよいかもしれません。ただ、srcには入れない方がよいでしょう。

4.Activityを定義する

ふぅ。ようやく本題に入る事が出来そうです(笑)

最初はワークフロー実行に必要なActivityを定義します。必要なものは以下です。
  • S3へのアップロード
  • S3からのダウンロード
  • 画像モノクロ変換
  • メール通知
ActivityはInterfaceとして定義して、@Activitiesアノテーションを加えるのが決まりです。こんな感じのソースを書きました。


5.Workflowを定義する

WorkflowもInterfaceとして定義して、@Workflowアノテーションを加えます。


6.Activityを実装する

実装の中身はそれほど重要じゃないと思うので、ここでは割愛しますね。コードはgithubに置いてありますので、ご興味あればどーぞ。


7.Workflowを実装する

Workflowの実装は独特なものがあります。実装コードはこんな感じです。



いきなりS3ActivitiesClientなどというクラスが出てきますが、これらはFlow Frameworkが自動生成したクラスになります。Workflowの実装はこのクライアントを利用するのがキモです。

このクライアントは、アクティビティで定義したメソッドにPromiseでラップした引数を加えたバージョンが多数追加されています。このPromiseですが、詳細な説明はAWSのドキュメントに譲るとして(今回はそんなのばっかりだなぁw)、ざっくり言うとメソッドの実行が非同期で行われ、結果を待たずして次のコードに移るというものです。コードは同期的に見えますが、実際は非同期な振る舞いをします。


8.ActivityWorker、WorkflowWorkerを実装する

さて、ActivityとWorkflowの準備ができましたので、これらを実際にホストするプログラムを書く必要があります。Flow Frameworkでは、ActivityWorker、WorkflowWorkerというものが用意されていますので、これらを素直に使いましょう。


それぞれ引数にドメイン名とタスクリスト名を渡しますが、ドメイン名は先ほどManagement Consoleで作ったドメイン名にします。このコードでは単にworker.start()してるだけですが、きちんと実装する際は不正な形で終了しないようにshutdownHookを引っ掛けてください。

現状のタイミングでActivityを実行するのかどうかを判断するのはWorkflowWorkerです。AWSの言う所のDeciderでしょうか?絵にするとこういうイメージかなぁ?
つ、伝わるかなぁ…っていうか、そもそもあってるかなぁ(汗)

9.ワークフローを実行するコードを書く

ようやくここまで来ました。な、長かった。こんな感じになります。


ここでもFlow Frameworkが自動生成してくれるWorkFlowExternalを使います。

10.実行してみる!

これで準備が整ったので、いざ実行してみましょう。
これには順番が大事です。初回は「8.ActivityWorker、WorkflowWorkerを実装する」で作ったActivityWorkerとWorkflowWorkerを実行します。実は、このWorkerは、ActivityとWorkflowの登録をSWFに対して自動でやってくれるんです。いちいちManagement Consoleで手動で登録する必要がありません。便利ですよ。

Workerを実行後にManagement Consoleで確認すると、ちゃんと登録されているのが分かります。

これでようやくワークフローを実行する事が出来ます。上記のWorkerを実行した状態で、「9.ワークフローを実行するコードを書く」で実装したStarter.javaを実行します。以下のようなログが起動済みの「ActivityHost」から吐き出されたら成功です!

Management Consoleで確認してみましょう。Workflow Executionsをクリックして、Execution StatusをClosedで指定します。

ちゃんとありました!
Workflow Execution IDをクリックすると、実行の履歴を見る事が出来ます。分かりやすい所で、Activityを見てみるとこんな感じです。ちゃんとワークフローが実行されているのが確認できます。


Management Consoleからは、Executionに対して「Signal」「Try-Cancel」「Terminate」「Re-Run」の操作をする事が可能です。分かりやすい所で「Re-Run」してみましょう。

すると、Execution Listに同じWorkflow Execution IDでRun ID違いのExecutionが追加されている事が分かります。ステータスはActiveです。


手元で起動しているWorkerが反応します。ActivityHostからは先ほど同じようなログが出ているのが確認できます。Management Consoleから手元のプログラムが動く感覚は、理屈は分かるとはいえ、実にミョ〜な感じですww


ワークフローが完了すると、ちゃんとCompletedになっている事が確認できます。


まとめ
というわけで、基本的な部分だけつまみ食いした感じですが、おおよその感じがつかんで頂ければ幸いです。

そして見て分かる通り、実際のアクティビティを実行するワーカーはクラウドだろうがオンプレだろうがモバイルだろうがどこにあっても良い、というのがキモだと思います。また、きちんと作れば容易にスケールする事も分かります。

AWSで非同期処理ならばSQSというのが定番ですが、二重処理を防ぐためにメッセージのステート管理を自前でやる必要があったり、SQSにポーリングするプログラムを別途仕込む必要があったりしましたが、こういった事はSWFを使うと一挙に解決されます。

DynamoDBは、SimpleDBで出来なかった事が増えてバンザイな感じでしたけども、SWFもSQSでちょっと面倒だった事が簡単にできるようになった気がします。


続くエントリはこちら:Amazon SWF Flow Frameworkのエラー処理について