Refactor exit handler mechanism of Entry

- Rename: whenComplete -> whenTerminate
- Execute the exit handler directly after the onExit hook of all slots

Signed-off-by: Eric Zhao <sczyh16@gmail.com>
This commit is contained in:
Eric Zhao 2020-08-18 16:42:58 +08:00
parent 55e038cc33
commit dae4621e6e
4 changed files with 40 additions and 31 deletions

View File

@ -15,7 +15,6 @@
*/
package com.alibaba.csp.sentinel;
import java.util.Iterator;
import java.util.LinkedList;
import com.alibaba.csp.sentinel.context.Context;
@ -42,7 +41,6 @@ class CtEntry extends Entry {
protected Context context;
protected LinkedList<BiConsumer<Context, Entry>> exitHandlers;
CtEntry(ResourceWrapper resourceWrapper, ProcessorSlot<Object> chain, Context context) {
super(resourceWrapper);
this.chain = chain;
@ -68,14 +66,33 @@ class CtEntry extends Entry {
trueExit(count, args);
}
/**
* Note: the exit handlers will be called AFTER onExit of slot chain.
*/
private void callExitHandlersAndCleanUp(Context ctx) {
if (exitHandlers != null && !exitHandlers.isEmpty()) {
for (BiConsumer<Context, Entry> handler : this.exitHandlers) {
try {
handler.accept(ctx, this);
} catch (Exception e) {
RecordLog.warn("Error occurred when invoking entry exit handler, current entry: "
+ resourceWrapper.getName(), e);
}
}
exitHandlers = null;
}
}
protected void exitForContext(Context context, int count, Object... args) throws ErrorEntryFreeException {
if (context != null) {
// Null context should exit without clean-up.
if (context instanceof NullContext) {
return;
}
if (context.getCurEntry() != this) {
String curEntryNameInContext = context.getCurEntry() == null ? null : context.getCurEntry().getResourceWrapper().getName();
String curEntryNameInContext = context.getCurEntry() == null ? null
: context.getCurEntry().getResourceWrapper().getName();
// Clean previous call stack.
CtEntry e = (CtEntry) context.getCurEntry();
while (e != null) {
@ -83,12 +100,17 @@ class CtEntry extends Entry {
e = (CtEntry) e.parent;
}
String errorMessage = String.format("The order of entry exit can't be paired with the order of entry"
+ ", current entry in context: <%s>, but expected: <%s>", curEntryNameInContext, resourceWrapper.getName());
+ ", current entry in context: <%s>, but expected: <%s>", curEntryNameInContext,
resourceWrapper.getName());
throw new ErrorEntryFreeException(errorMessage);
} else {
// Go through the onExit hook of all slots.
if (chain != null) {
chain.exit(context, resourceWrapper, count, args);
}
// Go through the existing terminate handlers (associated to this invocation).
callExitHandlersAndCleanUp(context);
// Restore the call stack.
context.setCurEntry(parent);
if (parent != null) {
@ -111,31 +133,17 @@ class CtEntry extends Entry {
}
@Override
public void whenComplete(BiConsumer<Context, Entry> consumer) {
public void whenTerminate(BiConsumer<Context, Entry> handler) {
if (this.exitHandlers == null) {
this.exitHandlers = new LinkedList<>();
}
this.exitHandlers.add(consumer);
this.exitHandlers.add(handler);
}
@Override
protected Entry trueExit(int count, Object... args) throws ErrorEntryFreeException {
exitForContext(context, count, args);
if (this.exitHandlers != null) {
Iterator<BiConsumer<Context, Entry>> it = this.exitHandlers.iterator();
BiConsumer<Context, Entry> cur;
while (it.hasNext()) {
cur = it.next();
try {
cur.accept(this.context, this);
} catch (Exception e) {
RecordLog.warn("Error invoking exit handler", e);
}
}
this.exitHandlers = null;
}
return parent;
}

View File

@ -180,12 +180,13 @@ public abstract class Entry implements AutoCloseable {
}
/**
* Like `CompletableFuture` since JDK8 it guarantees specified consumer
* is invoked when this entry exited.
* Like {@code CompletableFuture} since JDK 8, it guarantees specified handler
* is invoked when this entry terminated (exited), no matter it's blocked or permitted.
* Use it when you did some STATEFUL operations on entries.
*
* @param consumer
* @param handler handler function on the invocation terminates
* @since 1.8.0
*/
public abstract void whenComplete(BiConsumer<Context, Entry> consumer);
public abstract void whenTerminate(BiConsumer<Context, Entry> handler);
}

View File

@ -105,7 +105,7 @@ public abstract class AbstractCircuitBreaker implements CircuitBreaker {
if (currentState.compareAndSet(State.OPEN, State.HALF_OPEN)) {
notifyObservers(State.OPEN, State.HALF_OPEN, null);
Entry entry = context.getCurEntry();
entry.whenComplete(new BiConsumer<Context, Entry>() {
entry.whenTerminate(new BiConsumer<Context, Entry>() {
@Override
public void accept(Context context, Entry entry) {
// Note: This works as a temporary workaround for https://github.com/alibaba/Sentinel/issues/1638

View File

@ -68,7 +68,7 @@ public class EntryTest {
}
@Override
public void whenComplete(BiConsumer<Context, Entry> consumer) {
public void whenTerminate(BiConsumer<Context, Entry> consumer) {
// do nothing
}
}