Introduce support through a customized client builder `SentinelApacheHttpClientBuilder`.master
@@ -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 |