为了避免操作在异常情况下无限重试浪费资源,使用熔断让请求快速失败,避免浪费资源
circuit breaker状态图
closed状态: 正常情况熔断是关闭的,half-open状态中,如果调用成功进入closed状态。
open状态:达到预置条件后进开启熔断。
half-open状态: open状态后,达到预置时间会变为half-open.
示例代码
public class CircuitDemo {
public static void main(String[] args) {
ActorSystem system = ActorSystem.create("sys");
ActorRef ref = system.actorOf(Props.create(CircuitBreakerActor.class), "circuit");
Scanner sc = new Scanner(System.in);
while (true){
System.out.println("input a num");
int msg = sc.nextInt();
ref.tell("sync" + msg, ActorRef.noSender());
}
}
}
public class CircuitBreakerActor extends UntypedAbstractActor {
private ActorRef workerChild;
private static SupervisorStrategy strategy = new OneForOneStrategy(20,
Duration.create(1, "minute"),
DeciderBuilder.matchAny(e -> SupervisorStrategy.resume()).build());
@Override
public SupervisorStrategy supervisorStrategy() {
return strategy;
}
@Override
public void preStart() throws Exception {
super.preStart();
workerChild = getContext().actorOf(Props.create(WorkerActor.class), "workerActor");
}
@Override
public void onReceive(Object message) throws Throwable {
workerChild.tell(message, getSender());
}
}
public class WorkerActor extends UntypedAbstractActor {
private CircuitBreaker circuitBreaker;
@Override
public void preStart() throws Exception {
super.preStart();
//熔断器
circuitBreaker = new CircuitBreaker(
getContext().dispatcher(),
getContext().system().scheduler(),
5, //失败超过5次 开启
Duration.create(2, "second"), //超过2秒中视为超时即失败
Duration.create(1, "min")) //开启1分钟后允许重试,进入半开启
//监听
.addOnCallBreakerOpenListener(() -> System.out.println("Actor CircuitBreak 开启"))
.addOnHalfOpenListener(() -> System.out.println(DateUtil.now() + "Actor CircuitBreak 半开启"))
.addOnCloseListener(()-> System.out.println(DateUtil.now() + "Actor CircuitBreak 关闭"));
}
@Override
public void onReceive(Object message) throws Throwable {
if(message instanceof String){
if(((String) message).startsWith("sync")){
getSender().tell(circuitBreaker.callWithSyncCircuitBreaker(() -> {
System.out.println("msg : " + message);
long time = Integer.valueOf(((String) message).substring(4));
//模拟超时
Thread.sleep(time * 1000);
return message;
}), getSelf());
}
}
}
}
程序运行:
1秒时候正常执行
3秒时候出发超时,maxFailures计数器加1,达到5次后,再次输入提示开启,调用快速失败。
开启期间,发送正常消息(1),同样快速返回失败。1分钟后进入半开启,在次输入1成功处理后熔断关闭。
1
msg : sync1
[INFO] [akkaDeadLetter][07/08/2021 17:39:51.525] [sys-akka.actor.default-dispatcher-5] [akka://sys/deadLetters] Message [java.lang.String] from Actor[akka://sys/user/circuit/workerActor#-178031464] to Actor[akka://sys/deadLetters] was not delivered. [1] dead letters encountered. If this is not an expected behavior then Actor[akka://sys/deadLetters] may have terminated unexpectedly. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
3
msg : sync3
[WARN] [07/08/2021 17:39:56.321] [sys-akka.actor.default-dispatcher-5] [akka://sys/user/circuit/workerActor] Circuit Breaker Timed out.
3
msg : sync3
[WARN] [07/08/2021 17:40:00.455] [sys-akka.actor.default-dispatcher-6] [akka://sys/user/circuit/workerActor] Circuit Breaker Timed out.
3
msg : sync3
[WARN] [07/08/2021 17:40:04.707] [sys-akka.actor.default-dispatcher-8] [akka://sys/user/circuit/workerActor] Circuit Breaker Timed out.
3
msg : sync3
[WARN] [07/08/2021 17:40:09.244] [sys-akka.actor.default-dispatcher-5] [akka://sys/user/circuit/workerActor] Circuit Breaker Timed out.
3
msg : sync3
[WARN] [07/08/2021 17:40:15.984] [sys-akka.actor.default-dispatcher-6] [akka://sys/user/circuit/workerActor] Circuit Breaker Timed out.
3
Actor CircuitBreak 开启
[WARN] [07/08/2021 17:40:21.059] [sys-akka.actor.default-dispatcher-7] [akka://sys/user/circuit/workerActor] Circuit Breaker is open; calls are failing fast
1
Actor CircuitBreak 开启
[WARN] [07/08/2021 17:40:25.935] [sys-akka.actor.default-dispatcher-8] [akka://sys/user/circuit/workerActor] Circuit Breaker is open; calls are failing fast
2021-07-08 17:41:15Actor CircuitBreak 半开启
1
msg : sync1
2021-07-08 17:41:26Actor CircuitBreak 关闭
[INFO] [akkaDeadLetter][07/08/2021 17:41:26.574] [sys-akka.actor.default-dispatcher-5] [akka://sys/deadLetters] Message [java.lang.String] from Actor[akka://sys/user/circuit/workerActor#-178031464] to Actor[akka://sys/deadLetters] was not delivered. [2] dead letters encountered. If this is not an expected behavior then Actor[akka://sys/deadLetters] may have terminated unexpectedly. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.