Flink DataStream API 教程:用托管状态识别交易模式

DataStream API 是 Apache Flink 提供的底层流处理 API。它允许细致控制状态、时间和自定义处理逻辑,适合构建复杂的事件驱动应用。

要构建的应用

本教程通过信用卡交易示例,构建一个识别特定交易模式的教学程序。数据流如下:

Transactions (source) → Flink (KeyedProcessFunction) → Alerts (sink)

你将学习设置执行环境、创建数据源、用 keyBy 分区以支持并行处理、用 KeyedProcessFunction 实现业务逻辑,以及用托管的 ValueState 保存每个账户的状态。

前置知识与求助

最好了解 Java 或 Python;熟悉其他语言也可以跟随步骤学习。遇到问题时可查阅 社区支持资源,或通过 用户邮件列表求助。

准备开发环境

原教程列出的环境如下:

  • Java 路径:Java 11、17 或 21,以及 Maven。
  • Python 路径:Java 11、17 或 21,以及 Python 3.9、3.10、3.11 或 3.12。

版本补充:同版本的 Java 兼容性说明推荐 Java 17;Java 21 支持标为实验性。配置环境时应同时检查该说明及所用连接器的限制,而不是把教程的简表理解为所有组合均无条件支持。

Java 项目

Flink 提供 Maven archetype,可快速创建骨架项目,把注意力放在业务逻辑上。依赖包括流应用的核心库 flink-streaming-java,以及提供示例数据生成器等类的 flink-walkthrough-common。

$ mvn archetype:generate \
    -DarchetypeGroupId=org.apache.flink \
    -DarchetypeArtifactId=flink-walkthrough-datastream-java \
    -DarchetypeVersion=2.3.0 \
    -DgroupId=frauddetection \
    -DartifactId=frauddetection \
    -Dversion=0.1 \
    -Dpackage=frauddetection \
    -DinteractiveMode=false

可以按需修改 groupId、artifactId 和 package。以上参数创建名为 frauddetection 的目录,其中包含完成本教程所需的依赖。Maven archetype 仅供已发布版本使用;这里的 2.3.0 来自所读 2.3 文档的实际渲染值。

把项目导入编辑器后,可找到 FraudDetectionJob.java,并在 IDE 中运行。若遇到 java.lang.NoClassDefFoundError,可能是 classpath 缺少必要的 Flink 依赖。在 IntelliJ IDEA 中依次打开 Run → Edit Configurations → Modify options,并选择 “include dependencies with ‘Provided’ scope”。

Python 项目

Python DataStream API 需要 PyFlink。它可从 PyPI安装:

$ python -m pip install apache-flink

建议使用 虚拟环境隔离依赖。安装后创建 fraud_detection.py。原命令没有固定版本;要复现本教程,应核对安装的 PyFlink 版本与 2.3 文档对应关系。这里保留原命令,未进行安装。

完整程序概览

先看 Java 与 Python 的完整示例,再分解各部分:

Java

public class FraudDetectionJob {

    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        DataStream<Transaction> transactions = env
            .fromSource(
                TransactionSource.unbounded(),
                WatermarkStrategy.noWatermarks(),
                "transactions");

        DataStream<Alert> alerts = transactions
            .keyBy(Transaction::getAccountId)
            .process(new FraudDetector())
            .name("fraud-detector");

        alerts
            .addSink(new AlertSink())
            .name("send-alerts");

        env.execute("Fraud Detection");
    }
}
public class FraudDetector extends KeyedProcessFunction<Long, Transaction, Alert> {

    private static final long serialVersionUID = 1L;

    private static final double SMALL_AMOUNT = 1.00;
    private static final double LARGE_AMOUNT = 500.00;

    private transient ValueState<Boolean> flagState;

    @Override
    public void open(OpenContext openContext) {
        ValueStateDescriptor<Boolean> flagDescriptor = new ValueStateDescriptor<>(
                "flag",
                Types.BOOLEAN);
        flagState = getRuntimeContext().getState(flagDescriptor);
    }

    @Override
    public void processElement(
            Transaction transaction,
            Context context,
            Collector<Alert> collector) throws Exception {

        Boolean lastTransactionWasSmall = flagState.value();

        if (lastTransactionWasSmall != null) {
            if (transaction.getAmount() > LARGE_AMOUNT) {
                Alert alert = new Alert();
                alert.setId(transaction.getAccountId());
                collector.collect(alert);
            }
            flagState.clear();
        }

        if (transaction.getAmount() < SMALL_AMOUNT) {
            flagState.update(true);
        }
    }
}

Python

from pyflink.common.typeinfo import Types
from pyflink.datastream import StreamExecutionEnvironment
from pyflink.datastream.functions import KeyedProcessFunction, RuntimeContext
from pyflink.datastream.state import ValueStateDescriptor


class FraudDetector(KeyedProcessFunction):

    SMALL_AMOUNT = 1.00
    LARGE_AMOUNT = 500.00

    def __init__(self):
        self.flag_state = None

    def open(self, runtime_context: RuntimeContext):
        descriptor = ValueStateDescriptor("flag", Types.BOOLEAN())
        self.flag_state = runtime_context.get_state(descriptor)

    def process_element(self, transaction, ctx: 'KeyedProcessFunction.Context'):
        # transaction is a tuple: (account_id, timestamp, amount)
        account_id = transaction[0]
        amount = transaction[2]

        last_transaction_was_small = self.flag_state.value()

        if last_transaction_was_small is not None:
            if amount > self.LARGE_AMOUNT:
                yield f"Alert{{id={account_id}}}"
            self.flag_state.clear()

        if amount < self.SMALL_AMOUNT:
            self.flag_state.update(True)


def fraud_detection():
    env = StreamExecutionEnvironment.get_execution_environment()
    env.set_parallelism(1)

    # Sample transaction data: (account_id, timestamp, amount)
    transactions_data = [
        (1, 1000, 188.23),
        (2, 1001, 0.50),    # Small transaction
        (2, 1002, 600.00),  # Large transaction - ALERT!
        (3, 1003, 42.00),
        (1, 1004, 0.89),    # Small transaction
        (1, 1005, 300.00),  # Not large enough - no alert
        (4, 1006, 0.10),    # Small transaction
        (4, 1007, 520.00),  # Large transaction - ALERT!
        (3, 1008, 0.75),    # Small transaction
        (3, 1009, 800.00),  # Large transaction - ALERT!
    ]

    transactions = env.from_collection(
        transactions_data,
        type_info=Types.TUPLE([Types.LONG(), Types.LONG(), Types.DOUBLE()])
    )

    alerts = transactions \
        .key_by(lambda t: t[0]) \
        .process(FraudDetector())

    alerts.print()

    env.execute("Fraud Detection")


if __name__ == '__main__':
    fraud_detection()

逐步理解代码

主类定义应用的数据流;FraudDetector 定义识别交易模式的业务逻辑。

执行环境

首先取得 StreamExecutionEnvironment。通过执行环境设置作业属性、创建数据源,并触发作业执行。

Java

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

Python

env = StreamExecutionEnvironment.get_execution_environment()

创建数据源

source 把 Apache Kafka、RabbitMQ、Apache Pulsar 等外部系统的数据接入 Flink 作业。

Java

本示例使用 TransactionSource,它封装 DataGeneratorSource,持续生成无界的示例交易。每条交易包含账户 ID accountId、发生时间戳 timestamp 和金额 amount。

DataStream<Transaction> transactions = env
    .fromSource(
        TransactionSource.unbounded(),
        WatermarkStrategy.noWatermarks(),
        "transactions");

fromSource 的三个参数分别是 source、水位线策略和用于调试的名称。这里使用处理时间,不使用事件时间水位线,因此传入 noWatermarks()。

Python

Python 示例使用有限的样本集合。每笔交易是一个包含账户 ID、时间戳和金额的元组:

transactions_data = [
    (1, 1000, 188.23),
    (2, 1001, 0.50),    # Small transaction
    (2, 1002, 600.00),  # Large transaction - ALERT!
    # ...more transactions
]

transactions = env.from_collection(
    transactions_data,
    type_info=Types.TUPLE([Types.LONG(), Types.LONG(), Types.DOUBLE()])
)

from_collection 便于教学和测试。生产系统通常使用 Kafka 等 source 连接器。

按账户分区并检测模式

transactions 流包含许多用户的交易,可由多个任务并行处理。检测规则以账户为单位,因此同一账户的交易必须到达同一个检测算子的并行任务。

keyBy 按键分区,确保同一个键的记录由同一物理任务处理。process() 添加算子,逐条应用处理函数。通常说紧跟 keyBy 的算子运行在 keyed context 中;这里就是 FraudDetector。

Java

DataStream<Alert> alerts = transactions
    .keyBy(Transaction::getAccountId)
    .process(new FraudDetector())
    .name("fraud-detector");

Python

alerts = transactions \
    .key_by(lambda t: t[0]) \
    .process(FraudDetector())

输出结果

sink 把 DataStream 写入外部系统,例如 Kafka、Cassandra 或 AWS Kinesis。

Java

AlertSink 仅以 INFO 级别记录每条 Alert,便于观察结果,没有把它们写入持久化存储。

alerts.addSink(new AlertSink());

Python

print() 把告警输出到控制台:

alerts.print()

检测函数

检测器是一个 KeyedProcessFunction,每笔交易都会调用其 processElement,Python 中为 process_element。下面先展示最简单的版本:每笔交易都产生告警,显然比较保守。随后再加入真正的判断逻辑。

Java

public class FraudDetector extends KeyedProcessFunction<Long, Transaction, Alert> {

    private static final double SMALL_AMOUNT = 1.00;
    private static final double LARGE_AMOUNT = 500.00;

    @Override
    public void processElement(
            Transaction transaction,
            Context context,
            Collector<Alert> collector) throws Exception {

        Alert alert = new Alert();
        alert.setId(transaction.getAccountId());

        collector.collect(alert);
    }
}

Python

class FraudDetector(KeyedProcessFunction):

    SMALL_AMOUNT = 1.00
    LARGE_AMOUNT = 500.00

    def process_element(self, transaction, ctx: 'KeyedProcessFunction.Context'):
        account_id = transaction[0]
        yield f"Alert{{id={account_id}}}"

实现业务规则

第一版规则是:同一账户先发生一笔小额交易,紧接着发生大额交易,就发出告警。这里把小额定义为低于 1.00,大额定义为高于 500,等于阈值不属于对应类别。

原教程的 交易顺序示意图说明两种情况。关键关系如下,金额均为教学数据:

交易顺序 模式 结果
3 → 4 0.09 后紧接 510 触发告警
7 → 8 → 9 0.02 后有一笔中间交易,再出现大额交易 中间交易打断相邻模式,不告警

检测器必须跨事件记住信息:只有前一笔交易是小额时,当前的大额交易才符合规则。这需要 状态,也是采用 KeyedProcessFunction 的原因。它允许细致控制状态和时间,便于继续增加复杂要求。

最直接的思路是在遇到小额交易时设置布尔标志,下一笔大额交易到来时检查账户对应的标志。但不能简单把它放进 FraudDetector 的普通成员变量:同一算子实例会处理多个账户,账户 A 设置的标志可能导致账户 B 被误报。

即使使用 Map 为各账户保存标志,普通成员数据也不会自动成为 Flink 的托管状态。故障后重启会丢失这些信息,可能漏报。Flink 提供与普通变量相近的状态原语,让框架管理状态及其恢复。

建立 ValueState

最基础的 ValueState属于 keyed state,只能在按键上下文中使用,例如紧跟 keyBy 的算子。访问状态时,框架自动按当前记录的键限定范围。这里的键是账户 ID,因此每个账户都有独立状态。

创建状态时使用 ValueStateDescriptor,说明框架应如何管理这个变量。在开始处理数据前,通过 open() 注册状态:

Java

public class FraudDetector extends KeyedProcessFunction<Long, Transaction, Alert> {

    private static final long serialVersionUID = 1L;

    private transient ValueState<Boolean> flagState;

    @Override
    public void open(OpenContext openContext) {
        ValueStateDescriptor<Boolean> flagDescriptor = new ValueStateDescriptor<>(
                "flag",
                Types.BOOLEAN);
        flagState = getRuntimeContext().getState(flagDescriptor);
    }

Python

class FraudDetector(KeyedProcessFunction):

    def __init__(self):
        self.flag_state = None

    def open(self, runtime_context: RuntimeContext):
        descriptor = ValueStateDescriptor("flag", Types.BOOLEAN())
        self.flag_state = runtime_context.get_state(descriptor)

ValueState 是包装类型,类似 Java 的 AtomicReference 或 AtomicLong。update 设置值,value 读取值,clear 清空值。一个键尚无状态或被清空后,读取结果为 Java 的 null,或 Python 的 None。

直接修改 value 返回的对象,不保证被状态系统识别;变更必须通过 update 提交。状态的保存与恢复由 Flink 管理,但示例本身没有启用 checkpoint;故障恢复还需要实际配置检查点或使用保存点,不能仅凭声明 ValueState 就保证故障后恢复最近状态。

用标志追踪相邻交易

Java

@Override
public void processElement(
        Transaction transaction,
        Context context,
        Collector<Alert> collector) throws Exception {

    // Get the current state for the current key
    Boolean lastTransactionWasSmall = flagState.value();

    // Check if the flag is set
    if (lastTransactionWasSmall != null) {
        if (transaction.getAmount() > LARGE_AMOUNT) {
            // Output an alert downstream
            Alert alert = new Alert();
            alert.setId(transaction.getAccountId());

            collector.collect(alert);
        }

        // Clean up our state
        flagState.clear();
    }

    if (transaction.getAmount() < SMALL_AMOUNT) {
        // Set the flag to true
        flagState.update(true);
    }
}

Python

def process_element(self, transaction, ctx: 'KeyedProcessFunction.Context'):
    account_id = transaction[0]
    amount = transaction[2]

    # Get the current state for the current key
    last_transaction_was_small = self.flag_state.value()

    # Check if the flag is set
    if last_transaction_was_small is not None:
        if amount > self.LARGE_AMOUNT:
            # Output an alert downstream
            yield f"Alert{{id={account_id}}}"

        # Clean up our state
        self.flag_state.clear()

    if amount < self.SMALL_AMOUNT:
        # Set the flag to true
        self.flag_state.update(True)

每笔交易先读取该账户的标志。ValueState 的范围始终是当前账户。标志非空意味着该账户上一笔交易是小额;如果当前金额是大额,就输出告警。

完成检查后,清除前一笔留下的标志:若已告警,模式结束;若未告警,相邻模式已经被当前交易打断。最后检查当前交易是否为小额,若是则设置标志,留给下一笔交易检查。

ValueState<Boolean> 可处于未设置、真、假三种状态,因为 ValueState 可以为空。这里仅使用“未设置”和“真”,通过是否为空判断标志,而不会写入假值。

完整实现

Java

下面是带托管状态的完整 FraudDetector:

import org.apache.flink.api.common.functions.OpenContext;
import org.apache.flink.api.common.state.ValueState;
import org.apache.flink.api.common.state.ValueStateDescriptor;
import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.streaming.api.functions.KeyedProcessFunction;
import org.apache.flink.util.Collector;
import org.apache.flink.walkthrough.common.entity.Alert;
import org.apache.flink.walkthrough.common.entity.Transaction;

public class FraudDetector extends KeyedProcessFunction<Long, Transaction, Alert> {

    private static final long serialVersionUID = 1L;

    private static final double SMALL_AMOUNT = 1.00;
    private static final double LARGE_AMOUNT = 500.00;

    private transient ValueState<Boolean> flagState;

    @Override
    public void open(OpenContext openContext) {
        ValueStateDescriptor<Boolean> flagDescriptor = new ValueStateDescriptor<>(
                "flag",
                Types.BOOLEAN);
        flagState = getRuntimeContext().getState(flagDescriptor);
    }

    @Override
    public void processElement(
            Transaction transaction,
            Context context,
            Collector<Alert> collector) throws Exception {

        // Get the current state for the current key
        Boolean lastTransactionWasSmall = flagState.value();

        // Check if the flag is set
        if (lastTransactionWasSmall != null) {
            if (transaction.getAmount() > LARGE_AMOUNT) {
                // Output an alert downstream
                Alert alert = new Alert();
                alert.setId(transaction.getAccountId());

                collector.collect(alert);
            }

            // Clean up our state
            flagState.clear();
        }

        if (transaction.getAmount() < SMALL_AMOUNT) {
            // Set the flag to true
            flagState.update(true);
        }
    }
}

Python

下面是完整的 Python 程序:

from pyflink.common.typeinfo import Types
from pyflink.datastream import StreamExecutionEnvironment
from pyflink.datastream.functions import KeyedProcessFunction, RuntimeContext
from pyflink.datastream.state import ValueStateDescriptor


class FraudDetector(KeyedProcessFunction):

    SMALL_AMOUNT = 1.00
    LARGE_AMOUNT = 500.00

    def __init__(self):
        self.flag_state = None

    def open(self, runtime_context: RuntimeContext):
        descriptor = ValueStateDescriptor("flag", Types.BOOLEAN())
        self.flag_state = runtime_context.get_state(descriptor)

    def process_element(self, transaction, ctx: 'KeyedProcessFunction.Context'):
        # transaction is a tuple: (account_id, timestamp, amount)
        account_id = transaction[0]
        amount = transaction[2]

        # Get the current state for the current key
        last_transaction_was_small = self.flag_state.value()

        # Check if the flag is set
        if last_transaction_was_small is not None:
            if amount > self.LARGE_AMOUNT:
                # Output an alert downstream
                yield f"Alert{{id={account_id}}}"

            # Clean up our state
            self.flag_state.clear()

        if amount < self.SMALL_AMOUNT:
            # Set the flag to true
            self.flag_state.update(True)


def fraud_detection():
    env = StreamExecutionEnvironment.get_execution_environment()
    env.set_parallelism(1)

    # Sample transaction data: (account_id, timestamp, amount)
    transactions_data = [
        (1, 1000, 188.23),
        (2, 1001, 0.50),    # Small transaction
        (2, 1002, 600.00),  # Large transaction - ALERT!
        (3, 1003, 42.00),
        (1, 1004, 0.89),    # Small transaction
        (1, 1005, 300.00),  # Not large enough - no alert
        (4, 1006, 0.10),    # Small transaction
        (4, 1007, 520.00),  # Large transaction - ALERT!
        (3, 1008, 0.75),    # Small transaction
        (3, 1009, 800.00),  # Large transaction - ALERT!
    ]

    transactions = env.from_collection(
        transactions_data,
        type_info=Types.TUPLE([Types.LONG(), Types.LONG(), Types.DOUBLE()])
    )

    alerts = transactions \
        .key_by(lambda t: t[0]) \
        .process(FraudDetector())

    alerts.print()

    env.execute("Fraud Detection")


if __name__ == '__main__':
    fraud_detection()

运行应用

到这里,程序的数据源、按账户处理、状态与输出已连接起来。Java 输入是无界的,会持续处理,直到手动停止;Python 版本处理有限样本集合。

Java

在 IDE 中运行 FraudDetectionJob,可以观察流处理结果。原文展示的日志示例如下,时间戳属于原文示例,不是本机运行记录:

2024-01-01 14:22:06,220 INFO  org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
2024-01-01 14:22:11,383 INFO  org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}
2024-01-01 14:22:16,551 INFO  org.apache.flink.walkthrough.common.sink.AlertSink - Alert{id=3}

Python

可在命令行运行:

$ python fraud_detection.py

原文展示的输出示例如下:

Alert{id=2}
Alert{id=4}
Alert{id=3}

该命令在本地 mini cluster 中构建并运行 PyFlink 程序,也可把作业提交到远程集群,详见 作业提交示例。本文仅核对代码和控制流,没有执行 Flink 或这些命令。

下一步

给规则增加时间条件

当前实现只检测同一账户的“小额后紧接大额”模式,没有时间限制。实际检测往往还需要时间条件。要学习定时器,例如仅检测一分钟内出现的两笔交易,可继续阅读 事件驱动应用。这里的规则是教学模型,不能直接代表已经验证的真实反欺诈系统。

深入 DataStream

其他教程

生产部署

来源:Apache Flink 文档贡献者,DataStream API Tutorial,release-2.3 文档。Copyright 2014-2026 The Apache Software Foundation。原文及示例按 Apache License 2.0 授权。2026-10-03:全文翻译、按官方渲染值展开版本占位符,补充 Java 版本、状态恢复与教学规则的说明;其余代码未改写。原图关键关系以文字表格保留,并链接官方图。

Apache License 2.0 原文
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

保留的原仓库 NOTICE
Apache Flink
Copyright 2014-2026 The Apache Software Foundation

This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).

This project bundles the following dependencies under the MIT license.
See bundled license files for details.

- AnchorJS v3.1.0 (https://github.com/bryanbraun/anchorjs) Copyright (c) 2016 Bryan Braun
    -> in "docs/static/js/anchor.min.js"
- font-awesome:4.6.3 (css) (https://fontawesome.com/) - Created by Dave Gandy
    -> css in "docs/static/font-awesome/css"
- chroma (css generated by Hugo) (https://github.com/alecthomas/chroma) Copyright (C) 2017 Alec Thomas
    -> in "docs/assets/github.css"

This project bundles the following dependencies under the BSD license.
See bundled license files for details.

- cloudpickle:2.2.0
- net.sf.py4j:py4j:0.10.9.7

This project bundles the following dependencies under SIL OFL 1.1 license (https://opensource.org/licenses/OFL-1.1).
See bundled license files for details.

- font-awesome:4.6.3 (Font) (https://fontawesome.com/) - Created by Dave Gandy
    -> fonts in "docs/static/font-awesome/fonts"

The Apache Flink project contains or reuses code that is licensed under the ISC license from the following projects.

- simplejmx (http://256stuff.com/sources/simplejmx/) Copyright (c) - Gray Watson

Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
granted, provided that this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
USE OR PERFORMANCE OF THIS SOFTWARE.

The Apache Flink project contains or reuses code that is licensed under the Apache 2.0 license from the following projects:
- aws-sdk-java-s3 (https://github.com/aws/aws-sdk-java)

  See: flink/flink-filesystems/flink-s3-fs-base/src/main/java/com/amazonaws/services/s3/model/transform/XmlResponsesSaxParser.java

AWS SDK for Java
Copyright 2010-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.

This product includes software developed by
Amazon Technologies, Inc (http://www.amazon.com/).

**********************
THIRD PARTY COMPONENTS
**********************
This software includes third party software subject to the following copyrights:
- XML parsing and utility functions from JetS3t - Copyright 2006-2009 James Murty.
- PKCS#1 PEM encoded private key parsing and utility functions from oauth.googlecode.com - Copyright 1998-2010 AOL Inc.



© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容