Lightflus:云原生流计算框架,Demo 版本正式发布!

Introduction

Hi all,这是我首次在 SegmentFault 上发文章,先简单介绍下我自己吧,我叫 Jason Thon,魔都一枚小开发,喜欢撸猫撸狗打电玩,练习时长两年半,擅长各种 Bug 制作技巧与 Debug 手艺。

在这篇文章里,我将介绍一款与我一同练习时长一年,擅长流处理的新型云原生计算框架 Lightflus。在我的公众号文章: Lightflus: 云原生流处理框架 中,小范围地宣传了一下这款框架,并被 Rust 中文社区转发,一时得到了一点关注度。也因此有了动力继续做下去。

Lightflus 如我的文章中所言,是基于我对现代数据栈的思考,尝试去面向未来数据计算框架开发的一款框架。其目标要解决的问题是主流流计算框架如 Flink 所没有解决的:

  1. Cloud-Native:因为历史原因,目前 Flink 并没有真的做到 Cloud-Native;
  2. 上手门槛高:Flink 需要很专业的团队才能玩得转,不是所有公司都有 BAT 这样的开发资源的;
  3. 云中立:Flink 在商业上不是云中立的,客户只能在某云上使用 Flink 的云版本。这就导致 许多用户怕被云厂商绑定,只能去做各种重复建设的活,浪费大量时间和资源在一件人效比极低的事情上。而 Lightflus 与云无关,理论上什么云环境都能用,用户可以将 Lightflus 非常简单地部署到自己的公有云或私有云环境中;
  4. Pay-as-you-go:因为沉重的历史包袱,Flink 实现 SaaS 化比较艰难,用户需要付很多额外的钱在一个对主营业务没那么重要的东西上。但 Lightflus 模块化和轻量化的设计,SaaS 化的路径就会简单很多;

Lightflus 尝试通过技术创新,来解决如上的问题。简单来讲,Lightflus 给开发者搭了个云端的流计算“开发环境”,让他们能够更加简单(便宜)地去开发流数据的价值。在未来 Cloud IDE 成熟后,会进一步释放 Lightflus 在 DataOps 上的价值;

Github 与社区

欢迎大家到 Github 去试用下 Lightflus,我们非常希望能得到各位的任何关于产品的建议!

再谈谈社区,其实我是非常欢迎任何人来做 Lightflus 的 contributor 的,但社区是有规范的(包括 PR、提 issue 等),这一块我会逐步完善起来。对于开源生态,我都是拥抱的态度,我会让 Lightflus 加速融入到开源生态中。

下面让我们来详细领略下 Lightflus 这款新型流计算框架吧~

Lightflus 的技术架构

应用架构

Lightflus 的整体应用架构如下图

Lightflus:云原生流计算框架,Demo 版本正式发布!_第1张图片

Lightflus 本质是个分布式实时 DAG 计算引擎,它从指定的流数据源(如 Kafka、MQTT 等)消费数据,经过 User-Defined 的 DAG 计算出结果后,写入指定的数据存储层(如 MySQL,Redis,ElasticSearch 等)或数据管道(如 Kafka)中。

技术架构

Lightflus:云原生流计算框架,Demo 版本正式发布!_第2张图片

Lightflus 由 Master 和 Worker 节点组成,其中 Master 节点有 ApiServer 和 Coordinator 两个服务;

  • ApiServer 主要负责与 Client 端或 CLI 工具的交互,提供标准的 REST Api 接口;
  • Coordinator 负责集群管理、Checkpoint 管理、动态扩缩容管理等;
  • Worker 是主要的计算负载并负责与数据源 Connection 、计算中间结果的 Dispatch、状态管理等;计算图会被构建成一个 Actor System,通过 Channel 在 Actor 间同步计算结果;

API

Lightflus 采用 Typescirpt 作为 API 的开发语言,这么做有几个目的:

  1. Typescirpt 是类型安全的,可以避免很多类型错误,还能够规定 Dataflow 算子转换的输入输出结构,减轻 Runtime 的负担;
  2. Typescript 可以有两种编译中间产物:JavaScriptWebAssembly,而两者的 Runtime 现在正逐步融合,在未来,用户可以根据自己的需求来决定 Lightflus 使用哪种编译中间产物;
  3. Typescript 在写法上与 Java 差异不太大(当然,Typescript 玩法更加丰富),现有的数据开发工程师也能很容易转过来;
  4. Typescript 是 web 的语言,天生支持云端化;

Runtime

Lightflus 的核心计算引擎是 Rust 编写的,这么做的目的是:

  1. Rust 能很大程度上保证内存安全,并且拥有接近 C/C++ 的性能;
  2. Rust 生态内有成熟的技术可以作为 Typescirpt 运行的底层引擎;
  3. Rust 在工程化上更加成熟,能很好地模块化,还能通过软件工程来有效管理 Lightflus 的代码产出质量;

核心计算引擎采用 Actor 大规模并行计算模型,这种并行计算模型经过时间充分的检验,证明是目前大规模并行流处理最适合的计算模型,在可扩展性、高可用、容错上也都有成熟的解决方案。Lightflus 是站在巨人的肩膀上起步的,产品能够更快地到达较高的成熟度

Let's Try Lightflus!

本地部署 Standalone 集群

在 Flink 的世界里,即使是最简单的流任务(如 word count),想要在线上运转起来,都需要折腾不少时间。虽然 Flink 社区努力把 Flink 的使用门槛降到最低,但你想让它哪怕在本地环境正常工作,依旧需要花费不少力气。

而 Lightflus 就非常简单了,只需要执行一行命令

docker-compose up

docker-compose.yml 文件配置如下:

  coordinator:
    image: lightflus/coordinator:v1.0.0-alpha
    hostname: coordinator
    ports:
      - '8791:8791'
    environment:
      RUST_LOG: INFO
      WORKER_1: worker
    depends_on:
      - worker
    healthcheck:
      test:
        [
          "CMD",
          "/lightflus/runtime/tools/healthcheck",
          "-TARGET",
          "localhost:8791",
          "-METHOD",
          "/coordinator.CoordinatorApi/Probe",
          "-SERVICE",
          "0",
          "-PROBE",
          "1"
        ]
      interval: 3s
      timeout: 5s
      retries: 3
      start_period: 5s
  worker:
    image: lightflus/worker:v1.0.0-alpha
    hostname: worker
    ports:
      - '8792:8792'
    environment:
      RUST_LOG: INFO
    healthcheck:
      test:
        [
          "CMD",
          "/lightflus/runtime/tools/healthcheck",
          "-TARGET",
          "localhost:8792",
          "-METHOD",
          "/worker.TaskWorkerApi/Probe",
          "-SERVICE",
          "1",
          "-PROBE",
          "0"
        ]
      interval: 2s
      timeout: 1s
      retries: 3
      start_period: 5s
  apiserver:
    image: lightflus/apiserver:v1.0.0-alpha
    environment:
      RUST_LOG: INFO
      LIGHTFLUS_COORDINATOR_URI: http://coordinator:8791
    ports:
      - "8080:8080"
    depends_on:
      - coordinator

等待 docker 完成镜像拉取和服务启动、检测的过程后,Lightflus Standalone 就能成功启动了,这个时间你还可以喝杯咖啡养养神。

开始编写流任务

我们从最简单也是最常被拿来当例子的 Word Count 来看我们如何编写和部署 Lightflus 的流任务;

准备

由于 Lightflus API 依赖 NodeJS 环境和 Typescript 编译器,因此首先要安装 NodeJS,通过 npm 安装 typescript 依赖,具体可以参考微软的文档

环境准备

我们需要启动如下的服务

  1. kafka
  2. redis

我在这里提供一份 docker-compose.yml 文件供大家参考:

version: "3.9"
services:
  zookeeper:
    image: 'bitnami/zookeeper:latest'
    ports:
      - '2181:2181'
    environment:
      - ALLOW_ANONYMOUS_LOGIN=yes
  kafka:
    image: 'bitnami/kafka:latest'
    ports:
      - '9092:9092'
    environment:
      - KAFKA_BROKER_ID=1
      - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092
      - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092
      - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181
      - ALLOW_PLAINTEXT_LISTENER=yes
    depends_on:
      - zookeeper
  redis:
    image: 'bitnami/redis:latest'
    ports:
      - '6379:6379'
    environment:
      - ALLOW_EMPTY_PASSWORD=yes

执行命令:docker-compose up -f /path/to/docker-compose.yml 启动服务;

在 Lightflus 的 Github 项目仓库里,我准备了一份完整的 docker-compose.yml 文件,如有需要,大家可以直接 clone 仓库后在项目根目录启动即可;

初始化项目

创建一个 nodejs 项目,准备好 typescript 开发环境后,下载 lightflus-api 依赖:

npm i lightflus-api

并初始化 tsconfig.json 文件:

npx tsc --init

编写代码

在准备好后,我们开始编写 word count 的代码,代码非常简单:

// wordCount example
async function wordCount(ctx: ExecutionContext) {

   // 从 kafka 中获取字符串流
  let source = Kafka
    .builder()
    .brokers(["localhost:9092"])
    // 消费的 topic 为 topic
    .topic("topic")
    // groupId 为 word_count
    .group("word_count")
    // 反序列化的类型
    .build(undefined, typeof "");

   // 将统计的结果存储在 Redis 里
  let sink = Redis.new<{ t0: number, t1: string }>()
    .host("localhost")
    .keyExtractor((v) => v.t1)
    .valueExtractor((v) => v.t0.toString());
   
   // 创建一个 Dataflow
  let stream = source.createFlow(ctx);

    // 设计 Dataflow 的结构
  await stream.flatMap(value => value.split(" ").map(v => {
    return { t0: 1, t1: v };
  }))
    .keyBy(v => v.t1)
    .reduce((v1, v2) => {
      return { t1: v1.t1, t0: v1.t0 + v2.t0 };
    })
    // 将 Dataflow 计算的结果写入 redis sink 里
    .sink(sink)
    // 执行
    .execute();
}

// 我们指定这个任务的 id 和所属的 namespace
wordCount(ExecutionContext.new("wordCount", "default")).then();

保存该段代码为文件 wordCount.ts

部署流任务

首先设置 Lightflus 服务地址的环境变量

export LIGHTFLUS_ENDPOINT=localhost:8080

我们可以直接通过 nodejs 来编译上面的代码:

yarn tsc -p .

然后运行编译后的文件

node wordCount.js

这样我们就成功将 word count 任务部署到 Lightflus 本地集群上了。

让 Dataflow 跑起来!

接下来一个重要的步骤就是让刚刚部署的 word count 任务跑起来。

我们发送一条数据到 Kafka 对应的 topic 里:

hello hello hello world world world

如果一切安好,那么我们将在 Redis 里找到数据:

redis> GET hello
"3"

redis> GET world
"3"

如果此时再发送这个数据到 Kafka 里,状态就会更新为:

redis> GET hello
"6"

redis> GET world
"6"

Notes

  • 由于本地 standalone 模式默认使用内存存储的状态管理器,因此在 Lightflus 集群关闭后,所有状态都会被抹除;

In The Future

Lightflus 目前有两个大的 Roadmap。

  • 第一个是支持流批一体,应该说这是目前分布式计算框架的基本趋势,Lightflus 在产品规划上也必然是往这个方向努力的;
  • 第二个则是支持原生的存储,目前 Flink 也正在做这件事,但总的来讲流存储/流数仓整体还处于初期阶段。当然,因为 Flink 的 底层功能相对已经比较成熟了,他们可以很快在这些功能上搭起一个流数仓的产品。Lightflus 从规划上看,必然也是要完善基础功能后才会去发力存储的,这个是一个比较长远的规划。

All in All

Lightflus 因为起步较晚,目前还处于完善基础功能的阶段。但用户的使用是任何软件前进路上最大的动力,也因此我们会重视每一个前来试用 Lightflus 的用户的建议。我们也会不断去迭代 Lightflus,让它能不断逼近一个优秀的计算框架。

你可能感兴趣的:(Lightflus:云原生流计算框架,Demo 版本正式发布!)