现代应用程序需要长期运行的业务流程、内部服务和第三方 API 之间复杂的交互。说这对开发人员来说是一个挑战都算是轻描淡写了。管理这些流程意味着跟踪复杂的状态、准备对异步事件的响应以及与通常不可靠的外部依赖项进行通信。
开发人员通常使用同样复杂且令人费解的解决方案来应对这些复杂的挑战,组装笨拙的系统,利用无状态服务、数据库、重试算法和作业调度队列。由于这些复杂的系统掩盖了自身的业务逻辑,因此可用性问题很常见,通常源于应用程序对分散且未经证实的组件的依赖。开发人员的生产力经常被牺牲,以防止这些庞大且问题缠身的系统崩溃。
设计分布式应用程序
Cadence 通过提供高度可扩展的容错代码平台来解决这些问题。Cadence 通过其容错代码抽象化了实现容错和持久性的常见挑战。
标准的 Cadence 应用程序包括 Cadence 服务、工作流、活动工作器和外部客户端。如果需要,可以将工作流工作器、活动工作器和外部客户端的角色共置于单个应用程序进程中。
Cadence 服务

(Ben Slater,CC BY-SA 4.0)
Cadence 以其多租户服务及其实现的高可扩展性为中心。强类型 gRPC API 公开了所有 Cadence 服务功能。Cadence 集群可以在多个节点上运行多个服务,包括
- 前端: 无状态服务,处理传入的工作器请求,实例由外部负载均衡器支持。
- 历史服务: 处理工作流步骤和活动编排的核心逻辑。
- 匹配服务: 将工作流或活动任务与准备好完成它们的工作器进行匹配。
- 内部工作器服务: 通过引入 Cadence 工作流和活动来满足内部需求(例如归档)。
- 工作器: 充当 Cadence 客户端应用程序,执行用户创建的工作流和活动逻辑。
默认情况下,Cadence 支持 Apache Cassandra、MySQL、PostgreSQL、CockroachDB 和 TiDB 用作持久性存储,以及 ElasticSearch 和 OpenSearch 用于列出具有复杂谓词的工作流。
由于 Cadence 服务是多租户的,因此单个服务可以为一个或多个应用程序提供服务。本地 Cadence 服务实例可以使用 docker-compose 进行本地开发。Cadence 服务维护工作流状态、关联的持久计时器和内部“任务列表”队列,以将任务发送给外部工作器。
超越 Cadence 服务本身
-
工作流工作器:在 Cadence 服务外部托管容错代码。Cadence 服务向这些工作器发送“决策任务”。工作器将任务传递给工作流代码,并将完成的“决策”传回 Cadence 服务。工作流代码可以使用任何能够与 Cadence API 通信的语言来实现:目前提供生产就绪的 Java 和 Go 客户端。
-
活动工作器: 托管“活动”,或执行特定于应用程序的操作的代码,例如服务调用、数据库记录更新和文件下载。活动具有任务路由到特定进程、心跳、无限重试和无限执行时间等功能。Cadence 服务将活动任务发送给这些工作器,工作器完成任务并报告完成情况。
-
外部客户端: 启用工作流实例或“执行”的创建。外部客户端(如 UI、微服务或 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
compile group: 'com.uber.cadence', name: 'cadence-client', version: 'LATEST.RELEASE.VERSION'
Java Hello World 与 Cadence
了解 Cadence 功能的最佳方法是尝试一下,所以这里有一个简单的“Hello World”示例供您尝试。首先,将 Cadence Java 客户端依赖项添加到您的 Java 项目中。使用 Gradle,依赖项如下所示
compile group: 'com.uber.cadence', name: 'cadence-client', version: '<latest_version>'
也添加 cadence-client 需要的这些依赖项
compile group: 'commons-configuration', name: 'commons-configuration', version: '1.9'
compile group: 'ch.qos.logback', name: 'logback-classic', version: '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 配置文件放入您的类路径中
<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:40:28.308 [workflow-root] INFO c.u.c.samples.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:40:28.308 [workflow-root] INFO c.u.c.samples.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
TYPE | WORKFLOW ID | START | EXEC | END TIME
HelloWorld::sayHello | d2083532... | 20:42 | 20:42| 20:42:35
HelloWorld::sayHello | bcacfabd... | 20:40 | 20:41| 20:41: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/。
评论已关闭。