文章系列
Akka 基础篇就此结束了,Akka基础篇主要介绍Akka的基本概念与一些基本术语,使用方式
代码:https://github.com/Eason-shu/Akka
虽然Akka在单机上可以运行上百万的Actor,但出于容错、负载均衡、灰度发布、提高并行度等等原因,我们仍然需要能在多个不同的服务器上运行Actor。所以Akka提供了akka-remoting的扩展包,屏蔽底层网络传输的细节,让上层以及其简单的方式使用远程的Actor调度。
Akka Remoting 是一个以点对点方式连接 actor 系统的通信模块,它是 Akka 集群的基础。远程处理的设计由两个(相关的)设计决策驱动:
这些决定的结果是不可能安全地创建具有预定义角色的纯客户端-服务器设置(违反假设 2)。对于客户端-服务器设置,最好使用 HTTP 或 Akka I/O。
重要提示:使用涉及网络地址转换、负载平衡器或 Docker 容器的设置违反了假设 1,除非在网络配置中采取额外步骤以允许相关系统之间的对称通信。在这种情况下,Akka 可以配置为绑定到与用于在 Akka 节点之间建立连接的地址不同的网络地址。请参阅NAT 后面或 Docker 容器中的 Akka。
maven仓库地址:
注意版本匹配,不然会疯狂报错,运行不起来
我的版本:
scala:2.13.0
Akka版本:2.13
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.hcgroupId>
<artifactId>ActorDemo03artifactId>
<version>0.0.1-SNAPSHOTversion>
<name>ActorDemo03name>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
<scala.version>2.11.7scala.version>
properties>
<dependencies>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
<dependency>
<groupId>com.typesafe.akkagroupId>
<artifactId>akka-actor_2.13artifactId>
<version>2.5.23version>
dependency>
<dependency>
<groupId>com.typesafe.akkagroupId>
<artifactId>akka-remote_2.13artifactId>
<version>2.5.23version>
dependency>
<dependency>
<groupId>com.typesafe.akkagroupId>
<artifactId>akka-testkit_2.13artifactId>
<version>2.5.23version>
dependency>
<dependency>
<groupId>org.scala-lang.modulesgroupId>
<artifactId>scala-java8-compat_2.11artifactId>
<version>1.0.2version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.2version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.testnggroupId>
<artifactId>testngartifactId>
<version>RELEASEversion>
<scope>compilescope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<configuration>
<source>8source>
<target>8target>
configuration>
plugin>
plugins>
build>
project>
package pojo;
import java.io.Serializable;
/**
* @description: 设置消息
* @author: shu
* @createDate: 2022/11/28 11:51
* @version: 1.0
*/
public class SetRequest implements Serializable {
public final String key;
public final Object value;
public SetRequest(String key, Object value) {
this.key = key;
this.value = value;
}
}
package pojo;
import java.io.Serializable;
/**
* @description: 获取消息
* @author: shu
* @createDate: 2022/11/28 11:52
* @version: 1.0
*/
public class GetRequest implements Serializable {
public final String key;
public GetRequest(String key) {
this.key = key;
}
}
package pojo;
import java.io.Serializable;
/**
* @description:
* @author: shu
* @createDate: 2022/11/28 11:52
* @version: 1.0
*/
public class KeyNotFoundException extends Exception implements
Serializable {
public final String key;
public KeyNotFoundException(String key) {
this.key = key;
}
}
import akka.actor.*;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.japi.pf.ReceiveBuilder;
import pojo.GetRequest;
import pojo.KeyNotFoundException;
import pojo.SetRequest;
import java.util.HashMap;
import java.util.Map;
/**
* @description:
* @author: shu
* @createDate: 2022/11/28 12:31
* @version: 1.0
*/
public class RequestActor extends AbstractActor {
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
protected final Map<String, Object> map = new HashMap<>();
@Override
public void preStart() throws Exception {
log.info("ToFindRemoteActor is starting");
}
@Override
public Receive createReceive() {
return ReceiveBuilder.create()
// 设置消息
.match(SetRequest.class, message -> {
// 打印消息
log.info("Received Set request: {}", message.key);
// 缓存消息
map.put(message.key, message.value);
// 回应消息
sender().tell(new Status.Success(message.key), self());
})
// 得到消息
.match(GetRequest.class, message -> {
// 打印日志
log.info("Received Get request: {}", message.key);
// 获取消息
Object value = (Object) map.get(message.key);
Object response = (value!= null)
? value
: new Status.Failure(new KeyNotFoundException(message.key));
// 响应消息
sender().tell(response, self());
})
// 未找到消息
.matchAny(o ->
sender().tell(new Status.Failure(new ClassNotFoundException()), self())
)
.build();
}
/**
* 测试
* @param args
*/
public static void main(String[] args) {
// Config config = ConfigFactory.parseString(
// "akka.remote.netty.tcp.port=" + 2551)
// .withFallback(ConfigFactory.load("application.conf"));
// Create an Akka system
ActorSystem system = ActorSystem.create("akkademy");
// Create an actor
ActorRef ref = system.actorOf(Props.create(RequestActor.class), "akkademy-db");
System.out.println(ref);
}
}
akka {
stdout-loglevel = "DEBUG"
loglevel = "DEBUG"
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
enabled-transports = ["akka.remote.netty.tcp"]
netty.tcp {
hostname = "127.0.0.1"
}
}
log-sent-messages = on
log-received-messages = on
}
package client;
import akka.actor.ActorRef;
import akka.actor.ActorSelection;
import akka.actor.ActorSystem;
import akka.pattern.AskableActorSelection;
import akka.util.Timeout;
import pojo.GetRequest;
import pojo.SetRequest;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;
import static scala.compat.java8.FutureConverters.toJava;
/**
* @description:
* @author: shu
* @createDate: 2022/11/28 16:10
* @version: 1.0
*/
public class JClient {
private final ActorSystem system = ActorSystem.create("LocalSystem");
private final ActorSelection remoteDb;
public JClient(String remoteAddress) {
remoteDb = system.actorSelection("akka.tcp://akkademy@" +
remoteAddress + "/user/akkademy-db");
}
/**
* 缓存消息
* @param key
* @param value
* @return
*/
public CompletionStage set(String key, Object value) {
return toJava(new AskableActorSelection(remoteDb).ask(new SetRequest(key, value), Timeout.apply(5000, TimeUnit.SECONDS)));
}
/**
* 获取缓存消息
* @param key
* @return
*/
public CompletionStage get(String key){
return toJava(new AskableActorSelection(remoteDb).ask(new GetRequest(key), Timeout.apply(5000, TimeUnit.SECONDS)));
}
}
import akka.actor.ActorRef;
import akka.actor.ActorSelection;
import akka.actor.ActorSystem;
import client.JClient;
import org.junit.Test;
import pojo.SetRequest;
import java.util.concurrent.CompletableFuture;
public class AkkademyDbTest {
/**
* 测试注意需要放在不同的两个项目进行测试,不然会Caused by: java.net.BindException: Address already in use: bind
* @throws Exception
*/
@Test
public void itShouldSetRecord() throws Exception {
JClient client = new JClient("127.0.0.1:2552");
client.set("123", 123);
Integer result = (Integer) ((CompletableFuture) client.
get("123")).get();
System.out.println("获取的结果:"+result);
assert(result == 123);
}
}
akka {
stdout-loglevel = "DEBUG"
loglevel = "DEBUG"
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
enabled-transports = ["akka.remote.netty.tcp"]
netty.tcp {
hostname = "127.0.0.1"
port = 0
}
log-sent-messages = on
log-received-messages = on
}
}
注意:需要放在两个不一样的配置项目中
import akka.actor.{Actor, Status}
import akka.event.{Logging, LoggingAdapter}
import scala.collection.convert.ImplicitConversions.`map AsJavaMap`
import scala.collection.mutable
/**
* @description:
* @author: shu
* @createDate: 2022/11/28 12:41
* @version: 1.0
*/
class ScalaRequest extends Actor {
protected val log: LoggingAdapter = Logging.getLogger(context.system, this)
val map: mutable.Map[String, Object] = new mutable.HashMap[String, Object]
override def receive = {
case SetRequest(key, value) =>
log.info("received SetRequest - key: {} value: {}", key, value)
map.put(key, value)
sender() ! Status.Success
case GetRequest(key) =>
log.info("received GetRequest - key: {}", key)
val response: Option[Object] = map.get(key)
response match{
case Some(x) => sender() ! x
case None => sender() ! Status.Failure(new KeyNotFoundException(key))
}
case o => Status.Failure(new ClassNotFoundException)
}
}
case class SetRequest(key: String, value: Object)
case class GetRequest(key: String)
case class KeyNotFoundException(key: String) extends Exception
配置文件跟Java一样
import akka.actor.ActorSystem
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.duration.DurationInt
import scala.language.postfixOps
/**
* @description:
* @author: shu
* @createDate: 2022/12/1 11:43
* @version: 1.0
*/
class SClient(remoteAddress: String) {
private implicit val timeout = Timeout(2 seconds)
private implicit val system = ActorSystem("LocalSystem")
private val remoteDb = system.actorSelection(
s"akka.tcp://akkademy@$remoteAddress/user/akkademy-db")
def set(key: String, value: Object) = {
remoteDb ? SetRequest(key, value)
}
def get(key: String) = {
remoteDb ? GetRequest(key)
}
}
case class SetRequest(key: String, value: Object)
case class GetRequest(key: String)
case class KeyNotFoundException(key: String) extends Exception
import scala.concurrent.duration.DurationInt
import scala.concurrent.Await
import scala.language.postfixOps
/**
* @description:
* @author: shu
* @createDate: 2022/12/1 11:44
* @version: 1.0
*/
object Main extends App {
val client = new SClient("127.0.0.1:2552")
client.set("123", new Integer(123))
val futureResult = client.get("123")
val result = Await.result(futureResult, 10 seconds)
}
注意:需要放在两个不一样的配置项目中