大数据知识:快学Scala(三)Scala编程实战

简介

        在学习完Scala语言后,我们可以实现一个简单的RPC来巩固前面我们学习的Scala知识点,这里主要的知识点涉及样例类、模式匹配、类继承、隐式转化以及函数和方法,如果需要源码可以在我的Github上下载,地址是SRPC

项目概述

需求

        目前大多数的分布式架构底层通信都是通过RPC实现的,RPC框架非常多,比如前我们学过的Hadoop项目的RPC通信框架,但是Hadoop在设计之初就是为了运行长达数小时的批量而设计的,在某些极端的情况下,任务提交的延迟很高,所有Hadoop的RPC显得有些笨重。

        Spark 的RPC是通过Akka类库实现的,Akka用Scala语言开发,基于Actor并发模型实现,Akka具有高可靠、高性能、可扩展等特点,使用Akka可以轻松实现分布式RPC功能。

Akka简介

        Akka基于Actor模型,提供了一个用于构建可扩展的(Scalable)、弹性的(Resilient)、快速响应的(Responsive)应用程序的平台。

        Actor模型:在计算机科学领域,Actor模型是一个并行计算(Concurrent Computation)模型,它把actor作为并行计算的基本元素来对待:为响应一个接收到的消息,一个actor能够自己做出一些决策,如创建更多的actor,或发送更多的消息,或者确定如何去响应接收到的下一个消息。


        

        Actor是Akka中最核心的概念,它是一个封装了状态和行为的对象,Actor之间可以通过交换消息的方式进行通信,每个Actor都有自己的收件箱(Mailbox)。通过Actor能够简化锁及线程管理,可以非常容易地开发出正确地并发程序和并行系统,Actor具有如下特性:

         1.提供了一种高级抽象,能够简化在并发(Concurrency)/并行(Parallelism)应用场景下的编程开发

        2.提供了异步非阻塞的、高性能的事件驱动编程模型

        3.超级轻量级事件处理(每GB堆内存几百万Actor)

项目实现

架构图

实现的主要思路

    该简单的框架主要有两个类Master、Worker,Master类主要用于Worke的注册,定时的检查节点是否存活,感知节点的上下线;Worker主要向Master注册信息,然后定期的调度任务。

代码实现 

Master类的具体代码:
package cn.edu.hust

import akka.actor.{Actor, ActorSystem, Props}
import com.typesafe.config.{Config, ConfigFactory}
import scala.concurrent.duration._
import scala.collection.mutable

class Master(val  host:String,val port:Int) extends Actor{
  val works=new mutable.HashMap[String,WorkInfo]()
  val works_avaliable=new mutable.HashSet[WorkInfo]()
  val heartBeat_interval:Long=15000
  //在构造方法之后,receive方法之前启动
  override def preStart(): Unit = {
      println("start method invoked")
      import context.dispatcher
      context.system.scheduler.schedule(0 millis ,heartBeat_interval millis,self,CheckHeartBeat)
  }

  def func(worker:WorkInfo):Boolean={
    System.currentTimeMillis()-worker.Pre_Time>heartBeat_interval
  }

  override def receive():Receive = {
    //worker向服务端注册信息
    case RegisterWorkInfo(id,memory,cores) =>{
      //如果没有包含这个worker,那么就将这个worker的信息保存在本地,然后发送信息
      if(!works.contains(id))
        {
          val worker_new=new WorkInfo(id,memory,cores)
          works.put(id,worker_new)
          works_avaliable+=worker_new
        }
      //发送消息给worker,如果注册成功返回
      sender ! RegisterResponse(s"akka.tcp://system@$host:$port/user/Master")
    }

    case HeartBeatToMaster(id) =>
    {
      if(works.contains(id))
        works(id).Pre_Time=System.currentTimeMillis()
      //works_avaliable.
        //sender() ! "ok"
    }

    case CheckHeartBeat=>
    {
        //val func=(worker:WorkInfo)=>Boolean {}

       val toRemove=works_avaliable.filter(func)
       for(info <- toRemove) {
          works -= info.id
          works_avaliable -= info
       }
       println(works.size)
    }
  }
}
object Master {
  def main(args: Array[String]): Unit = {
    val host=args(0)
    val port=args(1).toInt
    val conf=s"""|akka.actor.provider = "akka.remote.RemoteActorRefProvider"
                    |akka.remote.netty.tcp.hostname = "$host"
                    |akka.remote.netty.tcp.port = "$port"
                 """.stripMargin
     val config=ConfigFactory.parseString(conf)
    //1.创建一个ActorSystem用于监控和管理所有的actor
     val actorSystem=ActorSystem.create("system",config)
    //创建一个actor,这里表示Master
     val master=actorSystem.actorOf(Props(new Master(host,port)), "Master")
      //发送异步消息
     //master!"connect"
    //退出
    actorSystem.awaitTermination()
  }
}

Worker类的具体代码:

package cn.edu.hust

import java.util.UUID

import akka.actor.{Actor, ActorSelection, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
import scala.concurrent.duration._

class Worker(val masterHost:String,val masterPort:Int) extends Actor{
  var master:ActorSelection= _
  //给每一个worker分配一个唯一的id
  val id=UUID.randomUUID().toString
  val beats=10000
  override def preStart(): Unit = {
    //1.连接server,获取master实例
    master=context.actorSelection(s"akka.tcp://system@$masterHost:$masterPort/user/Master")
    //2.使用master实例注册消息
    master! RegisterWorkInfo(id,10240,4)

  }

  override def receive ():Receive = {
    case RegisterResponse(url)=>{
      println(s"the master work in $url")
      import context.dispatcher
      //配置一个定时调度器,定时向master发送任务
      context.system.scheduler.schedule(0 millis ,beats millis,self,HeartBeat)
    }
    case HeartBeat=>{
      master!HeartBeatToMaster(id)
    }
  }
}
object Worker
{
  def main(args: Array[String]): Unit = {
      val masterHost=args(0)
      val masterPort=args(1).toInt
      val workerHost=args(2)
      val workerPort=args(3).toInt
      val conf=s"""|akka.actor.provider = "akka.remote.RemoteActorRefProvider"
                   |akka.remote.netty.tcp.hostname = "$workerHost"
                   |akka.remote.netty.tcp.port = "$workerPort"
                 """.stripMargin
      val config=ConfigFactory.parseString(conf)
      val system=ActorSystem.create("worker",config)
      val worker=system.actorOf(Props(new Worker(masterHost,masterPort)),"Worker")
    system.awaitTermination()
  }
}

附带的代码:

package cn.edu.hust

trait RemoteMessage extends Serializable
//从worker到master的消息
case class RegisterWorkInfo(id:String,memory:Int,cores:Int) extends RemoteMessage
//master向worker发送消息
case class RegisterResponse(url:String) extends RemoteMessage
//master向worker发送心跳消息
case class HeartBeatToMaster(id:String)extends RemoteMessage
//worker自己向自己发送心跳信息
case object HeartBeat
case object CheckHeartBeat

package cn.edu.hust

class WorkInfo(val id:String,val memory:Int, val cores:Int) {
  //存储上一次心跳通信的时间
  var Pre_Time:Long=_
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值