You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shenyu.apache.org by zh...@apache.org on 2022/12/08 06:18:29 UTC

[shenyu] branch master updated: [ISSUE #3450]Add brpc-client&starter-client-brpc (#4149)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 8b1f8608e [ISSUE #3450]Add brpc-client&starter-client-brpc (#4149)
8b1f8608e is described below

commit 8b1f8608ef8f20c17d3157998353eb3ed18dd324
Author: SongTao Zhuang <51...@users.noreply.github.com>
AuthorDate: Thu Dec 8 14:18:22 2022 +0800

    [ISSUE #3450]Add brpc-client&starter-client-brpc (#4149)
    
    * add: brpc client
    
    * add: brpc plugin enum
    
    * add: license header
    
    * pom: starlight stable version
    
    Co-authored-by: dragon-zhang <zh...@apache.org>
---
 pom.xml                                            |   1 +
 shenyu-client/pom.xml                              |   1 +
 shenyu-client/{ => shenyu-client-brpc}/pom.xml     |  40 ++---
 .../brpc/BrpcContextRefreshedEventListener.java    | 161 +++++++++++++++++++++
 .../brpc/common/annotation/ShenyuBrpcClient.java   |  72 +++++++++
 .../shenyu/client/brpc/common/dto/BrpcRpcExt.java  | 145 +++++++++++++++++++
 .../client/brpc/BrpcClientEventListenerTest.java   | 117 +++++++++++++++
 .../org/apache/shenyu/common/enums/PluginEnum.java |   5 +
 .../apache/shenyu/common/enums/RpcTypeEnum.java    |   6 +-
 .../shenyu/common/utils/PluginNameAdapter.java     |   2 +
 .../shenyu-spring-boot-starter-client/pom.xml      |   1 +
 .../pom.xml                                        |  42 +++---
 .../client/brpc/ShenyuBrpcClientConfiguration.java |  49 +++++++
 .../src/main/resources/META-INF/spring.factories   |  19 +++
 .../src/main/resources/META-INF/spring.provides    |  18 +++
 .../brpc/ShenyuBrpcClientConfigurationTest.java    |  73 ++++++++++
 16 files changed, 703 insertions(+), 49 deletions(-)

diff --git a/pom.xml b/pom.xml
index 9eb182996..0d8c4ae20 100644
--- a/pom.xml
+++ b/pom.xml
@@ -125,6 +125,7 @@
         <nacos-client.version>2.0.4</nacos-client.version>
         <spring-security.version>5.3.10.RELEASE</spring-security.version>
         <grpc.version>1.33.1</grpc.version>
+        <brpc.version>2022.2.0</brpc.version>
         <rocketmq-client.version>4.9.3</rocketmq-client.version>
         <pulsar-client.version>2.10.1</pulsar-client.version>
         <lz4-java.version>1.8.0</lz4-java.version>
diff --git a/shenyu-client/pom.xml b/shenyu-client/pom.xml
index 93965453d..7f669abe8 100644
--- a/shenyu-client/pom.xml
+++ b/shenyu-client/pom.xml
@@ -35,6 +35,7 @@
         <module>shenyu-client-grpc</module>
         <module>shenyu-client-motan</module>
         <module>shenyu-client-websocket</module>
+        <module>shenyu-client-brpc</module>
     </modules>
 
     <dependencies>
diff --git a/shenyu-client/pom.xml b/shenyu-client/shenyu-client-brpc/pom.xml
similarity index 52%
copy from shenyu-client/pom.xml
copy to shenyu-client/shenyu-client-brpc/pom.xml
index 93965453d..c649a2c95 100644
--- a/shenyu-client/pom.xml
+++ b/shenyu-client/shenyu-client-brpc/pom.xml
@@ -16,42 +16,34 @@
   ~ limitations under the License.
   -->
 
-<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">
+<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">
     <parent>
         <groupId>org.apache.shenyu</groupId>
-        <artifactId>shenyu</artifactId>
+        <artifactId>shenyu-client</artifactId>
         <version>2.5.1-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
-    <artifactId>shenyu-client</artifactId>
-    <packaging>pom</packaging>
-    
-    <modules>
-        <module>shenyu-client-core</module>
-        <module>shenyu-client-http</module>
-        <module>shenyu-client-dubbo</module>
-        <module>shenyu-client-sofa</module>
-        <module>shenyu-client-tars</module>
-        <module>shenyu-client-grpc</module>
-        <module>shenyu-client-motan</module>
-        <module>shenyu-client-websocket</module>
-    </modules>
+
+    <artifactId>shenyu-client-brpc</artifactId>
 
     <dependencies>
         <dependency>
-            <groupId>com.squareup.okhttp3</groupId>
-            <artifactId>okhttp</artifactId>
-            <version>${okhttp.version}</version>
+            <groupId>org.apache.shenyu</groupId>
+            <artifactId>shenyu-client-core</artifactId>
+            <version>${project.version}</version>
         </dependency>
+
         <dependency>
-            <groupId>com.google.code.gson</groupId>
-            <artifactId>gson</artifactId>
-            <version>${gson.version}</version>
+            <groupId>com.baidu.cloud</groupId>
+            <artifactId>spring-cloud-starter-baidu-starlight</artifactId>
+            <version>2022.2.0</version>
         </dependency>
         <dependency>
-            <groupId>org.springframework</groupId>
-            <artifactId>spring-test</artifactId>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-test</artifactId>
             <scope>test</scope>
         </dependency>
     </dependencies>
-</project>
+</project>
\ No newline at end of file
diff --git a/shenyu-client/shenyu-client-brpc/src/main/java/org/apache/shenyu/client/brpc/BrpcContextRefreshedEventListener.java b/shenyu-client/shenyu-client-brpc/src/main/java/org/apache/shenyu/client/brpc/BrpcContextRefreshedEventListener.java
new file mode 100644
index 000000000..9dbb06285
--- /dev/null
+++ b/shenyu-client/shenyu-client-brpc/src/main/java/org/apache/shenyu/client/brpc/BrpcContextRefreshedEventListener.java
@@ -0,0 +1,161 @@
+/*
+ * 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.shenyu.client.brpc;
+
+import com.baidu.cloud.starlight.springcloud.server.annotation.RpcService;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.shenyu.client.brpc.common.annotation.ShenyuBrpcClient;
+import org.apache.shenyu.client.brpc.common.dto.BrpcRpcExt;
+import org.apache.shenyu.client.core.client.AbstractContextRefreshedEventListener;
+import org.apache.shenyu.client.core.disruptor.ShenyuClientRegisterEventPublisher;
+import org.apache.shenyu.common.enums.RpcTypeEnum;
+import org.apache.shenyu.common.utils.GsonUtils;
+import org.apache.shenyu.common.utils.IpUtils;
+import org.apache.shenyu.register.client.api.ShenyuClientRegisterRepository;
+import org.apache.shenyu.register.common.config.PropertiesConfig;
+import org.apache.shenyu.register.common.dto.MetaDataRegisterDTO;
+import org.apache.shenyu.register.common.dto.URIRegisterDTO;
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
+import org.springframework.util.ReflectionUtils;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * The type Brpc context refreshed event listener.
+ */
+public class BrpcContextRefreshedEventListener extends AbstractContextRefreshedEventListener<Object, ShenyuBrpcClient> {
+
+    private final LocalVariableTableParameterNameDiscoverer localVariableTableParameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
+
+    private final ShenyuClientRegisterEventPublisher publisher = ShenyuClientRegisterEventPublisher.getInstance();
+
+    private ApplicationContext applicationContext;
+
+    /**
+     * Instantiates a new Brpc context refreshed event listener.
+     *
+     * @param clientConfig                   the client config
+     * @param shenyuClientRegisterRepository the shenyuClientRegisterRepository
+     */
+    public BrpcContextRefreshedEventListener(final PropertiesConfig clientConfig,
+                                             final ShenyuClientRegisterRepository shenyuClientRegisterRepository) {
+        super(clientConfig, shenyuClientRegisterRepository);
+    }
+
+    @Override
+    protected Map<String, Object> getBeans(final ApplicationContext context) {
+        applicationContext = context;
+        return context.getBeansWithAnnotation(ShenyuBrpcClient.class);
+    }
+
+    @Override
+    protected URIRegisterDTO buildURIRegisterDTO(final ApplicationContext context, final Map<String, Object> beans) {
+        return URIRegisterDTO.builder()
+                .contextPath(this.getContextPath())
+                .appName(this.getAppName())
+                .rpcType(RpcTypeEnum.BRPC.getName())
+                .host(this.getHost())
+                .port(Integer.parseInt(this.getPort()))
+                .build();
+    }
+
+    @Override
+    protected String buildApiSuperPath(final Class<?> clazz, final ShenyuBrpcClient shenyuBrpcClient) {
+        if (Objects.nonNull(shenyuBrpcClient) && !StringUtils.isBlank(shenyuBrpcClient.path())) {
+            return shenyuBrpcClient.path();
+        }
+        return "";
+    }
+
+    @Override
+    protected Class<ShenyuBrpcClient> getAnnotationType() {
+        return ShenyuBrpcClient.class;
+    }
+
+    @Override
+    protected String buildApiPath(final Method method, final String superPath, final ShenyuBrpcClient shenyuBrpcClient) {
+        return superPath.contains("*")
+                ? pathJoin(this.getContextPath(), superPath.replace("*", ""), method.getName())
+                : pathJoin(this.getContextPath(), superPath, shenyuBrpcClient.path());
+    }
+
+    @Override
+    protected void handleClass(final Class<?> clazz, final Object bean, final ShenyuBrpcClient shenyuBrpcClient, final String superPath) {
+        Method[] methods = ReflectionUtils.getDeclaredMethods(clazz);
+        for (Method method : methods) {
+            publisher.publishEvent(buildMetaDataDTO(bean, shenyuBrpcClient, superPath, clazz, method));
+        }
+    }
+
+    @Override
+    protected MetaDataRegisterDTO buildMetaDataDTO(final Object bean, final ShenyuBrpcClient shenyuBrpcClient, final String superPath, final Class<?> clazz, final Method method) {
+        String serviceName = clazz.getAnnotation(RpcService.class).serviceId();
+        String path = shenyuBrpcClient.path();
+        String desc = shenyuBrpcClient.desc();
+        String host = IpUtils.isCompleteHost(this.getHost()) ? this.getHost() : IpUtils.getHost(this.getHost());
+        String configRuleName = shenyuBrpcClient.ruleName();
+        String ruleName = ("".equals(configRuleName)) ? path : configRuleName;
+        int port = StringUtils.isBlank(this.getPort()) ? -1 : Integer.parseInt(this.getPort());
+        String methodName = method.getName();
+        Class<?>[] parameterTypesClazz = method.getParameterTypes();
+        String parameterTypes = Arrays.stream(parameterTypesClazz).map(Class::getName)
+                .collect(Collectors.joining(","));
+        return MetaDataRegisterDTO.builder()
+                .appName(this.getAppName())
+                .serviceName(serviceName)
+                .methodName(methodName)
+                .contextPath(this.getContextPath())
+                .path(path)
+                .port(port)
+                .host(host)
+                .pathDesc(desc)
+                .ruleName(ruleName)
+                .parameterTypes(parameterTypes)
+                .rpcType(RpcTypeEnum.BRPC.getName())
+                .rpcExt(buildRpcExt(method))
+                .enabled(shenyuBrpcClient.enabled())
+                .build();
+    }
+
+    private String buildRpcExt(final Method method) {
+        List<BrpcRpcExt.RpcExt> list = new ArrayList<>();
+        list.add(build(method));
+        BrpcRpcExt buildList = new BrpcRpcExt(list);
+        return GsonUtils.getInstance().toJson(buildList);
+    }
+
+    private BrpcRpcExt.RpcExt build(final Method method) {
+        String[] paramNames = localVariableTableParameterNameDiscoverer.getParameterNames(method);
+        List<Pair<String, String>> params = new ArrayList<>();
+        if (paramNames != null && paramNames.length > 0) {
+            Class<?>[] paramTypes = method.getParameterTypes();
+            for (int i = 0; i < paramNames.length; i++) {
+                params.add(Pair.of(paramTypes[i].getName(), paramNames[i]));
+            }
+        }
+        return new BrpcRpcExt.RpcExt(method.getName(), params);
+    }
+}
diff --git a/shenyu-client/shenyu-client-brpc/src/main/java/org/apache/shenyu/client/brpc/common/annotation/ShenyuBrpcClient.java b/shenyu-client/shenyu-client-brpc/src/main/java/org/apache/shenyu/client/brpc/common/annotation/ShenyuBrpcClient.java
new file mode 100644
index 000000000..994026ba5
--- /dev/null
+++ b/shenyu-client/shenyu-client-brpc/src/main/java/org/apache/shenyu/client/brpc/common/annotation/ShenyuBrpcClient.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.shenyu.client.brpc.common.annotation;
+
+import org.springframework.core.annotation.AliasFor;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The interface shenyu client.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Inherited
+public @interface ShenyuBrpcClient {
+    
+    /**
+     * Value string.
+     *
+     * @return the string
+     */
+    @AliasFor(attribute = "path")
+    String value() default "";
+    
+    /**
+     * Path string.
+     *
+     * @return the string
+     */
+    @AliasFor(attribute = "value")
+    String path() default "";
+    
+    /**
+     * Rule name string.
+     *
+     * @return the string
+     */
+    String ruleName() default "";
+
+    /**
+     * Desc string.
+     *
+     * @return String string
+     */
+    String desc() default "";
+
+    /**
+     * Enabled boolean.
+     *
+     * @return the boolean
+     */
+    boolean enabled() default true;
+}
diff --git a/shenyu-client/shenyu-client-brpc/src/main/java/org/apache/shenyu/client/brpc/common/dto/BrpcRpcExt.java b/shenyu-client/shenyu-client-brpc/src/main/java/org/apache/shenyu/client/brpc/common/dto/BrpcRpcExt.java
new file mode 100644
index 000000000..07b82bb72
--- /dev/null
+++ b/shenyu-client/shenyu-client-brpc/src/main/java/org/apache/shenyu/client/brpc/common/dto/BrpcRpcExt.java
@@ -0,0 +1,145 @@
+/*
+ * 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.shenyu.client.brpc.common.dto;
+
+import org.apache.commons.lang3.tuple.Pair;
+
+import java.util.List;
+
+/**
+ * Brpc rpc ext.
+ */
+public class BrpcRpcExt {
+
+    /**
+     * in order to be compatible with the old version,
+     * we can't change the type of this field.
+     */
+    private List<RpcExt> methodInfo;
+
+    /**
+     * constructor without params.
+     */
+    public BrpcRpcExt() {
+    }
+
+    /**
+     * constructor with all params.
+     *
+     * @param methodInfo methodInfo
+     */
+    public BrpcRpcExt(final List<RpcExt> methodInfo) {
+        this.methodInfo = methodInfo;
+    }
+
+    /**
+     * get methodInfo.
+     *
+     * @return methodInfo
+     */
+    public List<RpcExt> getMethodInfo() {
+        return methodInfo;
+    }
+
+    /**
+     * set methodInfo.
+     *
+     * @param methodInfo methodInfo
+     */
+    public void setMethodInfo(final List<RpcExt> methodInfo) {
+        this.methodInfo = methodInfo;
+    }
+
+    @Override
+    public String toString() {
+        return "BrpcRpcExt{"
+                + "methodInfo=" + methodInfo
+                + '}';
+    }
+
+    /**
+     * The type Rpc ext.
+     */
+    public static class RpcExt {
+        
+        private String methodName;
+        
+        private List<Pair<String, String>> paramTypes;
+
+        /**
+         * constructor without params.
+         */
+        public RpcExt() {
+        }
+
+        /**
+         * constructor with params.
+         *
+         * @param methodName methodName
+         * @param paramTypes params
+         */
+        public RpcExt(final String methodName, final List<Pair<String, String>> paramTypes) {
+            this.methodName = methodName;
+            this.paramTypes = paramTypes;
+        }
+
+        /**
+         * get methodName.
+         *
+         * @return methodName
+         */
+        public String getMethodName() {
+            return methodName;
+        }
+
+        /**
+         * set methodName.
+         *
+         * @param methodName methodName
+         */
+        public void setMethodName(final String methodName) {
+            this.methodName = methodName;
+        }
+
+        /**
+         * get param types.
+         *
+         * @return param types.
+         */
+        public List<Pair<String, String>> getParamTypes() {
+            return paramTypes;
+        }
+
+        /**
+         * set param types.
+         *
+         * @param paramTypes param types
+         */
+        public void setParamTypes(final List<Pair<String, String>> paramTypes) {
+            this.paramTypes = paramTypes;
+        }
+
+        @Override
+        public String toString() {
+            return "RpcExt{"
+                    + "methodName='" + methodName + '\''
+                    + ", paramTypes=" + paramTypes
+                    + '}';
+        }
+    }
+}
diff --git a/shenyu-client/shenyu-client-brpc/src/test/java/org/apache/shenyu/client/brpc/BrpcClientEventListenerTest.java b/shenyu-client/shenyu-client-brpc/src/test/java/org/apache/shenyu/client/brpc/BrpcClientEventListenerTest.java
new file mode 100644
index 000000000..815a7056b
--- /dev/null
+++ b/shenyu-client/shenyu-client-brpc/src/test/java/org/apache/shenyu/client/brpc/BrpcClientEventListenerTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.shenyu.client.brpc;
+
+import com.baidu.cloud.starlight.springcloud.server.annotation.RpcService;
+import org.apache.shenyu.client.brpc.common.annotation.ShenyuBrpcClient;
+import org.apache.shenyu.client.core.register.ShenyuClientRegisterRepositoryFactory;
+import org.apache.shenyu.register.client.http.utils.RegisterUtils;
+import org.apache.shenyu.register.common.config.PropertiesConfig;
+import org.apache.shenyu.register.common.config.ShenyuRegisterCenterConfig;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.stubbing.Answer;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.event.ContextRefreshedEvent;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Properties;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test for {@link BrpcClientEventListenerTest}.
+ */
+@ExtendWith(MockitoExtension.class)
+public class BrpcClientEventListenerTest {
+    
+    private final MockedStatic<RegisterUtils> registerUtilsMockedStatic = mockStatic(RegisterUtils.class);
+    
+    private final BrpcClientTestBean brpcClientTestBean = new BrpcClientTestBean();
+    
+    @Mock
+    private ApplicationContext applicationContext;
+    
+    private ContextRefreshedEvent contextRefreshedEvent;
+    
+    @BeforeEach
+    public void init() {
+        Map<String, Object> results = new LinkedHashMap<>();
+        results.put("brpcClientTestBean", brpcClientTestBean);
+        when(applicationContext.getBeansWithAnnotation(any())).thenReturn(results);
+        contextRefreshedEvent = new ContextRefreshedEvent(applicationContext);
+    }
+    
+    @Test
+    public void testNormalBeanProcess() {
+        registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.of("token"));
+        BrpcContextRefreshedEventListener springMvcClientEventListener = buildBrpcClientEventListener();
+        springMvcClientEventListener.onApplicationEvent(contextRefreshedEvent);
+        verify(applicationContext, times(1)).getBeansWithAnnotation(any());
+        registerUtilsMockedStatic.close();
+    }
+    
+    @Test
+    public void testWithShenyuClientAnnotation() {
+        registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.of("token"));
+        registerUtilsMockedStatic.when(() -> RegisterUtils.doRegister(any(), any(), any()))
+                .thenAnswer((Answer<Void>) invocation -> null);
+        BrpcContextRefreshedEventListener springMvcClientEventListener = buildBrpcClientEventListener();
+        springMvcClientEventListener.onApplicationEvent(contextRefreshedEvent);
+        verify(applicationContext, times(1)).getBeansWithAnnotation(any());
+        registerUtilsMockedStatic.close();
+    }
+    
+    private BrpcContextRefreshedEventListener buildBrpcClientEventListener() {
+        Properties properties = new Properties();
+        properties.setProperty("appName", "brpc");
+        properties.setProperty("contextPath", "/brpc");
+        properties.setProperty("ipAndPort", "127.0.0.1:21715");
+        properties.setProperty("host", "127.0.0.1");
+        properties.setProperty("port", "21715");
+        properties.setProperty("username", "admin");
+        properties.setProperty("password", "123456");
+        PropertiesConfig config = new PropertiesConfig();
+        config.setProps(properties);
+        ShenyuRegisterCenterConfig mockRegisterCenter = new ShenyuRegisterCenterConfig();
+        mockRegisterCenter.setServerLists("http://127.0.0.1:9095");
+        mockRegisterCenter.setRegisterType("http");
+        mockRegisterCenter.setProps(properties);
+        return new BrpcContextRefreshedEventListener(config, ShenyuClientRegisterRepositoryFactory.newInstance(mockRegisterCenter));
+    }
+
+    @RpcService
+    static class BrpcClientTestBean {
+        
+        @ShenyuBrpcClient("/hello")
+        public String hello(final String input) {
+            return "hello:" + input;
+        }
+    }
+    
+}
diff --git a/shenyu-common/src/main/java/org/apache/shenyu/common/enums/PluginEnum.java b/shenyu-common/src/main/java/org/apache/shenyu/common/enums/PluginEnum.java
index ab21df89a..05a0c8b41 100644
--- a/shenyu-common/src/main/java/org/apache/shenyu/common/enums/PluginEnum.java
+++ b/shenyu-common/src/main/java/org/apache/shenyu/common/enums/PluginEnum.java
@@ -237,6 +237,11 @@ public enum PluginEnum {
      */
     MOTAN(310, 0, "motan"),
 
+    /**
+     * Motan plugin enum.
+     */
+    BRPC(310, 0, "brpc"),
+
     /**
      * Cryptor response plugin enum.
      */
diff --git a/shenyu-common/src/main/java/org/apache/shenyu/common/enums/RpcTypeEnum.java b/shenyu-common/src/main/java/org/apache/shenyu/common/enums/RpcTypeEnum.java
index 572aee40f..4785fe276 100644
--- a/shenyu-common/src/main/java/org/apache/shenyu/common/enums/RpcTypeEnum.java
+++ b/shenyu-common/src/main/java/org/apache/shenyu/common/enums/RpcTypeEnum.java
@@ -66,8 +66,12 @@ public enum RpcTypeEnum {
     /**
      * grpc.
      */
-    GRPC("grpc", true);
+    GRPC("grpc", true),
 
+    /**
+     * brpc.
+     */
+    BRPC("brpc", true);
 
     private final String name;
 
diff --git a/shenyu-common/src/main/java/org/apache/shenyu/common/utils/PluginNameAdapter.java b/shenyu-common/src/main/java/org/apache/shenyu/common/utils/PluginNameAdapter.java
index 146768bd7..a82e8ba76 100644
--- a/shenyu-common/src/main/java/org/apache/shenyu/common/utils/PluginNameAdapter.java
+++ b/shenyu-common/src/main/java/org/apache/shenyu/common/utils/PluginNameAdapter.java
@@ -48,6 +48,8 @@ public class PluginNameAdapter {
                 return PluginEnum.WEB_SOCKET.getName();
             case MOTAN:
                 return PluginEnum.MOTAN.getName();
+            case BRPC:
+                return PluginEnum.BRPC.getName();
             case HTTP:
             default:
                 return PluginEnum.DIVIDE.getName();
diff --git a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/pom.xml b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/pom.xml
index 84535744a..e70a80d46 100644
--- a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/pom.xml
+++ b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/pom.xml
@@ -37,5 +37,6 @@
         <module>shenyu-spring-boot-starter-client-common</module>
         <module>shenyu-spring-boot-starter-client-motan</module>
         <module>shenyu-spring-boot-starter-client-spring-websocket</module>
+        <module>shenyu-spring-boot-starter-client-brpc</module>
     </modules>
 </project>
diff --git a/shenyu-client/pom.xml b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-brpc/pom.xml
similarity index 52%
copy from shenyu-client/pom.xml
copy to shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-brpc/pom.xml
index 93965453d..0f95cfba3 100644
--- a/shenyu-client/pom.xml
+++ b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-brpc/pom.xml
@@ -16,42 +16,36 @@
   ~ limitations under the License.
   -->
 
-<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">
+<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">
     <parent>
+        <artifactId>shenyu-spring-boot-starter-client</artifactId>
         <groupId>org.apache.shenyu</groupId>
-        <artifactId>shenyu</artifactId>
         <version>2.5.1-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
-    <artifactId>shenyu-client</artifactId>
-    <packaging>pom</packaging>
-    
-    <modules>
-        <module>shenyu-client-core</module>
-        <module>shenyu-client-http</module>
-        <module>shenyu-client-dubbo</module>
-        <module>shenyu-client-sofa</module>
-        <module>shenyu-client-tars</module>
-        <module>shenyu-client-grpc</module>
-        <module>shenyu-client-motan</module>
-        <module>shenyu-client-websocket</module>
-    </modules>
+    <artifactId>shenyu-spring-boot-starter-client-brpc</artifactId>
 
     <dependencies>
         <dependency>
-            <groupId>com.squareup.okhttp3</groupId>
-            <artifactId>okhttp</artifactId>
-            <version>${okhttp.version}</version>
+            <groupId>org.apache.shenyu</groupId>
+            <artifactId>shenyu-client-brpc</artifactId>
+            <version>${project.version}</version>
         </dependency>
         <dependency>
-            <groupId>com.google.code.gson</groupId>
-            <artifactId>gson</artifactId>
-            <version>${gson.version}</version>
+            <groupId>org.apache.shenyu</groupId>
+            <artifactId>shenyu-spring-boot-starter-client-common</artifactId>
+            <version>${project.version}</version>
         </dependency>
         <dependency>
-            <groupId>org.springframework</groupId>
-            <artifactId>spring-test</artifactId>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
             <scope>test</scope>
         </dependency>
     </dependencies>
-</project>
+</project>
\ No newline at end of file
diff --git a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-brpc/src/main/java/org/apache/shenyu/springboot/starter/client/brpc/ShenyuBrpcClientConfiguration.java b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-brpc/src/main/java/org/apache/shenyu/springboot/starter/client/brpc/ShenyuBrpcClientConfiguration.java
new file mode 100644
index 000000000..c352de42f
--- /dev/null
+++ b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-brpc/src/main/java/org/apache/shenyu/springboot/starter/client/brpc/ShenyuBrpcClientConfiguration.java
@@ -0,0 +1,49 @@
+/*
+ * 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.shenyu.springboot.starter.client.brpc;
+
+import org.apache.shenyu.client.brpc.BrpcContextRefreshedEventListener;
+import org.apache.shenyu.common.enums.RpcTypeEnum;
+import org.apache.shenyu.register.client.api.ShenyuClientRegisterRepository;
+import org.apache.shenyu.register.common.config.ShenyuClientConfig;
+import org.apache.shenyu.springboot.starter.client.common.config.ShenyuClientCommonBeanConfiguration;
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * The type shenyu brpc client configuration.
+ */
+@Configuration
+@ImportAutoConfiguration(ShenyuClientCommonBeanConfiguration.class)
+@ConditionalOnProperty(value = "shenyu.register.enabled", matchIfMissing = true, havingValue = "true")
+public class ShenyuBrpcClientConfiguration {
+
+    /**
+     * Brpc context refreshed event listener.
+     *
+     * @param clientConfig                   the client config
+     * @param shenyuClientRegisterRepository the shenyuClientRegisterRepository
+     * @return the brpc context refreshed event listener
+     */
+    @Bean
+    public BrpcContextRefreshedEventListener brpcContextRefreshedEventListener(final ShenyuClientConfig clientConfig, final ShenyuClientRegisterRepository shenyuClientRegisterRepository) {
+        return new BrpcContextRefreshedEventListener(clientConfig.getClient().get(RpcTypeEnum.BRPC.getName()), shenyuClientRegisterRepository);
+    }
+}
diff --git a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-brpc/src/main/resources/META-INF/spring.factories b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-brpc/src/main/resources/META-INF/spring.factories
new file mode 100644
index 000000000..4a3e943af
--- /dev/null
+++ b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-brpc/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+org.apache.shenyu.springboot.starter.client.brpc.ShenyuBrpcClientConfiguration
diff --git a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-brpc/src/main/resources/META-INF/spring.provides b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-brpc/src/main/resources/META-INF/spring.provides
new file mode 100644
index 000000000..aa38dbf21
--- /dev/null
+++ b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-brpc/src/main/resources/META-INF/spring.provides
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+provides: shenyu-spring-boot-starter-client-brpc
diff --git a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-brpc/src/test/java/org/apache/shenyu/springboot/starter/client/brpc/ShenyuBrpcClientConfigurationTest.java b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-brpc/src/test/java/org/apache/shenyu/springboot/starter/client/brpc/ShenyuBrpcClientConfigurationTest.java
new file mode 100644
index 000000000..1bcf6db6b
--- /dev/null
+++ b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-brpc/src/test/java/org/apache/shenyu/springboot/starter/client/brpc/ShenyuBrpcClientConfigurationTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.shenyu.springboot.starter.client.brpc;
+
+import org.apache.shenyu.client.brpc.BrpcContextRefreshedEventListener;
+import org.apache.shenyu.register.client.http.utils.RegisterUtils;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mockStatic;
+
+/**
+ * Test case for {@link ShenyuBrpcClientConfiguration}.
+ */
+@Configuration
+@EnableConfigurationProperties
+public class ShenyuBrpcClientConfigurationTest {
+    
+    private ApplicationContextRunner applicationContextRunner;
+    
+    @BeforeEach
+    public void before() {
+        applicationContextRunner = new ApplicationContextRunner()
+                .withConfiguration(AutoConfigurations.of(ShenyuBrpcClientConfiguration.class))
+                .withBean(ShenyuBrpcClientConfigurationTest.class)
+                .withPropertyValues(
+                        "debug=true",
+                        "shenyu.register.registerType=http",
+                        "shenyu.register.serverLists=http://localhost:9095",
+                        "shenyu.register.props.username=admin",
+                        "shenyu.register.props.password=123456",
+                        "shenyu.client.brpc.props[contextPath]=/brpc",
+                        "shenyu.client.brpc.props[ipAndPort]=127.0.0.1:21715",
+                        "shenyu.client.brpc.props[host]=127.0.0.1",
+                        "shenyu.client.brpc.props[port]=21715"
+                );
+    }
+
+    @Test
+    public void testBrpcContextRefreshedEventListener() {
+        MockedStatic<RegisterUtils> registerUtilsMockedStatic = mockStatic(RegisterUtils.class);
+        registerUtilsMockedStatic.when(() -> RegisterUtils.doLogin(any(), any(), any())).thenReturn(Optional.ofNullable("token"));
+        applicationContextRunner.run(context -> {
+            BrpcContextRefreshedEventListener listener = context.getBean("brpcContextRefreshedEventListener", BrpcContextRefreshedEventListener.class);
+            assertNotNull(listener);
+        });
+        registerUtilsMockedStatic.close();
+    }
+}