Akka:基于Actor的并发解决方案

本文介绍了Akka基于Actor模型的并发解决方案,详细讲解了Actor模型的基本概念,如Actor、消息传递、生命周期和行为切换。通过示例展示了如何在Java环境中使用Akka,包括Actor查找、自定义Dispatcher和MailBox。此外,还讨论了Actor的容错处理、熔断机制以及消息路由等高级特性,帮助读者深入理解Akka在高并发场景下的应用。
摘要由CSDN通过智能技术生成

Actor模型

在面向对象编程中,一个对象可以访问或修改另一个对象的值,在高并发情况下,由于机器性能的瓶颈,当有多个对象对同一竞争资源进行操作时,可能会出现数据错误的问题(即实际读取的数据不是预期数据,而是前面阶段到这一阶段未修改完成的数据)。Actor模型对此进行了修改,它不是直接对对象进行操作,而是通过消息传递的方式与外界进行交互。如图所示:1591594965284

Actor一次只接收处理一个消息,未处理消息会被放入队列等待处理。

Actor有几个重要概念:

  • Actor:处理消息并修改内部状态的工作节点。
  • 消息:用于在多个Actor之前通信的数据。
  • 消息传递:一种开发模式,通过传递消息来触发行为。
  • 邮箱地址:消息传递的目标地址,在Actor空闲时会从该地址获取消息。
  • 邮箱:存储多个未处理消息的队列。
  • Actor系统:由Actor集合、邮箱地址、邮箱和配置等组成的系统。

在一个应用中,所有Actor组成了ActorSystem(Actor系统),它是一个层级结构,除顶级Actor外所有Actor都有一个父Actor,当子Actor在处理消息时出现异常情况,父Actor可以通过预先指定的方式来处理子Actor,处理方式有:恢复子Actor、重启子Actor、停止子Actor以及扩大化失败。在ActorSystem创建时,默认会启动三个Actor。

所有Actor都有自己的生命周期,Akka提供了对应的函数来响应不同的生命周期。常见的操作是构建一个Actor来处理其他Actor死亡时传递的消息,这个Actor也被称为Death Wath

因为Actor是通过消息进行通信的,所以对于其他Actor是在本地还是在远程它都不在乎,Actor仅仅操作它的引用。

粗略了解了Actor的相关知识后,接下来,我们就开始Akka的学习。

我的环境为:

操作系统:Windows10

jdk版本:jdk11

Akka依赖:

<dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-actor_2.13</artifactId>
            <version>2.6.0</version>
        </dependency>

Hello Akka

Akka是一个用于高并发、分布式、弹性伸缩场景下的消息驱动应用开发框架,它基于Actor模型,为开发者提供了消息控制状态的开发思想。前面提到,Actor通过操作模型的引用来控制内部状态的变化,Akka对此的实现是ActorRef对象,Akka通过ActorSystem.create()方法获取Actor系统,然后调用API得到指定Actor的引用,通过引用发送消息来与其它Actor通信。

首先,让我们来尝试一下Akka版的Hello World

public class TestAkka{
   
    static ExecutorService threadPool = Executors.newFixedThreadPool(10000);

    public static void test(){
   
        Demo demo = new Demo();
        for (int i = 0; i < 100000; i++) {
   
            threadPool.execute(new TestAkkaThread(demo));
        }
    }

    public static void testAkka(){
   
        // 获取Actor系统
        ActorSystem sys = ActorSystem.create();
        // 获取指定Actor
        ActorRef ref = sys.actorOf(Props.create(AkkaDemo.class), "startActor");
        for (int i = 0; i < 100000; i++) {
   
            threadPool.execute(new TestAkkaThread(ref));
        }
    }

    public static void main(String[] args) {
   
        // test();
        testAkka();
    }
}

class AkkaDemo extends UntypedAbstractActor {
   

    private static int cnt = 1;

    public void onReceive(Object message){
   
        // 当Actor接收到消息时,自动调用此方法
        System.out.println(String.format("第:%d次接收消息", cnt++));
    }
}

class Demo {
   
    private static int cnt = 1;

    public void tell(){
   
        System.out.println(String.format("第:%d次接收消息", cnt++));
    }
}

class TestAkkaThread implements Runnable{
   

    private Object ref;

    public TestAkkaThread(Object ref) {
   
        this.ref = ref;
    }

    @Override
    public void run() {
   
        if (ref instanceof ActorRef)
            // 通过ActorRef向对应Actor传送消息
            ((ActorRef)ref).tell("", ActorRef.noSender());
        else ((Demo)ref).tell();
    }
}

getSelf():获取当前Actor的引用

getSender():返回当前Actor接收的消息的发送者的引用,比如如果Actor A向Actor B发送消息,则当B调用getSender()时,它将返回Actor A的引用。在这里可以简单理解为:返回来发送回应消息的目标的引用。

Akka提供两种发送消息的机制,分别为tellask,两者的主要区别有:

  1. tell为同步发送,ask为异步发送。
  2. tell无返回值,ask可获取发送后的结果。

ask的应用如下:

public class StartAkka extends UntypedAbstractActor {
   

    @Override
    public void onReceive(Object message){
   
        System.out.println("接收消息:" + message);
        getSender().tell("返回消息", getSelf());
    }

    public static void main(String[] args) {
   
        ActorSystem sys = ActorSystem.create();
        ActorRef ref = sys.actorOf(Props.create(StartAkka.class), "startAkka");
        ref.tell("Hello  Akka!", ActorRef.noSender());
        Timeout timeout = new Timeout(10, TimeUnit.SECONDS);
        Future<Object> akka_ask = Patterns.ask(ref, "Akka Ask", timeout);
        System.out.println("ask...");
        akka_ask.onComplete(new Function1<Try<Object>, Object>() {
   
            @Override
            public Object apply(Try<Object> v1) {
   
                // 获取回复成功的处理逻辑
                if (v1.isSuccess()) System.out.println("发送成功,收到消息:" + v1.get());
                // 获取回复失败的处理逻辑
                if (v1.isFailure()) System.out.println("发送失败:" + v1.get());
                return null;
            }
        }, sys.dispatcher());
        System.out.println("continue...");
    }
}

Patterns.ask 方法会异步执行,假如Actor返回消息超时了,会产生一个akka.pattern.AskTimeoutException

sys.dispatcher():返回当前Akka的消息分发器,该内容到后面会讲到。

Actor查找

Actor模型中我们了解到一个Actor系统其实就是一棵树,每一个Actor都是一个节点,对于已存在的Actor,我们可以通过路径(当前路径/绝对路径)来查找:

public class SearchAkka extends UntypedAbstractActor {
   

    private ActorRef target = getContext().actorOf(Props.create(Target.class), "targetActor");

    @Override
    public void onReceive(Object message) throws Throwable, Throwable {
   
        if (message instanceof String) {
   
            if ("find".equals(message)){
   
                /*
                   LookupActor在收到"find"消息后,会通过ActorContext查找出ActorSelection对象.
                   ActorSelection发送Identify时,需要指定一个messageId(用来区分Actor),
                   消息发送后,当前Actor会收到一个ActorIdentity,可以通过ActorIdentity.getRef()
                   方法来获取指定的ActorRef
                 */
                ActorSelection targetActor = getContext().actorSelection("targetActor");

                // 异步查找Actor
                Timeout timeout = new Timeout(10, TimeUnit.SECONDS);
                Future<Object> find = Patterns.ask(targetActor, "find", timeout);
                find.onComplete(new Function1<Try<Object>, Object>() {
   
                    @Override
                    public Object 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值