文章目录
RPC和AKKA
不管是RPC还是AKKA,都是大数据体系中不同框架中所采用的通信框架。比如RPC就是大名鼎鼎的Hadoop中Hdfs里面NameNode,dataNode等等通信所采用的框架,AKKA在早起的Spark版本中(1.6一下)所采用的通信框架,1.6以上也开始采用基于Netty的RPC框架(其实自己基于RPC,模仿AKKA手写一个通信的框架)。
这些通信框架都是干嘛?通信!主要指的是进程间的消息的传递与处理。
RPC和AKKA便是这些通信框架中的两个典型代表。除此以外,还有非常多的进程间的通信框架。
eg:
WebService、Thirft、RMI等等。
WebService:在传统的j2ee中使用还是比较广泛。数据传输基于xml,客户端读取到请求的响应之后,就需要解析xml。比如进行天气情况的显示、航班信息的显示等等。
Thirft: 是apache的一个顶级项目,是一种接口描述语言和二进制通讯协议,它被用来定义和创建跨语言的服务。
RMI: remote method invocation.远程方法调用,是jdk中自带的一个进程间通信的框架,在java.rmi包下面。
4.1. RPC
RPC是Remote process call(远程过程调用),是IPC(Inter-Process Communication进程间代用)中的一种,IPC分为了两种,一种便是RPC,还有一种LPC(Local process call),LPC相当于RPC要简单的多,在大多数分布式系统中,多采用RPC来完成消息的传递。
RPC的工作原理是这样的。首先RPC底层通过TCP/IP进行消息的传递,采用客户端和服务端通信的方式。
rpc的使用需要用到hadoop相关的依赖,导入maven依赖
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.7.6</version>
</dependency>
4.1.1. 基于RPC模拟DataNode和NameNode通信第一版
-
HelloService
/** * java.lang.NoSuchFieldException: versionID * 客户端和服务按进行通信,二者必须要基于一个严格的版本versionID进行匹配。 */ public interface HelloService extends VersionedProtocol { long versionID = 11111111111L; boolean connect(String cmd); String heartBeat(String cmd); }
-
HelloServiceImpl
public class HelloServiceImpl implements HelloService { private DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public boolean connect(String cmd) { System.out.println("connect---" + df.format(new Date()) + ">>>客户端传送过来的命令为:" + cmd); if(StringUtils.isBlank(cmd)) { return false; } if(cmd.equalsIgnoreCase("ok")) { return true; } return false; } public String heartBeat(String cmd) { System.out.println("heartBeat---" + df.format(new Date()) + ">>>客户端传送过来的心跳包为:" + cmd); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return "ok"; } public long getProtocolVersion(String protocol, long clientVersion) throws IOException { return versionID; } public ProtocolSignature getProtocolSignature(String protocol, long clientVersion, int clientMethodsHash) throws IOException { return new ProtocolSignature(); } }
-
NameNode
/** 服务端进程,自然需要主函数来启动 */ public class NameNode { public static void main(String[] args) throws IOException { //服务构建器 RPC.Builder builder = new RPC.Builder(new Configuration()); RPC.Server nameNode = builder .setBindAddress("localhost") .setPort(6789) //客户端和服务端通信所遵循的协议 .setProtocol(HelloService.class) //该实例便是对该协议Protocol的一个实现 .setInstance(new HelloServiceImpl()) .build(); nameNode.start(); System.out.println("NameNode服务在地址:" + nameNode.getListenerAddress().getHostName() + ":" + nameNode.getPort() + "监听客户端访问~"); } }
-
DataNode
/** * 客户端进程 */ public class DataNode { static String[] cmds = { "hdfs", "ok", "haha", "oh shit" }; static Random random = new Random(); public static void main(String[] args) throws Exception { final HelloService proxy = RPC.getProxy(HelloService.class, HelloService.versionID, new InetSocketAddress("localhost", 6789), new Configuration()); while(!proxy.connect(cmds[random.nextInt(cmds.length)])) { System.out.println("连接失败,请重试~"); Thread.sleep(1000); } //建立起连接了 System.out.println("客户端与服务端已经建立起连接了,可以开始心跳~"); //使用调度器开始执行,Timer Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { //心跳 String ret = proxy.heartBeat(cmds[random.nextInt(cmds.length)]); System.out.println("ret" + ret); } }, 2000, 3000); } }
4.1.1. 基于RPC模拟DataNode和NameNode通信第二版
在真实的项目中,处理通信过程,非常重要的两个方面,返回值的设计,还有一个序列化的问题。
-
RegisterInfo
package com.desheng.p7.rpc.second.register; import com.desheng.p7.rpc.second.DateUtil; import org.apache.hadoop.io.Writable; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.io.Serializable; /** * 注册信息, * 客户端地址,通信端口 * 资源信息 * 最大内存 * 最大磁盘空间 * cpu core的个数 * 时间 */ public class RigsterInfo implements Writable { private String id; private String cmd; private String address; private int port; private long memory; private long disk; private int core; private long registerTime; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getCmd() { return cmd; } public void setCmd(String cmd) { this.cmd = cmd; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public long getMemory() { return memory; } public void setMemory(long memory) { this.memory = memory; } public long getDisk() { return disk; } public void setDisk(long disk) { this.disk = disk; } public int getCore() { return core; } public void setCore(int core) { this.core = core; } public long getRegisterTime() { return registerTime; } public void setRegisterTime(long registerTime) { this.registerTime = registerTime; } @Override public String toString() { return "RigsterInfo{" + "cmd='" + cmd + '\'' + ", address='" + address + '\'' + ", port=" + port + ", memory=" + memory + ", disk=" + disk + ", core=" + core + ", registerTime=" + DateUtil.foratTime(registerTime) + '}'; } @Override public void write(DataOutput out) throws IOException { out.writeUTF(this.id); out.writeUTF(this.cmd); out.writeUTF(this.address); out.writeInt(this.port); out.writeLong(this.memory); out.writeLong(this.disk); out.writeInt(this.core); out.writeLong(this.registerTime); } @Override public void readFields(DataInput in) throws IOException { this.id = in.readUTF(); this.cmd = in.readUTF(); this.address = in.readUTF(); this.port = in.readInt(); this.memory = in.readLong(); this.disk = in.readLong(); this.core = in.readInt(); this.registerTime = in.readLong(); } }
-
RegisterResult
package com.desheng.p7.rpc.second.register; import org.apache.hadoop.io.Writable; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; public enum RigsterResult implements Writable { OK(200, "成功"){ @Override public void write(DataOutput out) throws IOException { out.writeInt(this.code); out.writeUTF(this.phrase); } @Override public void readFields(DataInput in) throws IOException { this.code = in.readInt(); this.phrase = in.readUTF(); } }, TIME_OUT(300, "注册超时"){ @Override public void write(DataOutput out) throws IOException { out.writeInt(this.code); out.writeUTF(this.phrase); } @Override public void readFields(DataInput in) throws IOException { this.code = in.readInt(); this.phrase = in.readUTF(); } },//注册超过2秒,就算超时 NOT_FOUND(404, "资源没有找到"){ @Override public void write(DataOutput out) throws IOException { out.writeInt(this.code); out.writeUTF(this.phrase); } @Override public void readFields(DataInput in) throws IOException { this.code = in.readInt(); this.phrase = in.readUTF(); } }, SERVER_INTER_ERROR(500, "服务内部异常"){ @Override public void write(DataOutput out) throws IOException { out.writeInt(this.code); out.writeUTF(this.phrase); } @Override public void readFields(DataInput in) throws IOException { this.code = in.readInt(); this.phrase = in.readUTF(); } }; protected int code; protected String phrase; RigsterResult(int code, String phrase) { this.code = code; this.phrase = phrase; } public int getCode() { return code; } public String getPhrase() { return phrase; } }
-
HeartBeat
package com.desheng.p7.rpc.second.heart; import org.apache.hadoop.io.Writable; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; public class HeartBeat implements Writable{ private String id; private String cmd; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getCmd() { return cmd; } public void setCmd(String cmd) { this.cmd = cmd; } @Override public String toString() { return "HeartBeat{" + "id='" + id + '\'' + ", cmd='" + cmd + '\'' + '}'; } @Override public void write(DataOutput out) throws IOException { out.writeUTF(this.id); out.writeUTF(this.cmd); } @Override public void readFields(DataInput in) throws IOException { this.id = in.readUTF(); this.cmd = in.readUTF(); } }
-
HeartBeatResult
package com.desheng.p7.rpc.second.heart; import org.apache.hadoop.io.Writable; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; public class HeartBeatResult implements Writable { private String result; public String getResult() { return result; } public void setResult(String result) { this.result = result; } @Override public void write(DataOutput out) throws IOException { out.writeUTF(this.result); } @Override public void readFields(DataInput in) throws IOException { this.result = in.readUTF(); } }
-
HelloService
package com.desheng.p7.rpc.second; import com.desheng.p7.rpc.second.heart.HeartBeat; import com.desheng.p7.rpc.second.heart.HeartBeatResult; import com.desheng.p7.rpc.second.register.RigsterInfo; import com.desheng.p7.rpc.second.register.RigsterResult; import org.apache.hadoop.ipc.VersionedProtocol; public interface HelloService extends VersionedProtocol { long versionID = 2L; RigsterResult register(RigsterInfo register); HeartBeatResult heartBeat(HeartBeat heartBeat); }
-
HelloServiceImpl
package com.desheng.p7.rpc.second.impl; import com.desheng.p7.rpc.second.HelloService; import com.desheng.p7.rpc.second.heart.HeartBeat; import com.desheng.p7.rpc.second.heart.HeartBeatResult; import com.desheng.p7.rpc.second.register.RigsterInfo; import com.desheng.p7.rpc.second.register.RigsterResult; import org.apache.commons.lang.StringUtils; import org.apache.hadoop.ipc.ProtocolSignature; import java.io.IOException; import java.util.Random; public class HelloServiceImpl implements HelloService { public long getProtocolVersion(String protocol, long clientVersion) throws IOException { return versionID; } public ProtocolSignature getProtocolSignature(String protocol, long clientVersion, int clientMethodsHash) throws IOException { return new ProtocolSignature(); } private Random random = new Random(); public RigsterResult register(RigsterInfo register) { System.out.println(register); String cmd = register.getCmd(); if(StringUtils.isBlank(cmd)) { return RigsterResult.NOT_FOUND; } RigsterResult result = null; switch (cmd) { case "ok": result = RigsterResult.OK; break; case "hdfs": try { long start = System.currentTimeMillis(); Thread.sleep(random.nextInt(3000)); long registerTime = System.currentTimeMillis() - start; if(registerTime > 2000) { //超时 result = RigsterResult.TIME_OUT; } else { result = RigsterResult.OK; } } catch (InterruptedException e) { e.printStackTrace(); } break; case "haha": result = RigsterResult.SERVER_INTER_ERROR; break; default: result = RigsterResult.SERVER_INTER_ERROR; break; } return result; } public HeartBeatResult heartBeat(HeartBeat heartBeat) { System.out.println(heartBeat); HeartBeatResult result = new HeartBeatResult(); try { long start = System.currentTimeMillis(); Thread.sleep(random.nextInt(3000)); long registerTime = System.currentTimeMillis() - start; if(registerTime > 2000) { //超时 result.setResult("success"); } else { result.setResult("fail"); } } catch (InterruptedException e) { e.printStackTrace(); } return result; } }
-
NameNode
package com.desheng.p7.rpc.second; import com.desheng.p7.rpc.second.impl.HelloServiceImpl; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.ipc.RPC; import java.io.IOException; public class NameNode { public static void main(String[] args) throws IOException { RPC.Builder builder = new RPC.Builder(new Configuration()); RPC.Server nameNode = builder.setBindAddress("localhost") .setPort(6789) .setProtocol(HelloService.class) .setInstance(new HelloServiceImpl()) .build(); nameNode.start(); System.out.println("NameNode服务在地址:" + nameNode.getListenerAddress().getHostName() + ":" + nameNode.getPort() + "监听客户端访问~"); } }
-
DataNode
package com.desheng.p7.rpc.second; import com.desheng.p7.rpc.second.heart.HeartBeat; import com.desheng.p7.rpc.second.heart.HeartBeatResult; import com.desheng.p7.rpc.second.register.RigsterInfo; import com.desheng.p7.rpc.second.register.RigsterResult; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.ipc.RPC; import java.net.InetSocketAddress; import java.util.Random; import java.util.Timer; import java.util.TimerTask; import java.util.UUID; /* 执行过程当中出现了一下: java.io.IOException: Can't write: */ public class DataNode { static String[] cmds = { "hdfs", "ok", "haha", "oh shit" }; static Random random = new Random(); public static void main(String[] args) throws Exception { final HelloService proxy = RPC.getProxy(HelloService.class, HelloService.versionID, new InetSocketAddress("localhost", 6789), new Configuration()); String id = UUID.randomUUID().toString().replace("-", ""); RigsterInfo rigsterInfo = new RigsterInfo(); rigsterInfo.setId(id); rigsterInfo.setAddress("127.0.0.1"); rigsterInfo.setCore(4); rigsterInfo.setPort(6790); rigsterInfo.setMemory(2000); rigsterInfo.setDisk(1000000L); rigsterInfo.setRegisterTime(System.currentTimeMillis()); rigsterInfo.setCmd(cmds[random.nextInt(cmds.length)]); RigsterResult rigsterResult = proxy.register(rigsterInfo); while(!rigsterResult.equals(RigsterResult.OK)) { System.out.println("注册失败:注册响应状态码:" + rigsterResult.getCode() + ",失败原因:" + rigsterResult.getPhrase()); Thread.sleep(1000); rigsterInfo.setCmd(cmds[random.nextInt(cmds.length)]); rigsterResult = proxy.register(rigsterInfo); } //建立起连接了 System.out.println("客户端与服务端已经建立起连接了,可以开始心跳~"); //使用调度器开始执行,Timer Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { //心跳 HeartBeat hb = new HeartBeat(); hb.setId(id); hb.setCmd(cmds[random.nextInt(cmds.length)]); HeartBeatResult ret = proxy.heartBeat(hb); System.out.println("ret" + ret.getResult()); } }, 2000, 3000); } }
-
DateUtil
package com.desheng.p7.rpc.second; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; public class DateUtil { private static DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static String foratTime(long timestampe) { return df.format(new Date(timestampe)); } public static String foratDate(Date date) { return df.format(date); } }
4.2. AKKA
4.2.1. Actor
依赖的引入
<!-- scala actor -->
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-actors</artifactId>
<version>2.11.8</version>
</dependency>
4.2.1.1. Scala actor的简单使用:单项打招呼
actor相当于java中的多线程编程,要想让scala中的一个类成为Actor,则必须要扩展一个类scala.actors.Actor,并且复写其中的一个方法 act方法。
要想使用Actor,首先创建该Actor的实例,调用start方法启动actor,最后通过实例 ! msg发送消息。
// 通过单向通信来学习scala actor的使用
object _01ActorOps {
def main(args: Array[String]): Unit = {
val hiActor = new HiActor()
hiActor.start() //启动actor
var msg = readLine("请输入消息:")
while(msg != "quit") {
//完成了消息的传递
hiActor ! msg
msg = readLine("请输入消息:")
}
}
}
class HiActor extends Actor {
//在本方法中接收消息
override def act(): Unit = {
while (true) {
receive {//在本方法中接收消息 ,会进行阻塞
case greeting: String => {
println(greeting)
}
case _ => println("default")
}
}
}
}
4.2.1.2. 基于case class来进行传递
上述案例中,在传递数据时,使用了String作为载体,但是String传递的消息有限,无法传递复杂的数据,所以String不变处理复杂的数据结构,要想传递复杂结构,需要使用bean,在scala中就是case class。
对上述案例进行重构
object _02ActorOps {
def main(args: Array[String]): Unit = {
val helloActor = new HelloActor
helloActor.start()
helloActor ! Greeting("张冠杰", "这位施主,xxx意欲为何?")
}
}
class HelloActor extends Actor {
override def act(): Unit = {
receive {
case greeting: String => {
println(greeting)
}
case Greeting(name, greet) => {
println(s"Hi, $name, $greet")
}
case _ => println("default")
}
}
}
case class Greeting(name:String, greet:String)
4.2.1.3. scala actor之间的相互通信
上述案例,做到了单向通信,要想进行双向通信,首先至少需要2个actor,其次,其中一个actor应该需要持有另一个actor的实例。
import scala.actors.Actor
object _03ActorOps {
def main(args: Array[String]): Unit = {
val master = new Master()
master.start()
val lhs = new LittleBuddhistMonk(master)
lhs.start()
//小和尚开始向师傅请教
lhs ! Sign
}
}
class Master extends Actor {
override def act(): Unit = {
receive {
case Request(req) => {
println("小和尚问师傅:" + req)
println("是否沉思片刻,开口道:")
Thread.sleep(1000)
//sender对象,便是Actor中存储的消息发送者的对象,这里就是小和尚的对象
sender ! Response("道可道,名可名~")
}
}
}
}
class LittleBuddhistMonk(master:Master) extends Actor {
override def act(): Unit = {
while (true) {
receive {
case Sign => {
println("小和尚,灵机一动,计上心来~")
master ! Request("师傅,何为色即是空空即是色?")
}
case Response(resp) => {
println(resp)
println("小和尚,若有所思的点点头~")
}
}
}
}
}
case class Sign()
case class Request(req:String)
case class Response(resp: String)
4.2.1.4. 消息的同步和Future
1、Scala在默认情况下,消息都是以异步进行发送的;但是如果发送的消息是同步的,即对方接受后,一定要给自己返回结果,那么可以使用!?的方式发送消息。即val response= activeActor !? activeMessage。
2、如果要异步发送一个消息,但是在后续要获得消息的返回值,那么可以使用Future。即!!语法。
val futureResponse = activeActor !! activeMessage。
val activeReply = future()。
4.2.2. AKKA
4.2.2.1. AKKA简介
略,参见ppt
4.2.2.2. AKKA单向通信
-
编程模型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lt1iAbRf-1583482710696)(assets/1573093642799.png)]
-
具体实践
依赖:
<!-- akka actor --> <dependency> <groupId>com.typesafe.akka</groupId> <artifactId>akka-actor_2.11</artifactId> <version>2.3.16</version> </dependency> <dependency> <groupId>com.typesafe.akka</groupId> <artifactId>akka-remote_2.11</artifactId> <version>2.3.16</version> </dependency>
编码:
//akka通信案例 object _01AkkaOps { def main(args: Array[String]): Unit = { val actorSystem = ActorSystem.create("MyFirstActorSystem") //代理对象,Props是一个配置对象,是不可变的线程安全的,只在创建actor使用 val teacherActorRef = actorSystem.actorOf(Props[TeacherActor], "teacherAcot") //发送消息 teacherActorRef ! Request("两小儿辩日,早上的太阳大呢,还是中午的太阳大?") Thread.sleep(2000) actorSystem.shutdown() } } class TeacherActor extends Actor { override def receive = { case Request(req) => { println(req) } } } case class Request(req:String)
4.2.2.3. AKKA双向通信
-
通信模型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wbh0LWPS-1583482710699)(assets/1573094541468.png)]
-
编码实例
object _01AKKAActorOps { def main(args: Array[String]): Unit = { val actorSystem = ActorSystem.create("lxebrSystem") val teacherActorRef = actorSystem.actorOf(Props[TeacherActor], "teacherActor") //Props(classOf[StudentActor], teacherActorRef)中第一个参数是类,后面的参数是类的参数列表 val stuActorRef = actorSystem.actorOf(Props(classOf[StudentActor], teacherActorRef), "studentActor") stuActorRef ! Sign Thread.sleep(2000) actorSystem.shutdown() } } class TeacherActor extends Actor with ActorLogging { override def receive = { case Request(req) => { log.warning("学生问老师说:" + req) Thread.sleep(2000) log.info("老师沉思片刻后答到:") sender ! Response("其实是一样大的,早上天气冷是因为晚上的水蒸气被蒸发带走了热量,所以冷,中午热是因为晒了一上午自然热~") } case Response(resp) => { log.info(resp) } } } class StudentActor(teacherActor: ActorRef) extends Actor with ActorLogging { override def receive = { case Sign => { log.info("两小儿激烈辩日,不分胜负,随请教老师~") teacherActor ! Request("老师啊,早上的太阳大呢,还是中午的太阳大?") } case Response(resp) => { log.warning("老师回复说:" + resp) //sender 用来发送响应 sender ! Response("sidisina") } } }
通信协议
object Protocol { } case class Sign() case class Request(req:String) case class Response(resp:String)
4.2.2.4. AKKA远程双向通信
通信协议
object Start extends Serializable
object Stop extends Serializable
trait Message {
val id: String
}
case class Shutdown(waitSecs: Int) extends Serializable
case class Heartbeat(id: String, magic:Int) extends Message with Serializable
case class Header(id: String, len: Int, encrypted: Boolean) extends Message with Serializable
case class Packet(id: String, seq: Long, content: String) extends Message with Serializable
配置文件
application.conf
MyRemoteServerSideActor {
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
enabled-transports = ["akka.remote.netty.tcp"]
netty.tcp {
hostname = "127.0.0.1"
port = 2552
}
}
}
}
MyRemoteClientSideActor {
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
}
}
服务端actor
package com.desheng.p8.akka.third
import akka.actor.{Actor, ActorLogging}
class RemoteActor extends Actor with ActorLogging {
def receive = {
case Start => { // 处理Start消息
log.info("Remote Server Start ==>RECV Start event : " + Start)
}
case Stop => { // 处理Stop消息
log.info("Remote Server Stop ==>RECV Stop event: " + Stop)
}
case Shutdown(waitSecs) => { // 处理Shutdown消息
log.info("Remote Server Shutdown ==>Wait to shutdown: waitSecs=" + waitSecs)
Thread.sleep(waitSecs)
log.info("Remote Server Shutdown ==>Shutdown this system.")
context.system.shutdown // 停止当前ActorSystem系统
}
case Header(id, len, encrypted) => log.info("Remote Server => RECV header: " + (id, len, encrypted)) // 处理Header消息
case _ =>
}
}
服务端启动类
package com.desheng.p8.akka.third
import akka.actor.{ActorSystem, Props}
import com.typesafe.config.ConfigFactory
object AkkaServerApplication extends App {
// 创建名称为remote-system的ActorSystem:从配置文件application.conf中获取该Actor的配置内容
val system = ActorSystem("remote-system",
ConfigFactory.load().getConfig("MyRemoteServerSideActor"))
val log = system.log
log.info("===>Remote server actor started: " + system)
// 创建一个名称为remoteActor的Actor,返回一个ActorRef,这里我们不需要使用这个返回值
system.actorOf(Props[RemoteActor], "remoteActor")
}
客户端Actor
package com.desheng.p8.akka.third
import akka.actor.{Actor, ActorLogging}
class ClientActor extends Actor with ActorLogging {
// akka.<protocol>://<actor system>@<hostname>:<port>/<actor path>
val path = "akka.tcp://remote-system@127.0.0.1:2552/user/remoteActor" // 远程Actor的路径,通过该路径能够获取到远程Actor的一个引用
val remoteServerRef = context.actorSelection(path) // 获取到远程Actor的一个引用,通过该引用可以向远程Actor发送消息
@volatile var connected = false
@volatile var stop = false
def receive = {
case Start => { // 发送Start消息表示要与远程Actor进行后续业务逻辑处理的通信,可以指示远程Actor初始化一些满足业务处理的操作或数据
send(Start)
if(!connected) {
connected = true
log.info("ClientActor==> Actor connected: " + this)
}
}
case Stop => {
send(Stop)
stop = true
connected = false
log.info("ClientActor=> Stopped")
}
case header: Header => {
log.info("ClientActor=> Header")
send(header)
}
case (seq, result) => log.info("RESULT: seq=" + seq + ", result=" + result) // 用于接收远程Actor处理一个Packet消息的结果
case m => log.info("Unknown message: " + m)
}
private def send(cmd: Serializable): Unit = {
log.info("Send command to server: " + cmd)
try {
remoteServerRef ! cmd // 发送一个消息到远程Actor,消息必须是可序列化的,因为消息对象要经过网络传输
} catch {
case e: Exception => {
connected = false
log.info("Try to connect by sending Start command...")
send(Start)
}
}
}
}
客户端服务
package com.desheng.p8.akka.third
import akka.actor.{ActorSystem, Props}
import com.typesafe.config.ConfigFactory
object AkkaClientApplication {
def main(args: Array[String]): Unit = {
// 通过配置文件application.conf配置创建ActorSystem系统
val system = ActorSystem("client-system",
ConfigFactory.load().getConfig("MyRemoteClientSideActor"))
val log = system.log
// 获取到ClientActor的一个引用
val clientActor = system.actorOf(Props[ClientActor], "clientActor")
clientActor ! Start // 发送一个Start消息,第一次与远程Actor握手(通过本地ClientActor进行转发)
Thread.sleep(2000)
// 发送一个Header消息到远程Actor(通过本地ClientActor进行转发)
clientActor ! Header("What's your name: Can you tell me ", 20, encrypted=false)
Thread.sleep(2000)
}
}
作业:
1.使用scala完成快排
2.使用scala编写二分查找(递归和非递归)