This is an automated email from the ASF dual-hosted git repository.

wusheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-java.git


The following commit(s) were added to refs/heads/main by this push:
     new f227543fc3 Fix a bug in Spring Cloud Gateway if 
HttpClientFinalizer#send does not invoke, the span created at 
NettyRoutingFilterInterceptor can not stop. (#672)
f227543fc3 is described below

commit f227543fc3a40170375b407c31376f5842771e2b
Author: cylx3126 <49175642+cylx3...@users.noreply.github.com>
AuthorDate: Thu Mar 7 15:55:08 2024 +0800

    Fix a bug in Spring Cloud Gateway if HttpClientFinalizer#send does not 
invoke, the span created at NettyRoutingFilterInterceptor can not stop. (#672)
---
 CHANGES.md                                         |   1 +
 .../v21x/NettyRoutingFilterInterceptor.java        |   4 +
 .../v21x/NettyRoutingFilterInterceptorTest.java    | 147 ++++++++++++++++++++-
 .../gateway/v3x/NettyRoutingFilterInterceptor.java |   4 +
 .../v3x/NettyRoutingFilterInterceptorTest.java     |  11 +-
 .../gateway/v4x/NettyRoutingFilterInterceptor.java |   4 +
 .../v4x/NettyRoutingFilterInterceptorTest.java     |  11 +-
 7 files changed, 172 insertions(+), 10 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 222b2b7a7f..5ea8c5da7b 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -16,6 +16,7 @@ Release Notes.
 * Rename system env name from `sw_plugin_kafka_producer_config` to 
`SW_PLUGIN_KAFKA_PRODUCER_CONFIG`.
 * Support for ActiveMQ-Artemis messaging tracing.
 * Archive the expired plugins `impala-jdbc-2.6.x-plugin`.
+* Fix a bug in Spring Cloud Gateway if HttpClientFinalizer#send does not 
invoke, the span created at NettyRoutingFilterInterceptor can not stop.
 
 #### Documentation
 * Update docs to describe `expired-plugins`.
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 578352e8e8..6e7dba5726 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
@@ -74,6 +74,10 @@ public class NettyRoutingFilterInterceptor implements 
InstanceMethodsAroundInter
     @Override
     public Object afterMethod(EnhancedInstance objInst, Method method, 
Object[] allArguments, Class<?>[] argumentsTypes,
                               Object ret) throws Throwable {
+        if (ContextManager.isActive()) {
+            // if HttpClientFinalizerSendInterceptor does not invoke, we will 
stop the span there to avoid context leak.
+            ContextManager.stopSpan();
+        }
         return ret;
     }
 
diff --git 
a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/NettyRoutingFilterInterceptorTest.java
 
b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/NettyRoutingFilterInterceptorTest.java
index 43d75b0cee..30bc2d6ce2 100644
--- 
a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/NettyRoutingFilterInterceptorTest.java
+++ 
b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-2.1.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v21x/NettyRoutingFilterInterceptorTest.java
@@ -21,13 +21,23 @@ package 
org.apache.skywalking.apm.plugin.spring.cloud.gateway.v21x;
 import java.security.Principal;
 import java.time.Instant;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.function.Function;
 import org.apache.skywalking.apm.agent.core.context.ContextManager;
+import org.apache.skywalking.apm.agent.core.context.ContextSnapshot;
+import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
+import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan;
+import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer;
+import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment;
+import 
org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.apache.skywalking.apm.agent.test.helper.SegmentHelper;
 import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule;
 import org.apache.skywalking.apm.agent.test.tools.SegmentStorage;
 import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint;
+import org.apache.skywalking.apm.agent.test.tools.SpanAssert;
 import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner;
+import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
@@ -47,6 +57,103 @@ import reactor.core.publisher.Mono;
 
 @RunWith(TracingSegmentRunner.class)
 public class NettyRoutingFilterInterceptorTest {
+
+    private static class ServerWebExchangeEnhancedInstance implements 
ServerWebExchange, EnhancedInstance {
+        private ContextSnapshot snapshot;
+        Map<String, Object> attributes = new HashMap<>();
+
+        @Override
+        public Object getSkyWalkingDynamicField() {
+            return snapshot;
+        }
+
+        @Override
+        public void setSkyWalkingDynamicField(Object value) {
+            this.snapshot = (ContextSnapshot) value;
+        }
+
+        @Override
+        public ServerHttpRequest getRequest() {
+            return null;
+        }
+
+        @Override
+        public ServerHttpResponse getResponse() {
+            return null;
+        }
+
+        @Override
+        public Map<String, Object> getAttributes() {
+            return attributes;
+        }
+
+        @Override
+        public Mono<WebSession> getSession() {
+            return null;
+        }
+
+        @Override
+        public <T extends Principal> Mono<T> getPrincipal() {
+            return null;
+        }
+
+        @Override
+        public Mono<MultiValueMap<String, String>> getFormData() {
+            return null;
+        }
+
+        @Override
+        public Mono<MultiValueMap<String, Part>> getMultipartData() {
+            return null;
+        }
+
+        @Override
+        public LocaleContext getLocaleContext() {
+            return null;
+        }
+
+        @Override
+        public ApplicationContext getApplicationContext() {
+            return null;
+        }
+
+        @Override
+        public boolean isNotModified() {
+            return false;
+        }
+
+        @Override
+        public boolean checkNotModified(Instant instant) {
+            return false;
+        }
+
+        @Override
+        public boolean checkNotModified(String s) {
+            return false;
+        }
+
+        @Override
+        public boolean checkNotModified(String s, Instant instant) {
+            return false;
+        }
+
+        @Override
+        public String transformUrl(String s) {
+            return null;
+        }
+
+        @Override
+        public void addUrlTransformer(Function<String, String> function) {
+
+        }
+
+        @Override
+        public String getLogPrefix() {
+            return null;
+        }
+    }
+
+    private final ServerWebExchangeEnhancedInstance enhancedInstance = new 
ServerWebExchangeEnhancedInstance();
     private final NettyRoutingFilterInterceptor interceptor = new 
NettyRoutingFilterInterceptor();
     @Rule
     public AgentServiceRule serviceRule = new AgentServiceRule();
@@ -151,12 +258,48 @@ public class NettyRoutingFilterInterceptorTest {
         interceptor.beforeMethod(null, null, new Object[]{exchange}, null, 
null);
         interceptor.afterMethod(null, null, null, null, null);
         
Assert.assertEquals(exchange.getAttributes().get(NETTY_ROUTING_FILTER_TRACED_ATTR),
 true);
-        Assert.assertNotNull(ContextManager.activeSpan());
+        Assert.assertFalse(ContextManager.isActive());
 
-        ContextManager.stopSpan();
+        // no more need this, span was stopped at interceptor#afterMethod
+        // ContextManager.stopSpan();
 
         interceptor.beforeMethod(null, null, new Object[]{exchange}, null, 
null);
         interceptor.afterMethod(null, null, null, null, null);
         
Assert.assertEquals(exchange.getAttributes().get(NETTY_ROUTING_FILTER_TRACED_ATTR),
 true);
     }
+
+    @Test
+    public void testWithNullDynamicField() throws Throwable {
+        interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, 
null, null);
+        interceptor.afterMethod(null, null, null, null, null);
+        // no more need this, span was stopped at interceptor#afterMethod
+        // ContextManager.stopSpan();
+        final List<TraceSegment> traceSegments = 
segmentStorage.getTraceSegments();
+        Assert.assertEquals(traceSegments.size(), 1);
+        final List<AbstractTracingSpan> spans = 
SegmentHelper.getSpans(traceSegments.get(0));
+        Assert.assertNotNull(spans);
+        Assert.assertEquals(spans.size(), 1);
+        SpanAssert.assertComponent(spans.get(0), 
ComponentsDefine.SPRING_CLOUD_GATEWAY);
+    }
+
+    @Test
+    public void testWithContextSnapshot() throws Throwable {
+        final AbstractSpan entrySpan = ContextManager.createEntrySpan("/get", 
null);
+        SpanLayer.asHttp(entrySpan);
+        entrySpan.setComponent(ComponentsDefine.SPRING_WEBFLUX);
+        enhancedInstance.setSkyWalkingDynamicField(ContextManager.capture());
+        interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, 
null, null);
+        interceptor.afterMethod(null, null, null, null, null);
+        // no more need this, span was stopped at interceptor#afterMethod
+        // ContextManager.stopSpan();
+        ContextManager.stopSpan(entrySpan);
+        final List<TraceSegment> traceSegments = 
segmentStorage.getTraceSegments();
+        Assert.assertEquals(traceSegments.size(), 1);
+        final List<AbstractTracingSpan> spans = 
SegmentHelper.getSpans(traceSegments.get(0));
+        Assert.assertNotNull(spans);
+        Assert.assertEquals(spans.size(), 2);
+        SpanAssert.assertComponent(spans.get(0), 
ComponentsDefine.SPRING_CLOUD_GATEWAY);
+        SpanAssert.assertComponent(spans.get(1), 
ComponentsDefine.SPRING_WEBFLUX);
+        SpanAssert.assertLayer(spans.get(1), SpanLayer.HTTP);
+    }
 }
diff --git 
a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/NettyRoutingFilterInterceptor.java
 
b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/NettyRoutingFilterInterceptor.java
index 8b53595e4e..014dd4b6c4 100644
--- 
a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/NettyRoutingFilterInterceptor.java
+++ 
b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/NettyRoutingFilterInterceptor.java
@@ -81,6 +81,10 @@ public class NettyRoutingFilterInterceptor implements 
InstanceMethodsAroundInter
     @Override
     public Object afterMethod(EnhancedInstance objInst, Method method, 
Object[] allArguments, Class<?>[] argumentsTypes,
             Object ret) throws Throwable {
+        if (ContextManager.isActive()) {
+            // if HttpClientFinalizerSendInterceptor does not invoke, we will 
stop the span there to avoid context leak.
+            ContextManager.stopSpan();
+        }
         return ret;
     }
 
diff --git 
a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/NettyRoutingFilterInterceptorTest.java
 
b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/NettyRoutingFilterInterceptorTest.java
index 36a262ab30..a2614b2245 100644
--- 
a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/NettyRoutingFilterInterceptorTest.java
+++ 
b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v3x/NettyRoutingFilterInterceptorTest.java
@@ -170,7 +170,8 @@ public class NettyRoutingFilterInterceptorTest {
     public void testWithNullDynamicField() throws Throwable {
         interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, 
null, null);
         interceptor.afterMethod(null, null, null, null, null);
-        ContextManager.stopSpan();
+        // no more need this, span was stopped at interceptor#afterMethod
+        // ContextManager.stopSpan();
         final List<TraceSegment> traceSegments = 
segmentStorage.getTraceSegments();
         Assert.assertEquals(traceSegments.size(), 1);
         final List<AbstractTracingSpan> spans = 
SegmentHelper.getSpans(traceSegments.get(0));
@@ -187,7 +188,8 @@ public class NettyRoutingFilterInterceptorTest {
         enhancedInstance.setSkyWalkingDynamicField(ContextManager.capture());
         interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, 
null, null);
         interceptor.afterMethod(null, null, null, null, null);
-        ContextManager.stopSpan();
+        // no more need this, span was stopped at interceptor#afterMethod
+        // ContextManager.stopSpan();
         ContextManager.stopSpan(entrySpan);
         final List<TraceSegment> traceSegments = 
segmentStorage.getTraceSegments();
         Assert.assertEquals(traceSegments.size(), 1);
@@ -207,9 +209,10 @@ public class NettyRoutingFilterInterceptorTest {
         interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, 
null, null);
         interceptor.afterMethod(null, null, null, null, null);
         
Assert.assertEquals(enhancedInstance.getAttributes().get(NETTY_ROUTING_FILTER_TRACED_ATTR),
 true);
-        Assert.assertNotNull(ContextManager.activeSpan());
+        Assert.assertFalse(ContextManager.isActive());
 
-        ContextManager.stopSpan();
+        // no more need this, span was stopped at interceptor#afterMethod
+        // ContextManager.stopSpan();
 
         interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, 
null, null);
         interceptor.afterMethod(null, null, null, null, null);
diff --git 
a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/NettyRoutingFilterInterceptor.java
 
b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/NettyRoutingFilterInterceptor.java
index c5271f9a82..f2337856e0 100644
--- 
a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/NettyRoutingFilterInterceptor.java
+++ 
b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/NettyRoutingFilterInterceptor.java
@@ -81,6 +81,10 @@ public class NettyRoutingFilterInterceptor implements 
InstanceMethodsAroundInter
     @Override
     public Object afterMethod(EnhancedInstance objInst, Method method, 
Object[] allArguments, Class<?>[] argumentsTypes,
             Object ret) throws Throwable {
+        if (ContextManager.isActive()) {
+            // if HttpClientFinalizerSendInterceptor does not invoke, we will 
stop the span there to avoid context leak.
+            ContextManager.stopSpan();
+        }
         return ret;
     }
 
diff --git 
a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/NettyRoutingFilterInterceptorTest.java
 
b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/NettyRoutingFilterInterceptorTest.java
index 25bbffb7cc..9eab1fdd36 100644
--- 
a/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/NettyRoutingFilterInterceptorTest.java
+++ 
b/apm-sniffer/optional-plugins/optional-spring-plugins/optional-spring-cloud/gateway-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/cloud/gateway/v4x/NettyRoutingFilterInterceptorTest.java
@@ -170,7 +170,8 @@ public class NettyRoutingFilterInterceptorTest {
     public void testWithNullDynamicField() throws Throwable {
         interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, 
null, null);
         interceptor.afterMethod(null, null, null, null, null);
-        ContextManager.stopSpan();
+        // no more need this, span was stopped at interceptor#afterMethod
+        // ContextManager.stopSpan();
         final List<TraceSegment> traceSegments = 
segmentStorage.getTraceSegments();
         Assert.assertEquals(traceSegments.size(), 1);
         final List<AbstractTracingSpan> spans = 
SegmentHelper.getSpans(traceSegments.get(0));
@@ -187,7 +188,8 @@ public class NettyRoutingFilterInterceptorTest {
         enhancedInstance.setSkyWalkingDynamicField(ContextManager.capture());
         interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, 
null, null);
         interceptor.afterMethod(null, null, null, null, null);
-        ContextManager.stopSpan();
+        // no more need this, span was stopped at interceptor#afterMethod
+        // ContextManager.stopSpan();
         ContextManager.stopSpan(entrySpan);
         final List<TraceSegment> traceSegments = 
segmentStorage.getTraceSegments();
         Assert.assertEquals(traceSegments.size(), 1);
@@ -207,9 +209,10 @@ public class NettyRoutingFilterInterceptorTest {
         interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, 
null, null);
         interceptor.afterMethod(null, null, null, null, null);
         
Assert.assertEquals(enhancedInstance.getAttributes().get(NETTY_ROUTING_FILTER_TRACED_ATTR),
 true);
-        Assert.assertNotNull(ContextManager.activeSpan());
+        Assert.assertFalse(ContextManager.isActive());
 
-        ContextManager.stopSpan();
+        // no more need this, span was stopped at interceptor#afterMethod
+        // ContextManager.stopSpan();
 
         interceptor.beforeMethod(null, null, new Object[]{enhancedInstance}, 
null, null);
         interceptor.afterMethod(null, null, null, null, null);

Reply via email to