From fda21de74855c2d3f83d958884e77eaf0b1049d6 Mon Sep 17 00:00:00 2001 From: seasidesky <62706379+seasidesky@users.noreply.github.com> Date: Mon, 15 Jun 2020 10:21:21 +0800 Subject: [PATCH] Add Sentinel annotation and JAX-RS plugins for Quarkus (#1542) * Add sentinel-quarkus-adapter module, which provides sentinel-annotation-quarkus-adapter and sentinel-jax-rs-quarkus-adapter to adapt sentinel-annotation-cdi-interceptor and sentinel-jax-rs-adapter for Quarkus. It also provides sentinel-native-image-quarkus-adapter to support running Sentinel with Quarkus in native image mode. --- sentinel-adapter/pom.xml | 1 + .../sentinel-quarkus-adapter/README.md | 86 +++++++ .../sentinel-quarkus-adapter/pom.xml | 55 ++++ .../pom.xml | 72 ++++++ ...inelAnnotationQuarkusAdapterProcessor.java | 46 ++++ .../adapter/deployment/FooService.java | 84 ++++++ .../quarkus/adapter/deployment/FooUtil.java | 37 +++ .../SentinelAnnotationQuarkusAdapterTest.java | 197 ++++++++++++++ .../pom.xml | 63 +++++ .../resources/META-INF/quarkus-extension.yaml | 11 + .../pom.xml | 77 ++++++ .../SentinelJaxRsQuarkusAdapterProcessor.java | 37 +++ .../SentinelJaxRsQuarkusAdapterTest.java | 242 ++++++++++++++++++ .../adapter/deployment/TestResource.java | 90 +++++++ .../pom.xml | 63 +++++ .../resources/META-INF/quarkus-extension.yaml | 11 + .../services/javax.ws.rs.ext.Providers | 2 + .../pom.xml | 60 +++++ .../SentinelNativeImageProcessor.java | 73 ++++++ .../pom.xml | 75 ++++++ .../nativeimage/SentinelRecorder.java | 52 ++++ .../resources/META-INF/quarkus-extension.yaml | 12 + 22 files changed, 1446 insertions(+) create mode 100644 sentinel-adapter/sentinel-quarkus-adapter/README.md create mode 100755 sentinel-adapter/sentinel-quarkus-adapter/pom.xml create mode 100644 sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-deployment/pom.xml create mode 100644 sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-deployment/src/main/java/com/alibaba/csp/sentinel/annotation/quarkus/adapter/deployment/SentinelAnnotationQuarkusAdapterProcessor.java create mode 100644 sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-deployment/src/test/java/com/alibaba/csp/sentinel/annotation/quarkus/adapter/deployment/FooService.java create mode 100644 sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-deployment/src/test/java/com/alibaba/csp/sentinel/annotation/quarkus/adapter/deployment/FooUtil.java create mode 100644 sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-deployment/src/test/java/com/alibaba/csp/sentinel/annotation/quarkus/adapter/deployment/SentinelAnnotationQuarkusAdapterTest.java create mode 100644 sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-runtime/pom.xml create mode 100644 sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-runtime/src/main/resources/META-INF/quarkus-extension.yaml create mode 100644 sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-deployment/pom.xml create mode 100644 sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-deployment/src/main/java/com/alibaba/csp/sentinel/jaxrs/quarkus/adapter/deployment/SentinelJaxRsQuarkusAdapterProcessor.java create mode 100644 sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-deployment/src/test/java/com/alibaba/csp/sentinel/jaxrs/quarkus/adapter/deployment/SentinelJaxRsQuarkusAdapterTest.java create mode 100644 sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-deployment/src/test/java/com/alibaba/csp/sentinel/jaxrs/quarkus/adapter/deployment/TestResource.java create mode 100644 sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-runtime/pom.xml create mode 100644 sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-runtime/src/main/resources/META-INF/quarkus-extension.yaml create mode 100644 sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-runtime/src/main/resources/META-INF/services/javax.ws.rs.ext.Providers create mode 100644 sentinel-adapter/sentinel-quarkus-adapter/sentinel-native-image-quarkus-adapter-deployment/pom.xml create mode 100644 sentinel-adapter/sentinel-quarkus-adapter/sentinel-native-image-quarkus-adapter-deployment/src/main/java/com/alibaba/csp/sentinel/nativeimage/SentinelNativeImageProcessor.java create mode 100644 sentinel-adapter/sentinel-quarkus-adapter/sentinel-native-image-quarkus-adapter-runtime/pom.xml create mode 100644 sentinel-adapter/sentinel-quarkus-adapter/sentinel-native-image-quarkus-adapter-runtime/src/main/java/com/alibaba/csp/sentinel/nativeimage/SentinelRecorder.java create mode 100644 sentinel-adapter/sentinel-quarkus-adapter/sentinel-native-image-quarkus-adapter-runtime/src/main/resources/META-INF/quarkus-extension.yaml diff --git a/sentinel-adapter/pom.xml b/sentinel-adapter/pom.xml index 32965816..d7a973f0 100755 --- a/sentinel-adapter/pom.xml +++ b/sentinel-adapter/pom.xml @@ -29,6 +29,7 @@ sentinel-zuul2-adapter sentinel-okhttp-adapter sentinel-jax-rs-adapter + sentinel-quarkus-adapter diff --git a/sentinel-adapter/sentinel-quarkus-adapter/README.md b/sentinel-adapter/sentinel-quarkus-adapter/README.md new file mode 100644 index 00000000..4c8794e0 --- /dev/null +++ b/sentinel-adapter/sentinel-quarkus-adapter/README.md @@ -0,0 +1,86 @@ +# sentinel quarkus adapter + +sentinel quarkus adapter provides `sentinel-annotation-quarkus-adapter` and `sentinel-jax-rs-quarkus-adapter` to adapt `sentinel-annotation-cdi-interceptor` and `sentinel-jax-rs-adapter` for quarkus + +sentinel quarkus adapter also provides `sentinel-native-image-quarkus-adapter` to support running sentinel with quarkus in native image mode. + +To use sentinel-jax-rs-quarkus-adapter, you can simply add the following dependency to your `pom.xml`: + +```xml + + com.alibaba.csp + sentinel-jax-rs-quarkus-adapter + x.y.z + +``` + +To use sentinel-annotation-quarkus-adapter, you can simply add the following dependency to your `pom.xml`: + +```xml + + com.alibaba.csp + sentinel-annotation-quarkus-adapter + x.y.z + +``` + +if your quarkus application want to use both `sentinel-annotation-quarkus-adapter` and `sentinel-jax-rs-quarkus-adapter` , then add these two dependency together to your `pom.xml`: + +```xml + + com.alibaba.csp + sentinel-jax-rs-quarkus-adapter + x.y.z + + + com.alibaba.csp + sentinel-annotation-quarkus-adapter + x.y.z + +``` + +when quarkus application started, you can see the enabled feature like: + +``` +INFO [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, resteasy, sentinel-annotation, sentinel-jax-rs] +``` + +## for quarkus native image + +if you want to make sentinel with quarkus running in native image mode, you should add the following dependency to your `pom.xml`: + +```xml + + com.alibaba.csp + sentinel-native-image-quarkus-adapter + x.y.z + +``` + +and then add `--allow-incomplete-classpath` to `quarkus.native.additional-build-args`. + +if you use `sentinel-jax-rs-quarkus-adapter` you should set `quarkus.native.auto-service-loader-registration` to true. + +you can refer to `sentinel-demo-quarkus`'s `pom.xml` for more details. + +when quarkus application started, you can see the enabled feature like: + +``` +INFO [io.quarkus] (main) Installed features: [cdi, resteasy, sentinel-annotation, sentinel-jax-rs, sentinel-native-image] +``` + +### notes for limitations + +`sentinel-native-image-quarkus-adapter` currently rely on `sentinel-logging-slf4j` to make sentinel run in native image mode easily, because `quarkus-core` provides `Target_org_slf4j_LoggerFactory` to substitue `getLogger` method. + +currently `sentinel-transport-simple-http` can work in native image mode, while `sentinel-transport-netty-http` cannot work in native image mode without extra config or substitutions. + +## references for build native image or AOT + +- [Quarkus - Tips for writing native applications](https://quarkus.io/guides/writing-native-applications-tips) + +- [Quarkus - Class Loading Reference](https://quarkus.io/guides/class-loading-reference) + +- [substratevm LIMITATIONS](https://github.com/oracle/graal/blob/master/substratevm/LIMITATIONS.md) + +- [Accessing resources in Substrate VM images](https://github.com/oracle/graal/blob/master/substratevm/RESOURCES.md) diff --git a/sentinel-adapter/sentinel-quarkus-adapter/pom.xml b/sentinel-adapter/sentinel-quarkus-adapter/pom.xml new file mode 100755 index 00000000..d389539e --- /dev/null +++ b/sentinel-adapter/sentinel-quarkus-adapter/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + + com.alibaba.csp + sentinel-adapter + 1.8.0-SNAPSHOT + + + sentinel-quarkus-adapter-parent + pom + + + UTF-8 + UTF-8 + 1.8 + 1.8 + true + 1.4.1.Final + 3.8.1 + + + + sentinel-annotation-quarkus-adapter-deployment + sentinel-annotation-quarkus-adapter-runtime + sentinel-jax-rs-quarkus-adapter-deployment + sentinel-jax-rs-quarkus-adapter-runtime + sentinel-native-image-quarkus-adapter-deployment + sentinel-native-image-quarkus-adapter-runtime + + + + + io.quarkus + quarkus-bom-deployment + ${quarkus.version} + pom + import + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${compiler-plugin.version} + + + + + diff --git a/sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-deployment/pom.xml b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-deployment/pom.xml new file mode 100644 index 00000000..008701c3 --- /dev/null +++ b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-deployment/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + com.alibaba.csp + sentinel-quarkus-adapter-parent + 1.8.0-SNAPSHOT + ../pom.xml + + + sentinel-annotation-quarkus-adapter-deployment + sentinel-annotation-quarkus-adapter-deployment + + + 1.8 + 1.8 + + + + + io.quarkus + quarkus-core-deployment + + + io.quarkus + quarkus-arc-deployment + + + com.alibaba.csp + sentinel-annotation-quarkus-adapter + ${project.version} + + + + + io.quarkus + quarkus-junit5-internal + test + + + org.assertj + assertj-core + test + + + io.quarkus + quarkus-arc-deployment + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + + diff --git a/sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-deployment/src/main/java/com/alibaba/csp/sentinel/annotation/quarkus/adapter/deployment/SentinelAnnotationQuarkusAdapterProcessor.java b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-deployment/src/main/java/com/alibaba/csp/sentinel/annotation/quarkus/adapter/deployment/SentinelAnnotationQuarkusAdapterProcessor.java new file mode 100644 index 00000000..3e958fa1 --- /dev/null +++ b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-deployment/src/main/java/com/alibaba/csp/sentinel/annotation/quarkus/adapter/deployment/SentinelAnnotationQuarkusAdapterProcessor.java @@ -0,0 +1,46 @@ +/* + * 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.annotation.quarkus.adapter.deployment; + +import com.alibaba.csp.sentinel.annotation.cdi.interceptor.SentinelResourceInterceptor; +import io.quarkus.arc.deployment.AdditionalBeanBuildItem; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.FeatureBuildItem; + +import java.util.Arrays; +import java.util.List; + +/** + * @author sea + */ +class SentinelAnnotationQuarkusAdapterProcessor { + + private static final String FEATURE_ANNOTATION = "sentinel-annotation"; + + @BuildStep + void feature(BuildProducer featureProducer) { + featureProducer.produce(new FeatureBuildItem(FEATURE_ANNOTATION)); + } + + @BuildStep + List additionalBeans() { + return Arrays.asList( + new AdditionalBeanBuildItem(SentinelResourceInterceptor.class) + ); + } + +} diff --git a/sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-deployment/src/test/java/com/alibaba/csp/sentinel/annotation/quarkus/adapter/deployment/FooService.java b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-deployment/src/test/java/com/alibaba/csp/sentinel/annotation/quarkus/adapter/deployment/FooService.java new file mode 100644 index 00000000..1115a9e3 --- /dev/null +++ b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-deployment/src/test/java/com/alibaba/csp/sentinel/annotation/quarkus/adapter/deployment/FooService.java @@ -0,0 +1,84 @@ +/* + * 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.annotation.quarkus.adapter.deployment; +import com.alibaba.csp.sentinel.annotation.cdi.interceptor.SentinelResourceBinding; +import com.alibaba.csp.sentinel.slots.block.BlockException; + +import javax.enterprise.context.ApplicationScoped; +import java.util.concurrent.ThreadLocalRandom; + +/** + * @author Eric Zhao + * @author sea + */ +@ApplicationScoped +public class FooService { + + @SentinelResourceBinding(value = "apiFoo", blockHandler = "fooBlockHandler", + exceptionsToTrace = {IllegalArgumentException.class}) + public String foo(int i) throws Exception { + if (i == 5758) { + throw new IllegalAccessException(); + } + if (i == 5763) { + throw new IllegalArgumentException(); + } + return "Hello for " + i; + } + + @SentinelResourceBinding(value = "apiFooWithFallback", blockHandler = "fooBlockHandler", fallback = "fooFallbackFunc", + exceptionsToTrace = {IllegalArgumentException.class}) + public String fooWithFallback(int i) throws Exception { + if (i == 5758) { + throw new IllegalAccessException(); + } + if (i == 5763) { + throw new IllegalArgumentException(); + } + return "Hello for " + i; + } + + @SentinelResourceBinding(value = "apiAnotherFooWithDefaultFallback", defaultFallback = "globalDefaultFallback", + fallbackClass = {FooUtil.class}) + public String anotherFoo(int i) { + if (i == 5758) { + throw new IllegalArgumentException("oops"); + } + return "Hello for " + i; + } + + @SentinelResourceBinding(blockHandler = "globalBlockHandler", blockHandlerClass = FooUtil.class) + public int random() { + return ThreadLocalRandom.current().nextInt(0, 30000); + } + + @SentinelResourceBinding(value = "apiBaz", blockHandler = "bazBlockHandler", + exceptionsToIgnore = {IllegalMonitorStateException.class}) + public String baz(String name) { + if (name.equals("fail")) { + throw new IllegalMonitorStateException("boom!"); + } + return "cheers, " + name; + } + + public String fooBlockHandler(int i, BlockException ex) { + return "Oops, " + i; + } + + public String fooFallbackFunc(int i) { + return "eee..."; + } +} diff --git a/sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-deployment/src/test/java/com/alibaba/csp/sentinel/annotation/quarkus/adapter/deployment/FooUtil.java b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-deployment/src/test/java/com/alibaba/csp/sentinel/annotation/quarkus/adapter/deployment/FooUtil.java new file mode 100644 index 00000000..38fd6874 --- /dev/null +++ b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-deployment/src/test/java/com/alibaba/csp/sentinel/annotation/quarkus/adapter/deployment/FooUtil.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.annotation.quarkus.adapter.deployment; + +import com.alibaba.csp.sentinel.slots.block.BlockException; + +/** + * @author Eric Zhao + */ +public class FooUtil { + + public static final int BLOCK_FLAG = 88888; + public static final String FALLBACK_DEFAULT_RESULT = "fallback"; + + public static int globalBlockHandler(BlockException ex) { + System.out.println("Oops: " + ex.getClass().getSimpleName()); + return BLOCK_FLAG; + } + + public static String globalDefaultFallback(Throwable t) { + System.out.println("Fallback caught: " + t.getClass().getSimpleName()); + return FALLBACK_DEFAULT_RESULT; + } +} diff --git a/sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-deployment/src/test/java/com/alibaba/csp/sentinel/annotation/quarkus/adapter/deployment/SentinelAnnotationQuarkusAdapterTest.java b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-deployment/src/test/java/com/alibaba/csp/sentinel/annotation/quarkus/adapter/deployment/SentinelAnnotationQuarkusAdapterTest.java new file mode 100644 index 00000000..6747a519 --- /dev/null +++ b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-deployment/src/test/java/com/alibaba/csp/sentinel/annotation/quarkus/adapter/deployment/SentinelAnnotationQuarkusAdapterTest.java @@ -0,0 +1,197 @@ +/* + * 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.annotation.quarkus.adapter.deployment; + +import com.alibaba.csp.sentinel.node.ClusterNode; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; +import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot; +import com.alibaba.csp.sentinel.util.MethodUtil; +import io.quarkus.arc.ArcUndeclaredThrowableException; +import io.quarkus.test.QuarkusUnitTest; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + +/** + * @author sea + */ +public class SentinelAnnotationQuarkusAdapterTest { + + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap + .create(JavaArchive.class) + .addClasses(FooService.class, FooUtil.class) + .addPackage("com.alibaba.csp.sentinel.annotation.cdi.interceptor") + ); + + @Inject + FooService fooService; + + @BeforeEach + public void setUp() throws Exception { + FlowRuleManager.loadRules(new ArrayList()); + ClusterBuilderSlot.resetClusterNodes(); + } + + @AfterEach + public void tearDown() throws Exception { + FlowRuleManager.loadRules(new ArrayList()); + ClusterBuilderSlot.resetClusterNodes(); + } + + @Test + public void testForeignBlockHandlerClass() throws Exception { + assertThat(fooService.random()).isNotEqualTo(FooUtil.BLOCK_FLAG); + String resourceName = MethodUtil.resolveMethodName(FooService.class.getDeclaredMethod("random")); + ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName); + assertThat(cn).isNotNull(); + assertThat(cn.passQps()).isPositive(); + + FlowRuleManager.loadRules(Collections.singletonList( + new FlowRule(resourceName).setCount(0) + )); + assertThat(fooService.random()).isEqualTo(FooUtil.BLOCK_FLAG); + assertThat(cn.blockQps()).isPositive(); + } + + @Test + public void testBlockHandlerNotFound() { + assertThat(fooService.baz("Sentinel")).isEqualTo("cheers, Sentinel"); + String resourceName = "apiBaz"; + ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName); + assertThat(cn).isNotNull(); + assertThat(cn.passQps()).isPositive(); + + FlowRuleManager.loadRules(Collections.singletonList( + new FlowRule(resourceName).setCount(0) + )); + + Assertions.assertThrows(ArcUndeclaredThrowableException.class, () -> { + fooService.baz("Sentinel"); + }); + } + + @Test + public void testAnnotationExceptionsToIgnore() { + assertThat(fooService.baz("Sentinel")).isEqualTo("cheers, Sentinel"); + String resourceName = "apiBaz"; + ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName); + assertThat(cn).isNotNull(); + assertThat(cn.passQps()).isPositive(); + + try { + fooService.baz("fail"); + fail("should not reach here"); + } catch (IllegalMonitorStateException ex) { + assertThat(cn.exceptionQps()).isZero(); + } + } + + @Test + public void testFallbackWithNoParams() throws Exception { + assertThat(fooService.fooWithFallback(1)).isEqualTo("Hello for 1"); + String resourceName = "apiFooWithFallback"; + ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName); + assertThat(cn).isNotNull(); + assertThat(cn.passQps()).isPositive(); + + // Fallback should be ignored for this. + try { + fooService.fooWithFallback(5758); + fail("should not reach here"); + } catch (IllegalAccessException e) { + assertThat(cn.exceptionQps()).isZero(); + } + + // Fallback should take effect. + assertThat(fooService.fooWithFallback(5763)).isEqualTo("eee..."); + assertThat(cn.exceptionQps()).isPositive(); + assertThat(cn.blockQps()).isZero(); + + FlowRuleManager.loadRules(Collections.singletonList( + new FlowRule(resourceName).setCount(0) + )); + // Fallback should not take effect for BlockException, as blockHandler is configured. + assertThat(fooService.fooWithFallback(2221)).isEqualTo("Oops, 2221"); + assertThat(cn.blockQps()).isPositive(); + } + + @Test + public void testDefaultFallbackWithSingleParam() { + assertThat(fooService.anotherFoo(1)).isEqualTo("Hello for 1"); + String resourceName = "apiAnotherFooWithDefaultFallback"; + ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName); + assertThat(cn).isNotNull(); + assertThat(cn.passQps()).isPositive(); + + // Default fallback should take effect. + assertThat(fooService.anotherFoo(5758)).isEqualTo(FooUtil.FALLBACK_DEFAULT_RESULT); + assertThat(cn.exceptionQps()).isPositive(); + assertThat(cn.blockQps()).isZero(); + + FlowRuleManager.loadRules(Collections.singletonList( + new FlowRule(resourceName).setCount(0) + )); + // Default fallback should also take effect for BlockException. + assertThat(fooService.anotherFoo(5758)).isEqualTo(FooUtil.FALLBACK_DEFAULT_RESULT); + assertThat(cn.blockQps()).isPositive(); + } + + @Test + public void testNormalBlockHandlerAndFallback() throws Exception { + assertThat(fooService.foo(1)).isEqualTo("Hello for 1"); + String resourceName = "apiFoo"; + ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName); + assertThat(cn).isNotNull(); + assertThat(cn.passQps()).isPositive(); + + // Test for biz exception. + try { + fooService.foo(5758); + fail("should not reach here"); + } catch (Exception ex) { + // Should not be traced. + assertThat(cn.exceptionQps()).isZero(); + } + + try { + fooService.foo(5763); + fail("should not reach here"); + } catch (Exception ex) { + assertThat(cn.exceptionQps()).isPositive(); + } + + // Test for blockHandler + FlowRuleManager.loadRules(Collections.singletonList( + new FlowRule(resourceName).setCount(0) + )); + assertThat(fooService.foo(1121)).isEqualTo("Oops, 1121"); + assertThat(cn.blockQps()).isPositive(); + } +} diff --git a/sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-runtime/pom.xml b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-runtime/pom.xml new file mode 100644 index 00000000..e58434ad --- /dev/null +++ b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-runtime/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + + com.alibaba.csp + sentinel-quarkus-adapter-parent + 1.8.0-SNAPSHOT + ../pom.xml + + + sentinel-annotation-quarkus-adapter + sentinel-annotation-quarkus-adapter + + + + io.quarkus + quarkus-core + ${quarkus.version} + + + com.alibaba.csp + sentinel-annotation-cdi-interceptor + ${project.version} + + + + + + + io.quarkus + quarkus-bootstrap-maven-plugin + ${quarkus.version} + + + + extension-descriptor + + compile + + ${project.groupId}:${project.artifactId}-deployment:${project.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + diff --git a/sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-runtime/src/main/resources/META-INF/quarkus-extension.yaml b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 00000000..b9bf0dac --- /dev/null +++ b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-annotation-quarkus-adapter-runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,11 @@ +--- +name: "sentinel annotation extension" +metadata: + keywords: + - "sentinel" + - "rate limit" + - "circuit breaker" + categories: + - "rate limit" + - "circuit breaker" + status: "preview" diff --git a/sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-deployment/pom.xml b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-deployment/pom.xml new file mode 100644 index 00000000..ba8d82ec --- /dev/null +++ b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-deployment/pom.xml @@ -0,0 +1,77 @@ + + + 4.0.0 + + com.alibaba.csp + sentinel-quarkus-adapter-parent + 1.8.0-SNAPSHOT + ../pom.xml + + + sentinel-jax-rs-quarkus-adapter-deployment + sentinel-jax-rs-quarkus-adapter-deployment + + + 1.8 + 1.8 + + + + + io.quarkus + quarkus-core-deployment + + + io.quarkus + quarkus-arc-deployment + + + io.quarkus + quarkus-resteasy-server-common-deployment + + + com.alibaba.csp + sentinel-jax-rs-quarkus-adapter + ${project.version} + + + + + io.quarkus + quarkus-junit5-internal + test + + + io.quarkus + quarkus-resteasy-deployment + test + + + io.rest-assured + rest-assured + test + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + + diff --git a/sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-deployment/src/main/java/com/alibaba/csp/sentinel/jaxrs/quarkus/adapter/deployment/SentinelJaxRsQuarkusAdapterProcessor.java b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-deployment/src/main/java/com/alibaba/csp/sentinel/jaxrs/quarkus/adapter/deployment/SentinelJaxRsQuarkusAdapterProcessor.java new file mode 100644 index 00000000..c5ceb0d3 --- /dev/null +++ b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-deployment/src/main/java/com/alibaba/csp/sentinel/jaxrs/quarkus/adapter/deployment/SentinelJaxRsQuarkusAdapterProcessor.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 + * + * 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.jaxrs.quarkus.adapter.deployment; + +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.FeatureBuildItem; +import org.jboss.logging.Logger; + +/** + * @author sea + */ +class SentinelJaxRsQuarkusAdapterProcessor { + + private static final Logger logger = Logger.getLogger(SentinelJaxRsQuarkusAdapterProcessor.class); + + private static final String FEATURE_JAX_RS = "sentinel-jax-rs"; + + @BuildStep + void feature(BuildProducer featureProducer) { + featureProducer.produce(new FeatureBuildItem(FEATURE_JAX_RS)); + } + +} diff --git a/sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-deployment/src/test/java/com/alibaba/csp/sentinel/jaxrs/quarkus/adapter/deployment/SentinelJaxRsQuarkusAdapterTest.java b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-deployment/src/test/java/com/alibaba/csp/sentinel/jaxrs/quarkus/adapter/deployment/SentinelJaxRsQuarkusAdapterTest.java new file mode 100644 index 00000000..2f7ac058 --- /dev/null +++ b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-deployment/src/test/java/com/alibaba/csp/sentinel/jaxrs/quarkus/adapter/deployment/SentinelJaxRsQuarkusAdapterTest.java @@ -0,0 +1,242 @@ +/* + * 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.jaxrs.quarkus.adapter.deployment; + +import com.alibaba.csp.sentinel.Constants; +import com.alibaba.csp.sentinel.adapter.jaxrs.config.SentinelJaxRsConfig; +import com.alibaba.csp.sentinel.adapter.jaxrs.fallback.SentinelJaxRsFallback; +import com.alibaba.csp.sentinel.adapter.jaxrs.request.RequestOriginParser; +import com.alibaba.csp.sentinel.node.ClusterNode; +import com.alibaba.csp.sentinel.node.EntranceNode; +import com.alibaba.csp.sentinel.node.Node; +import com.alibaba.csp.sentinel.slots.block.RuleConstant; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; +import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot; +import com.alibaba.csp.sentinel.util.StringUtil; +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.response.Response; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.core.MediaType; +import java.util.Collections; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author sea + */ +public class SentinelJaxRsQuarkusAdapterTest { + + private static final String HELLO_STR = "Hello!"; + + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap + .create(JavaArchive.class) + .addClasses(TestResource.class)); + + @AfterEach + public void cleanUp() { + FlowRuleManager.loadRules(null); + ClusterBuilderSlot.resetClusterNodes(); + } + + @Test + public void testGetHello() { + String url = "/test/hello"; + String resourceName = "GET:" + url; + Response response = given().get(url); + response.then().statusCode(200).body(equalTo(HELLO_STR)); + + ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName); + assertNotNull(cn); + assertEquals(1, cn.passQps(), 0.01); + + String context = ""; + for (Node n : Constants.ROOT.getChildList()) { + if (n instanceof EntranceNode) { + String id = ((EntranceNode) n).getId().getName(); + if (url.equals(id)) { + context = ((EntranceNode) n).getId().getName(); + } + } + } + assertEquals("", context); + } + + @Test + public void testAsyncGetHello() { + String url = "/test/async-hello"; + String resourceName = "GET:" + url; + Response response = given().get(url); + response.then().statusCode(200).body(equalTo(HELLO_STR)); + + ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName); + assertNotNull(cn); + assertEquals(1, cn.passQps(), 0.01); + + String context = ""; + for (Node n : Constants.ROOT.getChildList()) { + if (n instanceof EntranceNode) { + String id = ((EntranceNode) n).getId().getName(); + if (url.equals(id)) { + context = ((EntranceNode) n).getId().getName(); + } + } + } + assertEquals("", context); + } + + @Test + public void testUrlPathParam() { + String url = "/test/hello/{name}"; + String resourceName = "GET:" + url; + + String url1 = "/test/hello/abc"; + Response response1 = given().get(url1); + response1.then().statusCode(200).body(equalTo("Hello abc !")); + + String url2 = "/test/hello/def"; + Response response2 = given().get(url2); + response2.then().statusCode(200).body(equalTo("Hello def !")); + + ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName); + assertNotNull(cn); + assertEquals(2, cn.passQps(), 0.01); + + assertNull(ClusterBuilderSlot.getClusterNode("GET:" + url1)); + assertNull(ClusterBuilderSlot.getClusterNode("GET:" + url2)); + } + + @Test + public void testDefaultFallback() { + String url = "/test/hello"; + String resourceName = "GET:" + url; + configureRulesFor(resourceName, 0); + Response response = given().get(url); + response.then().statusCode(javax.ws.rs.core.Response.Status.TOO_MANY_REQUESTS.getStatusCode()) + .body(equalTo("Blocked by Sentinel (flow limiting)")); + + ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName); + assertNotNull(cn); + assertEquals(0, cn.passQps(), 0.01); + } + + @Test + public void testCustomFallback() { + String url = "/test/hello"; + String resourceName = "GET:" + url; + SentinelJaxRsConfig.setJaxRsFallback(new SentinelJaxRsFallback() { + @Override + public javax.ws.rs.core.Response fallbackResponse(String route, Throwable cause) { + return javax.ws.rs.core.Response.status(javax.ws.rs.core.Response.Status.OK) + .entity("Blocked by Sentinel (flow limiting)") + .type(MediaType.APPLICATION_JSON_TYPE) + .build(); + } + + @Override + public Future fallbackFutureResponse(final String route, final Throwable cause) { + return new FutureTask<>(new Callable() { + @Override + public javax.ws.rs.core.Response call() throws Exception { + return fallbackResponse(route, cause); + } + }); + } + }); + + + configureRulesFor(resourceName, 0); + Response response = given().get(url); + response.then().statusCode(javax.ws.rs.core.Response.Status.OK.getStatusCode()) + .body(equalTo("Blocked by Sentinel (flow limiting)")); + + ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName); + assertNotNull(cn); + assertEquals(0, cn.passQps(), 0.01); + } + + @Test + public void testCustomRequestOriginParser() { + String url = "/test/hello"; + String resourceName = "GET:" + url; + + String limitOrigin = "appB"; + final String headerName = "X-APP"; + configureRulesFor(resourceName, 0, limitOrigin); + + SentinelJaxRsConfig.setRequestOriginParser(new RequestOriginParser() { + @Override + public String parseOrigin(ContainerRequestContext request) { + String origin = request.getHeaderString(headerName); + return origin != null ? origin : ""; + } + }); + + Response response = given() + .header(headerName, "appA").get(url); + response.then().statusCode(200).body(equalTo(HELLO_STR)); + + Response blockedResp = given() + .header(headerName, "appB") + .get(url); + blockedResp.then().statusCode(javax.ws.rs.core.Response.Status.TOO_MANY_REQUESTS.getStatusCode()) + .body(equalTo("Blocked by Sentinel (flow limiting)")); + + ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName); + assertNotNull(cn); + assertEquals(1, cn.passQps(), 0.01); + assertEquals(1, cn.blockQps(), 0.01); + } + + @Test + public void testExceptionMapper() { + String url = "/test/ex"; + String resourceName = "GET:" + url; + Response response = given().get(url); + response.then().statusCode(javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).body(equalTo("test exception mapper")); + + ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName); + assertNotNull(cn); + } + + private void configureRulesFor(String resource, int count) { + configureRulesFor(resource, count, "default"); + } + + private void configureRulesFor(String resource, int count, String limitApp) { + FlowRule rule = new FlowRule() + .setCount(count) + .setGrade(RuleConstant.FLOW_GRADE_QPS); + rule.setResource(resource); + if (StringUtil.isNotBlank(limitApp)) { + rule.setLimitApp(limitApp); + } + FlowRuleManager.loadRules(Collections.singletonList(rule)); + } +} diff --git a/sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-deployment/src/test/java/com/alibaba/csp/sentinel/jaxrs/quarkus/adapter/deployment/TestResource.java b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-deployment/src/test/java/com/alibaba/csp/sentinel/jaxrs/quarkus/adapter/deployment/TestResource.java new file mode 100644 index 00000000..b982a985 --- /dev/null +++ b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-deployment/src/test/java/com/alibaba/csp/sentinel/jaxrs/quarkus/adapter/deployment/TestResource.java @@ -0,0 +1,90 @@ +/* + * 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.jaxrs.quarkus.adapter.deployment; + + +import javax.ws.rs.*; +import javax.ws.rs.container.AsyncResponse; +import javax.ws.rs.container.Suspended; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * @author sea + */ +@Path("/test") +public class TestResource { + + ExecutorService executor = Executors.newFixedThreadPool(5); + + @Path("/hello") + @GET + @Produces({ MediaType.APPLICATION_JSON }) + public String sayHello() { + return "Hello!"; + } + + @Path("/async-hello") + @GET + @Produces({ MediaType.APPLICATION_JSON }) + public void asyncSayHello(@Suspended final AsyncResponse asyncResponse) { + executor.submit(new Runnable() { + @Override + public void run() { + try { + TimeUnit.MILLISECONDS.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + asyncResponse.resume("Hello!"); + } + }); + } + + @Path("/hello/{name}") + @GET + @Produces({ MediaType.APPLICATION_JSON }) + public String sayHelloWithName(@PathParam(value = "name") String name) { + return "Hello " + name + " !"; + } + + @Path("/ex") + @GET + @Produces({ MediaType.APPLICATION_JSON }) + public String exception() { + throw new RuntimeException("test exception mapper"); + } + + @Path("/400") + @GET + @Produces({ MediaType.APPLICATION_JSON }) + public String badRequest() { + throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST) + .entity("test return 400") + .build()); + } + + @Path("/delay/{seconds}") + @GET + @Produces({ MediaType.APPLICATION_JSON }) + public String delay(@PathParam(value = "seconds") long seconds) throws InterruptedException { + TimeUnit.SECONDS.sleep(seconds); + return "finish"; + } +} diff --git a/sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-runtime/pom.xml b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-runtime/pom.xml new file mode 100644 index 00000000..4427156f --- /dev/null +++ b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-runtime/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + + com.alibaba.csp + sentinel-quarkus-adapter-parent + 1.8.0-SNAPSHOT + ../pom.xml + + + sentinel-jax-rs-quarkus-adapter + sentinel-jax-rs-quarkus-adapter + + + + io.quarkus + quarkus-core + ${quarkus.version} + + + com.alibaba.csp + sentinel-jax-rs-adapter + ${project.version} + + + + + + + io.quarkus + quarkus-bootstrap-maven-plugin + ${quarkus.version} + + + + extension-descriptor + + compile + + ${project.groupId}:${project.artifactId}-deployment:${project.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + diff --git a/sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-runtime/src/main/resources/META-INF/quarkus-extension.yaml b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 00000000..940f5555 --- /dev/null +++ b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,11 @@ +--- +name: "sentinel jax rs extension" +metadata: + keywords: + - "resteasy" + - "jaxrs" + - "sentinel" + categories: + - "web" + - "circuit breaker" + status: "preview" diff --git a/sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-runtime/src/main/resources/META-INF/services/javax.ws.rs.ext.Providers b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-runtime/src/main/resources/META-INF/services/javax.ws.rs.ext.Providers new file mode 100644 index 00000000..a26687b3 --- /dev/null +++ b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-jax-rs-quarkus-adapter-runtime/src/main/resources/META-INF/services/javax.ws.rs.ext.Providers @@ -0,0 +1,2 @@ +com.alibaba.csp.sentinel.adapter.jaxrs.SentinelJaxRsProviderFilter +com.alibaba.csp.sentinel.adapter.jaxrs.exception.DefaultExceptionMapper diff --git a/sentinel-adapter/sentinel-quarkus-adapter/sentinel-native-image-quarkus-adapter-deployment/pom.xml b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-native-image-quarkus-adapter-deployment/pom.xml new file mode 100644 index 00000000..83af4830 --- /dev/null +++ b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-native-image-quarkus-adapter-deployment/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + + com.alibaba.csp + sentinel-quarkus-adapter-parent + 1.8.0-SNAPSHOT + ../pom.xml + + + sentinel-native-image-quarkus-adapter-deployment + sentinel-native-image-quarkus-adapter-deployment + + + 1.8 + 1.8 + + + + + io.quarkus + quarkus-core-deployment + + + io.quarkus + quarkus-arc-deployment + + + org.graalvm.nativeimage + svm + + + com.alibaba.csp + sentinel-native-image-quarkus-adapter + ${project.version} + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + + diff --git a/sentinel-adapter/sentinel-quarkus-adapter/sentinel-native-image-quarkus-adapter-deployment/src/main/java/com/alibaba/csp/sentinel/nativeimage/SentinelNativeImageProcessor.java b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-native-image-quarkus-adapter-deployment/src/main/java/com/alibaba/csp/sentinel/nativeimage/SentinelNativeImageProcessor.java new file mode 100644 index 00000000..82935045 --- /dev/null +++ b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-native-image-quarkus-adapter-deployment/src/main/java/com/alibaba/csp/sentinel/nativeimage/SentinelNativeImageProcessor.java @@ -0,0 +1,73 @@ +/* + * 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.nativeimage; + +import com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Record; +import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; +import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; +import io.quarkus.deployment.pkg.steps.NativeBuild; + +import java.util.Arrays; +import java.util.List; + +/** + * @author sea + */ +class SentinelNativeImageProcessor { + + private static final String FEATURE_NATIVE_IMAGE = "sentinel-native-image"; + + @BuildStep + void feature(BuildProducer featureProducer) { + featureProducer.produce(new FeatureBuildItem(FEATURE_NATIVE_IMAGE)); + } + + @BuildStep(onlyIf = NativeBuild.class) + List runtimeInitializedClasses() { + return Arrays.asList( + new RuntimeInitializedClassBuildItem("com.alibaba.fastjson.serializer.JodaCodec"), + new RuntimeInitializedClassBuildItem("com.alibaba.fastjson.serializer.GuavaCodec"), + new RuntimeInitializedClassBuildItem("com.alibaba.fastjson.support.moneta.MonetaCodec"), + new RuntimeInitializedClassBuildItem("com.alibaba.csp.sentinel.Env"), + new RuntimeInitializedClassBuildItem("com.alibaba.csp.sentinel.init.InitExecutor"), + new RuntimeInitializedClassBuildItem("com.alibaba.csp.sentinel.cluster.ClusterStateManager"), + new RuntimeInitializedClassBuildItem("com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager"), + new RuntimeInitializedClassBuildItem("com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager"), + new RuntimeInitializedClassBuildItem("com.alibaba.csp.sentinel.node.metric.MetricTimerListener"), + new RuntimeInitializedClassBuildItem("com.alibaba.csp.sentinel.node.metric.MetricWriter"), + new RuntimeInitializedClassBuildItem("com.alibaba.csp.sentinel.util.TimeUtil"), + new RuntimeInitializedClassBuildItem("com.alibaba.csp.sentinel.eagleeye.StatLogController"), + new RuntimeInitializedClassBuildItem("com.alibaba.csp.sentinel.slots.logger.EagleEyeLogUtil"), + new RuntimeInitializedClassBuildItem("com.alibaba.csp.sentinel.eagleeye.EagleEye")); + } + + @BuildStep(onlyIf = NativeBuild.class) + ReflectiveClassBuildItem setupSentinelReflectiveClasses() { + return new ReflectiveClassBuildItem(true, true, true, + DefaultSlotChainBuilder.class.getName()); + } + + @BuildStep(onlyIf = NativeBuild.class) + @Record(ExecutionTime.STATIC_INIT) + void record(SentinelRecorder recorder) { + recorder.init(); + } +} diff --git a/sentinel-adapter/sentinel-quarkus-adapter/sentinel-native-image-quarkus-adapter-runtime/pom.xml b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-native-image-quarkus-adapter-runtime/pom.xml new file mode 100644 index 00000000..cbba7116 --- /dev/null +++ b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-native-image-quarkus-adapter-runtime/pom.xml @@ -0,0 +1,75 @@ + + + 4.0.0 + + com.alibaba.csp + sentinel-quarkus-adapter-parent + 1.8.0-SNAPSHOT + ../pom.xml + + + sentinel-native-image-quarkus-adapter + sentinel-native-image-quarkus-adapter + + + + io.quarkus + quarkus-core + ${quarkus.version} + + + org.graalvm.nativeimage + svm + + + com.alibaba.csp + sentinel-transport-simple-http + + + com.alibaba.csp + sentinel-parameter-flow-control + + + com.alibaba.csp + sentinel-logging-slf4j + ${project.version} + + + + + + + io.quarkus + quarkus-bootstrap-maven-plugin + ${quarkus.version} + + + + extension-descriptor + + compile + + ${project.groupId}:${project.artifactId}-deployment:${project.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + diff --git a/sentinel-adapter/sentinel-quarkus-adapter/sentinel-native-image-quarkus-adapter-runtime/src/main/java/com/alibaba/csp/sentinel/nativeimage/SentinelRecorder.java b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-native-image-quarkus-adapter-runtime/src/main/java/com/alibaba/csp/sentinel/nativeimage/SentinelRecorder.java new file mode 100644 index 00000000..8d17e548 --- /dev/null +++ b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-native-image-quarkus-adapter-runtime/src/main/java/com/alibaba/csp/sentinel/nativeimage/SentinelRecorder.java @@ -0,0 +1,52 @@ +/* + * 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.nativeimage; + +import com.alibaba.csp.sentinel.command.vo.NodeVo; +import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule; +import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; +import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; +import com.alibaba.csp.sentinel.slots.system.SystemRule; +import com.alibaba.fastjson.parser.ParserConfig; +import com.alibaba.fastjson.serializer.SerializeConfig; +import io.quarkus.runtime.annotations.Recorder; + +/** + * @author sea + */ +@Recorder +public class SentinelRecorder { + + /** + * register fastjson serializer deserializer class info + */ + public void init() { + SerializeConfig.getGlobalInstance().getObjectWriter(NodeVo.class); + SerializeConfig.getGlobalInstance().getObjectWriter(FlowRule.class); + SerializeConfig.getGlobalInstance().getObjectWriter(SystemRule.class); + SerializeConfig.getGlobalInstance().getObjectWriter(DegradeRule.class); + SerializeConfig.getGlobalInstance().getObjectWriter(AuthorityRule.class); + SerializeConfig.getGlobalInstance().getObjectWriter(ParamFlowRule.class); + + ParserConfig.getGlobalInstance().getDeserializer(NodeVo.class); + ParserConfig.getGlobalInstance().getDeserializer(FlowRule.class); + ParserConfig.getGlobalInstance().getDeserializer(SystemRule.class); + ParserConfig.getGlobalInstance().getDeserializer(DegradeRule.class); + ParserConfig.getGlobalInstance().getDeserializer(AuthorityRule.class); + ParserConfig.getGlobalInstance().getDeserializer(ParamFlowRule.class); + } +} diff --git a/sentinel-adapter/sentinel-quarkus-adapter/sentinel-native-image-quarkus-adapter-runtime/src/main/resources/META-INF/quarkus-extension.yaml b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-native-image-quarkus-adapter-runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 00000000..c072df2f --- /dev/null +++ b/sentinel-adapter/sentinel-quarkus-adapter/sentinel-native-image-quarkus-adapter-runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,12 @@ +--- +name: "sentinel native image extension" +metadata: + keywords: + - "sentinel" + - "rate limit" + - "circuit breaker" + - "native image" + categories: + - "rate limit" + - "circuit breaker" + status: "preview"