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:
parent
55e038cc33
commit
dae4621e6e
|
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package com.alibaba.csp.sentinel;
|
package com.alibaba.csp.sentinel;
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.context.Context;
|
import com.alibaba.csp.sentinel.context.Context;
|
||||||
|
|
@ -42,7 +41,6 @@ class CtEntry extends Entry {
|
||||||
protected Context context;
|
protected Context context;
|
||||||
protected LinkedList<BiConsumer<Context, Entry>> exitHandlers;
|
protected LinkedList<BiConsumer<Context, Entry>> exitHandlers;
|
||||||
|
|
||||||
|
|
||||||
CtEntry(ResourceWrapper resourceWrapper, ProcessorSlot<Object> chain, Context context) {
|
CtEntry(ResourceWrapper resourceWrapper, ProcessorSlot<Object> chain, Context context) {
|
||||||
super(resourceWrapper);
|
super(resourceWrapper);
|
||||||
this.chain = chain;
|
this.chain = chain;
|
||||||
|
|
@ -68,14 +66,33 @@ class CtEntry extends Entry {
|
||||||
trueExit(count, args);
|
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 {
|
protected void exitForContext(Context context, int count, Object... args) throws ErrorEntryFreeException {
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
// Null context should exit without clean-up.
|
// Null context should exit without clean-up.
|
||||||
if (context instanceof NullContext) {
|
if (context instanceof NullContext) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.getCurEntry() != this) {
|
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.
|
// Clean previous call stack.
|
||||||
CtEntry e = (CtEntry) context.getCurEntry();
|
CtEntry e = (CtEntry) context.getCurEntry();
|
||||||
while (e != null) {
|
while (e != null) {
|
||||||
|
|
@ -83,12 +100,17 @@ class CtEntry extends Entry {
|
||||||
e = (CtEntry) e.parent;
|
e = (CtEntry) e.parent;
|
||||||
}
|
}
|
||||||
String errorMessage = String.format("The order of entry exit can't be paired with the order of entry"
|
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);
|
throw new ErrorEntryFreeException(errorMessage);
|
||||||
} else {
|
} else {
|
||||||
|
// Go through the onExit hook of all slots.
|
||||||
if (chain != null) {
|
if (chain != null) {
|
||||||
chain.exit(context, resourceWrapper, count, args);
|
chain.exit(context, resourceWrapper, count, args);
|
||||||
}
|
}
|
||||||
|
// Go through the existing terminate handlers (associated to this invocation).
|
||||||
|
callExitHandlersAndCleanUp(context);
|
||||||
|
|
||||||
// Restore the call stack.
|
// Restore the call stack.
|
||||||
context.setCurEntry(parent);
|
context.setCurEntry(parent);
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
|
|
@ -111,31 +133,17 @@ class CtEntry extends Entry {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void whenComplete(BiConsumer<Context, Entry> consumer) {
|
public void whenTerminate(BiConsumer<Context, Entry> handler) {
|
||||||
if (this.exitHandlers == null) {
|
if (this.exitHandlers == null) {
|
||||||
this.exitHandlers = new LinkedList<>();
|
this.exitHandlers = new LinkedList<>();
|
||||||
}
|
}
|
||||||
this.exitHandlers.add(consumer);
|
this.exitHandlers.add(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Entry trueExit(int count, Object... args) throws ErrorEntryFreeException {
|
protected Entry trueExit(int count, Object... args) throws ErrorEntryFreeException {
|
||||||
exitForContext(context, count, args);
|
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;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -180,12 +180,13 @@ public abstract class Entry implements AutoCloseable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Like `CompletableFuture` since JDK8 it guarantees specified consumer
|
* Like {@code CompletableFuture} since JDK 8, it guarantees specified handler
|
||||||
* is invoked when this entry exited.
|
* is invoked when this entry terminated (exited), no matter it's blocked or permitted.
|
||||||
* Use it when you did some STATEFUL operations on entries.
|
* 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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ public abstract class AbstractCircuitBreaker implements CircuitBreaker {
|
||||||
if (currentState.compareAndSet(State.OPEN, State.HALF_OPEN)) {
|
if (currentState.compareAndSet(State.OPEN, State.HALF_OPEN)) {
|
||||||
notifyObservers(State.OPEN, State.HALF_OPEN, null);
|
notifyObservers(State.OPEN, State.HALF_OPEN, null);
|
||||||
Entry entry = context.getCurEntry();
|
Entry entry = context.getCurEntry();
|
||||||
entry.whenComplete(new BiConsumer<Context, Entry>() {
|
entry.whenTerminate(new BiConsumer<Context, Entry>() {
|
||||||
@Override
|
@Override
|
||||||
public void accept(Context context, Entry entry) {
|
public void accept(Context context, Entry entry) {
|
||||||
// Note: This works as a temporary workaround for https://github.com/alibaba/Sentinel/issues/1638
|
// Note: This works as a temporary workaround for https://github.com/alibaba/Sentinel/issues/1638
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ public class EntryTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void whenComplete(BiConsumer<Context, Entry> consumer) {
|
public void whenTerminate(BiConsumer<Context, Entry> consumer) {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue