Amazon Simple Workflow Service
NRIネットコム株式会社 
佐々木拓郎
2015/2/7JAWSUG関西特別編
佐々木拓郎 @dkfj
課長
AWSの事業推進の他に
モバイルとデータ解析チームの
マネジメントをしています
blog: http://blog.takuros.net
自己紹介
ちょっと宣伝
Rubyによるクローラー開発技法
巡回・解析機能の実装と21の運用例
http://amzn.to/1lsJ5id
ジュンク堂書店 コンピュータ書年間
総合ランキング14位
もう1つ宣伝
AWS本を執筆中
ある程度、AWSを理解した上で、
・AWSのサービスの選択のポイント
・セキュリティの考え方
・運用のノウハウ
・サードパーティ製の便利なサービスの紹介
・パターン別構築例
もう一歩レベルが上げられるような内容を目指しています。
想定の対象読者は、JAWSUGに参加している皆さんです
NRIネットコム
Web周りのビジネスを専門としている会社
• Webシステムの企画・設計・開発・運用
• 24時間365日の運用体制
• デザインを重視し、自社内でディレクタ/デザイナが多数在籍
• スマホ/タブレットも得意
• もちろんAWSをはじめとするクラウドにも力を入れている
会社の紹介
Amazon Simple Workflow Service
SWFは、SQSの上位互換
Simple Workflow Service
ワークフローエンジン
AWSのアプリケーションサービス(の一部)
SWF
SQS
SNS
SES
Simple Queue Service
キューサービス
Simple Notification Service
メッセージ通知サービス
Simple Email Service
メール送信サービス
SWFとは?
ワークフローエンジン
実行するタスクの決定
タスクのステータス管理
タスクの一貫性の管理
単純なクローラーの動き
取得対象のURLを決める
WebからHTMLを収集し、保存する
HTMLから任意のデータを抜き出す
クローラープログラムで考えてみる
単純なクローラーの動き
取得対象のURLを決める
WebからHTMLを収集し、保存する
HTMLから任意のデータを抜き出す
クローラープログラムで考えてみる
URLの決定
単純なクローラーの動き
取得対象のURLを決める
WebからHTMLを収集し、保存する
HTMLから任意のデータを抜き出す
クローラープログラムで考えてみる
URLの決定
URL
単純なクローラーの動き
取得対象のURLを決める
WebからHTMLを収集し、保存する
HTMLから任意のデータを抜き出す
クローラープログラムで考えてみる
URLの決定
HTMLの取得
&保存
URL
単純なクローラーの動き
取得対象のURLを決める
WebからHTMLを収集し、保存する
HTMLから任意のデータを抜き出す
クローラープログラムで考えてみる
URLの決定
HTMLの取得
&保存
URL
インターネット
ダウンロード
単純なクローラーの動き
取得対象のURLを決める
WebからHTMLを収集し、保存する
HTMLから任意のデータを抜き出す
クローラープログラムで考えてみる
URLの決定
HTMLの取得
&保存
URL
インターネット
ダウンロード
保存
単純なクローラーの動き
取得対象のURLを決める
WebからHTMLを収集し、保存する
HTMLから任意のデータを抜き出す
クローラープログラムで考えてみる
URLの決定
HTMLの取得
&保存
URL
インターネット
ダウンロード
保存
作業対象の
通知
単純なクローラーの動き
取得対象のURLを決める
WebからHTMLを収集し、保存する
HTMLから任意のデータを抜き出す
クローラープログラムで考えてみる
URLの決定
HTMLの取得
&保存
任意のデータ
の抜き出し
URL
インターネット
ダウンロード
保存
作業対象の
通知
単純なクローラーの動き
取得対象のURLを決める
WebからHTMLを収集し、保存する
HTMLから任意のデータを抜き出す
クローラープログラムで考えてみる
URLの決定
HTMLの取得
&保存
任意のデータ
の抜き出し
URL
インターネット
ダウンロード
保存
作業対象の
通知
対象データ取得
単純なクローラーの動き
取得対象のURLを決める
WebからHTMLを収集し、保存する
HTMLから任意のデータを抜き出す
クローラープログラムで考えてみる
URLの決定
HTMLの取得
&保存
任意のデータ
の抜き出し
URL
インターネット
ダウンロード
保存
作業対象の
通知
対象データ取得
データの抜き出し
密結合の為に、部分再実行が出来ない
SQSを使って、疎結合にする
ダウンロード
対象キュー
作業対象の
対象キュー
SQSを使って、疎結合にする
URLの決定
ダウンロード
対象キュー
作業対象の
対象キュー
SQSを使って、疎結合にする
URLの決定
ダウンロード
対象キュー
URL格納
作業対象の
対象キュー
SQSを使って、疎結合にする
URLの決定
HTMLの取得
&保存
ダウンロード
対象キュー
URL格納
作業対象の
対象キュー
SQSを使って、疎結合にする
URLの決定
HTMLの取得
&保存
ダウンロード
対象キュー
URL格納 URL取得
作業対象の
対象キュー
SQSを使って、疎結合にする
URLの決定
HTMLの取得
&保存
ダウンロード
対象キュー
URL格納 URL取得
インターネット
ダウンロード
作業対象の
対象キュー
SQSを使って、疎結合にする
URLの決定
HTMLの取得
&保存
保存
ダウンロード
対象キュー
URL格納 URL取得
インターネット
ダウンロード
作業対象の
対象キュー
SQSを使って、疎結合にする
URLの決定
HTMLの取得
&保存
保存
作業対象の
格納
ダウンロード
対象キュー
URL格納 URL取得
インターネット
ダウンロード
作業対象の
対象キュー
SQSを使って、疎結合にする
URLの決定
HTMLの取得
&保存
任意のデータ
の抜き出し
保存
作業対象の
格納
ダウンロード
対象キュー
URL格納 URL取得
インターネット
ダウンロード
作業対象の
対象キュー
SQSを使って、疎結合にする
URLの決定
HTMLの取得
&保存
任意のデータ
の抜き出し
保存
作業対象の
格納
ダウンロード
対象キュー
URL格納 URL取得
インターネット
ダウンロード
作業対象の
対象キュー
作業対象の
取得
SQSを使って、疎結合にする
URLの決定
HTMLの取得
&保存
任意のデータ
の抜き出し
保存
作業対象の
格納
ダウンロード
対象キュー
URL格納 URL取得
インターネット
ダウンロード
作業対象の
対象キュー
作業対象の
取得
対象データ取得
SQSを使って、疎結合にする
URLの決定
HTMLの取得
&保存
任意のデータ
の抜き出し
保存
作業対象の
格納
ダウンロード
対象キュー
URL格納 URL取得
インターネット
ダウンロード
作業対象の
対象キュー
作業対象の
取得
対象データ取得
データの抜き出し
これで、ええやん
これで、ええやん
でも、少しだけ課題があります
残る課題
重複実行の可能性
SQSは、複数のワーカーが同一のキューを取得する可能性がある
アプリ側で、一貫性を保つためのロジックが必要
実行制御の問題
エラー時に単純再実行の作りであれば、問題ない
処理ごとに分岐する場合は、自前で制御ロジック実装する必要
途中で処理の追加・変更・削除をする場合、前後の調整が必要
ワークフロー
エグゼキューション
SWFを使うと
URLの決定
ワークフロー
スターター
ワークフロー
エグゼキューション
ワークフロー
エグゼキューション
ディシジョン
タスクリスト
アクティビティ
タスクリスト
次のタスクを
決める
処理の実行
デサイダー
アクティビティ
ポーリング
ポーリング
ワークフロー
エグゼキューション
SWFを使うと
URLの決定
ワークフロー
スターター
ワークフロー
エグゼキューション
ワークフロー
エグゼキューション
ディシジョン
タスクリスト
アクティビティ
タスクリスト
次のタスクを
決める
処理の実行
デサイダー
アクティビティ
ポーリング
ポーリング
ワークフロー
エグゼキューション
SWFを使うと
URLの決定
ワークフロー
スターター
ワークフロー
エグゼキューション
ワークフロー
エグゼキューション
ディシジョン
タスクリスト
アクティビティ
タスクリスト
次のタスクを
決める
処理の実行
デサイダー
アクティビティ
ポーリング
ポーリング
デサイダーが、
次の処理を決める
ワークフロー
エグゼキューション
SWFを使うと
URLの決定
ワークフロー
スターター
ワークフロー
エグゼキューション
ワークフロー
エグゼキューション
ディシジョン
タスクリスト
アクティビティ
タスクリスト
次のタスクを
決める
処理の実行
デサイダー
アクティビティ
ポーリング
ポーリング
デサイダーが、
次の処理を決める
ワークフロー
エグゼキューション
SWFを使うと
URLの決定
ワークフロー
スターター
ワークフロー
エグゼキューション
ワークフロー
エグゼキューション
ディシジョン
タスクリスト
アクティビティ
タスクリスト
次のタスクを
決める
処理の実行
デサイダー
アクティビティ
ポーリング
ポーリング
デサイダーが、
次の処理を決める
ここだけ見ると、
SQSと同じ
用語の説明
ワークフロースターター
ワークフローをキックするプログラム
キックするたびに「ワークフローエグゼキューショ
ン」が作成される
ワークフローエグゼキューション
ワークフローのインスタンス
タスクを管理する為の情報をもつ
タスクリスト
処理すべきキュー
デサイダーとアクティビティごとにキューを設定
用語の説明
デサイダー
ワークフローの実体
タスクリストをポーリングし、タスクがあったら次
に実行するアクティビティを指定
アクティビティ
個々のタスク処理
タスクリストをポーリングし、タスクがあったら処
理を実行
解らん。
解らん。
実際にワークフローを動かしてみよう
SDK
言語別の各SDKからSWFは利用可能
Android
iOS
Java
.NET
Node.js
PHP
Python
Ruby
ブラウザ(JavaScript)
AWS Flow Framework
SWFの各種APを隠蔽して実行するフレームワーク
RubyとJavaのみ提供されている
Rubyで始めるSWF
事前に見ておくもの
Getting Started with AWS Flow Framework for Ruby for Amazon SWF
https://www.youtube.com/watch?v=Z_dvXy4AVEE
準備するもの
AWSのAccessKeyIDとSecretAccessKey
Rubyの実行環境
Gemのダウンロード
Gemのインストール
$ gem install aws-sdk
$ gem install aws-flow
サンプルプログラム
Samples/HelloWorld/
└── lib
├── aws-config.txt
├── hello_activity.rb
├── hello_workflow.rb
├── hello_world.rb
├── kill_hello.rb
├── run_hello
└── utils.rb
http://aws.amazon.com/code/Amazon-SWF/3015904745387737
サンプルプログラム
Samples/HelloWorld/
└── lib
├── aws-config.txt
├── hello_activity.rb
├── hello_workflow.rb
├── hello_world.rb
├── kill_hello.rb
├── run_hello
└── utils.rb
http://aws.amazon.com/code/Amazon-SWF/3015904745387737
アクティビティ
サンプルプログラム
Samples/HelloWorld/
└── lib
├── aws-config.txt
├── hello_activity.rb
├── hello_workflow.rb
├── hello_world.rb
├── kill_hello.rb
├── run_hello
└── utils.rb
http://aws.amazon.com/code/Amazon-SWF/3015904745387737
デサイダー
アクティビティ
サンプルプログラム
Samples/HelloWorld/
└── lib
├── aws-config.txt
├── hello_activity.rb
├── hello_workflow.rb
├── hello_world.rb
├── kill_hello.rb
├── run_hello
└── utils.rb
http://aws.amazon.com/code/Amazon-SWF/3015904745387737
デサイダー
アクティビティ
ワークフロースターター
初期設定
$ vi aws-config.txt
---
:access_key_id: “your key"
:secret_access_key: “your key"
アクセスキーとシークレットアクセスキーを埋め込む
ワークフロー
require_relative 'utils'
require_relative "./hello_activity"
class HelloWorldWorkflow
extend AWS::Flow::Workflows
workflow :hello_workflow do
{
:version => "1", :execution_start_to_close_timeout => 3600, :task_list => $TASK_LIST
}
end
activity_client(:activity) { {:from_class => "HelloWorldActivity"} }
def hello_workflow(name)
activity.hello_activity(name)
end
end
worker = AWS::Flow::WorkflowWorker.new($SWF.client, $HELLOWORLD_DOMAIN, $TASK_LIST,
HelloWorldWorkflow)
# Start the worker if this file is called directly from the command line.
worker.start if __FILE__ == $0
hello_workflow.rb
呼び出すアクティビティの設定
ワークフローの設定
アクティビティ
require_relative 'utils'
class HelloWorldActivity
extend AWS::Flow::Activities
activity :hello_activity do
{
:default_task_list => $TASK_LIST, :version => "my_first_activity",
:default_task_schedule_to_start_timeout => 30,
:default_task_start_to_close_timeout => 30
}
end
def hello_activity(name)
puts "Hello, #{name}!"
end
end
activity_worker = AWS::Flow::ActivityWorker.new($SWF.client, $HELLOWORLD_DOMAIN,
$TASK_LIST, HelloWorldActivity) { {:use_forking => false} }
# Start the worker if this file is called directly from the command line.
activity_worker.start if __FILE__ == $0
hello_activity.rb
アクティビティ
require_relative 'utils'
class HelloWorldActivity
extend AWS::Flow::Activities
activity :hello_activity do
{
:default_task_list => $TASK_LIST, :version => "my_first_activity",
:default_task_schedule_to_start_timeout => 30,
:default_task_start_to_close_timeout => 30
}
end
def hello_activity(name)
puts "Hello, #{name}!"
end
end
activity_worker = AWS::Flow::ActivityWorker.new($SWF.client, $HELLOWORLD_DOMAIN,
$TASK_LIST, HelloWorldActivity) { {:use_forking => false} }
# Start the worker if this file is called directly from the command line.
activity_worker.start if __FILE__ == $0
hello_activity.rb
アクティビティの設定
アクティビティ
require_relative 'utils'
class HelloWorldActivity
extend AWS::Flow::Activities
activity :hello_activity do
{
:default_task_list => $TASK_LIST, :version => "my_first_activity",
:default_task_schedule_to_start_timeout => 30,
:default_task_start_to_close_timeout => 30
}
end
def hello_activity(name)
puts "Hello, #{name}!"
end
end
activity_worker = AWS::Flow::ActivityWorker.new($SWF.client, $HELLOWORLD_DOMAIN,
$TASK_LIST, HelloWorldActivity) { {:use_forking => false} }
# Start the worker if this file is called directly from the command line.
activity_worker.start if __FILE__ == $0
hello_activity.rb
処理内容
アクティビティの設定
ワークフロースターター
require 'aws/decider'
require_relative 'utils'
require_relative 'hello_workflow'
# Get a workflow client to start the workflow
my_workflow_client = AWS::Flow.workflow_client($SWF.client, $HELLOWORLD_DOMAIN) do
{:from_class => "HelloWorldWorkflow"}
end
puts "Starting an execution..."
workflow_execution = my_workflow_client.start_execution("AWS Flow Framework for Ruby")
hello_world.rb
SWFの実行
$ ruby hello_activity.rb &
$ ruby hello_workflow.rb &
$ ruby hello_world.rb
バックグランドで実行
WorkFlow Starterで
ワークフローの開始
AWSコンソールで確認
US East
AWSコンソールで確認
US East
HelloWorldドメインが作成されている
アクティビティだけを落として、実行
$ kill <アクティビティのプロセスID>
$ ruby hello_world.rb
AWSコンソールで確認
一定時間後に、Failedに
デサイダーだけを落としてみる
$ kill <デサイダーのプロセスID>
$ ruby hello_world.rb
AWSコンソールで確認
AWSコンソールで確認
Active Excecutionに残る
AWSコンソールで確認
Active Excecutionに残る
AWSコンソールで確認
Active Excecutionに残る
AWSコンソールで確認
Active Excecutionに残る
AWSコンソールで確認
Active Excecutionに残る
AWSコンソールで確認
設定値である3600秒後に、TimeOut
Active Excecutionに残る
まとめ
SWFの実行処理は、どこでも動く
タスクの状態は、AWS上で管理されている
自分で動かすと、何となく解る
参考資料
Amazon Simple Workflow Service(SWF) AWS Black Belt Tech
http://www.slideshare.net/AmazonWebServicesJapan/amazon-simple-workflow-service-swf
Amazon Simple Workflow Service (SWF) 入門 | Developers.IO
http://dev.classmethod.jp/cloud/aws/introduction-to-amazon-simple-workflow-service/
20140315 ACEに聞け! SWFで始めるWorkFlow - jawsdays2014
http://www.slideshare.net/okeee0315/20140315-jawsdays2014-swf
ご静聴、ありがとうございました。

JAWSUG Kansai Simple Workflow Service (SWF)