Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sentinel-adapter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<module>sentinel-spring-webmvc-v6x-adapter</module>
<module>sentinel-zuul2-adapter</module>
<module>sentinel-okhttp-adapter</module>
<module>sentinel-spring-restclient-adapter</module>
<module>sentinel-jax-rs-adapter</module>
<module>sentinel-quarkus-adapter</module>
<module>sentinel-motan-adapter</module>
Expand Down
152 changes: 152 additions & 0 deletions sentinel-adapter/sentinel-spring-restclient-adapter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Sentinel Spring RestClient Adapter

## Overview

Sentinel Spring RestClient Adapter provides Sentinel integration for Spring Framework 6.0+ `RestClient`. With this adapter, you can easily add flow control, circuit breaking, and degradation features to HTTP requests made via `RestClient`.

## Features

- Flow control (QPS limiting)
- Circuit breaking (degradation)
- Custom resource name extraction
- Custom fallback responses
- HTTP 5xx error tracing

## Requirements

- Spring Framework 6.0+
- JDK 17+
- Sentinel Core 1.8.0+

## Usage

### 1. Add Dependency

```xml
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-restclient-adapter</artifactId>
<version>${sentinel.version}</version>
</dependency>
```

### 2. Basic Usage

```java
import com.alibaba.csp.sentinel.adapter.spring.restclient.SentinelRestClientInterceptor;
import org.springframework.web.client.RestClient;

// Create RestClient with Sentinel interceptor
RestClient restClient = RestClient.builder()
.requestInterceptor(new SentinelRestClientInterceptor())
.build();

// Use RestClient to send requests (protected by Sentinel)
String result = restClient.get()
.uri("https://httpbin.org/get")
.retrieve()
.body(String.class);
```

### 3. Custom Configuration

```java
import com.alibaba.csp.sentinel.adapter.spring.restclient.SentinelRestClientConfig;
import com.alibaba.csp.sentinel.adapter.spring.restclient.SentinelRestClientInterceptor;
import com.alibaba.csp.sentinel.adapter.spring.restclient.extractor.RestClientResourceExtractor;
import com.alibaba.csp.sentinel.adapter.spring.restclient.fallback.RestClientFallback;

// Custom resource name extractor
RestClientResourceExtractor customExtractor = request -> {
// Example: normalize RESTful path parameters
String path = request.getURI().getPath();
if (path.matches("/users/\\d+")) {
path = "/users/{id}";
}
return request.getMethod() + ":" + request.getURI().getHost() + path;
};

// Custom fallback: throw a custom exception when blocked
RestClientFallback customFallback = (request, body, execution, ex) -> {
throw new RuntimeException("Service temporarily unavailable, please retry later", ex);
};

// Create configuration
SentinelRestClientConfig config = new SentinelRestClientConfig(
"my-restclient:", // Resource name prefix
customExtractor,
customFallback
);

// Create interceptor with custom configuration
RestClient restClient = RestClient.builder()
.requestInterceptor(new SentinelRestClientInterceptor(config))
.build();
```

### 4. Configure Sentinel Rules

```java
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import java.util.Collections;

// Configure flow control rule
FlowRule rule = new FlowRule("restclient:GET:https://httpbin.org/get");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(10); // Max 10 requests per second
rule.setLimitApp("default");

FlowRuleManager.loadRules(Collections.singletonList(rule));
```

## Core Components

### SentinelRestClientInterceptor

The main interceptor implementation responsible for:

- Creating Sentinel resources for each HTTP request
- Catching BlockException and invoking fallback handler
- Tracing exceptions and 5xx errors

### SentinelRestClientConfig

Configuration class containing:

- `resourcePrefix`: Resource name prefix (default: `restclient:`)
- `resourceExtractor`: Resource name extractor
- `fallback`: Fallback handler

### RestClientResourceExtractor

Interface for resource name extraction, allowing customization of resource name generation logic.

### RestClientFallback

Interface for fallback handling, invoked when requests are blocked by flow control or circuit breaking.

## Resource Name Format

The default resource name format: `{prefix}{METHOD}:{URL}`

Examples:

- `restclient:GET:https://httpbin.org/get`
- `restclient:POST:http://localhost:8080/api/users`

## Notes

This adapter only supports `RestClient` from Spring Framework 6.0+, not `RestTemplate`.

## Integration with Spring Cloud Alibaba

This adapter provides basic Sentinel integration. For Spring Cloud Alibaba projects:

1. Add auto-configuration support in `spring-cloud-starter-alibaba-sentinel`
2. Use `@SentinelRestClient` annotation for simplified configuration

## License

Apache License 2.0
87 changes: 87 additions & 0 deletions sentinel-adapter/sentinel-spring-restclient-adapter/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-adapter</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

<name>${project.groupId}:${project.artifactId}</name>

<artifactId>sentinel-spring-restclient-adapter</artifactId>
<packaging>jar</packaging>

<properties>
<spring-web.version>6.1.0</spring-web.version>
<spring-boot.version>3.2.0</spring-boot.version>
<spring-test.version>6.1.0</spring-test.version>

<skip.spring.v6x.test>false</skip.spring.v6x.test>
</properties>

<dependencies>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring-web.version}</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>${spring-boot.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring-test.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven.surefire.version}</version>
<configuration>
<skipTests>${skip.spring.v6x.test}</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright 1999-2020 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.adapter.spring.restclient;

import com.alibaba.csp.sentinel.adapter.spring.restclient.extractor.DefaultRestClientResourceExtractor;
import com.alibaba.csp.sentinel.adapter.spring.restclient.extractor.RestClientResourceExtractor;
import com.alibaba.csp.sentinel.adapter.spring.restclient.fallback.DefaultRestClientFallback;
import com.alibaba.csp.sentinel.adapter.spring.restclient.fallback.RestClientFallback;
import com.alibaba.csp.sentinel.util.AssertUtil;

/**
* Configuration for Sentinel RestClient interceptor.
*
* @author QHT, uuuyuqi
*/
public class SentinelRestClientConfig {

public static final String DEFAULT_RESOURCE_PREFIX = "restclient:";

private final String resourcePrefix;
private final RestClientResourceExtractor resourceExtractor;
private final RestClientFallback fallback;

public SentinelRestClientConfig() {
this(DEFAULT_RESOURCE_PREFIX);
}

public SentinelRestClientConfig(String resourcePrefix) {
this(resourcePrefix, new DefaultRestClientResourceExtractor(), new DefaultRestClientFallback());
}

public SentinelRestClientConfig(RestClientResourceExtractor resourceExtractor, RestClientFallback fallback) {
this(DEFAULT_RESOURCE_PREFIX, resourceExtractor, fallback);
}

public SentinelRestClientConfig(String resourcePrefix,
RestClientResourceExtractor resourceExtractor,
RestClientFallback fallback) {
AssertUtil.notNull(resourceExtractor, "resourceExtractor cannot be null");
AssertUtil.notNull(fallback, "fallback cannot be null");
this.resourcePrefix = resourcePrefix;
this.resourceExtractor = resourceExtractor;
this.fallback = fallback;
}

public String getResourcePrefix() {
return resourcePrefix;
}

public RestClientResourceExtractor getResourceExtractor() {
return resourceExtractor;
}

public RestClientFallback getFallback() {
return fallback;
}

@Override
public String toString() {
return "SentinelRestClientConfig{" +
"resourcePrefix='" + resourcePrefix + '\'' +
", resourceExtractor=" + resourceExtractor +
", fallback=" + fallback +
'}';
}
}
Loading
Loading