AWS PHPSDK 2+ で利用できる SWF ワークフロー PHP サンプル コードがあるかどうか疑問に思っていました。
2 に答える
チュートリアルを探しましたが、見つかりませんでした。最終的に、Ruby と Web API を使用してドキュメントとサンプルを調べ、PHP SDK の操作の要点をまとめました。
最初に行う必要があるのは、ドメイン、ワークフロー、およびアクティビティを登録することです。これは、AWS コンソールまたは PHP SDK を使用して実行できます。SDK を使用して、次を使用します。
<?php
require_once "path/to/aws.phar";
use Aws\Swf\SwfClient;
// Create an instance of the SWF class
$client = SwfClient::factory(array(
"key" => "your_aws_key",
"secret" => "your_aws_secret_key",
"region" => "your_aws_region"
));
// Register your domain
$client->registerDomain(array(
"name" => "domain name you want",
"description" => "this is a test domain",
"workflowExecutionRetentionPeriodInDays" => "7"
));
// Register your workflow
$client->registerWorkflowType(array(
"domain" => "domain name you registered in previous call",
"name" => "workflow name you want",
"version" => "1.0",
"description" => "this is a sample",
"defaultTaskList" => array(
"name" => "mainTaskList"
),
"defaultChildPolicy" => "TERMINATE"
));
// Register an activity
$client->registerActivityType(array(
"domain" => "domain name you registered above",
"name" => "activity name you want",
"version" => "1.0",
"description" => "first activity in our workflow",
"defaultTaskList" => array(
"name" => "mainTaskList"
)
));
// Follow activity registration example above and register
// more activities as you wish
次のステップは、ディサイダーを作成することです。これは、アクティビティ (ワーカー) ノードの調整ノードとして機能するスクリプトです。
// Ask SWF for things the decider needs to know
$result = $client->pollForDecisionTask(array(
"domain" => "your domain name",
"taskList" => array(
"name" => "mainTaskList"
),
"identify" => "default",
"maximumPageSize" => 50,
"reverseOrder" => true
));
// Current version of activity types we are using
$activity_type_version = "1.0";
// Parse info we need returned from the pollForDecisionTask call
$task_token = $result["taskToken"];
$workflow_id = $result["workflowExecution"]["workflowId"];
$run_id = $result["workflowExecution"]["runId"];
$last_event = $result["events"][0]["eventId"];
// Our logic that decides what happens next
if($last_event == "3"){
$activity_type_name = "activity to start if last event ID was 3";
$task_list = "mainTaskList";
$activity_id = "1";
$continue_workflow = true;
}
elseif($last_event == "8"){
$activity_type_name = "activity to start if last event ID was 8";
$task_list = "mainTaskList";
$activity_id = "2";
$continue_workflow = false;
}
// Now that we populated our variables based on what we received
// from SWF, we need to tell SWF what we want to do next
if($continue_workflow === true){
$client->respondDecisionTaskCompleted(array(
"taskToken" => $task_token,
"decisions" => array(
array(
"decisionType" => "ScheduleActivityTask",
"scheduleActivityTaskDecisionAttributes" => array(
"activityType" => array(
"name" => $activity_type_name,
"version" => $activity_type_version
),
"activityId" => $activity_id,
"control" => "this is a sample message",
// Customize timeout values
"scheduleToCloseTimeout" => "360",
"scheduleToStartTimeout" => "300",
"startToCloseTimeout" => "60",
"heartbeatTimeout" => "60",
"taskList" => array(
"name" => $task_list
),
"input" => "this is a sample message"
)
)
)
));
}
// End workflow if last event ID was 8
else if($continue_workflow === false){
$client->respondDecisionTaskCompleted(array(
"taskToken" => $task_token,
"decisions" => array(
array(
"decisionType" => "CompleteWorkflowExecution"
)
)
));
}
最後のステップは、アクティビティ ワーカーを作成することです。次の形式を使用してスピンアップできます。
// Check with SWF for activities
$result = $client->pollForActivityTask(array(
"domain" => "domain name you registered",
"taskList" => array(
"name" => "mainTaskList"
)
));
// Take out task token from the response above
$task_token = $result["taskToken"];
// Do things on the computer that this script is saved on
exec("my program i want to execute");
// Tell SWF that we finished what we need to do on this node
$client->respondActivityTaskCompleted(array(
"taskToken" => $task_token,
"result" => "I've finished!"
));
アクティビティ ワーカーとディサイダーのスクリプトは、任意のサーバーに配置できます。これらのスクリプトはすべて、相互に通信するために SWF を呼び出します。
最後に、作成したばかりのワークフローを開始するには、次を使用します。
// Generate a random workflow ID
$workflowId = mt_rand(1000, 9999);
// Starts a new instance of our workflow
$client->startWorkflowExecution(array(
"domain" => "your domain name",
"workflowId" => $workflowId,
"workflowType" => array(
"name" => "your workflow name",
"version" => "1.0"
),
"taskList" => array(
"name" => "mainTaskList"
),
"input" => "a message goes here",
"executionStartToCloseTimeout" => "300",
'taskStartToCloseTimeout' => "300",
"childPolicy" => "TERMINATE"
));
上記の投稿に基づいて、目的に合わせて簡単に変更できる、完全に機能するワークフローを作成することができました。ありがとうございます。composer で AWS PHP SDK をインストールし、正しいファイルパスを割り当て、コンソールでドメイン、ワークフロータイプ、およびアクティビティタイプを設定した後 (これは非常に簡単です)、以下のスクリプトを使用してフローを順番に実行できます。これを投稿する前に、自分で実行することができました。すべての設定が完了したら、ExecuteFlow.php を実行するだけで十分です。
8 つのスクリプトがここに掲載されています (心配しないでください。3 つのスクリプトは非常に短いものです)。
ExecuteFlow.php :
<?php
//This script is used to begin a new workflow then activate the decider and workers to finish the job.
//
//SWF scripts originally developed by ~ in April, 2018 based on the first comment in https://stackoverflow.com/questions/22765377/amazon-aws-simple-workflow-service-swf-php-samples
//
//PROGRAMMER MODIFICATIONS sections are for updates to the Steps in Workflow, the rest of SWF Workflow should be automated with these scripts.
//
// Command Reference:
//
// https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-swf-2012-01-25.html
//
// For Reference:
// Workflow Type (Name) is MainWorkflow
// Activity Type (Names) are Step 1, Step 2, and Step 3
// Task List for MainWorkflow is MainTaskList
// Task List for Step 1 is TaskList1, Step 2 is TaskList2, etc.
//
//
//Software Required:
//
//Installed AWS PHP SDK
//ExecuteFlow.php
//Decider.php
//Worker.php
//DescribeExecution.php
//TerminateFlow.php
//Test.php
//Test2.php
//Test3.php
//Once it's installed using composer, this accesses the PHP AWS SDK
require_once "PHP-AWS-SDK/autoload.php";
//This grabs the client from the SDK so we can use it to do our SWF bidding.
//(Note: the version should stay synonymous with the currently packaged SDK version so it works with our code.)
$client = new Aws\Swf\SwfClient([
'version' => '2012-01-25',
'region' => 'us-east-1'
]);
//------------------------------------------------------------------------------
//----------------------PROGRAMMER MODIFICATIONS--------------------------------
//------------------------------------------------------------------------------
//Variables that we want to carry through the workflow
$Domain_Name='MyDomainName';
$Activity_Type_Version = "1";
$Workflow_Task_List_Name="MainTaskList";
$TotalNumberofSteps=3;
$TimeAllottedforEachStep="300"; //seconds
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//Clear the way for a new workflow
//How many workflows are open?
$result = $client->countOpenWorkflowExecutions([
'domain' => $Domain_Name,
'startTimeFilter' => [
'oldestDate' => 1524755000,
],
]);
$OpenWorkflowCount=$result["count"];
if ($OpenWorkflowCount <> 0){
//This loop terminates all the current workflows
for ($x = 1; $x <= $OpenWorkflowCount; $x++) {
$result = $client->listOpenWorkflowExecutions([
'domain' => $Domain_Name,
'startTimeFilter' => [
'oldestDate' => 1524755000,
],
]);
$OpenWorkflowID = $result["executionInfos"][0]["execution"]["workflowId"];
echo "You have an open workflow: $OpenWorkflowID.\n\n";
$WorkflowID=$OpenWorkflowID;
echo "Terminating Workflow $OpenWorkflowID...\n";
$Run_ID=$result["executionInfos"][0]["execution"]["runId"];
$Reason="Flow was terminated to clear the way for a new Workflow";
require 'TerminateFlow.php';
}
}
// Execution of the workflow begins now-----------------------------------------
// Generate a random workflow ID
$WorkflowID = mt_rand(1000, 9999);
//Turn it into a string for input to SWF
settype($WorkflowID,"string");
echo "\nRandomly Generated Workflow ID is ".$WorkflowID."\n";
//Let's get this party started.
$client->startWorkflowExecution([
'domain' => $Domain_Name,
'workflowId' => $WorkflowID,
'workflowType' => [
'name' => 'MainWorkflow',
'version' => '1'
],
'taskList' => [
'name' => $Workflow_Task_List_Name
],
'input' => 'Starting a Workflow',
//'executionStartToCloseTimeout' => '300',
// 'taskStartToCloseTimeout' => '300',
// 'childPolicy' => 'TERMINATE',
]);
echo "The workflow is now being executed!\n\n";
//echo "\nLet's ask the decider what to do next:\n";
require 'Decider.php';
//Here we will confirm completion of the workflow
echo "Verifying Completion: \n";
$result = $client->describeWorkflowExecution([
'domain' => $Domain_Name,
'execution' => [
'runId' => $Run_ID,
'workflowId' => $Workflow_ID,
],
]);
$Finished = $result["executionInfo"]["closeStatus"];
if ($Finished == 'COMPLETED')
{
echo "$Finished \nWe have completed the workflow!\n";
}
else {
echo "$Finished \nCheck on your application.\n";
}
//This is the amount of time it took to run the workflow
$WorkflowStartTimestamp=$result["executionInfo"]["startTimestamp"];
$WorkflowEndTimestamp=$result["executionInfo"]["closeTimestamp"];
$wfinterval = $WorkflowStartTimestamp->diff($WorkflowEndTimestamp);
$m=$wfinterval->format('%i minute(s)');
$s=$wfinterval->format('%s second(s)');
echo "Running time: $m $s.\n\n";
?>
Decider.php :
<?php
//DECIDER
// What's on the agenda today boss?
$result = $client->pollForDecisionTask(array(
"domain" => $Domain_Name,
"taskList" => array(
"name" => $Workflow_Task_List_Name
),
"identity" => "Decider is choosing whether to continue or end the workflow",
// "maximumPageSize" => 50,
"reverseOrder" => true //This makes sure the events are returned in reverse order. It makes it easier to tell which event is current/most recent (helps generate $WorkflowEventID variable below).
));
// Parse info we need returned from the pollForDecisionTask call
$task_token = $result["taskToken"];
$Workflow_ID = $result["workflowExecution"]["workflowId"];
$Run_ID=$result["workflowExecution"]["runId"];
$WorkflowEventID = $result["events"][0]["eventId"];
// Our logic that decides what happens next...
//If we have x steps, we will need 3+x*6 WorkflowEventID's before sending the command to end the workflow.
//Here's where this is automatically calculated:
$CalculatedEventID=3+$TotalNumberofSteps*6;
//Below, we decide on whether to continue the workflow.
//It would be simpler to skip this section and only modify the section below, but
//it allows us to avoid redundancy (for example, "require 'Worker.php';" below).
if($WorkflowEventID < $CalculatedEventID){
$task_list = $Workflow_Task_List_Name;
$continue_workflow = true;
}
elseif($WorkflowEventID == $CalculatedEventID){
$task_list = $Workflow_Task_List_Name;
$continue_workflow = false;
}
//------------------------------------------------------------------------------
//----------------------PROGRAMMER MODIFICATIONS--------------------------------
//------------------------------------------------------------------------------
// Now that we populated our variables based on what we received
// from SWF, we need to tell SWF what we want to do next
// These loops assign variables the appropriate names/values for each Step based
//on the Event ID we're on. These variables are carried through to the worker.
//Variable values should be synonymous with the activity types in the SWF console.
if($continue_workflow === true){
//Decider STEP 1---------------------------------------------------------------
if ($WorkflowEventID == "3")
{$Step = "Step 1";
$Activity_Task_List_Name = "TaskList1";
$Activity_ID = "1";
// echo "The decider says its current Workflow ID is ".$Workflow_ID."\n\n";
}
//Decider STEP 2---------------------------------------------------------------
elseif ($WorkflowEventID == "9")
{$Step = "Step 2";
$Activity_ID = "2";
$Activity_Task_List_Name = "TaskList2";
}
//Decider STEP 3--------------------------------------------------------------
elseif ($WorkflowEventID == "15")
{$Step = "Step 3";
$Activity_ID = "3";
$Activity_Task_List_Name = "TaskList3";
}
else
{echo "Something's Fishy\n\n";}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//We mark the Decision Task Complete before moving to the Worker
$client->respondDecisionTaskCompleted(array(
"taskToken" => $task_token,
"decisions" => array(
array(
"decisionType" => "ScheduleActivityTask",
"scheduleActivityTaskDecisionAttributes" => array(
"activityType" => array(
"name" => $Step,
"version" => $Activity_Type_Version
),
"activityId" => $Activity_ID,
"control" => "Have a great day!",
// Customize timeout values
//"scheduleToCloseTimeout" => "360",
//"scheduleToStartTimeout" => "300",
"startToCloseTimeout" => $TimeAllottedforEachStep,
//"heartbeatTimeout" => "60",
"taskList" => array(
"name" => $Workflow_Task_List_Name
),
"input" => "$Step Assigned to Worker"
)
)
)
));
//Here we have the worker do the decider's bidding.
require 'Worker.php';
//The Timestamp stuff and response here can also be placed at the end of the worker script.
//They are placed here to simplify the worker script since the decider is
//really supposed to be doing all the 'non-executing stuff' stuff.
$ReverseDescribeorNot=False;
require 'DescribeExecution.php';
$TimestampStart = $result["events"][$WorkflowEventID]["eventTimestamp"];
$TimestampEnd = $result["events"][$WorkflowEventID+4]["eventTimestamp"];
$interval = $TimestampStart->diff($TimestampEnd);
$m=$interval->format('%i minute(s)');
$s=$interval->format('%s second(s)');
echo $Step."/".$TotalNumberofSteps." completed in $m $s.\n\n";
//We require the Decider here because if we do so in the Worker script, the worker script is unable to finish before being executed again above.
//This allows the Decider to loop back up to the top and figure out where it's at in the workflow and decide from there.
//Although it puts the Decider into a loop of itself, the if statements make sure it executes the correct things based on its feeback from SWF.
require 'Decider.php';
}
// End workflow if last event ID was the final WorkflowEventID
else if($continue_workflow === false){
$client->respondDecisionTaskCompleted(array(
"taskToken" => $task_token,
"decisions" => array(
array(
"decisionType" => "CompleteWorkflowExecution",
),
)
));
}
?>
Worker.php :
<?php
//WORKER
//Typically, for SWF, a worker is a service, script, or person that performs a specific function.
//For our purposes, all our workers are php scripts.
//Therefore, in order to avoid redundancy, we simply use this single script to activate our workers.
//Because developers don't want to worry about putting a "pollForActivityTask" and "respondActivityTaskCompleted" call in each of their scripts,
//we simply make those calls here and execute each script when SWF is ready for it.
//echo "\nNow, the worker is working.\n\n";
// Check with SWF for activities
$result = $client->pollForActivityTask([
"domain" => "Cloud Optimization",
"taskList" => [
"name" => $Workflow_Task_List_Name
]
]);
// Take out task token from the response above
$task_token = $result["taskToken"];
//------------------------------------------------------------------------------
//----------------------PROGRAMMER MODIFICATIONS--------------------------------
//------------------------------------------------------------------------------
// This is where the Worker actually executes activities
//Worker STEP 1-----------------------------------------------------------------
if ($Step == 'Step 1') {
require 'Test.php';
}
//Worker STEP 2-----------------------------------------------------------------
elseif ($Step == 'Step 2') {
require 'Test2.php';
}
//Worker STEP 3-----------------------------------------------------------------
elseif ($Step == 'Step 3') {
require 'Test3.php';
}
else
{echo "Something's Super Duper Fishy\n\n";}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//If the Activity Timed out, we'll find out here before our app explodes
//This grabs the eventType statement associated with the current Workflow EventID
$ReverseDescribeorNot=True;
require 'DescribeExecution.php';
$MightHaveTimedOut = $result["events"][1]["eventType"];
if ($MightHaveTimedOut == "ActivityTaskTimedOut")
{
echo "\n$Step Timed Out, Cancelling Workflow...\n";
$Reason="$Step Activity Timed Out";
require 'TerminateFlow.php';
exit;
}
else {
$ResultResponse=$Step."/".$TotalNumberofSteps." completed.";
// Tell SWF that we finished what we need to do on this node
$client->respondActivityTaskCompleted(array(
"taskToken" => $task_token,
"result" => $ResultResponse
));
}
//echo "\nAaand now back to the decider.\n";
?>
TerminateFlow.php :
<?php
$result = $client->terminateWorkflowExecution([
'childPolicy' => 'TERMINATE',
'details' => $Reason,
'domain' => $Domain_Name, // REQUIRED
'reason' => $Reason,
'runId' => $Run_ID,
'workflowId' => $WorkflowID, // REQUIRED
]);
echo "Workflow $WorkflowID Terminated\n\n";
?>
DescribeExecution :
<?php
// echo "\n\nExecution History:\n\n";
$result = $client->getWorkflowExecutionHistory([
'domain' => $Domain_Name,
'execution' => [
'runId' => $Run_ID,
'workflowId' => $Workflow_ID,
],
// 'maximumPageSize' => <integer>,
// 'nextPageToken' => '<string>',
'reverseOrder' => $ReverseDescribeorNot,//true || false,
]);
?>
Test.php :
<?php
echo "Test.php is executing!\n";
?>
Test2.php :
<?php
echo "Test2.php is executing!\n";
?>
Test3.php :
<?php
echo "Test3.php is executing!\n";
?>
スクリプトが同じフォルダーにある限り、ファイル パスは問題になりません。幸運を!