# How SDS Filtering for Dubbo Integration Works: Implementation Principles and Source Code Analysis

> Understand SDS filtering for Dubbo integration. Discover how it intercepts RPC calls, checks degradation rules, and reports statistics with the didi/sds repository. No extra configuration needed.

- Repository: [DiDi/sds](https://github.com/didi/sds)
- Tags: internals
- Published: 2026-02-28

---

**SDS integrates with Dubbo by providing an SPI-registered Filter that intercepts every RPC call to check degradation rules before invocation and report statistics afterward, requiring no additional configuration beyond adding the `sds-dubbo` dependency.**

The didi/sds repository implements a robust service degradation system that integrates seamlessly with Apache Dubbo through a custom Filter. This integration allows applications to automatically trigger fallback logic based on real-time metrics without modifying existing service code. Understanding the implementation principle of SDS filtering for Dubbo integration reveals how the framework intercepts remote procedure calls to enforce circuit-breaking policies.

## Filter Registration and Activation Mechanism

### Dubbo SPI Auto-Registration

SDS hooks into Dubbo’s request chain using the standard Dubbo SPI (Service Provider Interface) mechanism. The filter class is declared in the file `sds-extension/sds-dubbo/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter`. When the application starts, Dubbo automatically scans this classpath resource and loads the `SdsDubboFilter` class into the filter chain.

This approach requires zero XML or annotation configuration in your service definitions. Simply adding the `sds-dubbo` module to your classpath triggers the integration.

### Dual-Side Activation via @Activate

The `SdsDubboFilter` class header uses the `@Activate` annotation to specify its activation scope. According to lines 14-16 in [`SdsDubboFilter.java`](https://github.com/didi/sds/blob/main/SdsDubboFilter.java), the annotation is configured as:

```java
@Activate(group = {Constants.PROVIDER, Constants.CONSUMER})

```

This ensures the filter executes on both the **provider** and **consumer** sides of every RPC call. Whether you are receiving requests or making outbound calls, the degradation logic applies uniformly.

## The RPC Interception Lifecycle in SdsDubboFilter

The core logic resides in [`sds-extension/sds-dubbo/src/main/java/com/didiglobal/sds/extension/dubbo/filter/SdsDubboFilter.java`](https://github.com/didi/sds/blob/main/sds-extension/sds-dubbo/src/main/java/com/didiglobal/sds/extension/dubbo/filter/SdsDubboFilter.java). The `invoke` method implements a pre-check/post-check pattern that wraps every Dubbo invocation.

### Client Acquisition and Point Construction

Inside the `invoke` method (lines 22-27), the filter obtains the singleton SDS client:

```java
SdsClient sdsClient = SdsClientFactory.getSdsClient();
if (sdsClient == null) {
    return invoker.invoke(invocation);
}

```

If the client is not initialized, the request proceeds normally without SDS handling.

Next, the filter constructs a unique **point** identifier (lines 30-33) using the service interface name, method name, and the current side (provider or consumer). This string acts as the key for locating specific degradation rules and statistics in the SDS configuration.

### Pre-Invocation Degradation Check

Before delegating to the actual service implementation, the filter checks whether the request should be short-circuited (lines 34-44):

```java
if (sdsClient.shouldDowngrade(point)) {
    Object fallback = SdsDowngradeReturnValueService.getDowngradeReturnValue(point, invoker.getInterface());
    return new RpcResult(fallback);
}

```

If `shouldDowngrade` returns true, the filter immediately returns a pre-defined fallback value wrapped in an `RpcResult`, bypassing the actual business logic entirely.

### Normal Invocation and Exception Handling

When no degradation is required, the filter delegates to the original Dubbo `Invoker` (lines 48-51):

```java
Result result = invoker.invoke(invocation);

```

Regardless of whether the invocation succeeds or throws an exception, the filter enters a `finally` block (lines 56-64) to update SDS metrics:

```java
finally {
    if (exception != null) {
        sdsClient.exceptionSign(point, exception);
    }
    sdsClient.downgradeFinally(point);
}

```

The `exceptionSign` method records error types for adaptive rule learning, while `downgradeFinally` updates sliding-window counters and token-bucket metrics to inform future degradation decisions.

## Configuration and Usage Example

### Maven Dependency

Add the following to your [`pom.xml`](https://github.com/didi/sds/blob/main/pom.xml) on both provider and consumer projects:

```xml
<dependency>
    <groupId>com.didiglobal.sds</groupId>
    <artifactId>sds-dubbo</artifactId>
    <version>1.0.16</version>
</dependency>

```

### SDS Client Initialization

Initialize the client once at application startup:

```java
import com.didiglobal.sds.client.SdsClientFactory;

// Load configuration from ZooKeeper or local properties
SdsClient sdsClient = SdsClientFactory.getOrCreateSdsClient();

```

The filter automatically retrieves this client via `SdsClientFactory.getSdsClient()`; no explicit injection is required.

### Defining Degradation Rules

Configure rules in SDS using the point identifier format `InterfaceName-methodName-SidePoint`:

```json
{
  "point": "OrderService-getOrder-ProviderPoint",
  "strategy": {
    "type": "exceptionRate",
    "threshold": 50
  },
  "downgrade": {
    "type": "returnValue",
    "fallback": {"orderId":"0","status":"DEGRADED"}
  }
}

```

With this rule, provider calls exceeding a 50% exception rate automatically return the fallback `Order` object without reaching the database or downstream services.

## Summary

- The filter registers via Dubbo's SPI mechanism in `META-INF/dubbo/com.alibaba.dubbo.rpc.Filter`, enabling automatic activation without XML configuration.
- The `@Activate(group = {Constants.PROVIDER, Constants.CONSUMER})` annotation ensures the filter intercepts both inbound and outbound RPC calls.
- Each request builds a unique point identifier (interface-method-side) to locate specific degradation rules and statistics.
- The `invoke` method implements a pre-check (`shouldDowngrade`) and post-check (`downgradeFinally`) pattern to wrap every call with SDS decision-making.
- Exceptions are reported via `sdsClient.exceptionSign()` to enable adaptive threshold learning based on real-time error patterns.

## Frequently Asked Questions

### How does SDS automatically activate without explicit Dubbo configuration?

Dubbo's SPI mechanism scans the `META-INF/dubbo/com.alibaba.dubbo.rpc.Filter` file packaged inside the `sds-dubbo` JAR at runtime. When Dubbo discovers the fully-qualified class name `com.didiglobal.sds.extension.dubbo.filter.SdsDubboFilter` listed in this file, it instantiates and inserts the filter into the RPC chain automatically. This design follows Dubbo's official extension loading convention, requiring no additional `@Bean` declarations or XML `<dubbo:filter>` entries.

### What happens if the SdsClient is not initialized when the filter executes?

The filter defensively checks `SdsClientFactory.getSdsClient()` at the beginning of the `invoke` method. If this returns null—indicating the client has not been initialized with configuration sources like ZooKeeper or local files—the filter immediately proceeds with `invoker.invoke(invocation)`. This fail-safe ensures that RPC traffic continues uninterrupted even if SDS experiences startup failures or configuration issues.

### Can SDS filtering distinguish between provider and consumer roles?

Yes, the implementation explicitly differentiates sides. The `@Activate` annotation specifies both `Constants.PROVIDER` and `Constants.CONSUMER` groups, causing Dubbo to instantiate separate filter instances for each role. Inside the `invoke` method, the filter constructs the point identifier by appending "ProviderPoint" or "ConsumerPoint" based on the current runtime context. This allows you to configure different degradation thresholds for service providers versus service consumers.

### How does the filter handle exceptions during RPC invocation?

The filter wraps the actual invocation in a try-catch-finally block. If `invoker.invoke()` throws an exception, the catch block stores it for reporting. In the mandatory `finally` block (lines 56-64), the filter first calls `sdsClient.exceptionSign(point, exception)` to record the error type and stack trace for statistical analysis, then always executes `sdsClient.downgradeFinally(point)` to update sliding-window counters. This ensures that error bursts trigger circuit-breaking behavior even when calls fail catastrophically.