用时间构建更可靠的应用程序
#go #docker #microservices #temporal

Temporal微服务编排器是时间平台的关键组成部分,在微服务相互交互以完成复杂的工作流程时,它们提供了协调和管理。乐队提供了诸如任务管理,错误处理,重试和补偿等功能,以确保即使面对失败或意外事件,也可以准确,一致地执行工作流程。
在应用程序开发中使用时间段可以帮助简化复杂的,时间敏感的工作流程的开发,并提高应用程序的可靠性和可扩展性。通过使用临时协调员,开发人员可以专注于实施其业务逻辑,并将基础微服务的协调和管理放在平台上。

插图

微服务体系结构在XYZ公司中使用。微服务方法的软件开发方法可以帮助他们的团队更快地部署,但它带来了一些问题,其中之一就是数据一致性。如何将微服务的数据变化传播到微服务B和C?通过活动发送吗?
是的,这有效,但是如果B更新自身并且C有打ic,并且无法进行更新?
那么这意味着我们需要有一种机制,使我们能够处理此类失败,进行重试以及还有什么?上面描述的几个情况需要我们写失败和重试逻辑?
因此,将暂时性用作微服务编排器有助于我们解决上述问题。

先决条件

本教程的先决条件正在Go和Docker安装和配置。
通过打开命令提示符并键入以下命令
来验证您已安装了已安装的操作

go version

它应该返回类似于此的东西

go version go1.19.3 darwin/amd64

此外,还安装了
的验证码头

docker --version
#Docker version 20.10.22, build 3a2c30b

选择

暂时性可以在您的项目目录内外使用,只要您的应用程序可以访问它即可。您的个人需求和项目的基础架构将决定您的选择。
如果您决定在项目目录内执行时间内执行时间内执行时间,则维护项目的依赖项和设置可能会更简单。但是,在项目目录之外运行时间段可能会提高关注程度的分离程度,并使管理共享单个时间实例的众多项目变得更加简单。
但是,在本指南中,我们将运行项目目录内的临时服务器。

Hello-workflow

我们将使用简单的Hello-Workflow来编排分布式系统中的任务。入门者和工人是使分布式系统运行的两个主要组件,在本指南中,我们将实施起动器和工人。
这是我们的项目目录的外观。

├── dynamicconfig
│ ├── development-cass.yaml
│ └── development-sql.yaml
| └── docker.yaml
|
├── src
│ ├── helloworkflow 
| |        └── workflow.go
│ ├── starter
| |        └── main.go
| └── worker
|          └── main.go
├── .env
├── docker-compose.yml

您创建一个新的GO项目,例如TutorialGuide和CD,然后在文本编辑器上打开目录。您可以在项目的根目录中创建DynamicConfig文件夹,并添加3个YAML文件。这些文件需要使临时服务器上升。

# development-cass.yaml
system.forceSearchAttributesCacheRefreshOnRead:
  - value: true # Dev setup only. Please don't turn this on in production.
    constraints: {}
# development-sql.yaml
limit.maxIDLength:
  - value: 255
    constraints: {}
system.forceSearchAttributesCacheRefreshOnRead:
  - value: true # Dev setup only. Please don't turn this on in production.
    constraints: {}
# docker.yaml
apiVersion: v1
clusters:
  - cluster:
      certificate-authority: path-to-your-ca.rt #e.g/System/Volumes/Data/Users/<name>/.minikube/ca.crt
      server: https://192.168.99.100:8443
    name: minikube
contexts:
  - context:
      cluster: minikube
      user: minikube
    name: minikube
current-context: minikube
kind: Config
preferences: {}
users:
  - name: minikube
    user:
      client-certificate: path-to-proxy-client-ca.key #e.g/System/Volumes/Data/Users/<name>/.minikube/proxy-client-ca.key
      client-key: path-to-proxy-client-ca.key #e.g/System/Volumes/Data/Users/<name>/.minikube/proxy-client-ca.key

使用docker.yaml文件覆盖默认的动态配置值(创建服务配置时将指定它们)。有关docker.yaml文件以及如何使用它的更多信息[https://github.com/temporalio/docker-compose/tree/main/dynamicconfig]

# docker-compose.yaml
version: '3.5'

services:
  elasticsearch:
    container_name: temporal-elasticsearch
    environment:
      - cluster.routing.allocation.disk.threshold_enabled=true
      - cluster.routing.allocation.disk.watermark.low=512mb
      - cluster.routing.allocation.disk.watermark.high=256mb
      - cluster.routing.allocation.disk.watermark.flood_stage=128mb
      - discovery.type=single-node
      - ES_JAVA_OPTS=-Xms256m -Xmx256m
      - xpack.security.enabled=false
    image: elasticsearch:${ELASTICSEARCH_VERSION}
    networks:
      - temporal-network
    expose:
      - 9200
    volumes:
      - /var/lib/elasticsearch/data
  postgresql:
    container_name: temporal-postgresql
    environment:
      POSTGRES_PASSWORD: temporal
      POSTGRES_USER: temporal
    image: postgres:${POSTGRESQL_VERSION}
    networks:
      - temporal-network
    expose:
      - 5432
    volumes:
      - /var/lib/postgresql/data
  temporal:
    container_name: temporal
    depends_on:
      - postgresql
      - elasticsearch
    environment:
      - DB=postgresql
      - DB_PORT=5432
      - POSTGRES_USER=temporal
      - POSTGRES_PWD=temporal
      - POSTGRES_SEEDS=postgresql
      - DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development-sql.yaml
      - ENABLE_ES=true
      - ES_SEEDS=elasticsearch
      - ES_VERSION=v7
    image: temporalio/auto-setup:${TEMPORAL_VERSION}
    networks:
      - temporal-network
    ports:
      - 7233:7233
    labels:
      kompose.volume.type: configMap
    volumes:
      - ./dynamicconfig:/etc/temporal/config/dynamicconfig
  temporal-admin-tools:
    container_name: temporal-admin-tools
    depends_on:
      - temporal
    environment:
      - TEMPORAL_CLI_ADDRESS=temporal:7233
    image: temporalio/admin-tools:${TEMPORAL_VERSION}
    networks:
      - temporal-network
    stdin_open: true
    tty: true
  temporal-ui:
    container_name: temporal-ui
    depends_on:
      - temporal
    environment:
      - TEMPORAL_ADDRESS=temporal:7233
      - TEMPORAL_CORS_ORIGINS=http://localhost:3000
    image: temporalio/ui:${TEMPORAL_UI_VERSION}
    networks:
      - temporal-network
    ports:
      - 8080:8080

networks:
  temporal-network:
    driver: bridge
    name: temporal-network

添加docker-compose.yaml文件所需的环境变量。

# .env
COMPOSE_PROJECT_NAME=temporal
CASSANDRA_VERSION=3.11.9
ELASTICSEARCH_VERSION=7.16.2
MYSQL_VERSION=8
POSTGRESQL_VERSION=13
TEMPORAL_VERSION=1.19.1
TEMPORAL_UI_VERSION=2.9.1

时间服务器运行的工作流的源代码位于workflow.go文件中。它负责生成活动,监视其完成并控制工作流程。它指定了工作流程必须采取的每个动作,并且是由事件开始的,例如队列中的消息或在系统中添加新数据项。

// workflow.go
package helloworkflow

import (
 "context"
 "time"

 "go.temporal.io/sdk/workflow"
)

func Workflow(ctx workflow.Context, name string) (string, error) {
 ao := workflow.ActivityOptions{
  ScheduleToStartTimeout: time.Minute,
  StartToCloseTimeout:    time.Minute,
 }

 ctx = workflow.WithActivityOptions(ctx, ao)

 logger := workflow.GetLogger(ctx)

 var result string
 err := workflow.ExecuteActivity(ctx, Activity, name).Get(ctx, &result)
 if err != nil {
  logger.Error("Activity failed", "Error", err)
 }

 return result, nil
}

func Activity(ctx context.Context, name string) (string, error) {
 return "Hello " + name, nil
}

启动器负责协调系统内的工作流执行。它负责安排执行任务并使任务状态保持最新。启动器还充当用户通过其API与系统进行交互的网关。
现在,我们将它们添加到我们的主。

// starter/main.go
package main

import (
 "context"
 "log"

 "github.com/theifedayo/hello-workflow/src/helloworkflow"
 "go.temporal.io/sdk/client"
)

func main() {
 c, err := client.NewClient(client.Options{

 })
 if err != nil {
  log.Fatalln("Unable to make client", err)
 }

 defer c.Close()

 workflowOptions := client.StartWorkflowOptions{
  ID:        "hello_world_workflowID",
  TaskQueue: "hello-world",
 }

 we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, helloworkflow.Workflow, "ifedayo")
 if err != nil {
  log.Fatalln("Unable to execute workflow", err)
 }

 var result string
 // store the result of the run
 err = we.Get(context.Background(), &result)
 if err != nil {
  log.Fatalln("Unable to get workflow result", err)
 }
 log.Println("workflow result:", result)
}

工人负责执行任务。这些工人部署在系统中的不同节点上,他们的工作是从入门者那里接收任务并执行它们。工人还负责将结果发送回入门者。工人可以根据工作量来缩放或向下缩放。

// worker/main.go
package main

import (
 "log"

 "github.com/theifedayo/hello-workflow/src/helloworkflow"
 "go.temporal.io/sdk/client"
 "go.temporal.io/sdk/worker"
)

func main() {
 c, err := client.NewClient(client.Options{})
 if err != nil {
  log.Fatalln("Unable to make client", err)
 }

 defer c.Close()

 w := worker.New(c, "hello-world", worker.Options{})
 w.RegisterWorkflow(helloworkflow.Workflow)
 w.RegisterActivity(helloworkflow.Activity)

 err = w.Run(worker.InterruptCh())
 if err != nil {
  log.Fatalln("Unable to start workflow", err)
 }
}

开始一切

要启动我们的工作流程,我们需要临时服务器。转到您的终端和CD到您的项目目录并运行

docker-compose up

screenshot of docker services up
这将在我们的Docker-Compose.yaml中启动所有服务。
接下来,我们开始我们的工人。
通常,您应该在入门者之前开始工人,因为起动器通常取决于工人可以执行其任务。这意味着在开始者开始协调其活动之前,应启动和初始化工人。

go run src/worker/main.go
2023/02/16 14:03:42 INFO  No logger configured for temporal client. Created default one.
2023/02/16 14:03:43 INFO  Started Worker Namespace default TaskQueue hello-world WorkerID 4465@Ifedayos-MacBook-Pro.local@

最后,我们开始工作流程

go run src/starter/main.go
2023/02/16 14:05:52 INFO  No logger configured for temporal client. Created default one.
2023/02/16 14:05:52 workflow result: Hello ifedayo

查看工作流程

在Localhost上导航到浏览器:8080,我们有一个UI,提供了有关工作流程的更多信息

screenshot of list of workflows

screenshot of detail of a selected workflow
Perimal还提供了一个CLI工具,用于与时间服务器TCTL进行交互,该工具还执行各种管理任务,例如启动和停止工作流程,查询工作流程信息以及管理工作流程执行。

tctl workflow list
WORKFLOW TYPE |      WORKFLOW ID       |                RUN ID                | TASK QUEUE  | START TIME | EXECUTION TIME | END TIME  
Workflow      | hello_world_workflowID | 0454098f-cdd9-4f64-af8b-ab77a6f86c35 | hello-world | 13:05:52   | 13:05:52       | 13:05:52

结论

在撰写本文中,我们已经了解了什么时间,为什么使用它,在公司中使用它的说明,设置时间服务器的选择,如何在GO项目中启动服务器,运行Workflow和工人,最后在UI或CLI中查看您的工作流程。

欢呼ð¥!您更接近使用暂时性构建更可靠的应用程序的一步。