Adapter: Support Apache HttpClient (#1455)
Introduce support through a customized client builder `SentinelApacheHttpClientBuilder`.
This commit is contained in:
parent
4f31b2c61b
commit
38911c5ec1
|
|
@ -18,6 +18,7 @@
|
||||||
<module>sentinel-web-servlet</module>
|
<module>sentinel-web-servlet</module>
|
||||||
<module>sentinel-dubbo-adapter</module>
|
<module>sentinel-dubbo-adapter</module>
|
||||||
<module>sentinel-apache-dubbo-adapter</module>
|
<module>sentinel-apache-dubbo-adapter</module>
|
||||||
|
<module>sentinel-apache-httpclient-adapter</module>
|
||||||
<module>sentinel-sofa-rpc-adapter</module>
|
<module>sentinel-sofa-rpc-adapter</module>
|
||||||
<module>sentinel-grpc-adapter</module>
|
<module>sentinel-grpc-adapter</module>
|
||||||
<module>sentinel-zuul-adapter</module>
|
<module>sentinel-zuul-adapter</module>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
# Sentinel Apache Httpclient Adapter
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Sentinel provides integration for OkHttp client to enable flow control for web requests.
|
||||||
|
|
||||||
|
Add the following dependency in `pom.xml` (if you are using Maven):
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-apache-httpclient-adapter</artifactId>
|
||||||
|
<version>x.y.z</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
We can use the `SentinelApacheHttpClientBuilder` when `CloseableHttpClient` at initialization, for example:
|
||||||
|
|
||||||
|
```java
|
||||||
|
CloseableHttpClient httpclient = new SentinelApacheHttpClientBuilder().build();
|
||||||
|
```
|
||||||
|
|
||||||
|
If we want to add some additional configurations, we can refer to the following code
|
||||||
|
|
||||||
|
```java
|
||||||
|
HttpClientBuilder builder = new SentinelApacheHttpClientBuilder();
|
||||||
|
//builder Other Definitions
|
||||||
|
CloseableHttpClient httpclient = builder.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
- `SentinelApacheHttpClientConfig` configuration:
|
||||||
|
|
||||||
|
| name | description | type | default value |
|
||||||
|
|------|------------|------|-------|
|
||||||
|
| prefix | customize resource prefix | `String` | `httpclient:` |
|
||||||
|
| extractor | customize resource extractor | `ApacheHttpClientResourceExtractor` | `DefaultApacheHttpClientResourceExtractor` |
|
||||||
|
| fallback | handle request when it is blocked | `ApacheHttpClientFallback` | `DefaultApacheHttpClientFallback` |
|
||||||
|
|
||||||
|
### extractor (resource extractor)
|
||||||
|
|
||||||
|
We can define `ApacheHttpClientResourceExtractor` to customize resource extractor replace `DefaultApacheHttpClientResourceExtractor` at `SentinelApacheHttpClientBuilder` default config, for example: httpclient:GET:/httpclient/back/1 ==> httpclient:GET:/httpclient/back/{id}
|
||||||
|
|
||||||
|
```java
|
||||||
|
SentinelApacheHttpClientConfig config = new SentinelApacheHttpClientConfig();
|
||||||
|
config.setExtractor(new ApacheHttpClientResourceExtractor() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String extractor(HttpRequestWrapper request) {
|
||||||
|
String contains = "/httpclient/back/";
|
||||||
|
String uri = request.getRequestLine().getUri();
|
||||||
|
if (uri.startsWith(contains)) {
|
||||||
|
uri = uri.substring(0, uri.indexOf(contains) + contains.length()) + "{id}";
|
||||||
|
}
|
||||||
|
return request.getMethod() + ":" + uri;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
CloseableHttpClient httpclient = new SentinelApacheHttpClientBuilder(config).build();
|
||||||
|
```
|
||||||
|
|
||||||
|
### fallback (Block handling)
|
||||||
|
|
||||||
|
We can define `ApacheHttpClientFallback` at `SentinelApacheHttpClientBuilder` default config, to handle request is blocked according to the actual scenario, for example:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class DefaultApacheHttpClientFallback implements ApacheHttpClientFallback {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CloseableHttpResponse handle(HttpRequestWrapper request, BlockException e) {
|
||||||
|
// Just wrap and throw the exception.
|
||||||
|
throw new SentinelRpcException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
<?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>
|
||||||
|
<artifactId>sentinel-adapter</artifactId>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<version>1.8.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>sentinel-apache-httpclient-adapter</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<apache.httpclient.version>4.5.6</apache.httpclient.version>
|
||||||
|
<spring.boot.version>2.1.3.RELEASE</spring.boot.version>
|
||||||
|
<spring-test.version>5.1.5.RELEASE</spring-test.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
<artifactId>httpclient</artifactId>
|
||||||
|
<version>${apache.httpclient.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>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* 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.apache.httpclient;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.*;
|
||||||
|
import com.alibaba.csp.sentinel.adapter.apache.httpclient.config.SentinelApacheHttpClientConfig;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||||
|
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||||
|
import org.apache.http.HttpException;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpExecutionAware;
|
||||||
|
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||||
|
import org.apache.http.client.protocol.HttpClientContext;
|
||||||
|
import org.apache.http.conn.routing.HttpRoute;
|
||||||
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
|
import org.apache.http.impl.execchain.ClientExecChain;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhaoyuguang
|
||||||
|
*/
|
||||||
|
public class SentinelApacheHttpClientBuilder extends HttpClientBuilder {
|
||||||
|
|
||||||
|
private final SentinelApacheHttpClientConfig config;
|
||||||
|
|
||||||
|
public SentinelApacheHttpClientBuilder(){
|
||||||
|
this.config = new SentinelApacheHttpClientConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SentinelApacheHttpClientBuilder(SentinelApacheHttpClientConfig config){
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ClientExecChain decorateMainExec(final ClientExecChain mainExec) {
|
||||||
|
return new ClientExecChain() {
|
||||||
|
@Override
|
||||||
|
public CloseableHttpResponse execute(HttpRoute route, HttpRequestWrapper request,
|
||||||
|
HttpClientContext clientContext, HttpExecutionAware execAware)
|
||||||
|
throws IOException, HttpException {
|
||||||
|
Entry entry = null;
|
||||||
|
try {
|
||||||
|
String name = config.getExtractor().extractor(request);
|
||||||
|
if (!StringUtil.isEmpty(config.getPrefix())) {
|
||||||
|
name = config.getPrefix() + name;
|
||||||
|
}
|
||||||
|
entry = SphU.entry(name, ResourceTypeConstants.COMMON_WEB, EntryType.OUT);
|
||||||
|
return mainExec.execute(route, request, clientContext, execAware);
|
||||||
|
} catch (BlockException e) {
|
||||||
|
return config.getFallback().handle(request, e);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
Tracer.traceEntry(t, entry);
|
||||||
|
throw t;
|
||||||
|
} finally {
|
||||||
|
if (entry != null) {
|
||||||
|
entry.exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* 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.apache.httpclient.config;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.adapter.apache.httpclient.extractor.ApacheHttpClientResourceExtractor;
|
||||||
|
import com.alibaba.csp.sentinel.adapter.apache.httpclient.extractor.DefaultApacheHttpClientResourceExtractor;
|
||||||
|
import com.alibaba.csp.sentinel.adapter.apache.httpclient.fallback.ApacheHttpClientFallback;
|
||||||
|
import com.alibaba.csp.sentinel.adapter.apache.httpclient.fallback.DefaultApacheHttpClientFallback;
|
||||||
|
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhaoyuguang
|
||||||
|
*/
|
||||||
|
public class SentinelApacheHttpClientConfig {
|
||||||
|
|
||||||
|
private String prefix = "httpclient:";
|
||||||
|
private ApacheHttpClientResourceExtractor extractor = new DefaultApacheHttpClientResourceExtractor();
|
||||||
|
private ApacheHttpClientFallback fallback = new DefaultApacheHttpClientFallback();
|
||||||
|
|
||||||
|
public String getPrefix() {
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrefix(String prefix) {
|
||||||
|
AssertUtil.notNull(prefix, "prefix cannot be null");
|
||||||
|
this.prefix = prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApacheHttpClientResourceExtractor getExtractor() {
|
||||||
|
return extractor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExtractor(ApacheHttpClientResourceExtractor extractor) {
|
||||||
|
AssertUtil.notNull(extractor, "extractor cannot be null");
|
||||||
|
this.extractor = extractor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApacheHttpClientFallback getFallback() {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFallback(ApacheHttpClientFallback fallback) {
|
||||||
|
AssertUtil.notNull(fallback, "fallback cannot be null");
|
||||||
|
this.fallback = fallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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.apache.httpclient.extractor;
|
||||||
|
|
||||||
|
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhaoyuguang
|
||||||
|
*/
|
||||||
|
public interface ApacheHttpClientResourceExtractor {
|
||||||
|
|
||||||
|
String extractor(HttpRequestWrapper request);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* 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.apache.httpclient.extractor;
|
||||||
|
|
||||||
|
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhaoyuguang
|
||||||
|
*/
|
||||||
|
public class DefaultApacheHttpClientResourceExtractor implements ApacheHttpClientResourceExtractor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String extractor(HttpRequestWrapper request) {
|
||||||
|
return request.getRequestLine().getUri();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* 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.apache.httpclient.fallback;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||||
|
import org.apache.http.HttpException;
|
||||||
|
import org.apache.http.HttpRequest;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||||
|
import org.apache.http.protocol.HttpContext;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhaoyuguang
|
||||||
|
*/
|
||||||
|
public interface ApacheHttpClientFallback {
|
||||||
|
|
||||||
|
CloseableHttpResponse handle(HttpRequestWrapper request, BlockException e);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* 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.apache.httpclient.fallback;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
|
||||||
|
import org.apache.http.HttpException;
|
||||||
|
import org.apache.http.HttpRequest;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||||
|
import org.apache.http.protocol.HttpContext;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhaoyuguang
|
||||||
|
*/
|
||||||
|
public class DefaultApacheHttpClientFallback implements ApacheHttpClientFallback {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CloseableHttpResponse handle(HttpRequestWrapper request, BlockException e) {
|
||||||
|
// Just wrap and throw the exception.
|
||||||
|
throw new SentinelRpcException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
* 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.apache.httpclient;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.Constants;
|
||||||
|
import com.alibaba.csp.sentinel.adapter.apache.httpclient.app.TestApplication;
|
||||||
|
import com.alibaba.csp.sentinel.adapter.apache.httpclient.config.SentinelApacheHttpClientConfig;
|
||||||
|
import com.alibaba.csp.sentinel.adapter.apache.httpclient.extractor.ApacheHttpClientResourceExtractor;
|
||||||
|
import com.alibaba.csp.sentinel.node.ClusterNode;
|
||||||
|
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.protocol.BasicHttpContext;
|
||||||
|
import org.apache.http.protocol.HttpContext;
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhaoyuguang
|
||||||
|
*/
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
@SpringBootTest(classes = TestApplication.class,
|
||||||
|
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
|
||||||
|
properties = {
|
||||||
|
"server.port=8084"
|
||||||
|
})
|
||||||
|
public class SentinelApacheHttpClientTest {
|
||||||
|
|
||||||
|
@Value("${server.port}")
|
||||||
|
private Integer port;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSentinelOkHttpInterceptor0() throws Exception {
|
||||||
|
|
||||||
|
CloseableHttpClient httpclient = new SentinelApacheHttpClientBuilder().build();
|
||||||
|
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + port + "/httpclient/back");
|
||||||
|
System.out.println(getRemoteString(httpclient, httpGet));
|
||||||
|
ClusterNode cn = ClusterBuilderSlot.getClusterNode("httpclient:/httpclient/back");
|
||||||
|
assertNotNull(cn);
|
||||||
|
Constants.ROOT.removeChildList();
|
||||||
|
ClusterBuilderSlot.getClusterNodeMap().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSentinelOkHttpInterceptor1() throws Exception {
|
||||||
|
SentinelApacheHttpClientConfig config = new SentinelApacheHttpClientConfig();
|
||||||
|
config.setExtractor(new ApacheHttpClientResourceExtractor() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String extractor(HttpRequestWrapper request) {
|
||||||
|
String contains = "/httpclient/back/";
|
||||||
|
String uri = request.getRequestLine().getUri();
|
||||||
|
if (uri.startsWith(contains)) {
|
||||||
|
uri = uri.substring(0, uri.indexOf(contains) + contains.length()) + "{id}";
|
||||||
|
}
|
||||||
|
return request.getMethod() + ":" + uri;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
CloseableHttpClient httpclient = new SentinelApacheHttpClientBuilder(config).build();
|
||||||
|
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + port + "/httpclient/back/1");
|
||||||
|
System.out.println(getRemoteString(httpclient, httpGet));
|
||||||
|
ClusterNode cn = ClusterBuilderSlot.getClusterNode("httpclient:GET:/httpclient/back/{id}");
|
||||||
|
assertNotNull(cn);
|
||||||
|
Constants.ROOT.removeChildList();
|
||||||
|
ClusterBuilderSlot.getClusterNodeMap().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getRemoteString(CloseableHttpClient httpclient, HttpGet httpGet) throws IOException {
|
||||||
|
String result;
|
||||||
|
HttpContext context = new BasicHttpContext();
|
||||||
|
CloseableHttpResponse response;
|
||||||
|
response = httpclient.execute(httpGet, context);
|
||||||
|
try {
|
||||||
|
HttpEntity entity = response.getEntity();
|
||||||
|
result = EntityUtils.toString(entity, "utf-8");
|
||||||
|
EntityUtils.consume(entity);
|
||||||
|
} finally {
|
||||||
|
response.close();
|
||||||
|
}
|
||||||
|
httpclient.close();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* 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.apache.httpclient.app;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhaoyuguang
|
||||||
|
*/
|
||||||
|
@SpringBootApplication
|
||||||
|
public class TestApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(TestApplication.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* 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.apache.httpclient.app.controller;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhaoyuguang
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
public class TestController {
|
||||||
|
|
||||||
|
@RequestMapping("/httpclient/back")
|
||||||
|
public String back() {
|
||||||
|
return "Welcome Back!";
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/httpclient/back/{id}")
|
||||||
|
public String back(@PathVariable String id) {
|
||||||
|
return "Welcome Back! " + id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* 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.apache.httpclient.config;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhaoyuguang
|
||||||
|
*/
|
||||||
|
public class SentinelApacheHttpClientConfigTest {
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testConfigSetPrefix() {
|
||||||
|
SentinelApacheHttpClientConfig config = new SentinelApacheHttpClientConfig();
|
||||||
|
config.setPrefix(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testConfigSetCleaner() {
|
||||||
|
SentinelApacheHttpClientConfig config = new SentinelApacheHttpClientConfig();
|
||||||
|
config.setExtractor(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testConfigSetFallback() {
|
||||||
|
SentinelApacheHttpClientConfig config = new SentinelApacheHttpClientConfig();
|
||||||
|
config.setFallback(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* 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.apache.httpclient.fallback;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhaoyuguang
|
||||||
|
*/
|
||||||
|
public class ApacheHttpClientFallbackTest {
|
||||||
|
|
||||||
|
@Test(expected = SentinelRpcException.class)
|
||||||
|
public void testDefaultOkHttpFallback() {
|
||||||
|
BlockException e = new FlowException("xxx");
|
||||||
|
ApacheHttpClientFallback fallback = new DefaultApacheHttpClientFallback();
|
||||||
|
fallback.handle(null, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -32,6 +32,7 @@
|
||||||
<module>sentinel-demo-command-handler</module>
|
<module>sentinel-demo-command-handler</module>
|
||||||
<module>sentinel-demo-spring-webflux</module>
|
<module>sentinel-demo-spring-webflux</module>
|
||||||
<module>sentinel-demo-apache-dubbo</module>
|
<module>sentinel-demo-apache-dubbo</module>
|
||||||
|
<module>sentinel-demo-apache-httpclient</module>
|
||||||
<module>sentinel-demo-sofa-rpc</module>
|
<module>sentinel-demo-sofa-rpc</module>
|
||||||
<module>sentinel-demo-spring-cloud-gateway</module>
|
<module>sentinel-demo-spring-cloud-gateway</module>
|
||||||
<module>sentinel-demo-zuul-gateway</module>
|
<module>sentinel-demo-zuul-gateway</module>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?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>
|
||||||
|
<artifactId>sentinel-demo</artifactId>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<version>1.8.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>sentinel-demo-apache-httpclient</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<spring.boot.version>2.1.3.RELEASE</spring.boot.version>
|
||||||
|
<test.framework.version>4.3</test.framework.version>
|
||||||
|
<apache.httpclient.version>4.5.6</apache.httpclient.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-transport-simple-http</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-apache-httpclient-adapter</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<version>${spring.boot.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<version>${spring.boot.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
<artifactId>httpclient</artifactId>
|
||||||
|
<version>${apache.httpclient.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* 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.demo.apache.httpclient;
|
||||||
|
|
||||||
|
import org.springframework.boot.CommandLineRunner;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhaoyuguang
|
||||||
|
*/
|
||||||
|
@SpringBootApplication
|
||||||
|
public class ApacheHttpClientDemoApplication implements CommandLineRunner {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(ApacheHttpClientDemoApplication.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(String... args) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
* 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.demo.apache.httpclient.controller;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.adapter.apache.httpclient.SentinelApacheHttpClientBuilder;
|
||||||
|
import com.alibaba.csp.sentinel.adapter.apache.httpclient.config.SentinelApacheHttpClientConfig;
|
||||||
|
import com.alibaba.csp.sentinel.adapter.apache.httpclient.extractor.ApacheHttpClientResourceExtractor;
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.protocol.BasicHttpContext;
|
||||||
|
import org.apache.http.protocol.HttpContext;
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhaoyuguang
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
public class ApacheHttpClientTestController {
|
||||||
|
|
||||||
|
@Value("${server.port}")
|
||||||
|
private Integer port;
|
||||||
|
|
||||||
|
@RequestMapping("/httpclient/back")
|
||||||
|
public String back() {
|
||||||
|
System.out.println("back");
|
||||||
|
return "Welcome Back!";
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/httpclient/back/{id}")
|
||||||
|
public String back(@PathVariable String id) {
|
||||||
|
System.out.println("back");
|
||||||
|
return "Welcome Back! " + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/httpclient/sync")
|
||||||
|
public String sync() throws Exception {
|
||||||
|
SentinelApacheHttpClientConfig config = new SentinelApacheHttpClientConfig();
|
||||||
|
config.setExtractor(new ApacheHttpClientResourceExtractor() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String extractor(HttpRequestWrapper request) {
|
||||||
|
String contains = "/httpclient/back/";
|
||||||
|
String uri = request.getRequestLine().getUri();
|
||||||
|
if (uri.startsWith(contains)) {
|
||||||
|
uri = uri.substring(0, uri.indexOf(contains) + contains.length()) + "{id}";
|
||||||
|
}
|
||||||
|
return request.getMethod() + ":" + uri;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
CloseableHttpClient httpclient = new SentinelApacheHttpClientBuilder(config).build();
|
||||||
|
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + port + "/httpclient/back");
|
||||||
|
return getRemoteString(httpclient, httpGet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/httpclient/sync/{id}")
|
||||||
|
public String sync(@PathVariable String id) throws Exception {
|
||||||
|
SentinelApacheHttpClientConfig config = new SentinelApacheHttpClientConfig();
|
||||||
|
config.setExtractor(new ApacheHttpClientResourceExtractor() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String extractor(HttpRequestWrapper request) {
|
||||||
|
String contains = "/httpclient/back/";
|
||||||
|
String uri = request.getRequestLine().getUri();
|
||||||
|
if (uri.startsWith(contains)) {
|
||||||
|
uri = uri.substring(0, uri.indexOf(contains) + contains.length()) + "{id}";
|
||||||
|
}
|
||||||
|
return request.getMethod() + ":" + uri;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
CloseableHttpClient httpclient = new SentinelApacheHttpClientBuilder(config).build();
|
||||||
|
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + port + "/httpclient/back/" + id);
|
||||||
|
return getRemoteString(httpclient, httpGet);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getRemoteString(CloseableHttpClient httpclient, HttpGet httpGet) throws IOException {
|
||||||
|
String result;
|
||||||
|
HttpContext context = new BasicHttpContext();
|
||||||
|
CloseableHttpResponse response;
|
||||||
|
response = httpclient.execute(httpGet, context);
|
||||||
|
try {
|
||||||
|
HttpEntity entity = response.getEntity();
|
||||||
|
result = EntityUtils.toString(entity, "utf-8");
|
||||||
|
EntityUtils.consume(entity);
|
||||||
|
} finally {
|
||||||
|
response.close();
|
||||||
|
}
|
||||||
|
httpclient.close();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
spring.application.name=sentinel-demo-apache-httpclient
|
||||||
|
server.port=8083
|
||||||
Loading…
Reference in New Issue