欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

启动体验 Cadence:一款开源工作流自动化工具

最编程 2024-07-26 20:23:46
...

Cadence简化了分布式系统的复杂性,使开发人员能够专注于创建为高耐久性、可用性和可扩展性而构建的应用程序。

现代应用程序需要在长期运行的业务流程、内部服务和第三方API之间进行复杂的交互。说这是对开发者的挑战是温和的。管理这些流程意味着跟踪复杂的状态,准备对异步事件的响应,并与往往不可靠的外部依赖关系进行沟通。

开发人员通常用同样错综复杂的解决方案来应对这些复杂的挑战,组装笨重的系统,利用无状态服务、数据库、重试算法和工作调度队列。由于这些复杂的系统掩盖了他们自己的业务逻辑,可用性问题很常见,通常是由于应用程序对分散的和未经验证的组件的依赖。开发人员的生产力经常被牺牲,以保持这些分散的、有问题的系统不崩溃。

设计一个分布式应用

Cadence通过提供一个高度可扩展的无故障代码平台解决了这些问题。Cadence用它的无故障代码抽象出了实现容错和持久性的通常挑战。

一个标准的Cadence应用程序包括一个Cadence服务、工作流、活动工作者和外部客户。如果需要,可以接受将工作流工作者、活动工作者和外部客户的角色共同安置在一个单一的应用进程中。

Cadence服务

Image of client application and Cadence service

Image by: (Ben Slater, CC BY-SA 4.0)

Cadence的核心是其多租户服务和它所实现的高可扩展性。一个强类型的gRPC API暴露了所有Cadence服务功能。一个Cadence集群可以在多个节点上运行多种服务,包括。

  • 前端 一个无状态的服务,处理传入的工人请求,其实例由外部负载平衡器支持。
  • 历史服务 处理工作流步骤和活动协调的核心逻辑。
  • 匹配服务 将工作流或活动任务与准备完成这些任务的工作者相匹配。
  • 内部工作者服务 通过引入Cadence工作流和活动来满足内部要求(如归档)。
  • 工作者 作为Cadence客户端应用程序的功能,执行用户创建的工作流和活动逻辑。

默认情况下,Cadence支持Apache Cassandra、MySQL、PostgreSQL、CockroachDB和TiDB作为持久性存储,以及ElasticSearch和OpenSearch用于列出具有复杂谓语的工作流程。

编程和开发

红帽开发者博客

编程小抄

免费试用。红帽学习订阅

电子书。用Bash编程的介绍

Bash shell脚本小抄

电子书。现代化的企业Java

因为Cadence服务是多租户的,所以一个服务可以为一个或多个应用程序服务。可以用docker-compose配置一个本地的Cadence服务实例,用于本地开发。Cadence服务维护工作流状态、相关的持久计时器和内部 "任务列表 "队列,以将任务发送给外部工作者。

除了Cadence服务本身之外。

  • 工作流工作者:在Cadence服务外部主持无故障的代码。Cadence服务向这些工作者发送 "决策任务"。工作者将任务传递给工作流代码,并将完成的 "决策 "传回给Cadence服务。工作流代码可以用任何能够与Cadence API通信的语言来实现:目前可以使用生产就绪的Java和Go客户端。

  • 活动工作者:主持 "活动",或执行应用程序特定动作的代码,如服务调用、数据库记录更新和文件下载。活动的特点是任务路由到特定的进程、心跳、无限重试和无限执行时间。Cadence服务将活动任务发送给这些工作者,他们完成任务并报告完成情况。

  • 外部客户端:能够创建工作流实例,或 "执行"。UIs、微服务或CLI等外部客户端使用StartWorkflowExecution Cadence服务API调用来实现执行。外部客户端也能够通知工作流的异步外部事件、同步工作流状态查询、等待同步工作流完成、工作流重启、取消,以及用List API搜索特定的工作流。

开始使用Cadence

在这个例子中,我们将使用Cadence的Java客户端。该客户端可从GitHub获得JavaDoc文档可在此找到。你也可以查看最新的发布版本

首先,像这样把cadence-client作为一个依赖项添加到你的pom.xml文件中。

<dependency>

<groupId>com.uber.cadence</groupId>

<artifactId>cadence-client</artifactId>

<version>LATEST.RELEASE.VERSION</version>

</dependency>

或者,你也可以使用build.gradle

编译组:'com.uber.cadence',名称:'cadence-client',版本:'LATEST.RELEASE.VERSION'

使用Cadence的Java Hello World

要了解Cadence的能力,最好的办法就是去尝试,所以这里有一个简单的 "Hello World "例子,你可以尝试一下。首先,在你的Java项目中添加Cadence Java客户端的依赖。使用Gradle,这个依赖关系看起来像这样。

编译组:'com.uber.cadence',名称:'cadence-client',版本:'<latest_version>'

也添加 cadence-client 需要的这些依赖项。

编译组: 'commons-configuration', name: 'commons-configuration', version: '1.9'

编译组:'ch.qos.logback',名称:'logback-classic',版本:'1.2.3'

然后编译这段代码。

import com.uber.cadence.workflow.Workflow;
import com.uber.cadence.workflow.WorkflowMethod;
import org.slf4j.Logger;
public class GettingStarted {
private static Logger logger = Workflow.getLogger(GettingStarted.class);
public interface HelloWorld {
@WorkflowMethod
void sayHello(String name);
}
}

如果你在构建文件时遇到问题,这些Cadence Java样本可以帮助你。

接下来,把这个logback配置文件放到你的classpath中。

<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="io.netty" level="INFO"/>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>

现在创建Hello World工作流。添加带有sayHello方法的HelloWorldImpl,它记录并返回 "Hello ..."。

import com.uber.cadence.worker.Worker;
import com.uber.cadence.workflow.Workflow;
import com.uber.cadence.workflow.WorkflowMethod;
import org.slf4j.Logger;
public class GettingStarted {
private static Logger logger = Workflow.getLogger(GettingStarted.class);
public interface HelloWorld {
@WorkflowMethod
void sayHello(String name);
}
public static class HelloWorldImpl implements HelloWorld {
@Override
public void sayHello(String name) {
logger.info("Hello " + name + "!");
}
}
}

将工作流实现注册到Cadence框架中,用一个连接到Cadence服务的工作者。工作者将默认连接到本地运行的Cadence服务。

public static void main(String[] args) {
WorkflowClient workflowClient =
WorkflowClient.newInstance(
new WorkflowServiceTChannel(ClientOptions.defaultInstance()),
WorkflowClientOptions.newBuilder().setDomain(DOMAIN).build());
// Get worker to poll the task list.
WorkerFactory factory = WorkerFactory.newInstance(workflowClient);
Worker worker = factory.newWorker(TASK_LIST);
worker.registerWorkflowImplementationTypes(HelloWorldImpl.class);
factory.start();
}

现在你已经准备好运行工作者程序了。下面是一个示例日志。

13:35:02.575 [main] INFO c.u.c.s.WorkflowServiceTChannel - Initialized TChannel for service cadence-frontend, LibraryVersion: 2.2.0, FeatureVersion: 1.0.0

13:35:02.671 [main] INFO c.u.cadence.internal.worker.Poller - start():Poller{options=PollerOptions{maximumPollRateIntervalMilliseconds=1000, maximumPollRatePerSecond=0.0, pollBackoffCoefficient=2.0, pollBackoffInitialInterval=PT0.2S, pollBackoffMaximumInterval=PT20S, pollThreadCount=1, pollThreadNamePrefix='Workflow Poller taskList="HelloWorldTaskList", domain="test-domain", type="workflow"'}, identity=45937@maxim-C02XD0AAJGH6 }

13:35:02.673 [main] INFO c.u.cadence.internal.worker.Poller - start():Poller{options=PollerOptions{maximumPollRateIntervalMilliseconds=1000, maximumPollRatePerSecond=0.0, pollBackoffCoefficient=2.0, pollBackoffInitialInterval=PT0.2S, pollBackoffMaximumInterval=PT20S, pollThreadCount=1, pollThreadNamePrefix='null'}, identity=81b8d0ac-ff89-47e8-b842-3dd26337feea}。

"Hello"'没有打印,因为工作者只承载了工作流的代码。要执行工作流,用Cadence CLI启动它。

$ docker run --network=host --rm ubercadence/cli:master --do test-domain workflow start --tasklist HelloWorldTaskList --workflow_type HelloWorld::sayHello --execution_timeout 3600 --input \"World\"
Started Workflow Id: bcacfabd-9f9a-46ac-9b25-83bcea5d7fd7, run Id: e7c40431-8e23-485b-9649-e8f161219efe

现在,该程序给出了这样的输出。

13:35:02.575 [main] INFO c.u.c.s.WorkflowServiceTChannel - Initialized TChannel for service cadence-frontend, LibraryVersion: 2.2.0, FeatureVersion: 1.0.0

13:35:02.671 [main] INFO c.u.cadence.internal.worker.Poller - start():Poller{options=PollerOptions{maximumPollRateIntervalMilliseconds=1000, maximumPollRatePerSecond=0.0, pollBackoffCoefficient=2.0, pollBackoffInitialInterval=PT0.2S, pollBackoffMaximumInterval=PT20S, pollThreadCount=1, pollThreadNamePrefix='Workflow Poller taskList="HelloWorldTaskList", domain="test-domain", type="workflow"'}, identity=45937@maxim-C02XD0AAJGH6 }

13:35:02.673 [main] INFO c.u.cadence.internal.worker.Poller - start():Poller{options=PollerOptions{maximumPollRateIntervalMilliseconds=1000, maximumPollRatePerSecond=0.0, pollBackoffCoefficient=2.0, pollBackoffInitialInterval=PT0.2S, pollBackoffMaximumInterval=PT20S, pollThreadCount=1, pollThreadNamePrefix='null'}, identity=81b8d0ac-ff89-47e8-b842-3dd26337feea}。

13:40:28.308 [workflow-root] INFO c.u.c.ples.hello.GettingStarted - Hello World!

成功了!现在运行这个工作流程的执行。

$ docker run --network=host --rm ubercadence/cli:master --do test-domain workflow start --tasklist HelloWorldTaskList --workflow_type HelloWorld::sayHello --execution_timeout 3600 --input \"Cadence\"

Started Workflow Id: d2083532-9c68-49ab-90e1-d960175377a7, run Id: 331bfa04-834b-45a7-861e-bcb9f6ddae3e

你应该得到这样的输出。

13:35:02.575 [main] INFO c.u.c.s.WorkflowServiceTChannel - Initialized TChannel for service cadence-frontend, LibraryVersion: 2.2.0, FeatureVersion: 1.0.0

13:35:02.671 [main] INFO c.u.cadence.internal.worker.Poller - start():Poller{options=PollerOptions{maximumPollRateIntervalMilliseconds=1000, maximumPollRatePerSecond=0.0, pollBackoffCoefficient=2.0, pollBackoffInitialInterval=PT0.2S, pollBackoffMaximumInterval=PT20S, pollThreadCount=1, pollThreadNamePrefix='Workflow Poller taskList="HelloWorldTaskList", domain="test-domain", type="workflow"'}, identity=45937@maxim-C02XD0AAJGH6 }

13:35:02.673 [main] INFO c.u.cadence.internal.worker.Poller - start():Poller{options=PollerOptions{maximumPollRateIntervalMilliseconds=1000, maximumPollRatePerSecond=0.0, pollBackoffCoefficient=2.0, pollBackoffInitialInterval=PT0.2S, pollBackoffMaximumInterval=PT20S, pollThreadCount=1, pollThreadNamePrefix='null'}, identity=81b8d0ac-ff89-47e8-b842-3dd26337feea}。

13:40:28.308 [workflow-root] INFO c.u.c.ples.hello.GettingStarted - Hello World!

13:42:34.994 [workflow-root] INFO c.u.c.samples.hello.GettingStarted - Hello Cadence!

最后,使用这个CLI来列出工作流程。

$ docker run --network=host --rm ubercadence/cli:master --do test-domain workflow list

WORKFLOW TYPE | WORKFLOW ID | RUN ID | START TIME | EXECUTION TIME | END TIME

HelloWorld::sayHello | d2083532-9c68-49ab-90e1-d960175377a7 | 331bfa04-834b-45a7-861e-bcb9f6ddae3e | 20:42:34 | 20:42:34 | 20:42:35

HelloWorld::sayHello | bcacfabd-9f9a-46ac-9b25-83bcea5d7fd7 | e7c40431-8e23-485b-9649-e8f161219efe | 20:40:28 | 20:40:28 | 20:40:29

也看一下工作流的执行历史。

$ docker run --network=host --rm ubercadence/cli:master --do test-domain workflow showid 1965109f-607f-4b14-a5f2-24399a7b8fa7
1 WorkflowExecutionStarted {WorkflowType:{Name:HelloWorld::sayHello},
TaskList:{Name:HelloWorldTaskList},
Input:["World"],
ExecutionStartToCloseTimeoutSeconds:3600,
TaskStartToCloseTimeoutSeconds:10,
ContinuedFailureDetails:[],
LastCompletionResult:[],
Identity:cadence-cli@linuxkit-025000000001,
Attempt:0,
FirstDecisionTaskBackoffSeconds:0}
2 DecisionTaskScheduled {TaskList:{Name:HelloWorldTaskList},
StartToCloseTimeoutSeconds:10,
Attempt:0}
3 DecisionTaskStarted {ScheduledEventId:2,
Identity:45937@maxim-C02XD0AAJGH6,
RequestId:481a14e5-67a4-436e-9a23-7f7fb7f87ef3}
4 DecisionTaskCompleted {ExecutionContext:[],
ScheduledEventId:2,
StartedEventId:3,
Identity:45937@maxim-C02XD0AAJGH6}
5 WorkflowExecutionCompleted {Result:[],
DecisionTaskCompletedEventId:4}

这可能是一个简单的工作流,但看一下历史记录是相当有意义的。历史记录作为故障排除、分析和合规工具的价值只会随着工作流程的复杂性而增加。作为一个最佳实践,当工作流完成时,自动将历史记录归档到一个长期的blob存储。

尝试Cadence

Cadence为负责创建和管理为高耐久性、可用性和可扩展性而构建的大规模分布式应用的组织和应用开发团队提供了变革性优势。Cadence作为免费的开源软件可供所有人使用,这使得团队可以简单地探索其功能并确定Cadence是否非常适合他们的组织。

使用Cadence就像克隆Cadence服务器的Git仓库容器镜像一样简单。关于开始使用的更多细节,请访问:https://cadenceworkflow.io/docs/get-started/。