You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2020/09/27 14:38:32 UTC

[skywalking] branch master updated: add webflux webclient plugin (#5493)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 806666b  add webflux webclient plugin (#5493)
806666b is described below

commit 806666b4cc7df864d3f20796ec9a171151e72c0e
Author: vcjmhg <55...@users.noreply.github.com>
AuthorDate: Sun Sep 27 22:38:07 2020 +0800

    add webflux webclient plugin (#5493)
---
 .../network/trace/component/ComponentsDefine.java  |   4 +-
 apm-sniffer/apm-sdk-plugin/spring-plugins/pom.xml  |   1 +
 .../pom.xml                                        |  38 +++----
 .../webclient/BodyInserterRequestInterceptor.java  |  54 ++++++++++
 .../v5/webclient/WebFluxWebClientInterceptor.java  | 116 +++++++++++++++++++++
 .../define/BodyInserterRequestInstrumentation.java |  65 ++++++++++++
 .../define/WebFluxWebClientInstrumentation.java    |  72 +++++++++++++
 .../src/main/resources/skywalking-plugin.def       |   6 +-
 .../setup/service-agent/java-agent/Plugin-list.md  |   1 +
 .../src/main/resources/component-libraries.yml     |   3 +
 test/e2e/e2e-protocol/src/main/proto               |   2 +-
 .../webflux-scenario/config/expectedData.yaml      |  38 +++++++
 .../webflux-scenario/support-version.list          |   5 +-
 .../webflux-projectA-scenario/pom.xml              |  10 ++
 .../projectA/controller/TestController.java        |  19 +++-
 .../controller/TestAnnotationController.java       |   5 +
 16 files changed, 406 insertions(+), 33 deletions(-)

diff --git a/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java
index 7bab86e..2494d1b 100755
--- a/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java
+++ b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java
@@ -178,4 +178,6 @@ public class ComponentsDefine {
     public static final OfficialComponent QUARTZ_SCHEDULER = new OfficialComponent(97, "quartz-scheduler");
 
     public static final OfficialComponent XXL_JOB = new OfficialComponent(98, "xxl-job");
-}
+
+    public static final OfficialComponent SPRING_WEBCLIENT = new OfficialComponent(99, "spring-webflux-webclient");
+}
\ No newline at end of file
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/pom.xml
index 4887ce4..1444e7f 100644
--- a/apm-sniffer/apm-sdk-plugin/spring-plugins/pom.xml
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/pom.xml
@@ -40,6 +40,7 @@
         <module>mvc-annotation-5.x-plugin</module>
         <module>spring-kafka-2.x-plugin</module>
         <module>scheduled-annotation-plugin</module>
+        <module>spring-webflux-5.x-webclient-plugin</module>
     </modules>
     <packaging>pom</packaging>
 
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/pom.xml
similarity index 59%
copy from apm-sniffer/apm-sdk-plugin/spring-plugins/pom.xml
copy to apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/pom.xml
index 4887ce4..076cfa0 100644
--- a/apm-sniffer/apm-sdk-plugin/spring-plugins/pom.xml
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/pom.xml
@@ -18,37 +18,27 @@
   -->
 
 <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>
+        <artifactId>spring-plugins</artifactId>
         <groupId>org.apache.skywalking</groupId>
-        <artifactId>apm-sdk-plugin</artifactId>
         <version>8.2.0-SNAPSHOT</version>
     </parent>
+    <modelVersion>4.0.0</modelVersion>
 
-    <artifactId>spring-plugins</artifactId>
-    <modules>
-        <module>async-annotation-plugin</module>
-        <module>concurrent-util-4.x-plugin</module>
-        <module>resttemplate-4.x-plugin</module>
-        <module>mvc-annotation-4.x-plugin</module>
-        <module>spring-cloud</module>
-        <module>mvc-annotation-3.x-plugin</module>
-        <module>core-patch</module>
-        <module>mvc-annotation-commons</module>
-        <module>spring-commons</module>
-        <module>mvc-annotation-5.x-plugin</module>
-        <module>spring-kafka-2.x-plugin</module>
-        <module>scheduled-annotation-plugin</module>
-    </modules>
-    <packaging>pom</packaging>
+    <artifactId>spring-webflux-5.x-webclient-plugin</artifactId>
 
-    <name>apm-sdk-plugin</name>
     <url>http://maven.apache.org</url>
 
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webflux</artifactId>
+            <version>5.0.0.RELEASE</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
     <properties>
-        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <sdk.plugin.related.dir>/..</sdk.plugin.related.dir>
+        <compiler.version>1.8</compiler.version>
     </properties>
-
-</project>
+</project>
\ No newline at end of file
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v5/webclient/BodyInserterRequestInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v5/webclient/BodyInserterRequestInterceptor.java
new file mode 100644
index 0000000..f65d7c0
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v5/webclient/BodyInserterRequestInterceptor.java
@@ -0,0 +1,54 @@
+/*
+ * 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.webflux.v5.webclient;
+
+import org.apache.skywalking.apm.agent.core.context.CarrierItem;
+import org.apache.skywalking.apm.agent.core.context.ContextCarrier;
+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.springframework.http.client.reactive.ClientHttpRequest;
+
+import java.lang.reflect.Method;
+
+public class BodyInserterRequestInterceptor implements InstanceMethodsAroundInterceptor {
+
+    @Override
+    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
+                             MethodInterceptResult result) throws Throwable {
+        ClientHttpRequest clientHttpRequest = (ClientHttpRequest) allArguments[0];
+        ContextCarrier contextCarrier = (ContextCarrier) objInst.getSkyWalkingDynamicField();
+        CarrierItem next = contextCarrier.items();
+        while (next.hasNext()) {
+            next = next.next();
+            clientHttpRequest.getHeaders().set(next.getHeadKey(), next.getHeadValue());
+        }
+    }
+
+    @Override
+    public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
+                              Object ret) throws Throwable {
+        return ret;
+    }
+
+    @Override
+    public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
+                                      Class<?>[] argumentsTypes, Throwable t) {
+    }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v5/webclient/WebFluxWebClientInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v5/webclient/WebFluxWebClientInterceptor.java
new file mode 100644
index 0000000..1674c48
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v5/webclient/WebFluxWebClientInterceptor.java
@@ -0,0 +1,116 @@
+/*
+ * 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.webflux.v5.webclient;
+
+import org.apache.skywalking.apm.agent.core.context.ContextCarrier;
+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.springframework.http.HttpStatus;
+import org.springframework.web.reactive.function.client.ClientRequest;
+import org.springframework.web.reactive.function.client.ClientResponse;
+import reactor.core.publisher.Mono;
+
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.util.function.BiConsumer;
+
+public class WebFluxWebClientInterceptor implements InstanceMethodsAroundInterceptor {
+
+    @Override
+    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
+        if (allArguments[0] == null) {
+            //illegal args,can't trace ignore
+            return;
+        }
+
+        ClientRequest request = (ClientRequest) allArguments[0];
+        final ContextCarrier contextCarrier = new ContextCarrier();
+
+        URI uri = request.url();
+        final String requestURIString = getRequestURIString(uri);
+        final String operationName = requestURIString;
+        final String remotePeer = getIPAndPort(uri);
+        AbstractSpan span = ContextManager.createExitSpan(operationName, contextCarrier, remotePeer);
+
+        //set componet name
+        span.setComponent(ComponentsDefine.SPRING_WEBCLIENT);
+        Tags.URL.set(span, uri.toString());
+        Tags.HTTP.METHOD.set(span, request.method().toString());
+        SpanLayer.asHttp(span);
+
+        if (request instanceof EnhancedInstance) {
+            ((EnhancedInstance) request).setSkyWalkingDynamicField(contextCarrier);
+        }
+        
+        //user async interface
+        span.prepareForAsync();
+        ContextManager.stopSpan();
+
+        objInst.setSkyWalkingDynamicField(span);
+    }
+
+    @Override
+    public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object ret) throws Throwable {
+        // fix the problem that allArgument[0] may be null
+        if (allArguments[0] == null) {
+            return ret;
+        }
+        Mono<ClientResponse> ret1 = (Mono<ClientResponse>) ret;
+        AbstractSpan span = (AbstractSpan) objInst.getSkyWalkingDynamicField();
+        return ret1.doAfterSuccessOrError(new BiConsumer<ClientResponse, Throwable>() {
+            @Override
+            public void accept(ClientResponse clientResponse, Throwable throwable) {
+                HttpStatus httpStatus = clientResponse.statusCode();
+                if (httpStatus != null) {
+                    Tags.STATUS_CODE.set(span, Integer.toString(httpStatus.value()));
+                    if (httpStatus.isError()) {
+                        span.errorOccurred();
+                    }
+                }
+            }
+        }).doOnError(error -> {
+            span.log(error);
+        }).doFinally(s -> {
+            span.asyncFinish();
+        });
+    }
+
+    @Override
+    public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Throwable t) {
+        AbstractSpan activeSpan = ContextManager.activeSpan();
+        activeSpan.errorOccurred();
+        activeSpan.log(t);
+    }
+
+    private String getRequestURIString(URI uri) {
+        String requestPath = uri.getPath();
+        return requestPath != null && requestPath.length() > 0 ? requestPath : "/";
+    }
+
+    // return ip:port
+    private String getIPAndPort(URI uri) {
+        return uri.getHost() + ":" + uri.getPort();
+    }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v5/webclient/define/BodyInserterRequestInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v5/webclient/define/BodyInserterRequestInstrumentation.java
new file mode 100644
index 0000000..e58be98
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v5/webclient/define/BodyInserterRequestInstrumentation.java
@@ -0,0 +1,65 @@
+/*
+ * 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.webflux.v5.webclient.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.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType;
+import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName;
+
+public class BodyInserterRequestInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
+
+    @Override
+    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+        return new ConstructorInterceptPoint[0];
+    }
+
+    @Override
+    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+        return new InstanceMethodsInterceptPoint[]{
+                new InstanceMethodsInterceptPoint() {
+                    @Override
+                    public ElementMatcher<MethodDescription> getMethodsMatcher() {
+                        return named("writeTo").and(takesArgumentWithType(0, "org.springframework.http.client.reactive.ClientHttpRequest"));
+                    }
+
+                    @Override
+                    public String getMethodsInterceptor() {
+                        return "org.apache.skywalking.apm.plugin.spring.webflux.v5.webclient.BodyInserterRequestInterceptor";
+                    }
+
+                    @Override
+                    public boolean isOverrideArgs() {
+                        return false;
+                    }
+                }
+        };
+    }
+
+    @Override
+    protected ClassMatch enhanceClass() {
+        return byName("org.springframework.web.reactive.function.client.DefaultClientRequestBuilder$BodyInserterRequest");
+    }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v5/webclient/define/WebFluxWebClientInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v5/webclient/define/WebFluxWebClientInstrumentation.java
new file mode 100644
index 0000000..1b2652e
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v5/webclient/define/WebFluxWebClientInstrumentation.java
@@ -0,0 +1,72 @@
+/*
+ * 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.webflux.v5.webclient.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.StaticMethodsInterceptPoint;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassEnhancePluginDefine;
+import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
+import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch;
+
+import static net.bytebuddy.matcher.ElementMatchers.named;
+
+public class WebFluxWebClientInstrumentation extends ClassEnhancePluginDefine {
+    private static final String ENHANCE_CLASS = "org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction";
+    private static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.spring.webflux.v5.webclient.WebFluxWebClientInterceptor";
+
+    @Override
+    protected ClassMatch enhanceClass() {
+        return NameMatch.byName(ENHANCE_CLASS);
+    }
+
+    @Override
+    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+        return new ConstructorInterceptPoint[0];
+    }
+
+    @Override
+    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+        return new InstanceMethodsInterceptPoint[]{
+                new InstanceMethodsInterceptPoint() {
+                    @Override
+                    public ElementMatcher<MethodDescription> getMethodsMatcher() {
+                        return named("exchange");
+                    }
+
+                    @Override
+                    public String getMethodsInterceptor() {
+                        return INTERCEPT_CLASS;
+                    }
+
+                    @Override
+                    public boolean isOverrideArgs() {
+                        return false;
+                    }
+                }
+        };
+    }
+
+    @Override
+    public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
+        return new StaticMethodsInterceptPoint[0];
+    }
+}
\ No newline at end of file
diff --git a/test/plugin/scenarios/webflux-scenario/support-version.list b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/src/main/resources/skywalking-plugin.def
similarity index 75%
copy from test/plugin/scenarios/webflux-scenario/support-version.list
copy to apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/src/main/resources/skywalking-plugin.def
index c5820da..260ce00 100644
--- a/test/plugin/scenarios/webflux-scenario/support-version.list
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/spring-webflux-5.x-webclient-plugin/src/main/resources/skywalking-plugin.def
@@ -14,7 +14,5 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# 2.0.0-2.1.0 are supported, but due to the status code bug(https://github.com/spring-projects/spring-framework/issues/21901)
-# we don’t test them
-
-2.1.7.RELEASE
+spring-webflux-5.x-webclient=org.apache.skywalking.apm.plugin.spring.webflux.v5.webclient.define.BodyInserterRequestInstrumentation
+spring-webflux-5.x-webclient=org.apache.skywalking.apm.plugin.spring.webflux.v5.webclient.define.WebFluxWebClientInstrumentation
diff --git a/docs/en/setup/service-agent/java-agent/Plugin-list.md b/docs/en/setup/service-agent/java-agent/Plugin-list.md
index 4d4c9fc..79a1410 100644
--- a/docs/en/setup/service-agent/java-agent/Plugin-list.md
+++ b/docs/en/setup/service-agent/java-agent/Plugin-list.md
@@ -87,6 +87,7 @@
 - spring-scheduled-annotation
 - spring-tx
 - spring-webflux-5.x
+- spring-webflux-5.x-webclient
 - spymemcached-2.x
 - struts2-2.x
 - tomcat-7.x/8.x
diff --git a/oap-server/server-bootstrap/src/main/resources/component-libraries.yml b/oap-server/server-bootstrap/src/main/resources/component-libraries.yml
index b12d93d..d86fe6d 100755
--- a/oap-server/server-bootstrap/src/main/resources/component-libraries.yml
+++ b/oap-server/server-bootstrap/src/main/resources/component-libraries.yml
@@ -326,6 +326,9 @@ quartz-scheduler:
 xxl-job:
   id: 98
   languages: Java
+spring-webflux-webclient:
+  id: 99
+  languages: Java
 
 # .NET/.NET Core components
 # [3000, 4000) for C#/.NET only
diff --git a/test/e2e/e2e-protocol/src/main/proto b/test/e2e/e2e-protocol/src/main/proto
index b2f381e..9933e2d 160000
--- a/test/e2e/e2e-protocol/src/main/proto
+++ b/test/e2e/e2e-protocol/src/main/proto
@@ -1 +1 @@
-Subproject commit b2f381e63702bc43216ef5576637195102302c6e
+Subproject commit 9933e2d17078c2bf07cd1c8d5ef36d52b5cbb917
diff --git a/test/plugin/scenarios/webflux-scenario/config/expectedData.yaml b/test/plugin/scenarios/webflux-scenario/config/expectedData.yaml
index f96d6ff..d6c62c7 100644
--- a/test/plugin/scenarios/webflux-scenario/config/expectedData.yaml
+++ b/test/plugin/scenarios/webflux-scenario/config/expectedData.yaml
@@ -193,6 +193,28 @@ segmentItems:
         parentSpanId: 8, parentTraceSegmentId: not null, parentServiceInstance: not
           null, parentService: not null, traceId: not null}
       skipAnalysis: 'false'
+  - segmentId: not null
+    spans:
+    - operationName: /testcase/webclient/server
+      operationId: 0
+      parentSpanId: -1
+      spanId: 0
+      spanLayer: Http
+      startTime: not null
+      endTime: not null
+      componentId: 67
+      isError: false
+      spanType: Entry
+      peer: ''
+      tags:
+      - {key: url, value: 'http://localhost:18080/testcase/webclient/server'}
+      - {key: http.method, value: GET}
+      - {key: status_code, value: '200'}
+      refs:
+      - {parentEndpoint: /projectA/testcase, networkAddress: 'localhost:18080', refType: CrossProcess,
+        parentSpanId: 9, parentTraceSegmentId: not null, parentServiceInstance: not
+          null, parentService: not null, traceId: not null}
+      skipAnalysis: 'false'
 - serviceName: webflux-projectA-scenario
   segmentSize: nq 0
   segments:
@@ -321,6 +343,22 @@ segmentItems:
       - {key: url, value: not null}
       - {key: http.method, value: GET}
       skipAnalysis: 'false'
+    - operationName: /testcase/webclient/server
+      operationId: 0
+      parentSpanId: 0
+      spanId: 9
+      spanLayer: Http
+      startTime: not null
+      endTime: not null
+      componentId: 99
+      isError: false
+      spanType: Exit
+      peer: not null
+      tags:
+      - {key: url, value: not null}
+      - {key: http.method, value: GET}
+      - {key: status_code, value:'200'}
+      skipAnalysis: 'false'
     - operationName: /projectA/testcase
       operationId: 0
       parentSpanId: -1
diff --git a/test/plugin/scenarios/webflux-scenario/support-version.list b/test/plugin/scenarios/webflux-scenario/support-version.list
index c5820da..ea20445 100644
--- a/test/plugin/scenarios/webflux-scenario/support-version.list
+++ b/test/plugin/scenarios/webflux-scenario/support-version.list
@@ -16,5 +16,6 @@
 
 # 2.0.0-2.1.0 are supported, but due to the status code bug(https://github.com/spring-projects/spring-framework/issues/21901)
 # we don’t test them
-
-2.1.7.RELEASE
+2.1.17.RELEASE
+2.2.10.RELEASE
+2.3.4.RELEASE
diff --git a/test/plugin/scenarios/webflux-scenario/webflux-projectA-scenario/pom.xml b/test/plugin/scenarios/webflux-scenario/webflux-projectA-scenario/pom.xml
index b8fecfd..966af80 100644
--- a/test/plugin/scenarios/webflux-scenario/webflux-projectA-scenario/pom.xml
+++ b/test/plugin/scenarios/webflux-scenario/webflux-projectA-scenario/pom.xml
@@ -39,6 +39,16 @@
             <artifactId>httpclient</artifactId>
             <version>4.5.6</version>
         </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webflux</artifactId>
+            <version>5.2.9.RELEASE</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-reactor-netty</artifactId>
+            <version>2.3.4.RELEASE</version>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/test/plugin/scenarios/webflux-scenario/webflux-projectA-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectA/controller/TestController.java b/test/plugin/scenarios/webflux-scenario/webflux-projectA-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectA/controller/TestController.java
index 4205145..b11f743 100644
--- a/test/plugin/scenarios/webflux-scenario/webflux-projectA-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectA/controller/TestController.java
+++ b/test/plugin/scenarios/webflux-scenario/webflux-projectA-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectA/controller/TestController.java
@@ -17,12 +17,15 @@
 
 package org.apache.skywalking.apm.testcase.sc.webflux.projectA.controller;
 
-import java.io.IOException;
 import org.apache.skywalking.apm.testcase.sc.webflux.projectA.utils.HttpUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Mono;
+
+import java.io.IOException;
 
 @RestController
 public class TestController {
@@ -43,6 +46,7 @@ public class TestController {
         visit("http://" + hostBAddress + "/testcase/route/error");
         visit("http://" + hostBAddress + "/notFound");
         visit("http://" + hostBAddress + "/testcase/annotation/mono/hello");
+        testGet("http://" + hostBAddress + "/testcase/webclient/server");
         return "test";
     }
 
@@ -59,4 +63,17 @@ public class TestController {
 
         }
     }
+
+    /**
+     * test webflux webclient plugin
+     */
+    private void testGet(String remoteUri) {
+        Mono<String> response = WebClient
+                .create()
+                .get()
+                .uri(remoteUri)
+                .retrieve()
+                .bodyToMono(String.class);
+        response.subscribe();
+    }
 }
diff --git a/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/webflux/projectB/controller/TestAnnotationController.java b/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/webflux/projectB/controller/TestAnnotationController.java
index dfd121f..293dc90 100644
--- a/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/webflux/projectB/controller/TestAnnotationController.java
+++ b/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/webflux/projectB/controller/TestAnnotationController.java
@@ -44,6 +44,11 @@ public class TestAnnotationController {
         }
         return "1";
     }
+    
+    @RequestMapping("/testcase/webclient/server")
+    public String webclientServer(){
+        return "success";
+    }
 
     @GetMapping("/testcase/annotation/{test}")
     public Mono<String> urlPattern(@PathVariable("test") String var) {