Flink的RPC实现:是基于Scala的网络编程库Akka来的。
Akka的特点
- 它是对并发模型进行了更高的抽象
- 它是异步、非阻塞、高性能的事件驱动编程模型
- 它是轻量级事件处理(1GB内存可以以容纳百万级别的Actor)
Akka简介
- Akka是一个网络编程库
- ActorSystem是管理Actor生命周期的组件,Actor是负责进行通信的组件
- 每个Actor都有一个MailBox(在Flink源码中经常可以看到,checkpoint通知就是基于此),别的Actor发送给它的消息都是首先存储在MailBox中,通过这种方式可以实现异步通信
- 单个Actor可以改变他自身的状态,可以接收消息,也可以发送消息,还可以生成新的Actor
- 每个Actor是单线程的处理,不断从MailBox中拉取消息执行处理,所以对于Actor的消息处理,不适合调用阻塞的处理方式。
- 每个ActorSystem和Actor都在启动时候会给一个Name,如果要从Actor获取一个Actor,则通过以下方式进行Actor的获取akka.tcp://actorsydtem_name:主机:端口/user/actor_name来进行定位的。
- 如果一个Actor要和另外一个Actor进行通信,则必须先获取对象的Actor的ActorRef对象,然后通过该对象发送消息即可。
- 通过tell发送异步消息,不接受响应,通过ask发送异步消息,得到future返回,通过异步回调返回处理结果。
我相信到现在大家对Akka应该有些了解了,可能大家看到这里还有很多同学很懵逼,没事我们接下来再用现实场景再好好探讨一下。
相信经过上面的特性介绍,我们对于Actor有个简单的认识了,首先Akka有两个重要的组成部分分别是Actor
,ActorSystem
这两个组成。那么我们应该怎么理解他呢,我们可以简单的理解为ActorSystem是一个小组,其中的Actor是一个组员,我们开发过程中不可能是组和组之间的交流吧,肯定是组员和组员之间的交流,也就是Actor和Actor之间的通信,我们可以理解一下这里,假如组员之间是打电话的话,那么是不是就阻塞了,你打电话啥都干不了了。所以他们之间可以发短信,每个人都有手机,他们通过手机交流,当然手机是有手机号的,也就是akka.tcp://actorsydtem_name:主机:端口/user/actor_name
。当然组员之间想要通信,知道了他的手机号还不够,得知道他的短信终端啊,这个也就是ActorRef。关于Akka我们就先简单的介绍到这里。
使用Akka模拟实现Flink Standalone集群通信
需求:
1、两个进程JobManager、TaskManager
2、当TaskManager启动的时候,向JobManager发送注册信息,报告本地的内存、CPU
3、当JobManager收到注册消息的时候,返回给TaskManager注册成功的消息
4、TaskManager每间隔三秒向JobManager发送心跳消息
5、JobManager每间隔3秒扫描一下,有哪些TaskManager下线了
接下来我们来实现一下这个需求。
public class TaskManager extends UntypedActor {
public static Props props() {
return Props.create(TaskManager.class, () -> new TaskManager());
}
private final static ActorSystem TASKMANAGER_SYSTEM;
private final static ActorRef TASKMANAGER_ACTOR;
private final static int PORT;
private final static StandaloneConfig STANDALONE_CONFIG = new StandaloneConfig();
static {
PORT = STANDALONE_CONFIG.getTaskManagerPort();
Map<String, Object> conf = new HashMap<>();
conf.put("akka.remote.netty.tcp.hostname", STANDALONE_CONFIG.getTaskManagerHost());
conf.put("akka.remote.netty.tcp.port", PORT);
conf.put("akka.actor.provider", "akka.remote.RemoteActorRefProvider");
Config config = ConfigFactory.parseMap(conf);
TASKMANAGER_SYSTEM = ActorSystem.create(STANDALONE_CONFIG.getTaskManagerSystemName(), config);
TASKMANAGER_ACTOR = TASKMANAGER_SYSTEM.actorOf(TaskManager.props(), STANDALONE_CONFIG.getJobManagerActorName());
}
@Override
public void onReceive(Object message) throws Throwable {
}
@Override
public void preStart() throws Exception {
RegistTaskManager registTaskManager = new RegistTaskManager();
registTaskManager.setCpu(64);
registTaskManager.setMemory(128);
registTaskManager.setHostname(STANDALONE_CONFIG.getTaskManagerHost());
registTaskManager.setPort(PORT);
String taskMId = (registTaskManager.getHostname() + ":" + PORT).hashCode() + "";
registTaskManager.setTaskManagerId(taskMId);
String template = "akka.tcp://%s@%s:%d/user/%s";
String jobmanagerUrl = String.format(template, STANDALONE_CONFIG.getJobManagerSystemName(),
STANDALONE_CONFIG.getJobManagerHost(), STANDALONE_CONFIG.getJobManagerPort(), STANDALONE_CONFIG.getJobManagerActorName());
System.out.println(jobmanagerUrl);
ActorSelection jobmanagerActor = getContext().actorSelection(jobmanagerUrl);
Timeout t = new Timeout(Duration.create(10, TimeUnit.SECONDS));
//注册获取回调,并且绑定发送心跳
Future<Object> ask = Patterns.ask(jobmanagerActor, registTaskManager, t);
ask.onSuccess(new OnSuccess<Object>() {
@Override
public void onSuccess(Object result) throws Throwable {
if ("注册成功".equalsIgnoreCase((String)result)) {
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
service.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
Heartbeat heartbeat = new Heartbeat();
heartbeat.setTaskId(registTaskManager.getTaskManagerId());
jobmanagerActor.tell(heartbeat,getSelf());
System.out.println("发送心跳");
}
}, 0, 3, TimeUnit.SECONDS);
}
}
}, TASKMANAGER_SYSTEM.dispatcher());
}
public static void main(String[] args) throws Exception {
}
public class JobManager extends UntypedActor {
public static Props props() {
return Props.create(JobManager.class, () -> new JobManager());
}
private final static ActorSystem JOBMANAGER_SYSTEM;
private final static ActorRef JOBMANAGER_ACTOR;
private ConcurrentHashMap<String,TaskManagerInfo> taskManagerInfoConcurrentHashMap = new ConcurrentHashMap<>();
static {
StandaloneConfig STANDALONE_CONFIG = new StandaloneConfig();
Map<String, Object> conf = new HashMap<>();
conf.put("akka.remote.netty.tcp.hostname", STANDALONE_CONFIG.getJobManagerHost());
conf.put("akka.remote.netty.tcp.port", STANDALONE_CONFIG.getJobManagerPort());
conf.put("akka.actor.provider","akka.remote.RemoteActorRefProvider");
Config config = ConfigFactory.parseMap(conf);
JOBMANAGER_SYSTEM = ActorSystem.create(STANDALONE_CONFIG.getJobManagerSystemName(), config);
JOBMANAGER_ACTOR = JOBMANAGER_SYSTEM.actorOf(JobManager.props(), STANDALONE_CONFIG.getJobManagerActorName());
}
@Override
public void onReceive(Object message) throws Throwable {
if (message instanceof CheckTimeout){
System.out.println("开始心跳检测");
System.out.println("要检测的taskmanager数量: "+taskManagerInfoConcurrentHashMap.size());
Set<Map.Entry<String, TaskManagerInfo>> entries = taskManagerInfoConcurrentHashMap.entrySet();
Iterator<Map.Entry<String, TaskManagerInfo>> iterator = entries.iterator();
while (iterator.hasNext()){
Map.Entry<String, TaskManagerInfo> entry = iterator.next();
TaskManagerInfo next = entry.getValue();
long l = System.currentTimeMillis();
if ((l - next.getLastHeartbeatTime()) > 6000){
iterator.remove();
taskManagerInfoConcurrentHashMap.remove(next.getTaskManagerId());
System.out.println(next.getTaskManagerId() +" 下线了");
}
}
}
if (message instanceof RegistTaskManager){
RegistTaskManager registTaskManager = (RegistTaskManager) message;
TaskManagerInfo taskManagerInfo = new TaskManagerInfo();
taskManagerInfo.setCpu(registTaskManager.getCpu());
taskManagerInfo.setHostname(registTaskManager.getHostname());
taskManagerInfo.setTaskManagerId(registTaskManager.getTaskManagerId());
taskManagerInfo.setMemory(registTaskManager.getMemory());
taskManagerInfoConcurrentHashMap.put(registTaskManager.getTaskManagerId(),taskManagerInfo);
System.out.println(taskManagerInfo.getTaskManagerId() +" 前来注册了");
getSender().tell("注册成功",getSelf());
}
if (message instanceof Heartbeat){
Heartbeat heartbeat = (Heartbeat) message;
String taskId = heartbeat.getTaskId();
TaskManagerInfo taskManagerInfo = taskManagerInfoConcurrentHashMap.get(taskId);
taskManagerInfo.setLastHeartbeatTime(System.currentTimeMillis());
System.out.println(taskId +" 前来心跳了");
}
}
@Override
public void preStart() throws Exception {
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
service.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
CheckTimeout checkTimeout = new CheckTimeout();
getSelf().tell(checkTimeout,getSelf());
}
}, 0, 3, TimeUnit.SECONDS);
}
public static void main(String[] args) throws Exception {
}
}
谢谢大家的观看。