今天搞了一下远程访问actor,顺便写个笔记记录一下过程。
废话少说,直接干代码:
第一步:构建项目结构。使用maven管理jar包(说实话,maven实在是太好使了,爱不释手),简单粗暴一点直接从akka官网自动生成一个基于java的maven项目,里面有完整的项目结构。其中build.sbt,sbt,sbt.bat, build.sbt这些乱七八糟的可以不用理他,对了在这里要注意jdk的版本问题,一定要在jdk8以上,但是本人当时使用了一个jdk1.8.0_20的版本,报了一堆乱七八糟的数字码,字符码错误。最后换了个jdk1.8.0_121的就可以了,真实有点坑,网上好多说用jdk8以上的版本就可以(难道我用了一个假的jdk8,哈哈),将项目导入到eclipse或者idea中,我使用的是idea,然后删除项目里的自带的java文件和测试的java文件。开始编写自己的代码。
第二歩:在项目的包下,新建一个消息对象SetRequest,记住要实现Serializable接口哦,否则可是无法再网络上传输的。
package com.lightbend.akka.sample;
import java.io.Serializable;
public class SetRequest implements Serializable {
public final String key ;
public final String value ;
public SetRequest(String key ,String value) {
this.key = key;
this.value = value;
}
}
第二歩:新建一个Actor,这可是akka的一个重要的家庭成员。
package com.lightbend.akka.sample;
import akka.actor.AbstractActor;
import akka.actor.Actor;
import akka.actor.ActorRef;
import akka.actor.Status;
import akka.japi.pf.FI;
import java.util.HashMap;
import java.util.Map;
public class SetRequestActor extends AbstractActor {
public final Map map = new HashMap();
@Override
public Receive createReceive() {
Receive receive = receiveBuilder().match(SetRequest.class, setRequest -> {
map.put(setRequest.key,setRequest.value);
//客户端那边的actor,这是一个临时的actor,被akka自动创建的
ActorRef actorRef = sender();
System.out.println("actorRef : " + actorRef);
/**
* 向发送请求的actor发送响应信息,actorRef是要接受响应信息的对象,tell第一个参数
* 是响应给客户端的信息,第二个参数是谁响应给actorRef的,在这里并没有告诉是谁响应的
* 下面其它类型的消息都可以这么理解。
*/
actorRef.tell("save success",ActorRef.noSender());
}).match(String.class, str ->{
System.out.println("receive string : " + str);
sender().tell("receive success", Actor.noSender());
}).matchAny(request -> {
ActorRef actorRef = sender();
actorRef.tell(new Status.Failure(new Exception("不知道是什么类型的信息")),ActorRef.noSender());
}).build();
return receive;
}
}
第三歩:添加远程支持。在这一步,我们干一些小事情,但不可小视,在pom.xml中引入远程依赖包。如果没有这个依赖,那就放弃吧。。。。。。。。。。。。。。。。。。。尽量版本要保持一致(讲真,版本真是一个让人蛋疼的东西)。
com.typesafe.akka
akka-remote_2.12
2.5.16
依赖完成之后,难道就行了吗,不可能的。我们还要创建远程配置文件。这个看书上的,虽然就这几行,能耐可不小。该文件的位置在src/main/resources文件夹下。这个文件不同介绍了吧,一看就明白。(不过在这里先给个warn,注意一下端口,因为端口让我踩了一个大坑,到文章末尾的时候,我会介绍一下)。
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
enabled-transports = ["akka.remote.netty.tcp"]
netty.tcp {
hostname = "127.0.0.1"
port = 2552
}
}
}
第四步: 开始我们表演的时候,启动该服务,创建启动类(就是个main方法!!!!!!!).
package com.lightbend.akka.sample;
import akka.actor.ActorSystem;
import akka.actor.Props;
public class BootAkka {
public static void main(String[] args) {
System.out.println("server start.......");
//创建服务端的actor系统,名字随便起
ActorSystem actorSystem = ActorSystem.create("akkademy");
//注册actor对象,并给actor起个名字,这里叫remoteActor,其它名字也可以,但在客户端访问的时候要和这里起的名字保持一致,否则数据时传输不过来的。
actorSystem.actorOf(Props.create(SetRequestActor.class),"remoteActor");
}
}
第五步:okay,让我们开始运行服务端吧。激动人心的时候到了。
server start.......
[INFO] [09/16/2018 19:36:28.425] [main] [akka.remote.Remoting] Starting remoting
[INFO] [09/16/2018 19:36:29.262] [main] [akka.remote.Remoting] Remoting started; listening on addresses :[akka.tcp://[email protected]:2552]
[INFO] [09/16/2018 19:36:29.265] [main] [akka.remote.Remoting] Remoting now listens on addresses: [akka.tcp://[email protected]:2552]
看到了吧,好使, 没毛病!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
服务端完事了之后,那就开始我们的客户端吧,没有用户的应用有个毛用。那么还是照旧,采取分步走战略,我就喜欢步骤清晰的(最讨厌这写一句,那讲一句,看的宝宝都没心情了,哈哈)介绍。
第一步:第一步干啥那?你猜,当然建工程了(正经的:我所使用的是服务端使用一个项目,客户端再建一个项目,能够从直观上产生远程的感觉,当然也可以在一个工程下做远程访问actor,所以我就采用使用新建了两个项目)。新建一个客户端对象类。
package com.lightbend.akka.sample.service;
import akka.actor.*;
import com.lightbend.akka.sample.GetRequest;
import com.lightbend.akka.sample.SetRequest;
import javax.annotation.processing.Completion;
import java.util.concurrent.CompletionStage;
import static akka.actor.TypedActor.self;
import static akka.pattern.Patterns.ask;
import static scala.compat.java8.FutureConverters.toJava;
public class JClient {
private final ActorSystem system = ActorSystem.create("LocalSystem"); //客户端actor系统,名字随便起
private final ActorSelection remoteDb; // 远程actor对象。
private ActorRef actorRef = null ;
public JClient(String remoteAddress) {
//通过远程actor地址,找到远程actor对象
remoteDb = system.actorSelection("akka.tcp://akkademy@" + remoteAddress + "/user/remoteActor");
if (remoteDb != null) { //做日志输出信息,并没有太大的用途,读者可以跳过这一个行代码
System.out.println("remoteDb ==null :" + remoteDb.path().mkString());
}
}
public CompletionStage set(String key, String value) {
//向远程actor发送请求,使用ask方法,第一个参数是远程actor对象引用,第二个参数是要发送的消息对象,第三个是超市的毫秒数,如果在2秒内未响应则按超时处理。
return toJava(ask(remoteDb,new SetRequest(key,value),2000));
}
}
第二歩:开启远程访问。引入远程访问的依赖,怎么引不用说了吧,和服务端一样,远程依赖引入之后,还要创建远程配置文件,注意一下端口,和上面的不一样,如果和上面的一样,那么在启动服务器之后,再启动客户端,此时会报端口被占用异常,也就是说ActorSystem不能运行在同一个服务器的同一个端口上,这个是一定要切记的,这也是在构建服务端时,我说的注意的端口的问题,这个端口被占用的问题搞了我一下午,就是不知道为什么端口异常,最后更改了客户端的port之后,一切就正常了。
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
enabled-transports = ["akka.remote.netty.tcp"]
netty.tcp {
hostname = "127.0.0.1"
port = 2550
}
}
}
第三歩:开启启动客户端。
package com.lightbend.akka.sample.service;
import com.lightbend.akka.sample.GetRequest;
import com.lightbend.akka.sample.KeyNotFoundMsg;
import java.util.concurrent.CompletionStage;
import static akka.pattern.Patterns.ask;
public class ClientMain {
public static void main(String[] args) {
JClient jClient = new JClient("127.0.0.1:2552");
CompletionStage completionStage = jClient.set("age","12");
completionStage.thenAccept(response ->{
System.out.println("response :" + response);
});
}
}
怎么样简单吧, 看着根本没什么压力,之前看别人写的例子,本来问题就不大,结果一个java文件就写了一大坨,宝宝流泪了。
还等什么,那就开始运行喽。
[INFO] [09/16/2018 20:05:02.286] [main] [akka.remote.Remoting] Starting remoting
[INFO] [09/16/2018 20:05:03.058] [main] [akka.remote.Remoting] Remoting started; listening on addresses :[akka.tcp://[email protected]:2550]
[INFO] [09/16/2018 20:05:03.061] [main] [akka.remote.Remoting] Remoting now listens on addresses: [akka.tcp://[email protected]:2550]
remoteDb ==null :userremoteActor
[WARN] [SECURITY][09/16/2018 20:05:03.472] [LocalSystem-akka.remote.default-remote-dispatcher-8] [akka.serialization.Serialization(akka://LocalSystem)] Using the default Java serializer for class [com.lightbend.akka.sample.SetRequest] which is not recommended because of performance implications. Use another serializer or disable this warning using the setting 'akka.actor.warn-about-java-serializer-usage'
response :save success
看到了吧,客户端的控制台打印出服务端给出的响应信息:save success,再看服务端。打印出了客户端的actor路径,actorRef:
Actor[akka.tcp://[email protected]:2550/temp/$a],此时嘚瑟一下。
server start.......
[INFO] [09/16/2018 19:36:28.425] [main] [akka.remote.Remoting] Starting remoting
[INFO] [09/16/2018 19:36:29.262] [main] [akka.remote.Remoting] Remoting started; listening on addresses :[akka.tcp://[email protected]:2552]
[INFO] [09/16/2018 19:36:29.265] [main] [akka.remote.Remoting] Remoting now listens on addresses: [akka.tcp://[email protected]:2552]
actorRef : Actor[akka.tcp://[email protected]:2550/temp/$a]
至此,就完成了一次客户端对远程actor的请求,以及远程actor对客户端的响应的操作。手工~!!!!!!!!!!!!!!!