diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/pom.xml b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/pom.xml index 182fa50ae..73fb22e7e 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/pom.xml +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/pom.xml @@ -34,6 +34,7 @@ 2.1.1.RELEASE + 1.8 diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/FilteringWebHandlerInterceptor.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/FilteringWebHandlerInterceptor.java new file mode 100644 index 000000000..9eb1bd0f7 --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/FilteringWebHandlerInterceptor.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x; + +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x.context.SWTransmitter; +import org.springframework.cloud.gateway.route.Route; +import org.springframework.http.HttpStatus; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; +import java.lang.reflect.Method; + +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR; + + +/** + * @author songxiaoyue + */ +public class FilteringWebHandlerInterceptor implements InstanceMethodsAroundInterceptor { + + private static final String SPRING_CLOUD_GATEWAY_ROUTE_PREFIX = "GATEWAY/"; + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + EnhancedInstance instance = NettyRoutingFilterInterceptor.getInstance(allArguments[0]); + if (instance == null) { + return; + } + AbstractSpan span = (AbstractSpan) instance.getSkyWalkingDynamicField(); + if (span == null) { + return; + } + ServerWebExchange exchange = (ServerWebExchange) allArguments[0]; + String operationName = SPRING_CLOUD_GATEWAY_ROUTE_PREFIX; + Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR); + operationName = operationName + route.getId(); + span.setOperationName(operationName); + SWTransmitter transmitter = new SWTransmitter(span.prepareForAsync(),ContextManager.capture(),operationName); + instance.setSkyWalkingDynamicField(transmitter); + ContextManager.stopSpan(span); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Object ret) throws Throwable { + EnhancedInstance instance = NettyRoutingFilterInterceptor.getInstance(allArguments[0]); + if (instance == null) { + return ret; + } + SWTransmitter swTransmitter = (SWTransmitter) instance.getSkyWalkingDynamicField(); + if (swTransmitter == null) { + return ret; + } + Mono mono = (Mono) ret; + return mono.doFinally(d -> { + ServerWebExchange exchange = (ServerWebExchange) allArguments[0]; + HttpStatus statusCode = exchange.getResponse().getStatusCode(); + if (statusCode == HttpStatus.TOO_MANY_REQUESTS) { + AbstractSpan localSpan = ContextManager.createLocalSpan(swTransmitter.getOperationName()); + Tags.STATUS_CODE.set(localSpan,statusCode.toString()); + SpanLayer.asHttp(localSpan); + localSpan.setComponent(ComponentsDefine.SPRING_CLOUD_GATEWAY); + ContextManager.continued(swTransmitter.getSnapshot()); + ContextManager.stopSpan(localSpan); + AbstractSpan spanWebflux = swTransmitter.getSpanWebflux(); + spanWebflux.asyncFinish(); + } + }); + } + + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + } + +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/NettyRoutingFilterInterceptor.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/NettyRoutingFilterInterceptor.java index 251198568..21180dbed 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/NettyRoutingFilterInterceptor.java +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/NettyRoutingFilterInterceptor.java @@ -19,20 +19,15 @@ package org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x; import org.apache.skywalking.apm.agent.core.context.ContextManager; -import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x.context.Constants; import org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x.context.SWTransmitter; -import org.springframework.cloud.gateway.route.Route; -import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchangeDecorator; import org.springframework.web.server.adapter.DefaultServerWebExchange; - import java.lang.reflect.Method; -import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR; /** @@ -40,24 +35,14 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.G */ public class NettyRoutingFilterInterceptor implements InstanceMethodsAroundInterceptor { - private static final String SPRING_CLOUD_GATEWAY_ROUTE_PREFIX = "GATEWAY/"; @Override public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { EnhancedInstance instance = NettyRoutingFilterInterceptor.getInstance(allArguments[0]); if (instance != null) { - ServerWebExchange exchange = (ServerWebExchange) allArguments[0]; - AbstractSpan span = (AbstractSpan) instance.getSkyWalkingDynamicField(); - String operationName = SPRING_CLOUD_GATEWAY_ROUTE_PREFIX; - if (span != null) { - Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR); - operationName = operationName + route.getId(); - span.setOperationName(operationName); - SWTransmitter transmitter = new SWTransmitter(span.prepareForAsync(), ContextManager.capture(), operationName); - ContextManager.stopSpan(span); - ContextManager.getRuntimeContext().put(Constants.SPRING_CLOUD_GATEWAY_TRANSMITTER, transmitter); - } + SWTransmitter swTransmitter = (SWTransmitter) instance.getSkyWalkingDynamicField(); + ContextManager.getRuntimeContext().put(Constants.SPRING_CLOUD_GATEWAY_TRANSMITTER, swTransmitter); } } @@ -99,4 +84,4 @@ public class NettyRoutingFilterInterceptor implements InstanceMethodsAroundInter throw new RuntimeException("Unknown parameter types:" + o.getClass()); } } -} +} \ No newline at end of file diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/define/FilteringWebHandlerInstrumentation.java b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/define/FilteringWebHandlerInstrumentation.java new file mode 100644 index 000000000..575db139c --- /dev/null +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/define/FilteringWebHandlerInstrumentation.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +/** + * @author songxiaoyue + */ +public class FilteringWebHandlerInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("handle"); + } + + @Override + public String getMethodsInterceptor() { + return "org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x.FilteringWebHandlerInterceptor"; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override + public ClassMatch enhanceClass() { + return byName("org.springframework.cloud.gateway.handler.FilteringWebHandler"); + } + +} diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/resources/skywalking-plugin.def index c78894a1d..f2f821eaf 100644 --- a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/resources/skywalking-plugin.def +++ b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/main/resources/skywalking-plugin.def @@ -16,4 +16,5 @@ spring-cloud-gateway-2.1.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x.define.DefaultHttpHeadersInstrumentation spring-cloud-gateway-2.1.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x.define.HttpClientOperationsInstrumentation -spring-cloud-gateway-2.1.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x.define.NettyRoutingFilterInstrumentation \ No newline at end of file +spring-cloud-gateway-2.1.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x.define.NettyRoutingFilterInstrumentation +spring-cloud-gateway-2.1.x=org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x.define.FilteringWebHandlerInstrumentation \ No newline at end of file diff --git a/test/plugin/scenarios/gateway-scenario/gateway-projectA-scenario/pom.xml b/test/plugin/scenarios/gateway-scenario/gateway-projectA-scenario/pom.xml index 0b6670b5d..99654a6a7 100644 --- a/test/plugin/scenarios/gateway-scenario/gateway-projectA-scenario/pom.xml +++ b/test/plugin/scenarios/gateway-scenario/gateway-projectA-scenario/pom.xml @@ -34,6 +34,11 @@ spring-cloud-starter-gateway ${test.framework.version} + + org.springframework.boot + spring-boot-starter-data-redis + 2.1.2.RELEASE + diff --git a/test/plugin/scenarios/gateway-scenario/gateway-projectA-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/gateway/projectA/ApiKeyResolver.java b/test/plugin/scenarios/gateway-scenario/gateway-projectA-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/gateway/projectA/ApiKeyResolver.java new file mode 100644 index 000000000..785d73b9b --- /dev/null +++ b/test/plugin/scenarios/gateway-scenario/gateway-projectA-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/gateway/projectA/ApiKeyResolver.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.apm.testcase.sc.gateway.projectA; + +import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * @author songxiaoyue + */ +@Component +public class ApiKeyResolver implements KeyResolver{ + + public Mono resolve(ServerWebExchange exchange) { + return Mono.just(exchange.getRequest().getPath().value()); + } +} diff --git a/test/plugin/scenarios/gateway-scenario/gateway-projectA-scenario/src/main/resources/application.yml b/test/plugin/scenarios/gateway-scenario/gateway-projectA-scenario/src/main/resources/application.yml index 51774a6b5..00e8ee3d8 100644 --- a/test/plugin/scenarios/gateway-scenario/gateway-projectA-scenario/src/main/resources/application.yml +++ b/test/plugin/scenarios/gateway-scenario/gateway-projectA-scenario/src/main/resources/application.yml @@ -24,4 +24,13 @@ spring: - id: provider_route uri: http://localhost:18070 predicates: - - Path=/provider/b/* \ No newline at end of file + - Path=/provider/b/* + filters: + - name: RequestRateLimiter + args: + redis-rate-limiter.replenishRate: 1 + redis-rate-limiter.burstCapacity: 1 + key-resolver: "#{@apiKeyResolver}" + redis: + host: 127.0.0.1 + port: 6379 \ No newline at end of file