How to integrate custom SPI with sentinel to realize fuse current limiting


We talked before Talk about how to implement an SPI with interceptor function . At that time, the core idea of our implementation was to use responsibility chain + dynamic agent. Today, let's talk about how to integrate sentinel to realize fuse current limiting through dynamic agent

Pre knowledge

Introduction to alibaba sentinel

Sentinel is a flow control component for distributed service architecture. It mainly takes flow as the starting point to help developers ensure the stability of micro services from multiple dimensions such as current limiting, flow shaping, fuse degradation, system load protection and hotspot protection.

sentinel workflow

sentinel keyword

Resource + rule

sentinel implementation template routine

Entry entry = null;
// Make sure finally is executed
try {
  // Any string with business semantics can be used for the resource name. Note that the number cannot be too large (more than 1K), and more than thousands. Please pass it in as a parameter instead of directly as the resource name
  // EntryType represents the flow type (inbound/outbound), where the system rule is only effective for embedded points of type IN
  entry = SphU.entry("Custom resource name");
  // Protected business logic
  // do something...
} catch (BlockException ex) {
  // Resource access is blocked, restricted or degraded
  // Carry out corresponding processing operations
} catch (Exception ex) {
  // If you need to configure degradation rules, you need to record business exceptions in this way
  Tracer.traceEntry(ex, entry);
} finally {
  // Ensure exit, and ensure that each entry is paired with exit
  if (entry != null) {

sentinel wiki

Realization idea

Overall implementation idea: dynamic agent + sentinel implementation routine template

Core code

Dynamic agent part

1. Define dynamic proxy interface

public interface CircuitBreakerProxy {

    Object getProxy(Object target);

    Object getProxy(Object target,@Nullable ClassLoader classLoader);

2. Define the specific dynamic implementation of JDK or cglib

Taking jdk dynamic agent as an example

public class CircuitBreakerJdkProxy implements CircuitBreakerProxy, InvocationHandler {

    private Object target;

    public Object getProxy(Object target) { = target;
        return getProxy(target,Thread.currentThread().getContextClassLoader());

    public Object getProxy(Object target, ClassLoader classLoader) { = target;
        return Proxy.newProxyInstance(classLoader,target.getClass().getInterfaces(),this);

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        CircuitBreakerInvocation invocation = new CircuitBreakerInvocation(target,method,args);
        try {
            return new CircuitBreakerInvoker().proceed(invocation);
            //Wrapping with InvocationTargetException is a java.lang.reflect.UndeclaredThrowableException problem
        } catch (InvocationTargetException e) {
            throw e.getTargetException();


3. Dynamic proxy concrete call

public class CircuitBreakerProxyFactory implements ProxyFactory{
    public Object createProxy(Object target) {
        if(target.getClass().isInterface() || Proxy.isProxyClass(target.getClass())){
            return new CircuitBreakerJdkProxy().getProxy(target);
        return new CircuitBreakerCglibProxy().getProxy(target);

ps: the above dynamic proxy implementation idea refers to the spring aop dynamic proxy implementation

Specific reference classes are as follows


sentinel implementation part

public class CircuitBreakerInvoker {

    public Object proceed(CircuitBreakerInvocation circuitBreakerInvocation) throws Throwable {

       Method method = circuitBreakerInvocation.getMethod();

        if ("equals".equals(method.getName())) {
            try {
                Object otherHandler = circuitBreakerInvocation.getArgs().length > 0 && circuitBreakerInvocation.getArgs()[0] != null
                        ? Proxy.getInvocationHandler(circuitBreakerInvocation.getArgs()[0]) : null;
                return equals(otherHandler);
            } catch (IllegalArgumentException e) {
                return false;
        } else if ("hashCode".equals(method.getName())) {
            return hashCode();
        } else if ("toString".equals(method.getName())) {
            return toString();

        Object result = null;

        String contextName = "spi_circuit_breaker: ";

        String className = ClassUtils.getClassName(circuitBreakerInvocation.getTarget());
        String resourceName = contextName + className + "." + method.getName();

        Entry entry = null;
        try {
            entry = SphU.entry(resourceName, EntryType.OUT, 1, circuitBreakerInvocation.getArgs());
            result = circuitBreakerInvocation.proceed();
        } catch (Throwable ex) {
            return doFallBack(ex, entry, circuitBreakerInvocation);
        } finally {
            if (entry != null) {
                entry.exit(1, circuitBreakerInvocation.getArgs());

        return result;

ps: if you are a careful friend, you will find that this logic is sentinel's native implementation routine logic

Example demonstration

Sample preparation

1. Define the interface implementation class and annotate it

@CircuitBreakerActivate(spiKey = "sqlserver",fallbackFactory = SqlServerDialectFallBackFactory.class)
public class SqlServerDialect implements SqlDialect {
    public String dialect() {
        return "sqlserver";


ps: @ CircuitBreakerActivate is a custom annotation. You will probably feel familiar with the @ FeignClient annotation of springcloud openfeign. You configure fallbackFactory or fallbackon the annotation

2. Define interface fusing factory

public class SqlServerDialectFallBackFactory implements FallbackFactory<SqlDialect> {

    public SqlDialect create(Throwable ex) {
        return () -> {
            return "SqlServerDialect FallBackFactory";

ps: see if this is also familiar. Isn't this the fuse factory of springcloud hystrix

3. Configure sentinel dashbord er address in the project

        dashboard: localhost:8080
        enabled: false

Example validation

1. Browser access http://localhost:8082/test/ciruitbreak

At this time, the access is normal. Take a look at sentinel dashborder

2. Configure current limiting rules

3. Quick access again

It can be seen from the figure that the fuse has been triggered

In this example project, if fallback or fallbackFactory is not configured, it will also have a default fallback when current limiting is triggered

Remove the fallbackFactory of the example annotation as follows

@CircuitBreakerActivate(spiKey = "sqlserver")
public class SqlServerDialect implements SqlDialect {
    public String dialect() {
        return "sqlserver";


Repeat the above access process. When the current limit is triggered, the following message will be prompted


The implementation series of custom spi is coming to an end. In fact, there are not many original things in this small demo. Most of them pull out some interesting things from the source codes of dubbo, shenyu, mybatis, spring and sentinel, and put together a demo.

We are still doing business development most of the time. Some people may feel crud every day and there is no room for growth, but as long as we pay a little attention to the framework we often use, we may find some different scenery

demo link

Tags: Java SQL Server github Spring Boot Framework

Posted on Tue, 07 Dec 2021 03:39:45 -0500 by alexinjamestown