To apply flow control to specific methods or code blocks, you must define them as resources. AHAS provides five ways to define resources. After a resource is defined, you can configure rules for it in the AHAS console. These rules then take effect.
Background information
AHAS works with resources. When you write code, you only need to define which methods or code blocks require protection. You do not need to specify how they are protected. You can define resources in the following ways:
- Define resources using annotations
- Define resources by throwing exceptions
- Define resources by returning a Boolean value
- Support for asynchronous invocations
- Default adaptation for mainstream frameworks
Method 1: Define resources using annotations
You can define a resource using the @SentinelResource annotation. Then, you can configure the blockHandler and fallback functions to handle blocked requests and execution exceptions.
Example:
// The original business method.
@SentinelResource(blockHandler = "blockHandlerForGetUser")
public User getUserById(String id) {
throw new RuntimeException("getUserById command failed");
}
// The blockHandler function. It is called when the original method invocation is throttled, degraded, or protected by the system.
public User blockHandlerForGetUser(String id, BlockException ex) {
return new User("admin");
}blockHandler function is called when a method triggers a throttling, degradation, or system protection rule. The fallback function is called only when the original method is degraded.For more information, see the Sentinel Annotation Support document.
Method 2: Define resources by throwing exceptions
When you define a resource by throwing an exception, a BlockException is thrown if the resource is blocked. You can catch the exception and handle the blocked request as needed. The following code is an example:
Entry entry = null;
// Make sure that the finally block is executed.
try {
// The resource name can be any string with business semantics.
entry = SphU.entry("custom_resource_name");
// The protected business logic.
// do something...
} catch (BlockException ex) {
// Resource access is blocked, throttled, or degraded.
// Perform corresponding processing.
} finally {
if (entry != null) {
entry.exit();
}
}SphU.entry(xxx) method must be paired with the entry.exit() method. Otherwise, the call chain is recorded incorrectly, and an ErrorEntryFreeException is thrown.Method 3: Define resources by returning a Boolean value
When you define a resource by returning a Boolean value, false is returned if the resource is blocked. You can handle the blocked request based on the return value. The following code is an example:
// The resource name can be any string with business semantics.
if (SphO.entry("custom_resource_name")) {
// Make sure that the finally block is executed.
try {
/**
* The protected business logic.
*/
} finally {
SphO.exit();
}
} else {
// Resource access is blocked, throttled, or degraded.
// Perform corresponding processing.
}Method 4: Support for asynchronous invocations
AHAS supports statistics collection for asynchronous invocation chains. For an asynchronous invocation, you can define the resource using the SphU.asyncEntry(xxx) method and then call the exit method in the asynchronous callback function. The following is an example:
try {
AsyncEntry entry = SphU.asyncEntry(resourceName);
// Asynchronous invocation.
doAsync(userId, result -> {
try {
// Process the result of the asynchronous invocation here.
} finally {
// Call exit after the callback ends.
entry.exit();
}
});
} catch (BlockException ex) {
// Request blocked
// Handle the exception (e.g. retry or fallback)
}The SphU.asyncEntry(xxx) method does not affect the context of the current calling thread. Therefore, the following two entries are peers at the same layer in the call chain, not nested:
// The call chain is similar to the following:
// -parent
// ---asyncResource
// ---syncResource
asyncEntry = SphU.asyncEntry(asyncResource);
entry = SphU.entry(normalResource);To nest other resource calls, such as entry or asyncEntry, in an asynchronous callback, you can use the context switching feature provided by Sentinel. At the point of the resource call, use ContextUtil.runOnContext(context, f) to switch to the generated asynchronous context. This maintains the correct call chain relationship. The following is an example:
public void handleResult(String result) {
Entry entry = null;
try {
entry = SphU.entry("handleResultForAsync");
// Handle your result here
} catch (BlockException ex) {
// Blocked for the result handler
} finally {
if (entry != null) {
entry.exit();
}
}
}
public void someAsync() {
try {
AsyncEntry entry = SphU.asyncEntry(resourceName);
// Asynchronous invocation
doAsync(userId, result -> {
// Switch the context in the asynchronous callback. Obtain the asynchronous context using the getAsyncContext method of AsyncEntry.
ContextUtil.runOnContext(entry.getAsyncContext(), () -> {
try {
// Nest a normal resource call here.
handleResult(result);
} finally {
entry.exit();
}
});
});
} catch (BlockException ex) {
// Request blocked
// Handle the exception (e.g. retry or fallback)
}
}The call chain is now as follows:
-parent
---asyncInvocation
-----handleResultForAsync
For an example of nesting normal and asynchronous resources, see AsyncEntryDemo.java.
Method 5: Default adaptation for mainstream frameworks
To reduce development complexity, AHAS provides adaptations for mainstream frameworks. For more information, see List of supported components. To use these adaptations, you only need to import a corresponding software development kit (SDK) dependency. The methods and services of the framework are then automatically defined as resources. You do not need to modify your existing code.