GoでTemporalを用いたワークフローアプリケーションの実装:完全ガイド

Temporal SDKを使用してGoでワークフローを構築

目次

Temporal はオープンソースでエンタープライズグレードのワークフローエンジンであり、Go のような馴染みのあるプログラミング言語を使用して、耐障害性があり、スケーラブルで、フォールトトレラントなワークフローアプリケーションを構築することを開発者に可能にします。

そして、複雑な状態遷移やリトライを伴う分散アプリケーションには、信頼できるオーケストレーションフレームワークが必要です。

このガイドでは、Temporal を Go で使用したワークフローアプリケーションの実装方法について説明し、設定、サンプルコード、デプロイ戦略、ベストプラクティス、およびトラブルシューティングをカバーします。

Go Workplace

Temporal とは、そして Go でそれを使う理由

Temporal は、耐障害性があり、長時間実行される分散アプリケーションを構築するために設計されたワークフローオーケストレーションフレームワークです。Temporal は状態管理、リトライ、タイマー、および障害回復をバックエンドで管理するため、開発者はボイラープレートなオーケストレーションコードを書くことなく、アプリケーションロジックに集中できます。Temporal は Temporal Go SDK 経由で Go などの言語をサポートしています。

Go と併用して Temporal を使うことで得られる利点:

  • ワークフローは耐久性があり、再生可能です。
  • アクティビティのリトライとタイムアウトは自動的に処理されます。
  • システム状態は障害後も保持されます。
  • タスクオーケストレーションロジックは、Goの慣用的なコード内に記述されます。

コア概念: ワークフロー、アクティビティ、ワーカー

最初の Temporal Go アプリケーションを構築する前に、以下の重要な概念を理解しましょう。

ワークフロー

ワークフロー はアクティビティを呼び出す耐久性のある協調ロジックです。決定論的である必要があります — Temporal エンジンによって確実に再生できる必要があります。Go では、ワークフローは特別な workflow.Context パラメータを持つ通常の Go 関数です。

アクティビティ

アクティビティ は、非決定論的操作(I/O、外部 API 呼び出し)を含む作業単位です。アクティビティはワークフローの外側で実行され、ワークフローロジックに戻って結果を返します。

ワーカー

ワーカー はワークフローおよびアクティビティ関数をホストし、実行します。ワーカーは Temporal サーバーのタスクキューをポーリングし、タスクを処理します。起動前にワークフローとアクティビティを登録する必要があります。

タスクキュー

タスクキュー は、ワーカーが Temporal サーバーからタスクを受信する方法です。ワークフローおよびアクティビティは、使用するタスクキュー名を指定します。


Temporal を使用した Go プロジェクトの設定

前提条件

  • Go (1.16+)
  • Temporal Server (ローカルまたはクラウド)
  • Docker (ローカルサーバー用)
  • go.temporal.io/sdk 依存関係

Temporal Go SDK のインストール

go get go.temporal.io/sdk

Temporal 開発サーバーの起動

ローカル開発用:

docker run -d --network host temporalio/temporal-server

または Temporal CLI を使用:

temporal server start-dev

これにより、デフォルトで Temporal サーバーと Web UI が起動します。


設定: クライアントとタスクキュー

Temporal クライアントの作成

c, err := client.NewClient(client.Options{
    HostPort: "localhost:7233",
})
if err != nil {
    log.Fatal(err)
}
defer c.Close()

タスクキューの選択

ワーカー用の一意のタスクキューを定義します:

const TaskQueue = "order-processing-queue"

ワーカーとワークフロー開始者は、同じタスクキュー名を使用しなければなりません


Go の例: ワークフローとアクティビティ

単純なワークフローの定義

func SampleWorkflow(ctx workflow.Context, input string) (string, error) {
    ao := workflow.ActivityOptions{
        TaskQueue:           TaskQueue,
        ScheduleToCloseTimeout: time.Minute,
    }
    ctx = workflow.WithActivityOptions(ctx, ao)

    var result string
    err := workflow.ExecuteActivity(ctx, SampleActivity, input).Get(ctx, &result)
    if err != nil {
        return "", err
    }
    return result, nil
}

アクティビティの定義

func SampleActivity(ctx context.Context, message string) (string, error) {
    return fmt.Sprintf("Hello %s!", message), nil
}

ワーカーの登録と実行

w := worker.New(c, TaskQueue, worker.Options{})
w.RegisterWorkflow(SampleWorkflow)
w.RegisterActivity(SampleActivity)

err = w.Start()
if err != nil {
    log.Fatal(err)
}

ワークフロー実行の開始

we, err := c.ExecuteWorkflow(context.Background(), client.StartWorkflowOptions{
    ID:        "sample-workflow-id",
    TaskQueue: TaskQueue,
}, SampleWorkflow, "Developer")

Temporal と Go ワーカーのデプロイ

本番環境デプロイメントのオプション

  • セルフホスト型 Temporal クラスター: 永続ストレージ(Cassandra、MySQL)と共に Temporal サービス(frontend、history、matching)を設定します。
  • Temporal Cloud: SLA とスケーリングを備えたマネージド Temporal サービス。
  • Docker Compose または Kubernetes: ステージングまたは本番環境用。

以下の設定を行ってください:

  • 永続化レイヤー
  • ネームスペース設定
  • TLS/安全な認証(API キー、mTLS)

ワーカーのスケーラビリティ

ロードバランサーの背後に複数のワーカーをデプロイします。ワーカーは同じタスクキューに参加することで水平にスケールし、ワークロードを共有します。


Go でのワークフローとアクティビティのテスト

Temporal にはテストスイートが含まれています:

testSuite := testsuite.WorkflowTestSuite{}
env := testSuite.NewTestWorkflowEnvironment()

env.RegisterWorkflow(SampleWorkflow)
env.RegisterActivity(SampleActivity)

env.ExecuteWorkflow(SampleWorkflow, "Tester")

結果のアサーション:

var result string
require.NoError(t, workflowRun.Get(context.Background(), &result))
require.Equal(t, "Hello Tester!", result)

テストスイート によるテストは、デプロイ前にワークフローコードの決定論性と信頼性を保証します。リトライループ、コンテキストのデッドライン、タイマー駆動ロジックを伴う Temporal アクティビティを支える Go サービス — 特にそれらについて — Testing Concurrent Go Code with testing/synctest では、フェイククロックと孤立したバブルを使用して、その時間依存の動作を分離してユニットテストする方法を解説しています。


本番環境でのベストプラクティス

  • タイムアウトとリトライポリシー: アクティビティとワークフローに対して適切なタイムアウトとリトライを定義します。
  • 構造化ログ: トレースIDと相関メタデータを付随してログを出力します。
  • ワークフローID: 追跡可能性のために意味のあるワークフローIDを使用します。
  • 子ワークフローと ContinueAsNew: 履歴サイズを削減するために、複雑なロジックをモジュール化された実行に分割します。
  • メトリクスとモニタリング: Prometheus やその他の観測性ツールと統合します。
  • アクティビティからの信頼できるイベント公開: アクティビティがデータベースに書き込み、かつ別のサービスに通知する必要がある場合、データベースコミットとブローカー公開の間にイベントが失われないことを保証するために トランザクショナルアウトボックスパターン を使用します。

一般的な問題のトラブルシューティング

ワーカーがポーリングしない

  • 正しいタスクキュー名であることを確認します。
  • Temporal サーバーへのネットワーク接続を確認します。

ワークフローが開始されない

  • ワーカー起動前にワークフロー登録を検証します。
  • クライアント接続パラメータを確認します。

アクティビティの失敗

  • リトライポリシー設定を確認します。
  • Web UI でエラースタックトレースを確認します。

非決定論的なワークフローエラー

Temporal は決定論的なワークフロー実行を強制します。以下のコードをレビューしてください:

  • math/rand の使用
  • ワークフローロジック内のゴルーチン
  • ワークフロー内の外部システム呼び出し

常に、アクティビティを介して外部システムを呼び出す一方で、ワークフローが純粋なオーケストレーションコードであることを保証してください。


Temporal in Go を使用したワークフローアプリケーションの実装により、慣用的な Go のイディオムを使用して、状態を持ち、堅牢で、スケーラブルなビジネスロジックを構築できます。Temporal の保証された実行モデル、組み込みのリトライ、タスクキュー、および観測性サポートにより、オーケストレーションを再発明することなく、コアアプリケーションロジックに集中できます。単純なワークフローとアクティビティから始め、自信を持って複雑な分散オーケストレーションへスケールアップしましょう。


関連リンク

購読する

システム、インフラ、AIエンジニアリングの新記事をお届けします。