Can Dubbo replace Feign, Hystrix, Sentinel, Ribbon?

Article directory

  • 1. Concept
  • 2. Function introduction
    • 1. @Service
    • 2.@Reference
    • 3. @Method
    • 4.@Argument
  • 3. Analysis
  • 4. How to achieve it?
    • 1. Circuit Breaker
      • XML configuration method
      • Annotation method
    • 2. Downgrade (Fallback)
      • XML configuration method
      • Annotation method
    • 3. Rate Limiting
      • XML configuration method
      • Annotation method
    • 4. Load Balancing
      • XML configuration method
      • Annotation method
  • 5. Expansion
    • 1. What is the difference between Feign and Openfeign?
    • 2. What is the difference between Hystrix and Sentinel?

1. Concept

Dubbo is an open source distributed service framework that provides a series of functions such as service governance, load balancing, and fault tolerance mechanisms. It is mainly used to improve the performance and scalability of applications and allow applications to communicate through remote calls.
Through Dubbo, a large application system can be split into multiple services, and each service can be deployed and upgraded independently, improving the flexibility and maintainability of the system.
Dubbo supports multiple protocols and transmission methods, including HTTP, REST, RMI, etc., and also provides core functions such as reliability guarantee, load balancing, service registration and discovery.
In general, Dubbo can help enterprises build distributed architectures and provide reliable service invocation and management mechanisms.

2. Function introduction

By looking at the dubbo-2.7.1 source code, we can see org.apache.dubbo.config.annotation. Four annotations are provided below: @Service, @Reference, @Method and @Argument

1.@Service

The @Service annotation is used to mark a class as a provider of Dubbo services. Add this annotation to the class that needs to expose the service and configure the corresponding interface. Dubbo will automatically register the interface implemented by the class as a Dubbo service.

The annotation source code is as follows:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({<!-- -->ElementType.TYPE})
@Inherited
public @interface Service {<!-- -->

    /**
     * Interface class, default value is void.class
     */
    Class<?> interfaceClass() default void.class;

    /**
     * Interface class name, default value is empty string
     */
    String interfaceName() default "";

    /**
     * Service version, default value is empty string
     */
    String version() default "";

    /**
     * Service group, default value is empty string
     */
    String group() default "";

    /**
     * Service path, default value is empty string
     */
    String path() default "";

    /**
     * Whether to export service, default value is true
     */
    boolean export() default true;

    /**
     * Service token, default value is false
     */
    String token() default "";

    /**
     * Whether the service is deprecated, default value is false
     */
    boolean deprecated() default false;

    /**
     * Whether the service is dynamic, default value is false
     */
    boolean dynamic() default false;

    /**
     * Access log for the service, default value is ""
     */
    String accesslog() default "";

    /**
     * Maximum concurrent executes for the service, default value is 0 - no limits
     */
    int executes() default 0;

    /**
     * Whether to register the service to register center, default value is true
     */
    boolean register() default true;

    /**
     * Service weight value, default value is 0
     */
    int weight() default 0;

    /**
     * Service doc, default value is ""
     */
    String document() default "";

    /**
     * Delay time for service registration, default value is 0
     */
    int delay() default 0;

    /**
     * @see Service#stub()
     * @deprecated
     */
    String local() default "";

    /**
     * Service stub name, use interface name + Local if not set
     */
    String stub() default "";

    /**
     * Cluster strategy, legal values include: failover, failfast, failsafe, failback, forking
     */
    String cluster() default "";

    /**
     * How the proxy is generated, legal values include: jdk, javassist
     */
    String proxy() default "";

    /**
     * Maximum connections service provider can accept, default value is 0 - connection is shared
     */
    int connections() default 0;

    /**
     * The callback instance limit peer connection
     *
     * @see Constants#DEFAULT_CALLBACK_INSTANCES
     */
    int callbacks() default Constants.DEFAULT_CALLBACK_INSTANCES;

    /**
     * Callback method name when connected, default value is empty string
     */
    String onconnect() default "";

    /**
     * Callback method name when disconnected, default value is empty string
     */
    String ondisconnect() default "";

    /**
     * Service owner, default value is empty string
     */
    String owner() default "";

    /**
     * Service layer, default value is empty string
     */
    String layer() default "";

    /**
     * Service invocation retry times
     *
     * @see Constants#DEFAULT_RETRIES
     */
    int retries() default Constants.DEFAULT_RETRIES;

    /**
     * Load balance strategy, legal values include: random, roundrobin, leastactive
     *
     * @see Constants#DEFAULT_LOADBALANCE
     */
    String loadbalance() default Constants.DEFAULT_LOADBALANCE;

    /**
     * Whether to enable async invocation, default value is false
     */
    boolean async() default false;

    /**
     * Maximum active requests allowed, default value is 0
     */
    int actives() default 0;

    /**
     * Whether the async request has already been sent, the default value is false
     */
    boolean sent() default false;

    /**
     * Service mock name, use interface name + Mock if not set
     */
    String mock() default "";

    /**
     * Whether to use JSR303 validation, legal values are: true, false
     */
    String validation() default "";

    /**
     * Timeout value for service invocation, default value is 0
     */
    int timeout() default 0;

    /**
     * Specify cache implementation for service invocation, legal values include: lru, threadlocal, jcache
     */
    String cache() default "";

    /**
     * Filters for service invocation
     *
     * @see Filter
     */
    String[] filter() default {<!-- -->};

    /**
     * Listeners for service exporting and unexporting
     *
     * @see ExporterListener
     */
    String[] listener() default {<!-- -->};

    /**
     * Customized parameter key-value pair, for example: {key1, value1, key2, value2}
     */
    String[] parameters() default {<!-- -->};

    /**
     *Application spring bean name
     */
    String application() default "";

    /**
     * Module spring bean name
     */
    String module() default "";

    /**
     * Provider spring bean name
     */
    String provider() default "";

    /**
     * Protocol spring bean names
     */
    String[] protocol() default {<!-- -->};

    /**
     * Monitor spring bean name
     */
    String monitor() default "";

    /**
     * Registry spring bean name
     */
    String[] registry() default {<!-- -->};

    /**
     * Service tag name
     */
    String tag() default "";

    /**
     * methods support
     * @return
     */
    Method[] methods() default {<!-- -->};
}

Common attribute descriptions:

  • interface: Specifies the implemented interface class, which is used to specify the service interface to be published. Including 2 methods, interfaceClass and interfaceName.
  • version: Specifies the version number of the service to support the coexistence of multiple versions of services.
  • group: Specifies the group of services, used to distinguish different service implementations.
  • cluster: Specifies a cluster fault-tolerance strategy for handling service provider failures and network anomalies.
  • actives: Specifies the maximum number of concurrent calls, which is used to limit the number of concurrent accesses to the service.
  • executes: Specifies the maximum thread pool size of the service, which is used to limit the concurrent processing capability of the service.

If used, it is similar to org.springframework.stereotype.Service. The example is as follows:

 @Service(interfaceClass=ForlanService.class,timeout = 300000)
 public class ForlanServiceImpl implements ForlanService{<!-- -->
     // Implement interface methods
 }

2.@Reference

The @Reference annotation is used to inject a reference to an interface into the current class. Add the annotation where Dubbo services are needed, and configure the corresponding interface. Dubbo will automatically inject a remote proxy of the interface into the current class.

The annotation source code is as follows:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({<!-- -->ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public @interface Reference {<!-- -->
    /**
     * Interface class, default value is void.class
     */
    Class<?> interfaceClass() default void.class;

    /**
     * Interface class name, default value is empty string
     */
    String interfaceName() default "";

    /**
     * Service version, default value is empty string
     */
    String version() default "";

    /**
     * Service group, default value is empty string
     */
    String group() default "";

    /**
     * Service target URL for direct invocation, if this is specified, then registry center takes no effect.
     */
    String url() default "";

    /**
     * Client transport type, default value is "netty"
     */
    String client() default "";

    /**
     * Whether to enable generic invocation, default value is false
     */
    boolean generic() default false;

    /**
     * When enable, prefer to call local service in the same JVM if it's present, default value is true
     */
    boolean injvm() default true;

    /**
     * Check if service provider is available during boot up, default value is true
     */
    boolean check() default true;

    /**
     * Whether eager initialize the reference bean when all properties are set, default value is false
     */
    boolean init() default false;

    /**
     * Whether to make connection when the client is created, the default value is false
     */
    boolean lazy() default false;

    /**
     * Export an stub service for event dispatch, default value is false.
     *
     * @see Constants#STUB_EVENT_METHODS_KEY
     */
    boolean stubevent() default false;

    /**
     * Whether to reconnect if connection is lost, if not specify, reconnect is enabled by default, and the interval
     * for retry connecting is 2000 ms
     *
     * @see Constants#DEFAULT_RECONNECT_PERIOD
     */
    String reconnect() default "";

    /**
     * Whether to stick to the same node in the cluster, the default value is false
     *
     * @see Constants#DEFAULT_CLUSTER_STICKY
     */
    boolean sticky() default false;

    /**
     * How the proxy is generated, legal values include: jdk, javassist
     */
    String proxy() default "";

    /**
     * Service stub name, use interface name + Local if not set
     */
    String stub() default "";

    /**
     * Cluster strategy, legal values include: failover, failfast, failsafe, failback, forking
     */
    String cluster() default "";

    /**
     * Maximum connections service provider can accept, default value is 0 - connection is shared
     */
    int connections() default 0;

    /**
     * The callback instance limit peer connection
     *
     * @see Constants#DEFAULT_CALLBACK_INSTANCES
     */
    int callbacks() default 0;

    /**
     * Callback method name when connected, default value is empty string
     */
    String onconnect() default "";

    /**
     * Callback method name when disconnected, default value is empty string
     */
    String ondisconnect() default "";

    /**
     * Service owner, default value is empty string
     */
    String owner() default "";

    /**
     * Service layer, default value is empty string
     */
    String layer() default "";

    /**
     * Service invocation retry times
     *
     * @see Constants#DEFAULT_RETRIES
     */
    int retries() default 2;

    /**
     * Load balance strategy, legal values include: random, roundrobin, leastactive
     *
     * @see Constants#DEFAULT_LOADBALANCE
     */
    String loadbalance() default "";

    /**
     * Whether to enable async invocation, default value is false
     */
    boolean async() default false;

    /**
     * Maximum active requests allowed, default value is 0
     */
    int actives() default 0;

    /**
     * Whether the async request has already been sent, the default value is false
     */
    boolean sent() default false;

    /**
     * Service mock name, use interface name + Mock if not set
     */
    String mock() default "";

    /**
     * Whether to use JSR303 validation, legal values are: true, false
     */
    String validation() default "";

    /**
     * Timeout value for service invocation, default value is 0
     */
    int timeout() default 0;

    /**
     * Specify cache implementation for service invocation, legal values include: lru, threadlocal, jcache
     */
    String cache() default "";

    /**
     * Filters for service invocation
     *
     * @see Filter
     */
    String[] filter() default {<!-- -->};

    /**
     * Listeners for service exporting and unexporting
     *
     * @see ExporterListener
     */
    String[] listener() default {<!-- -->};

    /**
     * Customized parameter key-value pair, for example: {key1, value1, key2, value2}
     */
    String[] parameters() default {<!-- -->};

    /**
     *Application spring bean name
     */
    String application() default "";

    /**
     * Module spring bean name
     */
    String module() default "";

    /**
     * Consumer spring bean name
     */
    String consumer() default "";

    /**
     * Monitor spring bean name
     */
    String monitor() default "";

    /**
     * Registry spring bean name
     */
    String[] registry() default {<!-- -->};

    /**
     * Protocol spring bean names
     */
    String protocol() default "";

    /**
     * methods support
     * @return
     */
    Method[] methods() default {<!-- -->};
}

Commonly used attribute descriptions:

  • interface: Specifies the interface class to be referenced and is used to specify the service interface to be called. Including 2 methods, interfaceClass and interfaceName.
  • version: Specify the version number of the service to be referenced to support the coexistence of multiple versions of services.
  • group: Specifies the group of services to be referenced, used to distinguish different service implementations.
  • url: Specify the URL address of the service to be referenced, which is used to directly call the service.
  • loadbalance: Specifies the load balancing strategy, which is used to decide which provider instance to choose when the service is called. The default is random, optional values include roundrobin (polling), leastactive (minimum number of connections)
  • cluster: Specifies a cluster fault-tolerance strategy for handling service provider failures and network anomalies.
  • timeout: Specifies the timeout of the service, which is used to limit the maximum time of service calls. The default is 0, which means no timeout is set.
  • check: Specifies whether to check service dependencies at startup, used to automatically detect missing dependent services. Default is true. The service will only be referenced if check=true and the service provider is available.
  • async: Specifies whether the service call is asynchronous to support asynchronous calling mode. Default is false. If set to true, the remote call will be executed asynchronously and a CompletableFuture object will be returned.
  • lazy: This attribute is used to specify whether to lazily initialize the service. Default is false. If set to true, initialization will not occur until the first call to the service.
  • retries: This attribute is used to specify the number of retries for calling the remote service method. The default is 2. If set to 0, no retries will be made.
  • mock: This attribute is used to specify the Mock implementation class of the remote service. It is empty by default. When the remote service is unavailable, the method of the Mock implementation class will be used to return the default value.

If used, it is similar to @Autowired. The example is as follows:

@Reference(version = "1.0.0")
private ForlanService forlanService;

3, @Method

The @Method annotation is used to mark method information for configuration when the method is called. It is usually used together with @Reference or @Service.

The annotation source code is as follows:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({<!-- -->ElementType.ANNOTATION_TYPE})
@Inherited
public @interface Method {<!-- -->
    String name();

    int timeout() default -1;

    int retries() default -1;

    String loadbalance() default "";

    boolean async() default false;

    boolean sent() default true;

    int actives() default 0;

    int executes() default 0;

    boolean deprecated() default false;

    boolean sticky() default false;

    boolean isReturn() default true;

    String oninvoke() default "";

    String onreturn() default "";

    String onthrow() default "";

    String cache() default "";

    String validation() default "";

    Argument[] arguments() default {<!-- -->};
}

Use it sparingly. If you want to use it, add the @Method annotation to the method you need to use and set the corresponding attributes.
For example, suppose you have an interface defined as follows:

public interface ForlanService {<!-- -->
    @Method(name = "getById", timeout = 5000)
    User getById(int id);
}

In the above example, the @Method annotation is applied to the getById method and two properties are set: name and timeout. The name attribute specifies the name of the method when it is called remotely, and the timeout attribute specifies the timeout period of the method call as 5000 milliseconds.

4.@Argument

The @Argument annotation is used to mark information about method parameters for configuration when the method is called. It is usually used together with @Method.

The annotation source code is as follows:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({<!-- -->ElementType.ANNOTATION_TYPE})
@Inherited
public @interface Argument {<!-- -->
    //argument: index -1 represents not set
    int index() default -1;

    //argument type
    String type() default "";

    //callback interface
    boolean callback() default false;
}

It is rarely used. If you want to use it, add @Argument annotation to the method parameters that need to be used, and set the corresponding attributes.
For example, suppose you have an interface defined as follows:

public interface ForlanService {<!-- -->
    Forlan getById(@Argument(index = 0, callback = true) int id);
}

In the above example, the @Argument annotation is applied to the id parameter of the getById method and sets two properties: index and callback. The index attribute specifies the sequential index of the parameter, and the callback attribute specifies whether to call back the parameter.

3. Analysis

We can see that the attributes of @Service and @Reference just meet certain characteristics, such as timeout to control the timeout; retries to specify the number of retries; when the remote service is unavailable, mock will use the method of the Mock implementation class to return the default value; executes limits the concurrent processing capability of the service; loadbalance specifies the load balancing strategy, etc.
Therefore, the circuit breaker, downgrade and current limiting functions of Hystrix and Sentinel can be replaced by Dubbo. It can also prevent the system from crashing due to a certain service problem and protect the normal operation of the entire system. In addition, Ribbon is a client load balancing tool, and Dubbo can also be replaced.

4. How to implement?

How does Dubbo implement circuit breaker, downgrade, current limiting, load balancing, and specific code implementation?

1. Circuit Breaker

The circuit breaker mechanism is used to temporarily interrupt calls to the service when a service failure or exception occurs to avoid cascading failures. Dubbo provides circuitbreaker configuration to enable the circuit breaker mechanism.

XML configuration method

 <dubbo:service interface="com.example.ForlanService" retries="3">
    <dubbo:parameter key="circuitbreaker" value="10"/>
</dubbo:service>

In the above example, the retries attribute sets the number of retries, and the circuitbreaker parameter sets the circuit breaker threshold. When the number of failed calls reaches the threshold, Dubbo will trigger the circuit breaker.

Annotation method

Use Dubbo’s @Service annotation to mark the service provider and configure the retries attribute to set the number of retries, for example:

@Service(retries = 3,parameters = {<!-- -->"circuitbreaker", "10"})
public class ForlanServiceImpl implements ForlanService{<!-- -->
    // ...
}

2. Downgrade (Fallback)

The downgrade mechanism is used to provide backup processing logic when the service is unavailable. Dubbo provides mock configuration to implement downgrade.

XML configuration method

Here is an example configuration:

<dubbo:reference id="forlanService" interface="com.example.ForlanService">
    <dubbo:parameter key="mock" value="com.example.ForlanServiceMock"/>
</dubbo:reference>

In the above example, the mock parameter specifies a ForlanServiceMock class that implements the ForlanService interface. When the service is unavailable, Dubbo will call methods in this class to provide backup logic.

Annotation method

  • Use Dubbo’s @Reference annotation to mark the service consumer, and configure the mock attribute to specify the downgrade logic, for example:
@Reference(mock = "com.example.ForlanServiceMock")
private ForlanService forlanService;
  • Create a ForlanServiceMock class that implements the UserService interface to provide downgrade logic, for example:
public class ForlanServiceMock implements ForlanService{<!-- -->
    // Implement downgrade logic
}

3. Rate Limiting

The current limiting mechanism is used to control the amount of concurrent access to the service to protect the stability of the service. Dubbo provides executes configuration to implement current limiting.

XML configuration method

<dubbo:service interface="com.example.ForlanService" executes="10"/>

In the above example, the executes attribute sets the maximum number of concurrent executions, and Dubbo will limit the number of requests executed simultaneously to not exceed this value.

Annotation method

Use Dubbo’s @Service annotation to mark the service provider, and configure the executes attribute to set the maximum number of concurrent executions, for example:

@Service(executes = 10)
public class ForlanServiceImpl implements ForlanService{<!-- -->
    // ...
}

4. Load Balancing

The load balancing mechanism is used to distribute requests among multiple service providers to achieve load balancing. Dubbo provides a variety of load balancing strategies, such as random, polling, consistent hashing, etc.

XML configuration method

<dubbo:reference id="userService" interface="com.example.ForlanService">
    <dubbo:parameter key="loadbalance" value="random"/>
</dubbo:reference>

In the above example, the loadbalance parameter sets the load balancing strategy to random, and Dubbo will randomly select a service provider to handle the request.

Annotation method

Use Dubbo’s @Reference annotation to mark service consumers, and configure the loadbalance attribute to specify the load balancing strategy, for example:

@Reference(loadbalance = "random")
private ForlanService forlanService;

5. Extension

1. What is the difference between Feign and Openfeign?

OpenFeign is Spring Cloud’s annotation that supports SpringMVC based on Feign.

2. What is the difference between Hystrix and Sentinel?

Hystrix and Sentinel are both open source frameworks for fault tolerance and circuit breaker protection in distributed systems, but they differ in some aspects.

  1. Project background: Hystrix was developed by Netflix, while Sentinel was developed by Alibaba. This means that Hystrix focuses more on Netflix’s internal needs, while Sentinel is more suitable for Alibaba’s business scenarios.
  2. Language support: Hystrix mainly supports Java language, while Sentinel not only supports Java, but also supports Go, C++ and other languages.
  3. Functional features: Hystrix provides a circuit breaker mechanism based on thread pool isolation and semaphore isolation, and provides thread pool monitoring and measurement functions. In addition to providing a circuit breaker mechanism, Sentinel also has functions such as flow control, system load protection, statistics and degradation.
  4. Ecosystem: Hystrix has entered maintenance mode and has stopped developing new features. Sentinel has been widely used in China, and has also been integrated with frameworks such as Spring Cloud and Dubbo.
  5. In summary, there are some differences between Hystrix and Sentinel in terms of functional features, language support, and ecosystem. Which one to choose depends on the actual business needs and technology stack.