scala基于akka实现RPC进程通讯,心跳机制,存活检查

1.创建Maven工程

maven工程中引入scala,akka,和akka联机模式的依赖

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <encoding>UTF-8</encoding>
        <scala.version>2.12.10</scala.version>
        <scala.compat.version>2.12</scala.compat.version>
        <akka.version>2.4.17</akka.version>
    </properties>


    <dependencies>
        <!-- scala的依赖 -->
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>${scala.version}</version>
        </dependency>

        <!-- akka actor依赖 -->
        <dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-actor_2.12</artifactId>
            <version>${akka.version}</version>
        </dependency>

        <!-- akka远程通信依赖 -->
        <dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-remote_2.12</artifactId>
            <version>${akka.version}</version>
        </dependency>

    </dependencies>

    <build>
        <pluginManagement>
            <plugins>
                <!-- 编译scala的插件 -->
                <plugin>
                    <groupId>net.alchim31.maven</groupId>
                    <artifactId>scala-maven-plugin</artifactId>
                    <version>3.2.2</version>
                </plugin>
                <!-- 编译java的插件 -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.5.1</version>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <groupId>net.alchim31.maven</groupId>
                <artifactId>scala-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <id>scala-compile-first</id>
                        <phase>process-resources</phase>
                        <goals>
                            <goal>add-source</goal>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>scala-test-compile</id>
                        <phase>process-test-resources</phase>
                        <goals>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>compile</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>


            <!-- 打jar插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.4.3</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <filters>
                                <filter>
                                    <artifact>*:*</artifact>
                                    <excludes>
                                        <exclude>META-INF/*.SF</exclude>
                                        <exclude>META-INF/*.DSA</exclude>
                                        <exclude>META-INF/*.RSA</exclude>
                                    </excludes>
                                </filter>
                            </filters>

                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>reference.conf</resource>
                                </transformer>

                                <!-- 指定maven方法 -->
                                <!--                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">-->
                                <!--                                    <mainClass>cn._51doit.rpc.Master</mainClass>-->
                                <!--                                </transformer>-->
                            </transformers>

                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

2.构建scala工程

创建一个scala的包,然后将此包设置为source root

主要流程

Master

主要职责

1启动后每隔15秒,检查结点存活情况

2接收worker的心跳机制,更新存活时间戳

3维护结点存活信息

package com.dt.rpc
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import com.typesafe.config.{Config, ConfigFactory}
import scala.concurrent.duration._
import scala.collection.mutable
class Master  extends Actor {
  //定义一个map,在启动的时候就开始定时检查检查
  //当注册的时候要把这个结点的注册信息保留下来放到一个map中


  override def preStart(): Unit = {
    import context.dispatcher  //关闭隐式转换
    //启动之后还是每15秒发送检查消息
    context.system.scheduler.schedule(0 millisecond,15000 millisecond,self,CheckHeart)
  }

  private val workmap = new mutable.HashMap[String,WorkerInfo]()
  override def receive: Receive = {
    //模式匹配字符串,接收到消息就执行了,说明这里的是直接调用了方法
    case "connect"=>{
      println("has one connected")
      sender()!"response"   //注意此处的发送是! 没有等号
    }
      //接收注册信息
    case RegisterInfo(id,workerMemory,workerCores) =>{
      //todo 将注册信息添加到内存中
      val l: Long = System.currentTimeMillis()
      workmap(id) = new WorkerInfo(id,workerMemory,workerCores)
      //todo 并回消息 success
      sender() ! "success"

    }
      //更新心跳机制
    case HeartBeat(id,time)=>{
      //todo 收到心跳之后将心跳更新到存活队列中
      if (workmap.contains(id)){
        //如果存在,就将时间更新进去
          val info: WorkerInfo = workmap(id)
          info.lastHeartbeatTime=time//更新进去时间
      }
    }
    case CheckHeart=>{
      //检查15秒没有发送消息的
      val stringToInfo: mutable.HashMap[String, WorkerInfo] = workmap.filter(x=>System.currentTimeMillis()-x._2.lastHeartbeatTime>=15000)
      stringToInfo.foreach(x=>workmap -=x._1)//将map中移除掉过滤出来的超时对象
    }
    println(s"当前存活的结点个数有"+workmap.size)
  }
}
object Master{
  val MASTER_ACTOR_SYSTEM="MASTER_ACTOR_SYSTEM"
  val MASTER_ACTOR="MASTER_ACTOR"
  val HOSTNAME="localhost"
  val PORT=8888

  def main(args: Array[String]): Unit = {
    //指定配置
    val confStr=
      s"""
        |akka.actor.provider = "akka.remote.RemoteActorRefProvider"
        |akka.remote.netty.tcp.hostname = $HOSTNAME
        |akka.remote.netty.tcp.port = $PORT
      """.stripMargin
    //通过stripMargin切分出,配置
    val config: Config = ConfigFactory.parseString(confStr)
    //使用配置工厂返回配置
    val actorSystem: ActorSystem = ActorSystem(MASTER_ACTOR_SYSTEM,config)
    actorSystem.actorOf(Props[Master],MASTER_ACTOR)
    //todo 启动完成之后,直接启动定时任务,检查队列
  }

}

Worker

主要职责

1.启动后向主节点发送消息

2.得到回复之后将自己的结点信息返回给master

3.每隔10秒向master发送心跳

package com.dt.rpc

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

/**
  * 功能:启动之后向master注册,收到master的相应之后,开始每隔10秒发送心跳通知自己发送消息
  * @param id
  * @param workerMemory
  * @param workerCores
  */
class Worker(id:String,workerMemory:Int,workerCores:Int) extends Actor{
  var selection: ActorSelection = _

  //在启动之前向master发送消息
  override def preStart(): Unit ={
    selection= context.actorSelection(s"akka.tcp://${Master.MASTER_ACTOR_SYSTEM}@${Master.HOSTNAME}:${Master.PORT}/user/${Master.MASTER_ACTOR}")
    //指定链接,链接格式akka.tcp://master的名字@ip地址:端口号/user/master名字
    //直接通过actor通道返回消息,返回注册信息
    selection !RegisterInfo(id,workerMemory,workerCores)
  }

  override def receive: Receive ={
    case "success"=>{
      //连接成功之后触发定时任务
      import context.dispatcher  //关闭隐式转换
      //从零秒开始,每隔10秒发送给自己一个标志信息
      context.system.scheduler.schedule(0 microsecond,10000 microsecond,self,CallSelfSend)
    }
      //发送消息到master中
    case CallSelfSend=> selection ! HeartBeat(id,System.currentTimeMillis())

  }
}
object Worker{

  def main(args: Array[String]): Unit = {
    val id:String =args(0)  //机器id
    val host:String=args(1) //本机ip
    val port:Int=args(2).toInt//端口
    val name:String=args(3) //name 留空
    val workerMemory = args(4).toInt
    val workerCores = args(5).toInt
    val configStr=
      s"""
        |akka.actor.provider = "akka.remote.RemoteActorRefProvider"
        |akka.remote.netty.tcp.hostname =$host
        |akka.remote.netty.tcp.port = $port
      """.stripMargin
    //指定worker的运行端口
    val config: Config = ConfigFactory.parseString(configStr)
    val worker_Actor_System: ActorSystem = ActorSystem("WORKER_ACTOR_SYSTEM",config)
    //创建一个actorsystem的类
    worker_Actor_System.actorOf(Props(new Worker(id,workerMemory,workerCores)))
    //创建一个actor

  }


}

ObjectClass

package com.dt.rpc


//用来返回成功消息的封装类
case object RegistedInfo
//worker通知自己发送心跳机制
case object CallSelfSend
//master检查存活
case object CheckHeart

CaseClass样例类

package com.dt.rpc

//worker注册信息
case class RegisterInfo(id:String,memory:Int,cores:Int)
//心跳机制
case class HeartBeat(id:String,time:Long)

//master内存维护
class WorkerInfo(val id: String, var memory: Int, cores: Int) {

  var lastHeartbeatTime: Long = _
}

 效果

先启动master,然后启动worker

Master日志

worker日志

当master启动后,存活结点为0,接着worker启动后注册,结点变成1,当worker停止之后,master存活节点变成0 

猜你喜欢

转载自blog.csdn.net/qq_44342234/article/details/107699192