-
Notifications
You must be signed in to change notification settings - Fork 8.1k
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
support jax-rs #1396
support jax-rs #1396
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# Sentinel for jax-rs | ||
|
||
Sentinel provides filter integration 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-jax-rs-adapter</artifactId> | ||
<version>x.y.z</version> | ||
</dependency> | ||
``` | ||
|
||
the `SentinelJaxRsProviderFilter` is auto activated in pure jax-rs application. | ||
|
||
For Spring web applications you can configure with Spring bean: | ||
|
||
```java | ||
@Configuration | ||
public class FilterConfig { | ||
|
||
@Bean | ||
public SentinelJaxRsProviderFilter sentinelJaxRsProviderFilter() { | ||
return new SentinelJaxRsProviderFilter(); | ||
} | ||
} | ||
``` | ||
|
||
For jax-rs client, register `SentinelJaxRsClientFilter` when build Client | ||
|
||
``` | ||
Client client = ClientBuilder.newClient() | ||
.register(new SentinelJaxRsClientFilter()); | ||
``` | ||
|
||
When a request is blocked, Sentinel jax-rs filter will return Response with status of TOO_MANY_REQUESTS indicating the request is rejected. | ||
|
||
You can customize it by implement your own `SentinelJaxRsFallback` and register to `SentinelJaxRsConfig`. | ||
|
||
The `RequestOriginParser` interface is useful for extracting request origin (e.g. IP or appName from HTTP Header) | ||
from HTTP request. You can implement your own `RequestOriginParser` and register to `SentinelJaxRsConfig`. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
<?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"> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<parent> | ||
<groupId>com.alibaba.csp</groupId> | ||
<artifactId>sentinel-adapter</artifactId> | ||
<version>1.8.0-SNAPSHOT</version> | ||
</parent> | ||
|
||
<artifactId>sentinel-jax-rs-adapter</artifactId> | ||
<packaging>jar</packaging> | ||
|
||
<properties> | ||
<javax.ws.rs-api.version>2.1.1</javax.ws.rs-api.version> | ||
</properties> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>com.alibaba.csp</groupId> | ||
<artifactId>sentinel-core</artifactId> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>javax.ws.rs</groupId> | ||
<artifactId>javax.ws.rs-api</artifactId> | ||
<version>${javax.ws.rs-api.version}</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>junit</groupId> | ||
<artifactId>junit</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-web</artifactId> | ||
<version>2.2.6.RELEASE</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-test</artifactId> | ||
<version>2.2.6.RELEASE</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>io.rest-assured</groupId> | ||
<artifactId>rest-assured</artifactId> | ||
<version>4.3.0</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.jboss.resteasy</groupId> | ||
<artifactId>resteasy-spring-boot-starter</artifactId> | ||
<version>4.5.1.Final</version> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-surefire-plugin</artifactId> | ||
<configuration> | ||
<forkMode>always</forkMode> | ||
</configuration> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
</project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/* | ||
* 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 | ||
* | ||
* https://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.jaxrs; | ||
|
||
|
||
import javax.ws.rs.core.Response; | ||
import javax.ws.rs.ext.ExceptionMapper; | ||
import javax.ws.rs.ext.Provider; | ||
|
||
/** | ||
* sentinel jax-rs adapter provide this exception mapper | ||
* in case of user throw exception which is not {@link javax.ws.rs.WebApplicationException} and not matched by any ExceptionMapper | ||
* this exception mapper convert exception to Response let ContainerResponseFilter to be called to exit sentinel entry | ||
* user can add custom ExceptionMapper and config with {@link javax.annotation.Priority} with lower value | ||
* @author sea | ||
*/ | ||
@Provider | ||
public class DefaultExceptionMapper implements ExceptionMapper<Throwable> { | ||
|
||
@Override | ||
public Response toResponse(Throwable exception) { | ||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR) | ||
.entity(exception.getMessage()) | ||
.build(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
/* | ||
* 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 | ||
* | ||
* https://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.jaxrs; | ||
|
||
import com.alibaba.csp.sentinel.Entry; | ||
import com.alibaba.csp.sentinel.EntryType; | ||
import com.alibaba.csp.sentinel.ResourceTypeConstants; | ||
import com.alibaba.csp.sentinel.SphU; | ||
import com.alibaba.csp.sentinel.adapter.jaxrs.config.SentinelJaxRsConfig; | ||
import com.alibaba.csp.sentinel.context.ContextUtil; | ||
import com.alibaba.csp.sentinel.slots.block.BlockException; | ||
import com.alibaba.csp.sentinel.util.StringUtil; | ||
|
||
import javax.ws.rs.client.ClientRequestContext; | ||
import javax.ws.rs.client.ClientRequestFilter; | ||
import javax.ws.rs.client.ClientResponseContext; | ||
import javax.ws.rs.client.ClientResponseFilter; | ||
import javax.ws.rs.container.ResourceInfo; | ||
import javax.ws.rs.core.Context; | ||
import java.io.IOException; | ||
|
||
/** | ||
* @author sea | ||
*/ | ||
public class SentinelJaxRsClientFilter implements ClientRequestFilter, ClientResponseFilter { | ||
|
||
private static final String SENTINEL_JAX_RS_CLIENT_ENTRY_PROPERTY = "sentinel_jax_rs_client_entry_property"; | ||
|
||
@Context | ||
private ResourceInfo resourceInfo; | ||
|
||
|
||
@Override | ||
public void filter(ClientRequestContext requestContext) throws IOException { | ||
try { | ||
String resourceName = getResourceName(requestContext); | ||
|
||
if (StringUtil.isNotEmpty(resourceName)) { | ||
Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.OUT); | ||
|
||
requestContext.setProperty(SENTINEL_JAX_RS_CLIENT_ENTRY_PROPERTY, entry); | ||
} | ||
} catch (BlockException e) { | ||
try { | ||
requestContext.abortWith(SentinelJaxRsConfig.getJaxRsFallback().fallbackResponse(requestContext.getUri().toString(), e)); | ||
} finally { | ||
ContextUtil.exit(); | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException { | ||
Entry entry = (Entry) requestContext.getProperty(SENTINEL_JAX_RS_CLIENT_ENTRY_PROPERTY); | ||
if (entry != null) { | ||
entry.exit(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will the response filter method still be executed when unexpected error occurs? We need to guarantee the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for provider sideaccording to
if WebApplicationException or its subclasses is thrown, there are automated converted to Response and can enter response filter. if throw exception which is not WebApplicationException or its subclass, and not matched by custom exception mapper, then the response filter cannot be called. for this case, I thank a default exception mapper maybe introduced. according to
in case of misuse, maybe a default exception mapper of if user also provide custom exception mapper of as describe in
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are there any similar problems on client side? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. client side may have similar problems, according to
I introduce |
||
} | ||
requestContext.removeProperty(SENTINEL_JAX_RS_CLIENT_ENTRY_PROPERTY); | ||
} | ||
|
||
public String getResourceName(ClientRequestContext requestContext) { | ||
return SentinelJaxRsConfig.getResourceNameParser().parse(requestContext); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/* | ||
* 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 | ||
* | ||
* https://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.jaxrs; | ||
|
||
import com.alibaba.csp.sentinel.*; | ||
import com.alibaba.csp.sentinel.adapter.jaxrs.config.SentinelJaxRsConfig; | ||
import com.alibaba.csp.sentinel.context.ContextUtil; | ||
import com.alibaba.csp.sentinel.slots.block.BlockException; | ||
import com.alibaba.csp.sentinel.util.StringUtil; | ||
|
||
import javax.ws.rs.container.*; | ||
import javax.ws.rs.core.Context; | ||
import javax.ws.rs.ext.Provider; | ||
import java.io.IOException; | ||
|
||
/** | ||
* @author sea | ||
*/ | ||
@Provider | ||
public class SentinelJaxRsProviderFilter implements ContainerRequestFilter, ContainerResponseFilter { | ||
|
||
private static final String SENTINEL_JAX_RS_PROVIDER_CONTEXT_NAME = "sentinel_jax_rs_provider_context"; | ||
|
||
|
||
private static final String SENTINEL_JAX_RS_PROVIDER_ENTRY_PROPERTY = "sentinel_jax_rs_provider_entry_property"; | ||
|
||
@Context | ||
private ResourceInfo resourceInfo; | ||
|
||
@Override | ||
public void filter(ContainerRequestContext containerRequestContext) throws IOException { | ||
|
||
try { | ||
String resourceName = getResourceName(containerRequestContext, resourceInfo); | ||
|
||
if (StringUtil.isNotEmpty(resourceName)) { | ||
// Parse the request origin using registered origin parser. | ||
String origin = parseOrigin(containerRequestContext); | ||
String contextName = getContextName(containerRequestContext); | ||
ContextUtil.enter(contextName, origin); | ||
Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN); | ||
|
||
containerRequestContext.setProperty(SENTINEL_JAX_RS_PROVIDER_ENTRY_PROPERTY, entry); | ||
} | ||
} catch (BlockException e) { | ||
try { | ||
containerRequestContext.abortWith(SentinelJaxRsConfig.getJaxRsFallback().fallbackResponse(containerRequestContext.getUriInfo().getPath(), e)); | ||
} finally { | ||
ContextUtil.exit(); | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public void filter(ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext) throws IOException { | ||
Entry entry = (Entry) containerRequestContext.getProperty(SENTINEL_JAX_RS_PROVIDER_ENTRY_PROPERTY); | ||
if (entry != null) { | ||
entry.exit(); | ||
} | ||
containerRequestContext.removeProperty(SENTINEL_JAX_RS_PROVIDER_ENTRY_PROPERTY); | ||
ContextUtil.exit(); | ||
} | ||
|
||
public String getResourceName(ContainerRequestContext containerRequestContext, ResourceInfo resourceInfo) { | ||
return SentinelJaxRsConfig.getResourceNameParser().parse(containerRequestContext, resourceInfo); | ||
} | ||
|
||
protected String getContextName(ContainerRequestContext request) { | ||
return SENTINEL_JAX_RS_PROVIDER_CONTEXT_NAME; | ||
} | ||
|
||
protected String parseOrigin(ContainerRequestContext request) { | ||
return SentinelJaxRsConfig.getRequestOriginParser().parseOrigin(request); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/* | ||
* 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 | ||
* | ||
* https://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.jaxrs.config; | ||
|
||
import com.alibaba.csp.sentinel.adapter.jaxrs.fallback.DefaultSentinelJaxRsFallback; | ||
import com.alibaba.csp.sentinel.adapter.jaxrs.fallback.SentinelJaxRsFallback; | ||
import com.alibaba.csp.sentinel.adapter.jaxrs.request.DefaultRequestOriginParser; | ||
import com.alibaba.csp.sentinel.adapter.jaxrs.request.DefaultResourceNameParser; | ||
import com.alibaba.csp.sentinel.adapter.jaxrs.request.RequestOriginParser; | ||
import com.alibaba.csp.sentinel.adapter.jaxrs.request.ResourceNameParser; | ||
|
||
/** | ||
* @author sea | ||
*/ | ||
public class SentinelJaxRsConfig { | ||
|
||
private static volatile ResourceNameParser resourceNameParser = new DefaultResourceNameParser(); | ||
|
||
private static volatile RequestOriginParser requestOriginParser = new DefaultRequestOriginParser(); | ||
|
||
private static volatile SentinelJaxRsFallback jaxRsFallback = new DefaultSentinelJaxRsFallback(); | ||
|
||
public static ResourceNameParser getResourceNameParser() { | ||
return resourceNameParser; | ||
} | ||
|
||
public static void setResourceNameParser(ResourceNameParser resourceNameParser) { | ||
SentinelJaxRsConfig.resourceNameParser = resourceNameParser; | ||
} | ||
|
||
public static RequestOriginParser getRequestOriginParser() { | ||
return requestOriginParser; | ||
} | ||
|
||
public static void setRequestOriginParser(RequestOriginParser originParser) { | ||
SentinelJaxRsConfig.requestOriginParser = originParser; | ||
} | ||
|
||
public static SentinelJaxRsFallback getJaxRsFallback() { | ||
return jaxRsFallback; | ||
} | ||
|
||
public static void setJaxRsFallback(SentinelJaxRsFallback jaxRsFallback) { | ||
SentinelJaxRsConfig.jaxRsFallback = jaxRsFallback; | ||
} | ||
|
||
private SentinelJaxRsConfig() { | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For client side we don't need to exit the context here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed, in the latest commit, the original SentinelJaxRsClientFilter is replaced by SentinelJaxRsClientTemplate to handle the exception and exit entry.