flink中的rpc框架使用的akka。在本节并不详细讲述akka,而是就flink中rpc来讲述akka的部分内容。本节,我从AkkaRpcActor.handleRpcInvocation方法讲起。
看过hadoop、yarn、hive、hbase、presto的rpc框架,感觉flink的通信框架是最容易让人绕晕的。虽然之前也看过一点spark中akka的通信,但现在早已忘得一干二净。如今重拾akka通信,感觉还是挺复杂的。因此,这里特意拿出一节来讲解。
1.这里首先要讲述的是flink中关于心跳的rpc交互。这里也是akka中第一种远程通信方式,也就是说通过tell方式异步传输。
这里我们从HeartbeatTarget.requestHeartbeat开始讲。真正调用的是ResourceManager.registerTaskExecutorInternal方法中类型为HeartbeatTarget的匿名类,其内部调用了taskExecutorGateway.heartbeatFromResourceManager。这里的taskExecutorGateway是一个代理类,其invocationHandler为AkkaInvocationHandler。因此,这里首先调用的是AkkaInvocationHandler.invoke,由于这里要调用的并非本地方法,因此接着调用了方法AkkaInvocationHandler.invokeRpc。在该方法中首先通过方法createRpcInvocationMessage封装了发现taskmanager端的请求RemoteRpcInvocation,接着获取了欲调用方法的返回值(这里的判断是为了后面使用不同的akka通信方式)。我们这里的返回值为Void。然后调用了AkkaInvocationHandler.tell。这里的入参是刚刚封装的RemoteRpcInvocation,该方法内部调用了ActorRef.tell。该actor就是taskmanager端的化生,发送了RemoteRpcInvocation(可序列化)。jobmanager端,也就是resourcemanager端的流程到这里就结束了,因为我们远程调用的方法是无返回值的。
接着,我们来到taskmanager端,这里的AkkaRpcActor.onReceive接收到resourcemanager端发来的消息。根据类型的匹配,我们来到AkkaRpcActor.handleRpcMessage。由于这里的信息是RemoteRpcInvocation,实现了接口RpcInvocation,因此,我们来到AkkaRpcActor.handleRpcInvocation方法。这里首先调用方法lookupRpcMethod根据方法名获取taskmanager端对应的方法,也就是TaskExecutor中对应的方法。接着,设置了其访问属性后,便开始反射调用。由于我们这里的方法返回值类型为Void,因此,在调用了TaskExecutor.heartbeatFromResourceManager后再无后续操作。

2.接着是akka中的第二种通信方式——异步返回。我这里的使用的是taskmanager向resourcemanager远程注册的例子来讲解。
从TaskExecutorToResourceManagerConnection.ResourceManagerRegistration.invokeRegistration讲起。该方法内部调用了resourceManager.registerTaskExecutor。这里的resourceManager实际类型是FencedAkkaInvocationHandler。FencedAkkaInvocationHandler继承自AkkaInvocationHandler。这里的部分调用流程与上面的异步无返回类似,我就从其中不同的地方讲起。由于我们这里的返回值类型为CompletableFuture<RegistrationResponse>,不是Void类型,因此,这里首先调用了FencedAkkaInvocationHandler.ask,接着调用了FencedAkkaInvocationHandler.fenceMessage将信息类型封装为RemoteFencedMessage,接着调用AkkaInvocationHandler.ask。这里是比较复杂的地方。首先调用了Patterns.ask(ActorRef, message),这里的ActorRef是resourcemanager端的化身,Patterns.ask是akka用于远程异步调用的一种方式。其返回值为scala.concurrent.Future,也就是scala类型的Future。该类型有方法onComplete,作用是当该Future完成是,不论是抛出异常或返回值完成此未来时,调用该方法入参中的函数。这里我们通过FutureUtils.toJava将scala中的Future转换为java中的CompletableFuture。得到CompletableFuture后,taskmanager端接着调用CompletableFuture.thenApply方法,内部调用了返回值的deserializeValue方法,也就是获取到远程的序列化的返回值后,将其反序列化。由于我们这里rpc调用的方法返回值是CompletableFuture类型,因此这里并不阻塞,直接返回。
然后,我们来到resourcemanager端,这里的AkkaRpcActor.onReceive方法被调用(注意,这里的实际类型是FencedAkkaRpcActor),由于传入的类型为RemoteFencedMessage,这里接着调用了FencedAkkaRpcActor.handleRpcMessage。经过几个判断后,这里调用了AkkaRpcActor.handleRpcMessage,此时,这里的入参为RemoteFencedMessage.getPayload,也就是RemoteRpcInvocation。接下来的流程我在上面已经提到,这里就不赘述了。所不同的是,我们这里的返回为类型为CompletableFuture,因此,这里接着会调用AkkaRpcActor.sendAsyncResponse。这里首先调用了方法——Patterns.pipe(promise.future(), getContext().dispatcher()).to(sender),这里的promise是scala中的Promise.DefaultPromise类型,该方法的作用其实就是讲java中的CompletableFuture转换为scala中的类型DefaultPromise,毕竟,java中的CompletableFuture类型无法实现rpc。sendAsyncResponse方法的作用就是,当入参asyncResponse完成后,会调用Promise.DefaultPromise的相应方法(success或failure)被调用。此时,由于Patterns.pipe(promise.future(), getContext().dispatcher()).to(sender)已经被调用,因此,taskmanager端调用Patterns.ask方法的返回的future为完成状态,也就是调用了其onComplete。接着,在taskmanager端将返回值反序列化,完成异步rpc的调用。

3.接着是akka的最后通信方式——阻塞返回。在flink中的对应的方法是AkkaRpcActor.sendSyncResponse(这里在flink中很少用到,因此我这里并没有举例)。
这里rpc调用方法的返回值为非CompletableFuture类型,前面的调用流程与上面讲述的异步返回一样,所不同的是,由于方法返回值类型为非CompletableFuture,因此,这里调用了CompletableFuture.get,这里会一直阻塞,直待该CompletableFuture的完成。这里的CompletableFuture其实就是通过FutureUtils.toJava实现了将scala中的future转换为java中的CompletableFuture。也就是说,这里会一直等到远程方法Promise.DefaultPromise的相应方法(success或failure)被调用,这里的阻塞才会被打断。
好了,到这里为止,关于flink中应用akka完成其rpc通信框架的流程就结束了,感谢大家的关注。
- [源码解析] 从TimeoutException看Flink的心跳机制
[源码解析] 从TimeoutException看Flink的心跳机制 目录 [源码解析] 从TimeoutException看Flink的心跳机制 0x00 摘要 0x01 缘由 0x02 背景概念 ...
- Flink架构分析之RPC详解
主要抽象 Flink RPC 框架主要抽象了RpcService,RpcEndpoint,RpcGateway,RpcServer这几个接口,具体实现可以采用多种方式,比如:akka,netty Rp ...
- 聊聊flink的log.file配置
本文主要研究一下flink的log.file配置 log4j.properties flink-release-1.6.2/flink-dist/src/main/flink-bin/conf/log ...
- Flink整合oozie shell Action 提交任务 带kerberos认证
最近这段时间一直在忙新集群迁移,上了最新的cdh6.3.0 于是Flink 提交遇到了许多的问题 还好有cloudera License 有了原厂的帮助和社区的伙伴,问题解决起来快了不少,手动滑稽 集 ...
- 整合 KAFKA+Flink 实例(第一部分,趟坑记录)
2017年后,一大波网络喧嚣,说流式处理如何牛叉,如何高大上,抱歉,工作满负荷,没空玩那个: 今年疫情隔离在家,无聊,开始学习 KAFKA+Flink ,目前的打算是用爬虫抓取网页数据,传递到Kafk ...
- Flink开发中的问题
1. 流与批处理的区别 流处理系统 流处理系统,其节点间数据传输的标准模型是:当一条数据被处理完成后,序列化到缓存中,然后立刻通过网络传输到下一个节点,由下一个节点继续处理. 批处理系统 批处理系统, ...
- Flink应用案例:How Trackunit leverages Flink to process real-time data from industrial IoT devices
January 22, 2019Use Cases, Apache Flink Lasse Nedergaard Recently there has been significant dis ...
- Akka并发编程——第五节:Actor模型(四)
本节主要内容: 1. 停止Actor 1. 停止Actor (1)通过ActorSystem.shutdown方法停止全部 Actor的执行 /* *停止Actor:ActorSystem.shutd ...
- apache flink源码挖坑 (未完待续)
Apache Flink 源码解读(一) By yyz940922原创 项目模块 (除去.git, .github, .idea, docs等): flink-annotations: flink ...
随机推荐
- The 2019 Asia Nanchang First Round Online Programming Contest C(cf原题,线段树维护矩阵)
题:https://nanti.jisuanke.com/t/41350 分析:先将字符串转置过来 状态转移,因为只有5个状态,所以 i 状态到 j 状态的最小代价就枚举[i][k]->[k][ ...
- python学习笔记(11)文件操作
一.读文件 读写文件是最常见的IO操作.Python内置了读写文件的函数,用法和C是兼容的. 读写文件前,我们先必须了解一下,在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直 ...
- python3下scrapy爬虫(第十卷:scrapy数据存储进mysql)
上一卷中我将爬取的数据文件直接写入文本文件中,现在我将数据存储到mysql中,我依然用的是pymysql,这个很麻烦建表需要在外面建 这次代码只需要改变pipyline就行 来 现在看下结果: 对比发 ...
- 深入JVM内核--JVM简介
JVM概念 jvm是指通过软件模拟的具有完整硬件系统功能的.运行在一个完全隔离环境中的完成计算机系统. 目前主要有vmMare.visual Box和JVM三款虚拟机. JVM使用软件模拟java字节 ...
- 吴裕雄--天生自然 神经网络人工智能项目:基于深度学习TENSORFLOW框架的图像分类与目标跟踪报告(续四)
2. 神经网络的搭建以及迁移学习的测试 7.项目总结 通过本次水果图片卷积池化全连接试验分类项目的实践,我对卷积.池化.全连接等相关的理论的理解更加全面和清晰了.试验主要采用python高级编程语言的 ...
- C#获取代码执行时间(精确到毫秒)
private void Time(int i) { Stopwatch sw = new Stopwatch(); sw.Start(); Thread.Sleep(i); sw.Stop(); C ...
- PHP常用接口数据过滤的方法
<?php /** * global.func.php 公共函数库 */ /** * 返回经addslashes处理过的字符串或数组 * @param $string 需要处理的字符串或数组 * ...
- CoreGraphic
public func UIGraphicsBeginImageContextWithOptions( size: CGSize, opaque: Bool, _ scale: CGFloat) si ...
- 海洋深处的数据中心——微软Natick项目
数据中心(data center)是云计算的支柱,云计算的蓬勃发展离不开数据中心在建造运营方面的不断创新.但云端数据中心的运行过程中会产生大量热量,冷却降温过程则意味着巨大的能源消耗,这意味着需要庞大 ...
- Python 安全修改私有属性
设置私有属性之后,如何修改私有属性 class Room: def __init__(self,name,length,width): self.__name = name self.__length ...