diff --git a/sentinel-adapter/pom.xml b/sentinel-adapter/pom.xml index d7a973f0..127aa8af 100755 --- a/sentinel-adapter/pom.xml +++ b/sentinel-adapter/pom.xml @@ -18,6 +18,7 @@ sentinel-web-servlet sentinel-dubbo-adapter sentinel-apache-dubbo-adapter + sentinel-apache-httpclient-adapter sentinel-sofa-rpc-adapter sentinel-grpc-adapter sentinel-zuul-adapter diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/README.md b/sentinel-adapter/sentinel-apache-httpclient-adapter/README.md new file mode 100755 index 00000000..3ec548d2 --- /dev/null +++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/README.md @@ -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 + + com.alibaba.csp + sentinel-apache-httpclient-adapter + x.y.z + +``` + +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); + } +} +``` \ No newline at end of file diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/pom.xml b/sentinel-adapter/sentinel-apache-httpclient-adapter/pom.xml new file mode 100644 index 00000000..3dde07bf --- /dev/null +++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/pom.xml @@ -0,0 +1,69 @@ + + + + sentinel-adapter + com.alibaba.csp + 1.8.0-SNAPSHOT + + 4.0.0 + + sentinel-apache-httpclient-adapter + jar + + + 4.5.6 + 2.1.3.RELEASE + 5.1.5.RELEASE + + + + + com.alibaba.csp + sentinel-core + + + org.apache.httpcomponents + httpclient + ${apache.httpclient.version} + provided + + + + junit + junit + test + + + + org.mockito + mockito-core + test + + + com.alibaba + fastjson + test + + + + org.springframework.boot + spring-boot-starter-web + ${spring.boot.version} + test + + + org.springframework.boot + spring-boot-test + ${spring.boot.version} + test + + + org.springframework + spring-test + ${spring-test.version} + test + + + \ No newline at end of file diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/SentinelApacheHttpClientBuilder.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/SentinelApacheHttpClientBuilder.java new file mode 100644 index 00000000..4b6aad37 --- /dev/null +++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/SentinelApacheHttpClientBuilder.java @@ -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(); + } + } + } + }; + } +} diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/config/SentinelApacheHttpClientConfig.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/config/SentinelApacheHttpClientConfig.java new file mode 100644 index 00000000..9dbc1520 --- /dev/null +++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/config/SentinelApacheHttpClientConfig.java @@ -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; + } +} diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/extractor/ApacheHttpClientResourceExtractor.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/extractor/ApacheHttpClientResourceExtractor.java new file mode 100644 index 00000000..ab494d67 --- /dev/null +++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/extractor/ApacheHttpClientResourceExtractor.java @@ -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); +} diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/extractor/DefaultApacheHttpClientResourceExtractor.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/extractor/DefaultApacheHttpClientResourceExtractor.java new file mode 100644 index 00000000..02ccf697 --- /dev/null +++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/extractor/DefaultApacheHttpClientResourceExtractor.java @@ -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(); + } +} diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/fallback/ApacheHttpClientFallback.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/fallback/ApacheHttpClientFallback.java new file mode 100644 index 00000000..f670eca8 --- /dev/null +++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/fallback/ApacheHttpClientFallback.java @@ -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); +} diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/fallback/DefaultApacheHttpClientFallback.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/fallback/DefaultApacheHttpClientFallback.java new file mode 100644 index 00000000..81994bfc --- /dev/null +++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/fallback/DefaultApacheHttpClientFallback.java @@ -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); + } +} diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/SentinelApacheHttpClientTest.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/SentinelApacheHttpClientTest.java new file mode 100644 index 00000000..882edafe --- /dev/null +++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/SentinelApacheHttpClientTest.java @@ -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; + } +} diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/app/TestApplication.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/app/TestApplication.java new file mode 100644 index 00000000..e073dda5 --- /dev/null +++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/app/TestApplication.java @@ -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); + } +} diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/app/controller/TestController.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/app/controller/TestController.java new file mode 100644 index 00000000..2ae4275f --- /dev/null +++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/app/controller/TestController.java @@ -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; + } +} \ No newline at end of file diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/config/SentinelApacheHttpClientConfigTest.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/config/SentinelApacheHttpClientConfigTest.java new file mode 100644 index 00000000..414322b7 --- /dev/null +++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/config/SentinelApacheHttpClientConfigTest.java @@ -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); + } +} diff --git a/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/fallback/ApacheHttpClientFallbackTest.java b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/fallback/ApacheHttpClientFallbackTest.java new file mode 100644 index 00000000..56865918 --- /dev/null +++ b/sentinel-adapter/sentinel-apache-httpclient-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/apache/httpclient/fallback/ApacheHttpClientFallbackTest.java @@ -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); + } +} diff --git a/sentinel-demo/pom.xml b/sentinel-demo/pom.xml index 62a3937c..280d486e 100755 --- a/sentinel-demo/pom.xml +++ b/sentinel-demo/pom.xml @@ -32,6 +32,7 @@ sentinel-demo-command-handler sentinel-demo-spring-webflux sentinel-demo-apache-dubbo + sentinel-demo-apache-httpclient sentinel-demo-sofa-rpc sentinel-demo-spring-cloud-gateway sentinel-demo-zuul-gateway diff --git a/sentinel-demo/sentinel-demo-apache-httpclient/pom.xml b/sentinel-demo/sentinel-demo-apache-httpclient/pom.xml new file mode 100644 index 00000000..b83a5faf --- /dev/null +++ b/sentinel-demo/sentinel-demo-apache-httpclient/pom.xml @@ -0,0 +1,52 @@ + + + + sentinel-demo + com.alibaba.csp + 1.8.0-SNAPSHOT + + 4.0.0 + + sentinel-demo-apache-httpclient + + + 2.1.3.RELEASE + 4.3 + 4.5.6 + + + + + com.alibaba.csp + sentinel-core + + + com.alibaba.csp + sentinel-transport-simple-http + + + com.alibaba.csp + sentinel-apache-httpclient-adapter + ${project.version} + + + org.springframework.boot + spring-boot-starter-web + ${spring.boot.version} + + + org.springframework.boot + spring-boot-starter-test + ${spring.boot.version} + + + + org.apache.httpcomponents + httpclient + ${apache.httpclient.version} + + + + \ No newline at end of file diff --git a/sentinel-demo/sentinel-demo-apache-httpclient/src/main/java/com/alibaba/csp/sentinel/demo/apache/httpclient/ApacheHttpClientDemoApplication.java b/sentinel-demo/sentinel-demo-apache-httpclient/src/main/java/com/alibaba/csp/sentinel/demo/apache/httpclient/ApacheHttpClientDemoApplication.java new file mode 100644 index 00000000..797e8e1b --- /dev/null +++ b/sentinel-demo/sentinel-demo-apache-httpclient/src/main/java/com/alibaba/csp/sentinel/demo/apache/httpclient/ApacheHttpClientDemoApplication.java @@ -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) { + } +} diff --git a/sentinel-demo/sentinel-demo-apache-httpclient/src/main/java/com/alibaba/csp/sentinel/demo/apache/httpclient/controller/ApacheHttpClientTestController.java b/sentinel-demo/sentinel-demo-apache-httpclient/src/main/java/com/alibaba/csp/sentinel/demo/apache/httpclient/controller/ApacheHttpClientTestController.java new file mode 100644 index 00000000..3b1bf897 --- /dev/null +++ b/sentinel-demo/sentinel-demo-apache-httpclient/src/main/java/com/alibaba/csp/sentinel/demo/apache/httpclient/controller/ApacheHttpClientTestController.java @@ -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; + } +} diff --git a/sentinel-demo/sentinel-demo-apache-httpclient/src/main/resources/application.properties b/sentinel-demo/sentinel-demo-apache-httpclient/src/main/resources/application.properties new file mode 100644 index 00000000..e3e48949 --- /dev/null +++ b/sentinel-demo/sentinel-demo-apache-httpclient/src/main/resources/application.properties @@ -0,0 +1,2 @@ +spring.application.name=sentinel-demo-apache-httpclient +server.port=8083 \ No newline at end of file