AWS步骤功能活动工作人员在工作人员停止时未看到执行

时间:2018-03-15 06:32:36

标签: amazon-web-services go aws-step-functions aws-sdk-go

AWS SDK for Go的版本?

V2.0.0-preview.3

Go的版本(go version)?

go1.9.3 darwin / amd64

您看到了什么问题?

我正在为Go中的步骤函数编写一个Activity Worker。

当:

  • 活动工作正在运行,
  • 然后,我们从SFN控制台开始执行工作流程

一切似乎都运转正常。

然而,当:

  • 活动工作人员已停止,
  • 然后,我们从SFN控制台开始执行工作流,
  • 然后工人开始备份,

工作程序似乎轮询SFN,但它不执行在停止期间启动的任务。 如果我们此时启动新的工作流程执行(当工作程序正在运行时),则工作程序将成功执行新任务。在工人停止期间执行的工作流程没有被工人接收。

编辑:查看执行历史记录,我看到Timed Out状态和以下事件日志:

enter image description here

重现的步骤

这里是我的SFN状态机:

{
  "Comment": "An example using a Task state.",
  "StartAt": "getGreeting",
  "Version": "1.0",
  "TimeoutSeconds": 300,
  "States":
  {
    "getGreeting": {
      "Type": "Task",
      "Resource": "arn:aws:states:ap-southeast-1:196709014601:activity:get-greeting",
      "End": true
    }
  }
}

这是我的SFN工作人员:

package main

import (
    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/aws/endpoints"
    "github.com/aws/aws-sdk-go-v2/aws/external"
    "github.com/aws/aws-sdk-go-v2/service/sfn"
    "fmt"
    "encoding/json"
)

type Worker struct {
    svc             *sfn.SFN
    activityARN     string
}

type Task struct {
    input   *string
    token   *string
}

func New(activityARN string) *Worker {
    cfg, err := external.LoadDefaultAWSConfig()
    if err != nil {
        panic("unable to load SDK config, " + err.Error())
    }
    // Set the AWS Region that the service clients should use
    cfg.Region = endpoints.ApSoutheast1RegionID

    // Using the Config value, create the Step Functions client
    svc := sfn.New(cfg)

    w := &Worker{
        svc: svc,
        activityARN: activityARN,
    }
    return w
}

func (w *Worker) getTask() *Task {
    req := w.svc.GetActivityTaskRequest(&sfn.GetActivityTaskInput{
        ActivityArn: aws.String(w.activityARN),
    })
    res, err := req.Send()
    if err != nil { fmt.Println("failed to get tasks, "+err.Error()) }
    return &Task{
        input: res.Input,
        token: res.TaskToken,
    }
}

// Call SendTaskSuccess on success
func (w *Worker) handleSuccess(taskToken *string, json *string) error {
    req := w.svc.SendTaskSuccessRequest(&sfn.SendTaskSuccessInput{
        TaskToken: taskToken,
        Output: json, // JSON string
    })
    _, err := req.Send()
    if err != nil { fmt.Println("failed to send task success result, "+err.Error()) }
    return err
}

// Call SendTaskFailure on error
func (w *Worker) handleFailure(taskToken *string, err error) error {
    errorMessage := err.Error()
    req := w.svc.SendTaskFailureRequest(&sfn.SendTaskFailureInput{
        TaskToken: taskToken,
        Error: &errorMessage,
    })

    _, err = req.Send()
    if err != nil { fmt.Println("failed to send task failure result, "+err.Error()) }
    return err
}

func main() {
    activityARN := "arn:aws:states:ap-southeast-1:196709014601:activity:get-greeting"
    worker := New(activityARN)

    fmt.Println("Starting worker")
    for {
        // 1. Poll GetActivityTask API for tasks
        fmt.Println("Polling for tasks")
        task := worker.getTask()
        if task.token == nil { continue }

        // 2. Do some actual work
        fmt.Println("Working")
        result, err := work(task.input)

        // 3. Notify SFN on success and failure
        fmt.Println("Sending results")
        if err == nil {
            worker.handleSuccess(task.token, result)
        } else {
            worker.handleFailure(task.token, err)
        }
    }
}

// Handles marshalling and un-marshalling JSON
func work(jsonInput *string) (*string, error) {
    input := &GreetInput{}
    json.Unmarshal([]byte(*jsonInput), input)

    result, err := Greet(input) // Actual work
    if err != nil { return nil, err }

    outputBytes, _ := json.Marshal(result)
    output := string(outputBytes)
    return &output, nil
}

// Actual handler code
type GreetInput struct {
    Who string
}

type GreetOutput struct {
    Message string
}

func Greet(input *GreetInput) (*GreetOutput, error) {
    message := fmt.Sprintf("hello %s", input.Who)
    output := &GreetOutput {
        Message: message,
    }
    fmt.Println(message)
    return output, nil
}

运行:

go build worker.go && ./worker

1 个答案:

答案 0 :(得分:0)

根据您的更新,我认为工作人员没有被优雅地停止(即杀死工作人员时你没有等到GetActivityTask请求结束),因此步骤功能可以响应(已经死亡)工作人员。

所以工作流程如下:

  1. 工作人员发送GetActivityTask请求并暂停(直到达到超时)。
  2. 工作人员在没有等待GetActivityTask结束的情况下被杀死。
  3. 创建新执行。
  4. Step Functions看到某些GetActivityTask仍然悬挂 - 将任务从新执行发送到它。
  5. 然而,工人已经死了,所以它不会接受这项任务。步骤功能认为任务已经交付,因此等待任务结束或超时。
  6. 要检查是否是这种情况,只需等待一段时间后杀死工作人员(我不知道AWS SDK for Go中GetActivityTask的默认等待时间 - 5分钟应该完成工作)然后创建执行。如果新执行按预期工作,那么您应该向工作者添加gracefull退出(等待GetActivityTask结束并最终处理任务)。