flink 源码解析 (2) actor模型

963 阅读3分钟

  我正在参与掘金创作者训练营第4期,点击了解活动详情,一起学习吧

写在前面

  大家好,今天我们来聊一个有意思的话题,大家都知道流式处理系统flink对于数据的顺序性和正确性并不是保证的。flink处理的数据可能会丢失,前后颠倒。那么导致这样的问题的原因是什么?flink 所有的job manager ,task manager之间互相是依靠什么rpc框架进行互相交互的? 每个slot算子之间如果有消息需要发送给彼此,那么它们是靠什么样的机制发送的?实际上这些问题都与flink使用的异步消息分布式rpc通信系统Akka息息相关的。

Actor模型

  首先我们来讨论一下什么是actor模型,actor模型本质是一种利用actor作为基本计算单元的并行计算模型,在利用actor模型计算的程序中,所有的一切都是在acotr中执行的,就像面向对象编程中,一切都是在对象之中执行的。

  相对的,actor模型中一切都是互相隔离的actor,它们也不会共享任何的变量。在java代码中并不存在原生的actor模型功能的类库,只能使用第三方的类库AKKA来实现actor模型。flink也是利用AKKA来实现actor模型。

   actor中的消息机制其实更像是一个单线程的邮箱系统,大概类似于下图。调用actor发送消息就等于发送了一封邮件,发送消息以后,接收到该消息的acotr并不一定会立马去执行或者处理。就像日常打开邮箱,大家往往会顺序的浏览未读的邮件,而别的未读邮件就会积压在邮箱中等待处理。所以acotr模型的消息机制是完全异步的,发送消息和接受消息的acotr不需要在同一线程,甚至不需要在同一台机器上,所有actor都是单独处理和决策自己的邮件处理。这对与flink的分布式多线程计算提供了重要的支持,对于flink集群的搭建也是一种重要的支撑。

image.png

Actor Systems在 flink中的实现

由于本文的关键在于actor模型的在flink中的实现,这里就不再赘言AKKA的部分. 在最新的版本我们可以看到 flink的rpc的代码不再放在runTime包下,而是单独的抽出来一个rpc来安置这些部分的代码,flink-rpc-core中包括了非常重要的RpcEndpoint等等.

 class TaskExecutor extends RpcEndpoint

屏幕快照 2022-02-23 下午11.55.22.png

public TaskManagerRunner(
        Configuration configuration,
        PluginManager pluginManager,
        TaskExecutorServiceFactory taskExecutorServiceFactory)
        throws Exception {
    this.configuration = checkNotNull(configuration);
    //通过配置启动rpcSystem
    rpcSystem = RpcSystem.load(configuration);
)

我们首先从 TaskManagerRunner这个flink非常重要的基础类来看,在taskMannger初始化的时候的最初就会出初始化整个RpcSystem。

/**
 * 循环初始化  RpcSystem.
 */
static RpcSystem load(Configuration config) {
    final Iterator<RpcSystemLoader> iterator =
            ServiceLoader.load(RpcSystemLoader.class).iterator();

    Exception loadError = null;
    while (iterator.hasNext()) {
        final RpcSystemLoader next = iterator.next();
        try {
            return next.loadRpcSystem(config);
        } catch (Exception e) {
            loadError = ExceptionUtils.firstOrSuppressed(e, loadError);
        }
    }
    throw new RpcLoaderException("Could not load RpcSystem.", loadError);
}

AkkaRpcSystemLoader的具体实现

public class AkkaRpcSystemLoader implements RpcSystemLoader {

     //akkajar的地址
    private static final String FLINK_RPC_AKKA_FAT_JAR = "flink-rpc-akka.jar";

    static final String HINT_USAGE =
            "mvn clean package -pl flink-rpc/flink-rpc-akka,flink-rpc/flink-rpc-akka-loader -DskipTests";
            

    @Override
    public RpcSystem loadRpcSystem(Configuration config) {
        try {
            final ClassLoader flinkClassLoader = RpcSystem.class.getClassLoader();

            final Path tmpDirectory = Paths.get(ConfigurationUtils.parseTempDirectories(config)[0]);
            Files.createDirectories(tmpDirectory);
            final Path tempFile =
                    Files.createFile(
                            tmpDirectory.resolve("flink-rpc-akka_" + UUID.randomUUID() + ".jar"));

            final InputStream resourceStream =
                    flinkClassLoader.getResourceAsStream(FLINK_RPC_AKKA_FAT_JAR);
            if (resourceStream == null) {
                throw new RpcLoaderException(
                        String.format(
                                "Akka RPC system could not be found. If this happened while running a test in the IDE, "
                                        + "run '%s' on the command-line, "
                                        + "or add a test dependency on the flink-rpc-akka-loader test-jar.",
                                HINT_USAGE));
            }

            IOUtils.copyBytes(resourceStream, Files.newOutputStream(tempFile));

            final SubmoduleClassLoader submoduleClassLoader =
                    new SubmoduleClassLoader(
                            new URL[] {tempFile.toUri().toURL()}, flinkClassLoader);

            return new CleanupOnCloseRpcSystem(
                    ServiceLoader.load(RpcSystem.class, submoduleClassLoader).iterator().next(),
                    submoduleClassLoader,
                    tempFile);
        } catch (IOException e) {
            throw new RuntimeException("Could not initialize RPC system.", e);
        }
    }
}

动态代理

借助 AkkaInvocationHandler 进行动态代理

参考文献:

cwiki.apache.org/confluence/…