Skip to content

Add Apache HttpClient integration #1455

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sentinel-adapter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<module>sentinel-web-servlet</module>
<module>sentinel-dubbo-adapter</module>
<module>sentinel-apache-dubbo-adapter</module>
<module>sentinel-apache-httpclient-adapter</module>
<module>sentinel-sofa-rpc-adapter</module>
<module>sentinel-grpc-adapter</module>
<module>sentinel-zuul-adapter</module>
Expand Down
75 changes: 75 additions & 0 deletions sentinel-adapter/sentinel-apache-httpclient-adapter/README.md
Original file line number Diff line number Diff line change
@@ -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);
}
}
```
69 changes: 69 additions & 0 deletions sentinel-adapter/sentinel-apache-httpclient-adapter/pom.xml
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
};
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
Loading