Menunggu menggunakan callback

Callback memungkinkan eksekusi alur kerja menunggu layanan lain membuat permintaan ke endpoint callback; permintaan tersebut melanjutkan eksekusi alur kerja.

Dengan callback, Anda dapat memberi sinyal ke alur kerja bahwa peristiwa tertentu telah terjadi, dan menunggu peristiwa tersebut tanpa melakukan polling. Misalnya, Anda dapat membuat alur kerja yang memberi tahu Anda saat produk tersedia kembali atau saat item telah dikirim; atau yang menunggu untuk memungkinkan interaksi manusia seperti meninjau pesanan atau memvalidasi terjemahan.

Halaman ini menunjukkan cara membuat alur kerja yang mendukung endpoint callback, dan yang menunggu permintaan HTTP dari proses eksternal tiba di endpoint tersebut. Anda juga dapat menunggu peristiwa menggunakan callback dan pemicu Eventarc.

Callback memerlukan penggunaan dua fungsi bawaan library standar:

Membuat endpoint yang menerima permintaan callback

Buat endpoint callback yang dapat menerima permintaan HTTP untuk mencapai endpoint tersebut.

  1. Ikuti langkah-langkah untuk membuat alur kerja baru, atau pilih alur kerja yang ada untuk memperbarui tetapi jangan deploy dulu.
  2. Dalam definisi alur kerja, tambahkan langkah untuk membuat endpoint callback:

    YAML

        - create_callback:
            call: events.create_callback_endpoint
            args:
                http_callback_method: "METHOD"
            result: callback_details
        

    JSON

        [
          {
            "create_callback": {
              "call": "events.create_callback_endpoint",
              "args": {
                "http_callback_method": "METHOD"
              },
              "result": "callback_details"
            }
          }
        ]
          

    Ganti METHOD dengan metode HTTP yang diharapkan, salah satu dari GET, HEAD, POST, PUT, DELETE, OPTIONS, atau PATCH. Defaultnya adalah POST.

    Hasilnya adalah peta, callback_details, dengan kolom url yang menyimpan URL endpoint yang dibuat.

    Endpoint callback kini siap menerima permintaan masuk dengan metode HTTP yang ditentukan. URL endpoint yang dibuat dapat digunakan untuk memicu panggilan balik dari proses di luar alur kerja; misalnya, dengan meneruskan URL ke fungsi Cloud Run.

  3. Dalam definisi alur kerja, tambahkan langkah untuk menunggu permintaan callback:

    YAML

        - await_callback:
            call: events.await_callback
            args:
                callback: ${callback_details}
                timeout: TIMEOUT
            result: callback_request
        

    JSON

        [
          {
            "await_callback": {
              "call": "events.await_callback",
              "args": {
                "callback": "${callback_details}",
                "timeout": TIMEOUT
              },
              "result": "callback_request"
            }
          }
        ]
          

    Ganti TIMEOUT dengan jumlah maksimum detik yang harus ditunggu alur kerja untuk permintaan. Nilai defaultnya adalah 43200 (12 jam). Jika waktu berlalu sebelum permintaan diterima, TimeoutError akan ditampilkan.

    Perhatikan bahwa ada durasi eksekusi maksimum. Untuk mengetahui informasi selengkapnya, lihat batas permintaan.

    Peta callback_details dari langkah create_callback sebelumnya diteruskan sebagai argumen.

  4. Deploy alur kerja Anda untuk menyelesaikan pembuatan atau pembaruannya.

    Saat permintaan diterima, semua detail permintaan disimpan dalam peta callback_request. Kemudian, Anda memiliki akses ke seluruh permintaan HTTP, termasuk header, isi, dan peta query untuk parameter kueri apa pun. Contoh:

    YAML

        http_request:
          body:
          headers: {...}
          method: GET
          query: {}
          url: "/v1/projects/350446661175/locations/us-central1/workflows/workflow-1/executions/46804f42-dc83-46d6-87e4-93962866ed81/callbacks/49c80102-74d2-49cd-a70e-805a9fded94f_2de9b413-6332-412d-99c3-d7e9b6eeeda2"
        received_time: 2021-06-24 12:49:16.988072651 -0700 PDT m=+742581.005780667
        type: HTTP
        

    JSON

        {
           "http_request":{
              "body":null,
              "headers":{
                 ...
              },
              "method":"GET",
              "query":{
              },
              "url":"/v1/projects/350446661175/locations/us-central1/workflows/workflow-1/executions/46804f42-dc83-46d6-87e4-93962866ed81/callbacks/49c80102-74d2-49cd-a70e-805a9fded94f_2de9b413-6332-412d-99c3-d7e9b6eeeda2"
           },
           "received_time":"2021-06-24 12:49:16.988072651 -0700 PDT m=+742581.005780667",
           "type":"HTTP"
        }
          

    Jika isi HTTP adalah teks atau JSON, Workflows akan mencoba mendekode isi; jika tidak, byte mentah akan ditampilkan.

Mengizinkan permintaan ke endpoint callback

Untuk mengirim permintaan ke endpoint callback,layanan Google Cloud seperti Cloud Run dan fungsi Cloud Run, serta layanan pihak ketiga, harus diizinkan untuk melakukannya dengan memiliki izin Identity and Access Management (IAM) yang sesuai; khususnya, workflows.callbacks.send (tercakup dalam peran Workflows Invoker).

Membuat permintaan langsung

Cara paling sederhana untuk membuat kredensial berumur pendek untuk akun layanan adalah dengan membuat permintaan langsung. Dua identitas terlibat dalam alur ini: pemanggil, dan akun layanan yang kredensialnya dibuat. Panggilan ke alur kerja dasar di halaman ini adalah contoh permintaan langsung. Untuk mengetahui informasi selengkapnya, lihat Menggunakan IAM untuk mengontrol akses dan Izin permintaan langsung.

Membuat token akses OAuth 2.0

Untuk mengizinkan aplikasi memanggil endpoint callback, Anda dapat membuat token akses OAuth 2.0 untuk akun layanan yang terkait dengan alur kerja. Dengan asumsi bahwa Anda memiliki izin yang diperlukan (untuk peran Workflows Editor atau Workflows Admin, dan Service Account Token Creator), Anda juga dapat membuat token sendiri dengan menjalankan metode generateAccessToken.

Jika permintaan generateAccessToken berhasil, isi respons yang ditampilkan akan berisi token akses OAuth 2.0 dan waktu habis masa berlaku. (Secara default, token akses OAuth 2.0 berlaku maksimum 1 jam.) Misalnya:

  {
  "accessToken": "eyJ0eXAi...NiJ9",
  "expireTime": "2020-04-07T15:01:23.045123456Z"
  }
Kode accessToken kemudian dapat digunakan dalam panggilan curl ke URL endpoint callback seperti dalam contoh berikut:
  curl -X GET -H "Authorization: Bearer ACCESS_TOKEN_STRING" CALLBACK_URL
  curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer ACCESS_TOKEN_STRING" -d '{"foo" : "bar"}' CALLBACK_URL

Membuat token OAuth untuk fungsi Cloud Run

Jika Anda memanggil callback dari fungsi Cloud Run menggunakan akun layanan yang sama dengan alur kerja dan dalam project yang sama, Anda dapat membuat token akses OAuth dalam fungsi itu sendiri. Contoh:

const {GoogleAuth} = require('google-auth-library');
const auth = new GoogleAuth();
const token = await auth.getAccessToken();
console.log("Token", token);

try {
  const resp = await fetch(url, {
      method: 'POST',
      headers: {
          'accept': 'application/json',
          'content-type': 'application/json',
          'authorization': `Bearer ${token}`
      },
      body: JSON.stringify({ approved })
  });
  console.log("Response = ", JSON.stringify(resp));

  const result = await resp.json();
  console.log("Outcome = ", JSON.stringify(result));

Untuk mengetahui konteks selengkapnya, lihat tutorial tentang Membuat alur kerja yang memerlukan interaksi manusia menggunakan callback.

Meminta akses offline

Token akses akan habis masa berlakunya secara berkala dan menjadi kredensial yang tidak valid untuk permintaan API terkait. Anda dapat me-refresh token akses tanpa meminta izin pengguna jika Anda meminta akses offline ke cakupan yang terkait dengan token. Meminta akses offline adalah persyaratan untuk aplikasi apa pun yang perlu mengakses Google API saat pengguna tidak ada. Untuk mengetahui informasi selengkapnya, lihat Memperbarui token akses (akses offline).

Memanggil alur kerja tepat satu kali menggunakan callback

Callback sepenuhnya idempotent yang berarti Anda dapat mencoba lagi callback jika gagal tanpa menghasilkan hasil atau efek samping yang tidak terduga.

Setelah Anda membuat endpoint callback, URL siap menerima permintaan masuk, dan biasanya ditampilkan ke pemanggil sebelum panggilan yang sesuai ke await_callback dilakukan. Namun, jika URL callback belum diterima saat langkah await_callback dieksekusi, eksekusi alur kerja akan diblokir hingga endpoint diterima (atau terjadi waktu tunggu). Setelah diterima, eksekusi alur kerja akan dilanjutkan, dan callback akan diproses.

Setelah Anda mengeksekusi langkah create_callback_endpoint dan membuat endpoint callback, satu slot callback tersedia untuk alur kerja. Saat permintaan callback diterima, slot ini diisi dengan payload callback hingga callback dapat diproses. Saat langkah await_callback dieksekusi, callback diproses, dan slot dikosongkan serta tersedia untuk callback lain. Kemudian, Anda dapat menggunakan kembali endpoint callback dan memanggil await_callback lagi.

Jika await_callback hanya dipanggil satu kali, tetapi callback kedua diterima, salah satu skenario berikut terjadi dan kode status HTTP yang sesuai akan ditampilkan:

  • HTTP 429: Too Many Requests menunjukkan bahwa callback pertama diterima dengan berhasil, tetapi belum diproses; callback tersebut masih menunggu untuk diproses. Panggilan balik kedua ditolak oleh alur kerja.

  • HTTP 200: Success menunjukkan bahwa callback pertama diterima dengan berhasil dan respons ditampilkan. Callback kedua disimpan dan mungkin tidak akan pernah diproses kecuali jika await_callback dipanggil untuk kedua kalinya. Jika alur kerja berakhir sebelum hal itu terjadi, permintaan callback kedua tidak akan pernah diproses dan akan dibatalkan.

  • HTTP 404: Page Not Found menunjukkan bahwa alur kerja tidak lagi berjalan. Callback pertama telah diproses dan alur kerja telah selesai, atau alur kerja telah gagal. Untuk menentukannya, Anda harus membuat kueri status eksekusi alur kerja.

Callback paralel

Saat langkah-langkah dijalankan secara paralel, dan callback dibuat oleh thread induk dan ditunggu di langkah-langkah turunan, pola yang sama seperti yang dijelaskan sebelumnya akan diikuti.

Dalam contoh berikut, saat langkah create_callback_endpoint dieksekusi, satu slot callback dibuat. Setiap panggilan berikutnya ke await_callback akan membuka slot callback baru. Sepuluh callback dapat dilakukan secara bersamaan, jika semua thread berjalan dan menunggu sebelum permintaan callback dilakukan. Callback tambahan dapat dilakukan, tetapi akan disimpan dan tidak pernah diproses.

YAML

  - createCallbackInParent:
    call: events.create_callback_endpoint
    args:
      http_callback_method: "POST"
    result: callback_details
  - parallelStep:
    parallel:
        for:
            range: [1, 10]
            value: loopValue
            steps:
              - waitForCallbackInChild:
                  call: events.await_callback
                  args:
                      callback: ${callback_details}

JSON

  [
    {
      "createCallbackInParent": {
        "call": "events.create_callback_endpoint",
        "args": {
          "http_callback_method": "POST"
        },
        "result": "callback_details"
      }
    },
    {
      "parallelStep": {
        "parallel": {
          "for": {
            "range": [
              1,
              10
            ],
            "value": "loopValue",
            "steps": [
              {
                "waitForCallbackInChild": {
                  "call": "events.await_callback",
                  "args": {
                    "callback": "${callback_details}"
                  }
                }
              }
            ]
          }
        }
      }
    }
  ]

Perhatikan bahwa callback diproses dalam urutan yang sama dengan setiap panggilan yang dilakukan oleh cabang ke await_callback. Namun, urutan eksekusi cabang bersifat non-deterministik dan dapat menghasilkan hasil menggunakan berbagai jalur. Untuk mengetahui informasi selengkapnya, lihat Langkah paralel.

Mencoba alur kerja callback dasar

Anda dapat membuat alur kerja dasar, lalu menguji panggilan ke endpoint callback alur kerja tersebut menggunakan curl. Anda harus memiliki izin Workflows Editor atau Workflows Admin yang diperlukan untuk project tempat alur kerja berada.

  1. Buat dan deploy alur kerja berikut, lalu jalankan alur kerja tersebut.

    YAML

        - create_callback:
            call: events.create_callback_endpoint
            args:
                http_callback_method: "GET"
            result: callback_details
        - print_callback_details:
            call: sys.log
            args:
                severity: "INFO"
                text: ${"Listening for callbacks on " + callback_details.url}
        - await_callback:
            call: events.await_callback
            args:
                callback: ${callback_details}
                timeout: 3600
            result: callback_request
        - print_callback_request:
            call: sys.log
            args:
                severity: "INFO"
                text: ${"Received " + json.encode_to_string(callback_request.http_request)}
        - return_callback_result:
            return: ${callback_request.http_request}
        

    JSON

        [
          {
            "create_callback": {
              "call": "events.create_callback_endpoint",
              "args": {
                "http_callback_method": "GET"
              },
              "result": "callback_details"
            }
          },
          {
            "print_callback_details": {
              "call": "sys.log",
              "args": {
                "severity": "INFO",
                "text": "${\"Listening for callbacks on \" + callback_details.url}"
              }
            }
          },
          {
            "await_callback": {
              "call": "events.await_callback",
              "args": {
                "callback": "${callback_details}",
                "timeout": 3600
              },
              "result": "callback_request"
            }
          },
          {
            "print_callback_request": {
              "call": "sys.log",
              "args": {
                "severity": "INFO",
                "text": "${\\"Received \\" + json.encode_to_string(callback_request.http_request)}"
              }
            }
          },
          {
            "return_callback_result": {
              "return": "${callback_request.http_request}"
            }
          }
        ]
          

    Setelah mengeksekusi alur kerja, status eksekusi alur kerja adalah ACTIVE hingga permintaan callback diterima atau waktu tunggu berakhir.

  2. Konfirmasi status eksekusi dan ambil URL callback:

    Konsol

    1. Di konsol Google Cloud , buka halaman Workflows:

      Buka Workflows
    2. Klik nama alur kerja yang baru saja Anda jalankan.

      Status eksekusi alur kerja ditampilkan.

    3. Klik tab Logs.
    4. Cari entri log yang mirip dengan berikut ini:

      Listening for callbacks on https://workflowexecutions.googleapis.com/v1/projects/...
    5. Salin URL callback untuk digunakan di perintah berikutnya.

    gcloud

    1. Pertama, ambil ID eksekusi:
      gcloud logging read "Listening for callbacks" --freshness=DURATION
      Ganti DURATION dengan jumlah waktu yang sesuai untuk membatasi entri log yang ditampilkan (jika Anda telah menjalankan alur kerja beberapa kali).

      Misalnya, --freshness=t10m menampilkan entri log yang tidak lebih lama dari 10 menit. Untuk mengetahui detailnya, lihat gcloud topic datetimes.

      ID eksekusi ditampilkan. Perhatikan bahwa URL callback juga ditampilkan di kolom textPayload. Salin kedua nilai untuk digunakan pada langkah berikutnya.

    2. Jalankan perintah berikut:
      gcloud workflows executions describe WORKFLOW_EXECUTION_ID --workflow=WORKFLOW_NAME
      Status eksekusi alur kerja ditampilkan.
  3. Sekarang Anda dapat memanggil endpoint callback menggunakan perintah curl:
    curl -X GET -H "Authorization: Bearer $(gcloud auth print-access-token)" CALLBACK_URL

    Perhatikan bahwa untuk endpoint POST, Anda harus menggunakan header representasi Content-Type. Contoh:

    curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $(gcloud auth print-access-token)" -d '{"foo" : "bar"}' CALLBACK_URL

    Ganti CALLBACK_URL dengan URL yang Anda salin di langkah sebelumnya.

  4. Melalui konsol Google Cloud atau menggunakan Google Cloud CLI, konfirmasi bahwa status eksekusi alur kerja kini adalah SUCCEEDED.
  5. Cari entri log dengan textPayload yang ditampilkan yang menyerupai berikut ini:
    Received {"body":null,"headers":...

Sampel

Contoh ini menunjukkan sintaksisnya.

Menangkap error waktu tunggu habis

Sampel ini menambahkan ke sampel sebelumnya dengan menangkap error waktu tunggu, dan menulis error ke log sistem.

YAML

    main:
      steps:
        - create_callback:
            call: events.create_callback_endpoint
            args:
                http_callback_method: "GET"
            result: callback_details
        - print_callback_details:
            call: sys.log
            args:
                severity: "INFO"
                text: ${"Listening for callbacks on " + callback_details.url}
        - await_callback:
            try:
                call: events.await_callback
                args:
                    callback: ${callback_details}
                    timeout: 3600
                result: callback_request
            except:
                as: e
                steps:
                    - log_error:
                        call: sys.log
                        args:
                            severity: "ERROR"
                            text: ${"Received error " + e.message}
                        next: end
        - print_callback_result:
            call: sys.log
            args:
                severity: "INFO"
                text: ${"Received " + json.encode_to_string(callback_request.http_request)}
    

JSON

    {
      "main": {
        "steps": [
          {
            "create_callback": {
              "call": "events.create_callback_endpoint",
              "args": {
                "http_callback_method": "GET"
              },
              "result": "callback_details"
            }
          },
          {
            "print_callback_details": {
              "call": "sys.log",
              "args": {
                "severity": "INFO",
                "text": "${\"Listening for callbacks on \" + callback_details.url}"
              }
            }
          },
          {
            "await_callback": {
              "try": {
                "call": "events.await_callback",
                "args": {
                  "callback": "${callback_details}",
                  "timeout": 3600
                },
                "result": "callback_request"
              },
              "except": {
                "as": "e",
                "steps": [
                  {
                    "log_error": {
                      "call": "sys.log",
                      "args": {
                        "severity": "ERROR",
                        "text": "${\"Received error \" + e.message}"
                      },
                      "next": "end"
                    }
                  }
                ]
              }
            }
          },
          {
            "print_callback_result": {
              "call": "sys.log",
              "args": {
                "severity": "INFO",
                "text": "${\"Received \" + json.encode_to_string(callback_request.http_request)}"
              }
            }
          }
        ]
      }
    }
      

Menunggu callback dalam loop percobaan ulang

Contoh ini mengubah contoh sebelumnya dengan menerapkan langkah percobaan ulang. Dengan menggunakan predikat percobaan ulang kustom, alur kerja mencatat peringatan saat terjadi waktu tunggu dan kemudian mencoba ulang penantian di endpoint callback, hingga lima kali. Jika kuota percobaan ulang habis sebelum callback diterima, error waktu tunggu terakhir akan menyebabkan alur kerja gagal.

YAML

    main:
      steps:
        - create_callback:
            call: events.create_callback_endpoint
            args:
                http_callback_method: "GET"
            result: callback_details
        - print_callback_details:
            call: sys.log
            args:
                severity: "INFO"
                text: ${"Listening for callbacks on " + callback_details.url}
        - await_callback:
            try:
                call: events.await_callback
                args:
                    callback: ${callback_details}
                    timeout: 60.0
                result: callback_request
            retry:
                predicate: ${log_timeout}
                max_retries: 5
                backoff:
                    initial_delay: 1
                    max_delay: 10
                    multiplier: 2
        - print_callback_result:
            call: sys.log
            args:
                severity: "INFO"
                text: ${"Received " + json.encode_to_string(callback_request.http_request)}
    log_timeout:
        params: [e]
        steps:
          - when_to_repeat:
              switch:
                - condition: ${"TimeoutError" in e.tags}
                  steps:
                      - log_error_and_retry:
                          call: sys.log
                          args:
                              severity: "WARNING"
                              text: "Timed out waiting for callback, retrying"
                      - exit_predicate:
                          return: true
          - otherwise:
              return: false
    

JSON

    {
      "main": {
        "steps": [
          {
            "create_callback": {
              "call": "events.create_callback_endpoint",
              "args": {
                "http_callback_method": "GET"
              },
              "result": "callback_details"
            }
          },
          {
            "print_callback_details": {
              "call": "sys.log",
              "args": {
                "severity": "INFO",
                "text": "${\"Listening for callbacks on \" + callback_details.url}"
              }
            }
          },
          {
            "await_callback": {
              "try": {
                "call": "events.await_callback",
                "args": {
                  "callback": "${callback_details}",
                  "timeout": 60
                },
                "result": "callback_request"
              },
              "retry": {
                "predicate": "${log_timeout}",
                "max_retries": 5,
                "backoff": {
                  "initial_delay": 1,
                  "max_delay": 10,
                  "multiplier": 2
                }
              }
            }
          },
          {
            "print_callback_result": {
              "call": "sys.log",
              "args": {
                "severity": "INFO",
                "text": "${\"Received \" + json.encode_to_string(callback_request.http_request)}"
              }
            }
          }
        ]
      },
      "log_timeout": {
        "params": [
          "e"
        ],
        "steps": [
          {
            "when_to_repeat": {
              "switch": [
                {
                  "condition": "${\"TimeoutError\" in e.tags}",
                  "steps": [
                    {
                      "log_error_and_retry": {
                        "call": "sys.log",
                        "args": {
                          "severity": "WARNING",
                          "text": "Timed out waiting for callback, retrying"
                        }
                      }
                    },
                    {
                      "exit_predicate": {
                        "return": true
                      }
                    }
                  ]
                }
              ]
            }
          },
          {
            "otherwise": {
              "return": false
            }
          }
        ]
      }
    }
      

Langkah berikutnya