Flink底层实现Akka

Flink的RPC实现:是基于Scala的网络编程库Akka来的。

Akka的特点
  • 它是对并发模型进行了更高的抽象
  • 它是异步、非阻塞、高性能的事件驱动编程模型
  • 它是轻量级事件处理(1GB内存可以以容纳百万级别的Actor)
Akka简介
  1. Akka是一个网络编程库
  2. ActorSystem是管理Actor生命周期的组件,Actor是负责进行通信的组件
  3. 每个Actor都有一个MailBox(在Flink源码中经常可以看到,checkpoint通知就是基于此),别的Actor发送给它的消息都是首先存储在MailBox中,通过这种方式可以实现异步通信
  4. 单个Actor可以改变他自身的状态,可以接收消息,也可以发送消息,还可以生成新的Actor
  5. 每个Actor是单线程的处理,不断从MailBox中拉取消息执行处理,所以对于Actor的消息处理,不适合调用阻塞的处理方式。
  6. 每个ActorSystem和Actor都在启动时候会给一个Name,如果要从Actor获取一个Actor,则通过以下方式进行Actor的获取akka.tcp://actorsydtem_name:主机:端口/user/actor_name来进行定位的。
  7. 如果一个Actor要和另外一个Actor进行通信,则必须先获取对象的Actor的ActorRef对象,然后通过该对象发送消息即可。
  8. 通过tell发送异步消息,不接受响应,通过ask发送异步消息,得到future返回,通过异步回调返回处理结果。

image-20220312224626342

我相信到现在大家对Akka应该有些了解了,可能大家看到这里还有很多同学很懵逼,没事我们接下来再用现实场景再好好探讨一下。

相信经过上面的特性介绍,我们对于Actor有个简单的认识了,首先Akka有两个重要的组成部分分别是ActorActorSystem这两个组成。那么我们应该怎么理解他呢,我们可以简单的理解为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下线了

image-20220313142903804

接下来我们来实现一下这个需求。

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 {

    }
}

谢谢大家的观看。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值