Skip to content

Commit

Permalink
support jax-rs
Browse files Browse the repository at this point in the history
  • Loading branch information
seasidesky committed Apr 13, 2020
1 parent a0196b6 commit ca91234
Show file tree
Hide file tree
Showing 24 changed files with 1,407 additions and 0 deletions.
1 change: 1 addition & 0 deletions sentinel-adapter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<module>sentinel-spring-cloud-gateway-adapter</module>
<module>sentinel-spring-webmvc-adapter</module>
<module>sentinel-zuul2-adapter</module>
<module>sentinel-jax-rs-adapter</module>
</modules>

<dependencyManagement>
Expand Down
41 changes: 41 additions & 0 deletions sentinel-adapter/sentinel-jax-rs-adapter/README.md
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`.
74 changes: 74 additions & 0 deletions sentinel-adapter/sentinel-jax-rs-adapter/pom.xml
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,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();
}
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() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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.fallback;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

/**
* @author sea
*/
public class DefaultSentinelJaxRsFallback implements SentinelJaxRsFallback {
@Override
public Response fallbackResponse(String route, Throwable cause) {
return Response.status(Response.Status.TOO_MANY_REQUESTS)
.entity("Blocked by Sentinel (flow limiting)")
.type(MediaType.APPLICATION_JSON_TYPE)
.build();
}
}
Loading

0 comments on commit ca91234

Please sign in to comment.