Actor的熔断(CircuitBreaker)机制

A股曾经搞过熔断,而Actor也有熔断机制。其原理如下:



三个状态: 关闭 打开 半打开

Akka的熔断对actor定义了三种状态,分别是open,half-open,close

Open:表示熔断打开,所有后续的请求都直接返回,不进行逻辑处理

Half-open:表示熔断半开,如果第一次请求,能正确执行,即不超时无异常,则熔断机制关闭,切换状态到CLOSED,否则切换加Open,等待重置时间。

Close:表示熔断关闭,所有请求都进行逻辑处理


当actor失败次数达到配置中允许最大次数,则切换至open状态,即熔断开启,后序的请求会直接返回,不进行真正的逻辑处理


当到达重置时间,状态会切换至half-open状态,其后的请求若处理成功,则状态切换回closed,即熔断关闭,若请求仍然超时不成功,则状态切换回open,等待下一个重置时间

核心代码:

     this.breaker = new CircuitBreaker(
                getContext().dispatcher(),
                getContext().system().scheduler(),
                2,//允许最大失败次数
                Duration.create(2, TimeUnit.SECONDS),//超时时间
                Duration.create(8, TimeUnit.SECONDS))//重置时间
                .onOpen(this::notifyMeOnOpen).onHalfOpen(this::notifyHalfOpen)
                .onClose(this::notifyClose);


代码示例:

package com.zte.sunquan.cb.actor;

import akka.actor.AbstractActor;
import akka.actor.Props;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.pattern.CircuitBreaker;
import scala.Function0;
import scala.concurrent.duration.Duration;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import static akka.pattern.PatternsCS.pipe;

/**
 * Created by sunquan on 2017/9/19.
 * 1超时,2超时,进入half-open
 */
public class DangerousJavaActor2 extends AbstractActor {
    private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);

    private final CircuitBreaker breaker;

    public static Props props() {
        return Props.create(DangerousJavaActor2.class);
    }

    public DangerousJavaActor2() {
        this.breaker = new CircuitBreaker(
                getContext().dispatcher(),
                getContext().system().scheduler(),
                2,
                Duration.create(2, TimeUnit.SECONDS),
                Duration.create(8, TimeUnit.SECONDS))
                .onOpen(this::notifyMeOnOpen)
                .onHalfOpen(this::notifyMeOnHalfOpen)
                .onClose(this::notifyMeOnClose);
    }

    @Override
    public Receive createReceive() {
        return receiveBuilder().match(String.class, m -> m.startsWith("invoke"), (s) ->
                pipe(breaker.callWithCircuitBreakerCS(() ->
                                CompletableFuture.supplyAsync(this::blockCall)
                        ), getContext().dispatcher()
                ).to(sender()))
                .match(String.class, m -> m.startsWith("call"), (s) ->
                        sender().tell(breaker
                                .callWithSyncCircuitBreaker(this::blockCall), self()))
//                .match(String.class, (s) ->{
//                            int i=0;
//                            sender().tell(breaker
//                                    .withSyncCircuitBreaker(new Function0<Object>() {
//                                    }), self());
//                        }

                .build();
    }


    public void notifyMeOnOpen() {
        log.warning("My CircuitBreaker is now open.");
    }

    public void notifyMeOnHalfOpen() {
        log.warning("My CircuitBreaker is now half-open.");
    }

    public void notifyMeOnClose() {
        log.warning("My CircuitBreaker is now close.");
    }

    private int i = 2;


    public String bolockCall(int i) {
        System.out.println("blockCall:" + i + ",thread:" + Thread.currentThread().getName());
//        throw new RuntimeException("sq");
        if (i <= 0)
            return "hello,world";
        else {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return "error";
    }

    public String blockCall() {
        System.out.println("blockCall:" + i + ",thread:" + Thread.currentThread().getName());
//        throw new RuntimeException("sq");
        if (i <= 0)
            return "hello,world";
        else {
            i--;
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return "error";
    }

}


    @Test
    public void startTest2() throws InterruptedException {
        ActorSystem system = ActorSystem.create();
        ActorRef printActor = system.actorOf(Props.create(PrintActor.class));
        ActorRef myActor = system.actorOf(DangerousJavaActor2.props());
        //1.由于maxFailure为2次,且超时时间为2秒,而事件处理需要3秒,两次调用后,熔断机制触发 open
        myActor.tell("invoke1", printActor);
        myActor.tell("invoke2", printActor);
        //2.无消息发送,等待最终超时8秒后,切换至half-open
        Thread.sleep(15000);
        //3.发送后,又超时,又熔断,切换回open
        myActor.tell("invoke3", printActor);
        Scanner scanner = new Scanner(System.in);
        scanner.next();
    }

打印:

blockCall:2,thread:ForkJoinPool.commonPool-worker-2
blockCall:1,thread:ForkJoinPool.commonPool-worker-3
[WARN] [12/19/2017 14:43:13.281] [default-akka.actor.default-dispatcher-10] [akka://default/user/$b] My CircuitBreaker is now open.
[WARN] [12/19/2017 14:43:21.282] [default-akka.actor.default-dispatcher-2] [akka://default/user/$b] My CircuitBreaker is now half-open.
blockCall:0,thread:ForkJoinPool.commonPool-worker-1
[WARN] [12/19/2017 14:43:26.216] [default-akka.actor.default-dispatcher-2] [akka://default/user/$b] My CircuitBreaker is now close.
hello,world:Tue Dec 19 14:43:26 CST 2017

分析:

invoke1,invoke2的两次调用,i都大于0,等待3秒,造成超时,从而触发熔断

等待8秒重置时间后,进行半开

第三次invoke,i此时为0,直接返回hello,world,执行成功,熔断关闭


@Test
    public void startTest3() throws InterruptedException {
        ActorSystem system = ActorSystem.create();
        ActorRef printActor = system.actorOf(Props.create(PrintActor.class));
        ActorRef myActor = system.actorOf(DangerousJavaActor2.props());
        //1.由于maxFailure为2次,且超时时间为2秒,而事件处理需要3秒,两次调用后,熔断机制触发 open
        myActor.tell("call1", printActor);
        myActor.tell("call2", printActor);
        //2.无消息发送,等待最终超时8秒后,切换至half-open
        Thread.sleep(25000);
        //3.发送后,又超时,又熔断,切换回open
        myActor.tell("call3", printActor);
        Scanner scanner = new Scanner(System.in);
        scanner.next();
    }
打印:

blockCall:2,thread:default-akka.actor.default-dispatcher-5
blockCall:2,thread:default-akka.actor.default-dispatcher-12
[ERROR] [12/19/2017 15:06:37.817] [default-akka.actor.default-dispatcher-5] [akka://default/user/$b] Circuit Breaker Timed out. (akka.pattern.CircuitBreaker$$anon$1: Circuit Breaker Timed out.)
[ERROR] [12/19/2017 15:06:40.826] [default-akka.actor.default-dispatcher-9] [akka://default/user/$b] Circuit Breaker Timed out. (akka.pattern.CircuitBreaker$$anon$1: Circuit Breaker Timed out.)
blockCall:2,thread:default-akka.actor.default-dispatcher-9
[ERROR] [12/19/2017 15:07:02.791] [default-akka.actor.default-dispatcher-11] [akka://default/user/$b] Circuit Breaker Timed out. (akka.pattern.CircuitBreaker$$anon$1: Circuit Breaker Timed out.)


分析:

首先i的值,在每次执行时都没有变,可见
callWithCircuitBreakerCS与callWithSyncCircuitBreaker的区别,还在于后者里面的方法如果超时了,所有已执行逻辑将会回滚。(!!!!非回滚,见评论)


为了使用熔断,所有的业务处理方法,都需要封装,以确保在熔断触发时,可以直接返回,下面为封装的几种方式:

 

 

· withCircuitBreaker 异步执行

· withSyncCircuitBreaker 同步执行

· callWithCircuitBreaker  异步执行

· callWithCircuitBreakerCS  异步执行

· callWithSyncCircuitBreaker  同步执行

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值