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

hefengen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu-plugin-store.git


The following commit(s) were added to refs/heads/master by this push:
     new 53f6918  [type: refactor] init tarts plugin (#2)
53f6918 is described below

commit 53f6918f7753468015c9308b0c9a61214598b4fc
Author: moremind <[email protected]>
AuthorDate: Mon Mar 30 22:04:22 2026 +0800

    [type: refactor] init tarts plugin (#2)
    
    * [type:init] add tars plugin
    
    * [type:init] add tars plugin
---
 pom.xml                                            |  18 +-
 shenyu-plugin/pom.xml                              |   4 +
 shenyu-plugin/shenyu-plugin-proxy/pom.xml          |   1 +
 .../shenyu-plugin-proxy/shenyu-plugin-tars/pom.xml |  76 ++++
 .../org/apache/shenyu/plugin/tars/TarsPlugin.java  | 134 ++++++
 .../plugin/tars/cache/ApplicationConfigCache.java  | 450 +++++++++++++++++++++
 .../tars/context/TarsShenyuContextDecorator.java   |  43 ++
 .../tars/exception/ShenyuTarsPluginException.java  |  38 ++
 .../plugin/tars/handler/TarsMetaDataHandler.java   |  73 ++++
 .../plugin/tars/handler/TarsPluginDataHandler.java |  72 ++++
 .../shenyu/plugin/tars/proxy/TarsInvokePrx.java    |  81 ++++
 .../plugin/tars/proxy/TarsInvokePrxList.java       | 131 ++++++
 .../shenyu/plugin/tars/util/PrxInfoUtil.java       | 180 +++++++++
 .../plugin/tars/util/ReturnValueResolver.java      |  65 +++
 .../apache/shenyu/plugin/tars/TarsPluginTest.java  | 167 ++++++++
 .../tars/cache/ApplicationConfigCacheTest.java     | 147 +++++++
 .../tars/handler/TarsMetaDataHandlerTest.java      |  60 +++
 .../tars/handler/TarsPluginDataHandlerTest.java    |  63 +++
 .../shenyu/plugin/tars/util/PrxInfoUtilTest.java   |  80 ++++
 19 files changed, 1878 insertions(+), 5 deletions(-)

diff --git a/pom.xml b/pom.xml
index 35a2433..d186893 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,11 +21,6 @@
         <artifactId>apache</artifactId>
         <version>21</version>
     </parent>
-    <modules>
-        <module>shenyu-plugin</module>
-        <module>shenyu-spring-boot-starter-plugin</module>
-        <module>shenyu-plugin/shenyu-plugin-proxy</module>
-    </modules>
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.apache.shenyu</groupId>
     <artifactId>shenyu-plugin-store</artifactId>
@@ -62,6 +57,11 @@
         <url>https://github.com/apache/shenyu/issues</url>
     </issueManagement>
 
+    <modules>
+        <module>shenyu-plugin</module>
+        <module>shenyu-spring-boot-starter-plugin</module>
+    </modules>
+
     <properties>
         <java.version>17</java.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -90,6 +90,8 @@
         <!-- dependency version start -->
         <motan.version>1.2.1</motan.version>
         <log4j-1.2-api.vetsion>2.17.2</log4j-1.2-api.vetsion>
+        <bytebuddy.version>1.14.11</bytebuddy.version>
+        <tars.version>1.7.2</tars.version>
         <!-- dependency version end -->
     </properties>
 
@@ -118,6 +120,12 @@
                 <artifactId>motan-springsupport</artifactId>
                 <version>${motan.version}</version>
             </dependency>
+
+            <dependency>
+                <groupId>com.tencent.tars</groupId>
+                <artifactId>tars-spring-boot-starter</artifactId>
+                <version>${tars.version}</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 
diff --git a/shenyu-plugin/pom.xml b/shenyu-plugin/pom.xml
index d44cc5b..6d6e073 100644
--- a/shenyu-plugin/pom.xml
+++ b/shenyu-plugin/pom.xml
@@ -30,5 +30,9 @@
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>
+    <modules>
+        <module>shenyu-plugin-proxy</module>
+    </modules>
+
 
 </project>
\ No newline at end of file
diff --git a/shenyu-plugin/shenyu-plugin-proxy/pom.xml 
b/shenyu-plugin/shenyu-plugin-proxy/pom.xml
index 79deb12..d0b4f4e 100644
--- a/shenyu-plugin/shenyu-plugin-proxy/pom.xml
+++ b/shenyu-plugin/shenyu-plugin-proxy/pom.xml
@@ -32,6 +32,7 @@
     </properties>
     <modules>
         <module>shenyu-plugin-motan</module>
+        <module>shenyu-plugin-tars</module>
     </modules>
 
 </project>
\ No newline at end of file
diff --git a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/pom.xml 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/pom.xml
new file mode 100644
index 0000000..7061ff2
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/pom.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+
+<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-plugin-proxy</artifactId>
+        <version>2.7.1-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>shenyu-plugin-tars</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>net.bytebuddy</groupId>
+            <artifactId>byte-buddy</artifactId>
+            <version>${bytebuddy.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.tencent.tars</groupId>
+            <artifactId>tars-spring-boot-starter</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-api</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-log4j12</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>log4j</groupId>
+                    <artifactId>log4j</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-dependencies</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.google.guava</groupId>
+                    <artifactId>guava</artifactId>
+                </exclusion>
+            </exclusions>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git 
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/TarsPlugin.java
 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/TarsPlugin.java
new file mode 100644
index 0000000..01593e0
--- /dev/null
+++ 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/TarsPlugin.java
@@ -0,0 +1,134 @@
+/*
+ * 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.plugin.tars;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shenyu.common.constant.Constants;
+import org.apache.shenyu.common.dto.MetaData;
+import org.apache.shenyu.common.dto.RuleData;
+import org.apache.shenyu.common.dto.SelectorData;
+import org.apache.shenyu.common.enums.PluginEnum;
+import org.apache.shenyu.common.enums.ResultEnum;
+import org.apache.shenyu.common.enums.RpcTypeEnum;
+import org.apache.shenyu.common.exception.ShenyuException;
+import org.apache.shenyu.plugin.api.ShenyuPluginChain;
+import org.apache.shenyu.plugin.api.context.ShenyuContext;
+import org.apache.shenyu.plugin.api.result.ShenyuResultEnum;
+import org.apache.shenyu.plugin.api.result.ShenyuResultWrap;
+import org.apache.shenyu.plugin.api.utils.RequestUrlUtils;
+import org.apache.shenyu.plugin.api.utils.WebFluxResultUtils;
+import org.apache.shenyu.plugin.base.AbstractShenyuPlugin;
+import org.apache.shenyu.plugin.tars.cache.ApplicationConfigCache;
+import org.apache.shenyu.plugin.tars.proxy.TarsInvokePrxList;
+import org.apache.shenyu.plugin.tars.util.PrxInfoUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+import java.lang.reflect.Method;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * The tars plugin.
+ */
+public class TarsPlugin extends AbstractShenyuPlugin {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(TarsPlugin.class);
+
+    @Override
+    protected String getRawPath(final ServerWebExchange exchange) {
+        return RequestUrlUtils.getRewrittenRawPath(exchange);
+    }
+
+    @Override
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    protected Mono<Void> doExecute(final ServerWebExchange exchange, final 
ShenyuPluginChain chain, final SelectorData selector, final RuleData rule) {
+        String body = exchange.getAttribute(Constants.PARAM_TRANSFORM);
+        ShenyuContext shenyuContext = exchange.getAttribute(Constants.CONTEXT);
+        Objects.requireNonNull(shenyuContext);
+        MetaData metaData = exchange.getAttribute(Constants.META_DATA);
+        if (!checkMetaData(metaData)) {
+            Objects.requireNonNull(metaData);
+            LOG.error(" path is :{}, meta data have error.... {}", 
shenyuContext.getPath(), metaData);
+            
exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
+            Object error = ShenyuResultWrap.error(exchange, 
ShenyuResultEnum.META_DATA_ERROR);
+            return WebFluxResultUtils.result(exchange, error);
+        }
+        if (StringUtils.isNoneBlank(metaData.getParameterTypes()) && 
StringUtils.isBlank(body)) {
+            
exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
+            Object error = ShenyuResultWrap.error(exchange, 
ShenyuResultEnum.TARS_HAVE_BODY_PARAM);
+            return WebFluxResultUtils.result(exchange, error);
+        }
+        TarsInvokePrxList tarsInvokePrxList = 
ApplicationConfigCache.getInstance().get(metaData.getPath());
+        int index = 
ThreadLocalRandom.current().nextInt(tarsInvokePrxList.getTarsInvokePrxList().size());
+        Object prx = 
tarsInvokePrxList.getTarsInvokePrxList().get(index).getInvokePrx();
+        Method method = tarsInvokePrxList.getMethod();
+        CompletableFuture future;
+        try {
+            future = (CompletableFuture) method
+                    .invoke(prx, 
PrxInfoUtil.getParamArray(tarsInvokePrxList.getParamTypes(), 
tarsInvokePrxList.getParamNames(), body));
+        } catch (Exception e) {
+            LOG.error("Invoke tars error", e);
+            
exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
+            Object error = ShenyuResultWrap.error(exchange, 
ShenyuResultEnum.TARS_INVOKE);
+            return WebFluxResultUtils.result(exchange, error);
+        }
+        return Mono.fromFuture(future.thenApply(ret -> {
+            Object result = ret;
+            if (Objects.isNull(result)) {
+                result = Constants.TARS_RPC_RESULT_EMPTY;
+            }
+            exchange.getAttributes().put(Constants.RPC_RESULT, result);
+            
exchange.getAttributes().put(Constants.CLIENT_RESPONSE_RESULT_TYPE, 
ResultEnum.SUCCESS.getName());
+            return result;
+        })).onErrorMap(m -> new ShenyuException("failed to invoke 
tars")).then(chain.execute(exchange));
+    }
+
+    @Override
+    public int getOrder() {
+        return PluginEnum.TARS.getCode();
+    }
+
+    @Override
+    public String named() {
+        return PluginEnum.TARS.getName();
+    }
+
+    @Override
+    public boolean skip(final ServerWebExchange exchange) {
+        return skipExcept(exchange, RpcTypeEnum.TARS);
+    }
+
+    @Override
+    protected Mono<Void> handleSelectorIfNull(final String pluginName, final 
ServerWebExchange exchange, final ShenyuPluginChain chain) {
+        return WebFluxResultUtils.noSelectorResult(pluginName, exchange);
+    }
+
+    @Override
+    protected Mono<Void> handleRuleIfNull(final String pluginName, final 
ServerWebExchange exchange, final ShenyuPluginChain chain) {
+        return WebFluxResultUtils.noRuleResult(pluginName, exchange);
+    }
+
+    private boolean checkMetaData(final MetaData metaData) {
+        return Objects.nonNull(metaData) && 
!StringUtils.isBlank(metaData.getMethodName()) && 
!StringUtils.isBlank(metaData.getServiceName());
+    }
+}
diff --git 
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/cache/ApplicationConfigCache.java
 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/cache/ApplicationConfigCache.java
new file mode 100644
index 0000000..1422da5
--- /dev/null
+++ 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/cache/ApplicationConfigCache.java
@@ -0,0 +1,450 @@
+/*
+ * 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.plugin.tars.cache;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.qq.tars.client.Communicator;
+import com.qq.tars.client.CommunicatorConfig;
+import com.qq.tars.client.CommunicatorFactory;
+import com.qq.tars.protocol.annotation.Servant;
+import net.bytebuddy.ByteBuddy;
+import net.bytebuddy.description.annotation.AnnotationDescription;
+import net.bytebuddy.description.modifier.Visibility;
+import net.bytebuddy.dynamic.DynamicType;
+import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.shenyu.common.concurrent.ShenyuThreadFactory;
+import org.apache.shenyu.common.concurrent.ShenyuThreadPoolExecutor;
+import org.apache.shenyu.common.constant.Constants;
+import org.apache.shenyu.common.dto.MetaData;
+import org.apache.shenyu.common.dto.SelectorData;
+import org.apache.shenyu.common.dto.convert.plugin.TarsRegisterConfig;
+import org.apache.shenyu.common.dto.convert.selector.TarsUpstream;
+import org.apache.shenyu.common.exception.ShenyuException;
+import org.apache.shenyu.common.utils.GsonUtils;
+import org.apache.shenyu.plugin.api.utils.SpringBeanUtils;
+import org.apache.shenyu.plugin.tars.exception.ShenyuTarsPluginException;
+import org.apache.shenyu.plugin.tars.proxy.TarsInvokePrx;
+import org.apache.shenyu.plugin.tars.proxy.TarsInvokePrxList;
+import org.apache.shenyu.plugin.tars.util.PrxInfoUtil;
+import org.apache.shenyu.plugin.tars.util.ReturnValueResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.lang.NonNull;
+import org.springframework.util.ReflectionUtils;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
+
+/**
+ * Tars config cache.
+ */
+public final class ApplicationConfigCache {
+    
+    private static final Logger LOG = 
LoggerFactory.getLogger(ApplicationConfigCache.class);
+    
+    private static final ReentrantLock LOCK = new ReentrantLock();
+    
+    private final LoadingCache<String, TarsInvokePrxList> cache = 
CacheBuilder.newBuilder()
+            .maximumSize(Constants.CACHE_MAX_COUNT)
+            .build(new CacheLoader<>() {
+                @NonNull
+                @Override
+                public TarsInvokePrxList load(@NonNull final String key) {
+                    return new TarsInvokePrxList(null, null, null);
+                }
+            });
+    
+    private final ConcurrentHashMap<String, List<MetaData>> ctxPathCache = new 
ConcurrentHashMap<>();
+    
+    private final ConcurrentHashMap<String, Class<?>> prxClassCache = new 
ConcurrentHashMap<>();
+    
+    private final ConcurrentHashMap<String, TarsParamInfo> prxParamCache = new 
ConcurrentHashMap<>();
+    
+    private final ConcurrentHashMap<String, List<TarsUpstream>> 
refreshUpstreamCache = new ConcurrentHashMap<>();
+    
+    private Communicator communicator;
+
+    private final ThreadFactory factory = 
ShenyuThreadFactory.create("shenyu-tars", true);
+
+    private ThreadPoolExecutor threadPool;
+
+    private ApplicationConfigCache() {
+        communicator = 
CommunicatorFactory.getInstance().getCommunicator(CommunicatorConfig.getDefault());
+    }
+
+    /**
+     * Init.
+     *
+     * @param tarsRegisterConfig the tars register config
+     */
+    public void init(final TarsRegisterConfig tarsRegisterConfig) {
+        if (StringUtils.isEmpty(tarsRegisterConfig.getThreadpool())) {
+            CommunicatorConfig communicatorConfig = 
CommunicatorConfig.getDefault();
+            
Optional.ofNullable(tarsRegisterConfig.getCorethreads()).ifPresent(communicatorConfig::setCorePoolSize);
+            
Optional.ofNullable(tarsRegisterConfig.getThreads()).ifPresent(communicatorConfig::setMaxPoolSize);
+            
Optional.ofNullable(tarsRegisterConfig.getQueues()).ifPresent(communicatorConfig::setQueueSize);
+            communicator = 
CommunicatorFactory.getInstance().getCommunicator(communicatorConfig);
+        } else {
+            initThreadPool(tarsRegisterConfig);
+            
Optional.ofNullable(threadPool).ifPresent(this::setCommunicatorThreadPool);
+        }
+    }
+
+    /**
+     * init thread pool.
+     */
+    private void initThreadPool(final TarsRegisterConfig config) {
+        if (Objects.nonNull(threadPool)) {
+            return;
+        }
+        switch (config.getThreadpool()) {
+            case Constants.SHARED:
+                try {
+                    threadPool = 
SpringBeanUtils.getInstance().getBean(ShenyuThreadPoolExecutor.class);
+                    return;
+                } catch (NoSuchBeanDefinitionException t) {
+                    throw new ShenyuException("shared thread pool is not 
enable, config ${shenyu.sharedPool.enable} in your xml/yml !", t);
+                }
+            case Constants.FIXED:
+            case Constants.EAGER:
+            case Constants.LIMITED:
+                throw new UnsupportedOperationException();
+            case Constants.CACHED:
+                int corePoolSize = 
Optional.ofNullable(config.getCorethreads()).orElse(0);
+                int maximumPoolSize = 
Optional.ofNullable(config.getThreads()).orElse(Integer.MAX_VALUE);
+                int queueSize = 
Optional.ofNullable(config.getQueues()).orElse(0);
+                threadPool = new ThreadPoolExecutor(corePoolSize, 
maximumPoolSize, 60L, TimeUnit.SECONDS,
+                        queueSize > 0 ? new LinkedBlockingQueue<>(queueSize) : 
new SynchronousQueue<>(), factory);
+                return;
+            default:
+        }
+    }
+
+    /**
+     * Set communicator thread pool.
+     */
+    private void setCommunicatorThreadPool(final ThreadPoolExecutor 
threadPool) {
+        Field field = ReflectionUtils.findField(Communicator.class, 
"threadPoolExecutor");
+        ReflectionUtils.makeAccessible(field);
+        ReflectionUtils.setField(field, communicator, threadPool);
+    }
+
+    /**
+     * Get reference config.
+     *
+     * @param path path
+     * @return the reference config
+     */
+    public TarsInvokePrxList get(final String path) {
+        try {
+            return cache.get(path);
+        } catch (ExecutionException e) {
+            throw new ShenyuTarsPluginException(e.getCause());
+        }
+    }
+    
+    /**
+     * Init prx.<br>
+     * Try to load the meta information defined by meta data to the local 
cache.<br>
+     * eg: class definition, all method definition params,context path.<br>
+     *
+     * @param metaData metaData
+     */
+    public void initPrx(final MetaData metaData) {
+        while (true) {
+            Class<?> prxClass = prxClassCache.get(metaData.getPath());
+            try {
+                if (Objects.isNull(prxClass)) {
+                    // Spin's Attempt to Load
+                    tryLockedLoadMetaData(metaData);
+                } else {
+                    if (Objects.nonNull(metaData.getContextPath()) && 
Objects.nonNull(refreshUpstreamCache.get(metaData.getContextPath()))) {
+                        refreshTarsInvokePrxList(metaData, 
refreshUpstreamCache.get(metaData.getContextPath()));
+                    }
+                    break;
+                }
+            } catch (Exception e) {
+                LOG.error("ShenyuTarsPluginInitializeException: init tars ref 
ex:{}", e.getMessage());
+                break;
+            }
+        }
+    }
+    
+    /**
+     * Try to load once, if it fails, it will give up.<br>
+     * add class cache to {@link #prxClassCache}.<br>
+     * add method params cache to {@link #prxParamCache}.<br>
+     * add paths cache to {@link #ctxPathCache}.<br>
+     *
+     * @param metaData metaData
+     * @throws ClassNotFoundException meta data class definition not found
+     * @see ReentrantLock
+     */
+    private void tryLockedLoadMetaData(final MetaData metaData) throws 
ClassNotFoundException {
+        Objects.requireNonNull(LOCK);
+        if (LOCK.tryLock()) {
+            try {
+                if (StringUtils.isEmpty(metaData.getRpcExt())) {
+                    throw new 
ShenyuTarsPluginException("ShenyuTarsPluginInitializeException: can't init prx 
with empty ext string");
+                }
+                Class<?> prxClazz = buildClassDefinition(metaData);
+                prxClassCache.put(metaData.getPath(), prxClazz);
+                List<MetaData> paths = 
ctxPathCache.getOrDefault(metaData.getContextPath(), new ArrayList<>());
+                if (!IterableUtils.matchesAny(paths, p -> 
p.getPath().equals(metaData.getPath()))) {
+                    paths.add(metaData);
+                }
+                ctxPathCache.put(metaData.getContextPath(), paths);
+            } finally {
+                LOCK.unlock();
+            }
+        }
+    }
+    
+    /**
+     * build target class definition.
+     *
+     * @param metaData metadata
+     * @return class definition
+     * @throws ClassNotFoundException meta data class definition not found
+     */
+    private Class<?> buildClassDefinition(final MetaData metaData) throws 
ClassNotFoundException {
+        String clazzName = PrxInfoUtil.getPrxName(metaData);
+        DynamicType.Builder<?> classDefinition = new 
ByteBuddy().makeInterface().name(clazzName);
+        TarsParamExtInfo tarsParamExtInfo = 
GsonUtils.getInstance().fromJson(metaData.getRpcExt(), TarsParamExtInfo.class);
+        for (MethodInfo methodInfo : tarsParamExtInfo.getMethodInfo()) {
+            DynamicType.Builder.MethodDefinition.ParameterDefinition<?> 
definition =
+                    
classDefinition.defineMethod(PrxInfoUtil.getMethodName(methodInfo.methodName),
+                            
ReturnValueResolver.getCallBackType(PrxInfoUtil.getParamClass(methodInfo.getReturnType())),
+                            Visibility.PUBLIC);
+            if (CollectionUtils.isNotEmpty(methodInfo.getParams())) {
+                Class<?>[] paramTypes = new 
Class[methodInfo.getParams().size()];
+                String[] paramNames = new 
String[methodInfo.getParams().size()];
+                for (int i = 0; i < methodInfo.getParams().size(); i++) {
+                    Pair<String, String> pair = methodInfo.getParams().get(i);
+                    paramTypes[i] = PrxInfoUtil.getParamClass(pair.getKey());
+                    paramNames[i] = pair.getValue();
+                    definition = definition.withParameter(paramTypes[i], 
paramNames[i]);
+                    prxParamCache.put(getClassMethodKey(clazzName, 
methodInfo.getMethodName()), new TarsParamInfo(paramTypes, paramNames));
+                }
+                classDefinition = definition.withoutCode();
+            }
+        }
+        return 
classDefinition.annotateType(AnnotationDescription.Builder.ofType(Servant.class).build())
+                .make()
+                .load(Servant.class.getClassLoader(), 
ClassLoadingStrategy.Default.INJECTION)
+                .getLoaded();
+        
+    }
+    
+    /**
+     * Get param info key.
+     *
+     * @param className  className
+     * @param methodName methodName
+     * @return the key
+     */
+    public static String getClassMethodKey(final String className, final 
String methodName) {
+        return String.join("_", className, methodName);
+    }
+    
+    /**
+     * Gets instance.
+     *
+     * @return the instance
+     */
+    public static ApplicationConfigCache getInstance() {
+        return ApplicationConfigCacheInstance.INSTANCE;
+    }
+    
+    /**
+     * initPrxClass.
+     *
+     * @param selectorData selectorData
+     */
+    public void initPrxClass(final SelectorData selectorData) {
+        try {
+            final List<TarsUpstream> upstreamList = 
GsonUtils.getInstance().fromList(selectorData.getHandle(), TarsUpstream.class);
+            if (CollectionUtils.isEmpty(upstreamList)) {
+                invalidate(selectorData.getName());
+                return;
+            }
+            refreshUpstreamCache.put(selectorData.getName(), upstreamList);
+            List<MetaData> metaDataList = 
ctxPathCache.getOrDefault(selectorData.getName(), new ArrayList<>());
+            for (MetaData metaData : metaDataList) {
+                refreshTarsInvokePrxList(metaData, upstreamList);
+            }
+        } catch (ExecutionException | NoSuchMethodException e) {
+            throw new ShenyuException(e.getCause());
+        }
+    }
+    
+    /**
+     * refresh metaData path upstream url.
+     *
+     * @param metaData     metaData
+     * @param upstreamList upstream list
+     */
+    private void refreshTarsInvokePrxList(final MetaData metaData, final 
List<TarsUpstream> upstreamList) throws NoSuchMethodException, 
ExecutionException {
+        Class<?> prxClass = prxClassCache.get(metaData.getPath());
+        if (Objects.isNull(prxClass)) {
+            return;
+        }
+        TarsInvokePrxList tarsInvokePrxList = cache.get(metaData.getPath());
+        tarsInvokePrxList.getTarsInvokePrxList().clear();
+        if (Objects.isNull(tarsInvokePrxList.getMethod())) {
+            TarsParamInfo tarsParamInfo = 
prxParamCache.get(getClassMethodKey(prxClass.getName(), 
metaData.getMethodName()));
+            Object prx = communicator.stringToProxy(prxClass, 
PrxInfoUtil.getObjectName(upstreamList.get(0).getUpstreamUrl(), 
metaData.getServiceName()));
+            Method method = prx.getClass().getDeclaredMethod(
+                    PrxInfoUtil.getMethodName(metaData.getMethodName()), 
tarsParamInfo.getParamTypes());
+            tarsInvokePrxList.setMethod(method);
+            tarsInvokePrxList.setParamTypes(tarsParamInfo.getParamTypes());
+            tarsInvokePrxList.setParamNames(tarsParamInfo.getParamNames());
+        }
+        
tarsInvokePrxList.getTarsInvokePrxList().addAll(upstreamList.stream().map(upstream
 -> {
+            Object strProxy = communicator.stringToProxy(prxClass, 
PrxInfoUtil.getObjectName(upstream.getUpstreamUrl(), 
metaData.getServiceName()));
+            return new TarsInvokePrx(strProxy, upstream.getUpstreamUrl());
+        }).collect(Collectors.toList()));
+    }
+    
+    /**
+     * invalidate.
+     *
+     * @param contextPath context path
+     */
+    public void invalidate(final String contextPath) {
+        List<MetaData> metaDataList = ctxPathCache.getOrDefault(contextPath, 
new ArrayList<>());
+        metaDataList.forEach(metaData -> cache.invalidate(metaData.getPath()));
+    }
+    
+    /**
+     * The type Application config cache instance.
+     */
+    static final class ApplicationConfigCacheInstance {
+        /**
+         * The Instance.
+         */
+        static final ApplicationConfigCache INSTANCE = new 
ApplicationConfigCache();
+        
+        private ApplicationConfigCacheInstance() {
+        
+        }
+    }
+    
+    /**
+     * The type Tars param ext info.
+     */
+    static class MethodInfo {
+        
+        private String methodName;
+        
+        private List<Pair<String, String>> params;
+        
+        private String returnType;
+        
+        public String getMethodName() {
+            return methodName;
+        }
+        
+        public void setMethodName(final String methodName) {
+            this.methodName = methodName;
+        }
+        
+        public List<Pair<String, String>> getParams() {
+            return params;
+        }
+        
+        public void setParams(final List<Pair<String, String>> params) {
+            this.params = params;
+        }
+        
+        public String getReturnType() {
+            return returnType;
+        }
+        
+        public void setReturnType(final String returnType) {
+            this.returnType = returnType;
+        }
+    }
+    
+    /**
+     * The type Tars param ext info.
+     */
+    static class TarsParamExtInfo {
+        
+        private List<MethodInfo> methodInfo;
+        
+        public List<MethodInfo> getMethodInfo() {
+            return methodInfo;
+        }
+        
+        public void setMethodInfo(final List<MethodInfo> methodInfo) {
+            this.methodInfo = methodInfo;
+        }
+    }
+    
+    /**
+     * The type Tars param ext info.
+     */
+    static class TarsParamInfo {
+        
+        private Class<?>[] paramTypes;
+        
+        private String[] paramNames;
+        
+        TarsParamInfo(final Class<?>[] paramTypes, final String[] paramNames) {
+            this.paramTypes = paramTypes;
+            this.paramNames = paramNames;
+        }
+        
+        public Class<?>[] getParamTypes() {
+            return paramTypes;
+        }
+        
+        public void setParamTypes(final Class<?>[] paramTypes) {
+            this.paramTypes = paramTypes;
+        }
+        
+        public String[] getParamNames() {
+            return paramNames;
+        }
+        
+        public void setParamNames(final String[] paramNames) {
+            this.paramNames = paramNames;
+        }
+    }
+}
diff --git 
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/context/TarsShenyuContextDecorator.java
 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/context/TarsShenyuContextDecorator.java
new file mode 100644
index 0000000..28efb4d
--- /dev/null
+++ 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/context/TarsShenyuContextDecorator.java
@@ -0,0 +1,43 @@
+/*
+ * 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.plugin.tars.context;
+
+import org.apache.shenyu.common.dto.MetaData;
+import org.apache.shenyu.common.enums.RpcTypeEnum;
+import org.apache.shenyu.plugin.api.context.ShenyuContext;
+import org.apache.shenyu.plugin.api.context.ShenyuContextDecorator;
+
+/**
+ * The type Tars shenyu context decorator.
+ */
+public class TarsShenyuContextDecorator implements ShenyuContextDecorator {
+
+    @Override
+    public ShenyuContext decorator(final ShenyuContext shenyuContext, final 
MetaData metaData) {
+        shenyuContext.setModule(metaData.getAppName());
+        shenyuContext.setMethod(metaData.getServiceName());
+        shenyuContext.setContextPath(metaData.getContextPath());
+        shenyuContext.setRpcType(RpcTypeEnum.TARS.getName());
+        return shenyuContext;
+    }
+
+    @Override
+    public String rpcType() {
+        return RpcTypeEnum.TARS.getName();
+    }
+}
diff --git 
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/exception/ShenyuTarsPluginException.java
 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/exception/ShenyuTarsPluginException.java
new file mode 100644
index 0000000..4f3bb82
--- /dev/null
+++ 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/exception/ShenyuTarsPluginException.java
@@ -0,0 +1,38 @@
+/*
+ * 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.plugin.tars.exception;
+
+import org.apache.shenyu.common.exception.ShenyuException;
+
+/**
+ * ShenyuTarsPluginException.
+ */
+public class ShenyuTarsPluginException extends ShenyuException {
+    
+    public ShenyuTarsPluginException(final Throwable e) {
+        super(e);
+    }
+    
+    public ShenyuTarsPluginException(final String message) {
+        super(message);
+    }
+    
+    public ShenyuTarsPluginException(final String message, final Throwable 
throwable) {
+        super(message, throwable);
+    }
+}
diff --git 
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/handler/TarsMetaDataHandler.java
 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/handler/TarsMetaDataHandler.java
new file mode 100644
index 0000000..418e0f2
--- /dev/null
+++ 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/handler/TarsMetaDataHandler.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.plugin.tars.handler;
+
+import com.google.common.collect.Maps;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.shenyu.common.dto.MetaData;
+import org.apache.shenyu.common.enums.RpcTypeEnum;
+import org.apache.shenyu.plugin.base.handler.MetaDataHandler;
+import org.apache.shenyu.plugin.tars.cache.ApplicationConfigCache;
+import org.apache.shenyu.plugin.tars.proxy.TarsInvokePrx;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
+
+/**
+ * The tars metadata handler.
+ */
+public class TarsMetaDataHandler implements MetaDataHandler {
+
+    private static final ConcurrentMap<String, MetaData> META_DATA = 
Maps.newConcurrentMap();
+    
+    @Override
+    public void handle(final MetaData metaData) {
+        metaData.updateContextPath();
+        MetaData metaExist = META_DATA.get(metaData.getPath());
+        List<TarsInvokePrx> prxList = ApplicationConfigCache.getInstance()
+                .get(metaData.getPath()).getTarsInvokePrxList();
+        boolean exist = prxList.stream().anyMatch(tarsInvokePrx -> 
tarsInvokePrx.getHost().equals(metaData.getAppName()));
+        if (!exist) {
+            ApplicationConfigCache.getInstance().initPrx(metaData);
+        }
+        if (Objects.isNull(metaExist)) {
+            META_DATA.put(metaData.getPath(), metaData);
+        }
+    }
+    
+    @Override
+    public void remove(final MetaData metaData) {
+        metaData.updateContextPath();
+        List<TarsInvokePrx> prxList = ApplicationConfigCache.getInstance()
+                .get(metaData.getPath()).getTarsInvokePrxList();
+        List<TarsInvokePrx> removePrxList = prxList.stream()
+                .filter(tarsInvokePrx -> 
tarsInvokePrx.getHost().equals(metaData.getAppName()))
+                .collect(Collectors.toList());
+        prxList.removeAll(removePrxList);
+        if (CollectionUtils.isEmpty(prxList)) {
+            META_DATA.remove(metaData.getPath());
+        }
+    }
+    
+    @Override
+    public String rpcType() {
+        return RpcTypeEnum.TARS.getName();
+    }
+}
diff --git 
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/handler/TarsPluginDataHandler.java
 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/handler/TarsPluginDataHandler.java
new file mode 100644
index 0000000..606e717
--- /dev/null
+++ 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/handler/TarsPluginDataHandler.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.plugin.tars.handler;
+
+import org.apache.shenyu.common.dto.PluginData;
+import org.apache.shenyu.common.dto.SelectorData;
+import org.apache.shenyu.common.dto.convert.plugin.TarsRegisterConfig;
+import org.apache.shenyu.common.enums.PluginEnum;
+import org.apache.shenyu.common.utils.GsonUtils;
+import org.apache.shenyu.common.utils.Singleton;
+import org.apache.shenyu.plugin.base.handler.PluginDataHandler;
+import org.apache.shenyu.plugin.tars.cache.ApplicationConfigCache;
+
+import java.util.Objects;
+
+/**
+ * The type tars plugin data handler.
+ */
+public class TarsPluginDataHandler implements PluginDataHandler {
+
+    @Override
+    public void handlerPlugin(final PluginData pluginData) {
+        if (Objects.nonNull(pluginData) && 
Boolean.TRUE.equals(pluginData.getEnabled())) {
+            TarsRegisterConfig tarsRegisterConfig = 
GsonUtils.getInstance().fromJson(pluginData.getConfig(), 
TarsRegisterConfig.class);
+            TarsRegisterConfig exist = 
Singleton.INST.get(TarsRegisterConfig.class);
+            if (Objects.isNull(tarsRegisterConfig)) {
+                return;
+            }
+            if (Objects.isNull(exist) || !tarsRegisterConfig.equals(exist)) {
+                // If it is null, cache it
+                ApplicationConfigCache.getInstance().init(tarsRegisterConfig);
+            }
+            Singleton.INST.single(TarsRegisterConfig.class, 
tarsRegisterConfig);
+        }
+    }
+    
+    @Override
+    public String pluginNamed() {
+        return PluginEnum.TARS.getName();
+    }
+    
+    @Override
+    public void handlerSelector(final SelectorData selectorData) {
+        if (Objects.isNull(selectorData.getName())) {
+            return;
+        }
+        ApplicationConfigCache.getInstance().initPrxClass(selectorData);
+    }
+    
+    @Override
+    public void removeSelector(final SelectorData selectorData) {
+        if (Objects.isNull(selectorData.getName())) {
+            return;
+        }
+        
ApplicationConfigCache.getInstance().invalidate(selectorData.getName());
+    }
+}
diff --git 
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/proxy/TarsInvokePrx.java
 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/proxy/TarsInvokePrx.java
new file mode 100644
index 0000000..9a7a321
--- /dev/null
+++ 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/proxy/TarsInvokePrx.java
@@ -0,0 +1,81 @@
+/*
+ * 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.plugin.tars.proxy;
+
+/**
+ * Tars path invoke metadata.
+ */
+public class TarsInvokePrx {
+
+    private Object invokePrx;
+
+    private String host;
+
+    /**
+     * Instantiates a new Tars invoke prx.
+     */
+    public TarsInvokePrx() {
+    }
+
+    /**
+     * Instantiates a new Tars invoke prx.
+     *
+     * @param invokePrx the invoke prx
+     * @param host      the host
+     */
+    public TarsInvokePrx(final Object invokePrx, final String host) {
+        this.invokePrx = invokePrx;
+        this.host = host;
+    }
+
+    /**
+     * Gets invoke prx.
+     *
+     * @return the invoke prx
+     */
+    public Object getInvokePrx() {
+        return invokePrx;
+    }
+
+    /**
+     * Sets invoke prx.
+     *
+     * @param invokePrx the invoke prx
+     */
+    public void setInvokePrx(final Object invokePrx) {
+        this.invokePrx = invokePrx;
+    }
+
+    /**
+     * Gets host.
+     *
+     * @return the host
+     */
+    public String getHost() {
+        return host;
+    }
+
+    /**
+     * Sets host.
+     *
+     * @param host the host
+     */
+    public void setHost(final String host) {
+        this.host = host;
+    }
+}
diff --git 
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/proxy/TarsInvokePrxList.java
 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/proxy/TarsInvokePrxList.java
new file mode 100644
index 0000000..0699cd5
--- /dev/null
+++ 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/proxy/TarsInvokePrxList.java
@@ -0,0 +1,131 @@
+/*
+ * 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.plugin.tars.proxy;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Tars path invoke metadata.
+ */
+public class TarsInvokePrxList {
+
+    private final List<TarsInvokePrx> tarsInvokePrxList;
+
+    private Method method;
+
+    private Class<?>[] paramTypes;
+
+    private String[] paramNames;
+
+    /**
+     * Instantiates a new Tars invoke prx list.
+     */
+    public TarsInvokePrxList() {
+        tarsInvokePrxList = new CopyOnWriteArrayList<>();
+    }
+
+    /**
+     * Instantiates a new Tars invoke prx list.
+     *
+     * @param method            the method
+     * @param paramTypes        the param types
+     * @param paramNames        the param names
+     */
+    public TarsInvokePrxList(final Method method,
+                             final Class<?>[] paramTypes,
+                             final String[] paramNames) {
+        this.tarsInvokePrxList = new CopyOnWriteArrayList<>();
+        this.method = method;
+        this.paramTypes = paramTypes;
+        this.paramNames = paramNames;
+    }
+
+    /**
+     * Gets tars invoke prx list.
+     *
+     * @return the tars invoke prx list
+     */
+    public List<TarsInvokePrx> getTarsInvokePrxList() {
+        return tarsInvokePrxList;
+    }
+
+    /**
+     * Sets tars invoke prx list.
+     *
+     * @param tarsInvokePrxList the tars invoke prx list
+     */
+    public void addTarsInvokePrxList(final List<TarsInvokePrx> 
tarsInvokePrxList) {
+        this.tarsInvokePrxList.addAll(tarsInvokePrxList);
+    }
+
+    /**
+     * Gets method.
+     *
+     * @return the method
+     */
+    public Method getMethod() {
+        return method;
+    }
+
+    /**
+     * Sets method.
+     *
+     * @param method the method
+     */
+    public void setMethod(final Method method) {
+        this.method = method;
+    }
+
+    /**
+     * Get param types class [ ].
+     *
+     * @return the class [ ]
+     */
+    public Class<?>[] getParamTypes() {
+        return paramTypes;
+    }
+
+    /**
+     * Sets param types.
+     *
+     * @param paramTypes the param types
+     */
+    public void setParamTypes(final Class<?>[] paramTypes) {
+        this.paramTypes = paramTypes;
+    }
+
+    /**
+     * Get param names string [ ].
+     *
+     * @return the string [ ]
+     */
+    public String[] getParamNames() {
+        return paramNames;
+    }
+
+    /**
+     * Sets param names.
+     *
+     * @param paramNames the param names
+     */
+    public void setParamNames(final String[] paramNames) {
+        this.paramNames = paramNames;
+    }
+}
diff --git 
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/util/PrxInfoUtil.java
 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/util/PrxInfoUtil.java
new file mode 100644
index 0000000..1441959
--- /dev/null
+++ 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/util/PrxInfoUtil.java
@@ -0,0 +1,180 @@
+/*
+ * 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.plugin.tars.util;
+
+import org.apache.shenyu.common.dto.MetaData;
+import org.apache.shenyu.common.utils.GsonUtils;
+import org.apache.shenyu.common.utils.ParamCheckUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+
+/**
+ * Proxy info util.
+ */
+public final class PrxInfoUtil {
+    
+    private static final Map<String, PrimitiveType> PRIMITIVE_TYPE;
+    
+    static {
+        PRIMITIVE_TYPE = new HashMap<>();
+        PRIMITIVE_TYPE.put("int", new PrimitiveType(int.class, o -> {
+            if (o instanceof String) {
+                return Integer.valueOf((String) o);
+            }
+            return ((Long) o).intValue();
+        }));
+        PRIMITIVE_TYPE.put("double", new PrimitiveType(double.class, o -> {
+            if (o instanceof String) {
+                return Double.valueOf((String) o);
+            }
+            return o;
+        }));
+        PRIMITIVE_TYPE.put("long", new PrimitiveType(long.class, o -> {
+            if (o instanceof String) {
+                return Long.valueOf((String) o);
+            }
+            return o;
+        }));
+        PRIMITIVE_TYPE.put("short", new PrimitiveType(short.class, o -> {
+            if (o instanceof String) {
+                return Short.valueOf((String) o);
+            }
+            return ((Long) o).shortValue();
+        }));
+        PRIMITIVE_TYPE.put("byte", new PrimitiveType(byte.class, o -> {
+            if (o instanceof String) {
+                return Byte.valueOf((String) o);
+            }
+            return ((Long) o).byteValue();
+        }));
+        PRIMITIVE_TYPE.put("boolean", new PrimitiveType(boolean.class, o -> {
+            if (o instanceof String) {
+                return Byte.valueOf((String) o);
+            }
+            return o;
+        }));
+        PRIMITIVE_TYPE.put("char", new PrimitiveType(char.class, o -> {
+            if (o instanceof String) {
+                return String.valueOf(o).charAt(0);
+            }
+            return o;
+        }));
+        PRIMITIVE_TYPE.put("float", new PrimitiveType(float.class, o -> {
+            if (o instanceof String) {
+                return Float.valueOf((String) o);
+            }
+            return ((Double) o).floatValue();
+        }));
+    }
+    
+    private PrxInfoUtil() {
+    }
+    
+    /**
+     * Get class type by name.
+     *
+     * @param className className
+     * @return the type to invoke
+     * @throws ClassNotFoundException ClassNotFoundException
+     */
+    public static Class<?> getParamClass(final String className) throws 
ClassNotFoundException {
+        if (PRIMITIVE_TYPE.containsKey(className)) {
+            return PRIMITIVE_TYPE.get(className).getClazz();
+        } else {
+            return Class.forName(className);
+        }
+    }
+    
+    /**
+     * Get proxy class name to get tars proxy.
+     *
+     * @param metaData metaData
+     * @return className
+     */
+    public static String getPrxName(final MetaData metaData) {
+        return metaData.getPath().replace("/", "") + metaData.getMethodName() 
+ "Prx";
+    }
+    
+    /**
+     * Get methodName to get tars proxy.
+     *
+     * @param methodName methodName
+     * @return methodName
+     */
+    public static String getMethodName(final String methodName) {
+        return "promise_" + methodName;
+    }
+    
+    /**
+     * Get objectName to get tars proxy.
+     *
+     * @param upstreamUrl upstream url
+     * @param serviceName service name
+     * @return objectName
+     */
+    public static String getObjectName(final String upstreamUrl, final String 
serviceName) {
+        String[] ipAndPort = upstreamUrl.split(":");
+        return serviceName + "@tcp -h " + ipAndPort[0] + " -p " + ipAndPort[1];
+    }
+    
+    /**
+     * Get param to invoke tars server.
+     *
+     * @param paramTypes paramTypes
+     * @param paramNames paramNames
+     * @param body       body
+     * @return the param to invoke
+     */
+    public static Object[] getParamArray(final Class<?>[] paramTypes, final 
String[] paramNames, final String body) {
+        Map<String, Object> bodyMap = 
GsonUtils.getInstance().convertToMap(body);
+        ParamCheckUtils.checkParamsLength(bodyMap.size(), paramNames.length);
+        Object[] param = new Object[paramNames.length];
+        for (int i = 0; i < paramNames.length; i++) {
+            String paramName = paramNames[i];
+            Class<?> paramType = paramTypes[i];
+            if (PRIMITIVE_TYPE.containsKey(paramType.getName())) {
+                param[i] = 
PRIMITIVE_TYPE.get(paramType.getName()).getFunc().apply(bodyMap.get(paramName));
+            } else {
+                param[i] = bodyMap.get(paramName);
+            }
+        }
+        return param;
+    }
+    
+    static class PrimitiveType {
+        
+        private final Class<?> clazz;
+        
+        private final Function<Object, Object> func;
+        
+        PrimitiveType(final Class<?> clazz, final Function<Object, Object> 
func) {
+            this.clazz = clazz;
+            this.func = func;
+        }
+        
+        public Class<?> getClazz() {
+            return clazz;
+        }
+        
+        public Function<Object, Object> getFunc() {
+            return func;
+        }
+    }
+}
diff --git 
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/util/ReturnValueResolver.java
 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/util/ReturnValueResolver.java
new file mode 100644
index 0000000..983dafe
--- /dev/null
+++ 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/main/java/org/apache/shenyu/plugin/tars/util/ReturnValueResolver.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.shenyu.plugin.tars.util;
+
+import com.google.common.reflect.TypeParameter;
+import com.google.common.reflect.TypeToken;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * The tars return value type resolver.
+ */
+public final class ReturnValueResolver {
+    
+    @SuppressWarnings("rawtypes")
+    private static final Map<Class, Class> WRAPPER_TYPE_MAP;
+    
+    static {
+        WRAPPER_TYPE_MAP = new HashMap<>();
+        WRAPPER_TYPE_MAP.put(int.class, Integer.class);
+        WRAPPER_TYPE_MAP.put(byte.class, Byte.class);
+        WRAPPER_TYPE_MAP.put(char.class, Character.class);
+        WRAPPER_TYPE_MAP.put(boolean.class, Boolean.class);
+        WRAPPER_TYPE_MAP.put(double.class, Double.class);
+        WRAPPER_TYPE_MAP.put(float.class, Float.class);
+        WRAPPER_TYPE_MAP.put(long.class, Long.class);
+        WRAPPER_TYPE_MAP.put(short.class, Short.class);
+    }
+    
+    private ReturnValueResolver() {
+    }
+    
+    /**
+     * Get return type.
+     *
+     * @param <T>   T
+     * @param clazz clazz
+     * @return the type
+     */
+    @SuppressWarnings("all")
+    public static <T> Type getCallBackType(final Class<T> clazz) {
+        return new TypeToken<CompletableFuture<T>>() { }
+                .where(new TypeParameter<T>() { }, 
TypeToken.of(WRAPPER_TYPE_MAP.getOrDefault(clazz, clazz)))
+                .getType();
+    }
+    
+}
diff --git 
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/test/java/org/apache/shenyu/plugin/tars/TarsPluginTest.java
 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/test/java/org/apache/shenyu/plugin/tars/TarsPluginTest.java
new file mode 100644
index 0000000..33bcff2
--- /dev/null
+++ 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/test/java/org/apache/shenyu/plugin/tars/TarsPluginTest.java
@@ -0,0 +1,167 @@
+/*
+ * 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.plugin.tars;
+
+import org.apache.shenyu.common.concurrent.ShenyuThreadFactory;
+import org.apache.shenyu.common.constant.Constants;
+import org.apache.shenyu.common.dto.MetaData;
+import org.apache.shenyu.common.dto.RuleData;
+import org.apache.shenyu.common.dto.SelectorData;
+import org.apache.shenyu.common.enums.PluginEnum;
+import org.apache.shenyu.common.enums.RpcTypeEnum;
+import org.apache.shenyu.plugin.api.ShenyuPluginChain;
+import org.apache.shenyu.plugin.api.context.ShenyuContext;
+import org.apache.shenyu.plugin.api.result.DefaultShenyuResult;
+import org.apache.shenyu.plugin.api.result.ShenyuResult;
+import org.apache.shenyu.plugin.api.utils.SpringBeanUtils;
+import org.apache.shenyu.plugin.tars.cache.ApplicationConfigCache;
+import org.apache.shenyu.plugin.tars.proxy.TarsInvokePrxList;
+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.junit.jupiter.MockitoExtension;
+import org.mockito.junit.jupiter.MockitoSettings;
+import org.mockito.quality.Strictness;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
+import org.springframework.mock.web.server.MockServerWebExchange;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test case for {@link TarsPlugin}.
+ */
+@ExtendWith(MockitoExtension.class)
+@MockitoSettings(strictness = Strictness.LENIENT)
+public class TarsPluginTest {
+
+    @Mock
+    private ShenyuPluginChain chain;
+
+    private MetaData metaData;
+
+    private ServerWebExchange exchange;
+
+    private TarsPlugin tarsPluginUnderTest;
+
+    @BeforeEach
+    public void setUp() {
+        ConfigurableApplicationContext applicationContext = 
mock(ConfigurableApplicationContext.class);
+        when(applicationContext.getBean(ShenyuResult.class)).thenReturn(new 
DefaultShenyuResult());
+        SpringBeanUtils springBeanUtils = SpringBeanUtils.getInstance();
+        springBeanUtils.setApplicationContext(applicationContext);
+        metaData = new MetaData("id", "127.0.0.1:8080", "contextPath",
+                "path", RpcTypeEnum.TARS.getName(), "serviceName", "method1",
+                "parameterTypes", 
"{\"methodInfo\":[{\"methodName\":\"method1\",\"params\":"
+                + 
"[{\"left\":\"java.lang.String\",\"right\":\"param1\"},{\"left\":\"java.lang.String\","
+                + 
"\"right\":\"param2\"}],\"returnType\":\"java.lang.String\"}]}", false, 
Constants.SYS_DEFAULT_NAMESPACE_ID);
+        ApplicationConfigCache.getInstance().initPrx(metaData);
+        exchange = 
MockServerWebExchange.from(MockServerHttpRequest.get("localhost").build());
+        tarsPluginUnderTest = new TarsPlugin();
+    }
+
+    @Test
+    public void testTarsPluginWithEmptyBody() {
+        ShenyuContext context = mock(ShenyuContext.class);
+        exchange.getAttributes().put(Constants.CONTEXT, context);
+        exchange.getAttributes().put(Constants.META_DATA, metaData);
+        when(chain.execute(exchange)).thenReturn(Mono.empty());
+        RuleData data = mock(RuleData.class);
+        SelectorData selectorData = mock(SelectorData.class);
+        StepVerifier.create(tarsPluginUnderTest.doExecute(exchange, chain, 
selectorData, data)).expectSubscription().verifyComplete();
+    }
+
+    @Test
+    public void testTarsPluginWithEmptyMetaData() {
+        ShenyuContext context = mock(ShenyuContext.class);
+        exchange.getAttributes().put(Constants.CONTEXT, context);
+        metaData.setServiceName("");
+        exchange.getAttributes().put(Constants.META_DATA, metaData);
+        when(chain.execute(exchange)).thenReturn(Mono.empty());
+        RuleData data = mock(RuleData.class);
+        SelectorData selectorData = mock(SelectorData.class);
+        StepVerifier.create(tarsPluginUnderTest.doExecute(exchange, chain, 
selectorData, data)).expectSubscription().verifyComplete();
+    }
+
+    @Test
+    public void testTarsPluginWithArgumentTypeMissMatch() {
+        ShenyuContext context = mock(ShenyuContext.class);
+        exchange.getAttributes().put(Constants.CONTEXT, context);
+        exchange.getAttributes().put(Constants.META_DATA, metaData);
+        exchange.getAttributes().put(Constants.PARAM_TRANSFORM, 
"{\"param1\":1,\"param2\":2}");
+        when(chain.execute(exchange)).thenReturn(Mono.empty());
+        RuleData data = mock(RuleData.class);
+        SelectorData selectorData = mock(SelectorData.class);
+        assertThrows(IllegalArgumentException.class, () -> 
StepVerifier.create(tarsPluginUnderTest.doExecute(exchange, chain, 
selectorData, data)).expectSubscription().verifyComplete());
+    }
+
+    @Test
+    public void testTarsPluginNormal() throws InvocationTargetException, 
IllegalAccessException {
+        ShenyuContext context = mock(ShenyuContext.class);
+        exchange.getAttributes().put(Constants.CONTEXT, context);
+        exchange.getAttributes().put(Constants.META_DATA, metaData);
+        exchange.getAttributes().put(Constants.PARAM_TRANSFORM, 
"{\"param1\":\"1\",\"param2\":\"1\"}");
+        when(chain.execute(exchange)).thenReturn(Mono.empty());
+        RuleData data = mock(RuleData.class);
+        SelectorData selectorData = mock(SelectorData.class);
+        TarsInvokePrxList tarsInvokePrxList = 
ApplicationConfigCache.getInstance().get(metaData.getPath());
+        Method method = mock(Method.class);
+        ExecutorService executorService = Executors.newFixedThreadPool(1,
+                ShenyuThreadFactory.create("long-polling", true));
+        CompletableFuture<String> stringCompletableFuture = 
CompletableFuture.supplyAsync(() -> "", executorService);
+        when(method.invoke(any(), any())).thenReturn(stringCompletableFuture);
+        tarsInvokePrxList.setMethod(method);
+        assertThrows(IllegalArgumentException.class, () -> 
StepVerifier.create(tarsPluginUnderTest.doExecute(exchange, chain, 
selectorData, data)).expectSubscription().verifyComplete());
+    }
+
+    @Test
+    public void testGetOrder() {
+        int result = tarsPluginUnderTest.getOrder();
+        assertEquals(PluginEnum.TARS.getCode(), result);
+    }
+
+    @Test
+    public void testNamed() {
+        String result = tarsPluginUnderTest.named();
+        assertEquals(PluginEnum.TARS.getName(), result);
+    }
+
+    @Test
+    public void testSkip() {
+        ShenyuContext context = mock(ShenyuContext.class);
+        when(context.getRpcType()).thenReturn(RpcTypeEnum.TARS.getName());
+        exchange.getAttributes().put(Constants.CONTEXT, context);
+        boolean result = tarsPluginUnderTest.skip(exchange);
+        assertFalse(result);
+    }
+}
diff --git 
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/test/java/org/apache/shenyu/plugin/tars/cache/ApplicationConfigCacheTest.java
 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/test/java/org/apache/shenyu/plugin/tars/cache/ApplicationConfigCacheTest.java
new file mode 100644
index 0000000..cbf2286
--- /dev/null
+++ 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/test/java/org/apache/shenyu/plugin/tars/cache/ApplicationConfigCacheTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.plugin.tars.cache;
+
+import com.qq.tars.protocol.annotation.Servant;
+import org.apache.shenyu.common.concurrent.ShenyuThreadFactory;
+import org.apache.shenyu.common.constant.Constants;
+import org.apache.shenyu.common.dto.MetaData;
+import org.apache.shenyu.common.enums.RpcTypeEnum;
+import org.apache.shenyu.plugin.tars.proxy.TarsInvokePrxList;
+import org.apache.shenyu.plugin.tars.util.PrxInfoUtil;
+import org.assertj.core.util.Lists;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Test case for {@link ApplicationConfigCache}.
+ */
+@ExtendWith(MockitoExtension.class)
+public final class ApplicationConfigCacheTest {
+
+    private ApplicationConfigCache applicationConfigCacheUnderTest;
+
+    @BeforeEach
+    public void setUp() {
+        applicationConfigCacheUnderTest = ApplicationConfigCache.getInstance();
+    }
+
+    @Test
+    public void testGet() throws ClassNotFoundException {
+        final String rpcExt = 
"{\"methodInfo\":[{\"methodName\":\"method1\",\"params\":"
+                + 
"[{\"left\":\"int\",\"right\":\"param1\"},{\"left\":\"java.lang.Integer\","
+                + 
"\"right\":\"param2\"}],\"returnType\":\"java.lang.String\"}]}";
+
+        final MetaData metaData = new MetaData("id", "127.0.0.1:8080", 
"contextPath",
+                "path5", RpcTypeEnum.TARS.getName(), "serviceName5", "method1",
+                "parameterTypes", rpcExt, false, 
Constants.SYS_DEFAULT_NAMESPACE_ID);
+
+        assertThrows(NullPointerException.class, () -> {
+            applicationConfigCacheUnderTest.initPrx(metaData);
+            final TarsInvokePrxList result = 
applicationConfigCacheUnderTest.get("path5");
+            assertNotNull(result);
+            assertEquals("promise_method1", result.getMethod().getName());
+            assertEquals(2, result.getParamTypes().length);
+            assertEquals(2, result.getParamNames().length);
+            Class<?> prxClazz = 
Class.forName(PrxInfoUtil.getPrxName(metaData));
+            
assertTrue(Arrays.stream(prxClazz.getAnnotations()).anyMatch(annotation -> 
annotation instanceof Servant));
+
+        });
+    }
+
+    @Test
+    public void testConcurrentInitPrx() {
+        final String rpcExt1 = 
"{\"methodInfo\":[{\"methodName\":\"method1\",\"params\":"
+                + 
"[{\"left\":\"int\",\"right\":\"param1\"},{\"left\":\"java.lang.Integer\","
+                + 
"\"right\":\"param2\"}],\"returnType\":\"java.lang.String\"}]}";
+        final String rpcExt2 = 
"{\"methodInfo\":[{\"methodName\":\"method2\",\"params\":"
+                + 
"[{\"left\":\"int\",\"right\":\"param1\"},{\"left\":\"java.lang.Integer\","
+                + 
"\"right\":\"param2\"}],\"returnType\":\"java.lang.String\"}]}";
+        final String rpcExt3 = 
"{\"methodInfo\":[{\"methodName\":\"method3\",\"params\":"
+                + 
"[{\"left\":\"int\",\"right\":\"param1\"},{\"left\":\"java.lang.Integer\","
+                + 
"\"right\":\"param2\"}],\"returnType\":\"java.lang.String\"}]}";
+        final String rpcExt4 = 
"{\"methodInfo\":[{\"methodName\":\"method4\",\"params\":"
+                + 
"[{\"left\":\"int\",\"right\":\"param1\"},{\"left\":\"java.lang.Integer\","
+                + 
"\"right\":\"param2\"}],\"returnType\":\"java.lang.String\"}]}";
+
+        final MetaData metaData1 = new MetaData("id", "127.0.0.1:8080", 
"contextPath",
+                "path1", RpcTypeEnum.TARS.getName(), "serviceName1", "method1",
+                "parameterTypes", rpcExt1, false, 
Constants.SYS_DEFAULT_NAMESPACE_ID);
+        final MetaData metaData2 = new MetaData("id", "127.0.0.1:8080", 
"contextPath",
+                "path2", RpcTypeEnum.TARS.getName(), "serviceName2", "method2",
+                "parameterTypes", rpcExt2, false, 
Constants.SYS_DEFAULT_NAMESPACE_ID);
+        final MetaData metaData3 = new MetaData("id", "127.0.0.1:8080", 
"contextPath",
+                "path3", RpcTypeEnum.TARS.getName(), "serviceName3", "method3",
+                "parameterTypes", rpcExt3, false, 
Constants.SYS_DEFAULT_NAMESPACE_ID);
+        final MetaData metaData4 = new MetaData("id", "127.0.0.1:8080", 
"contextPath",
+                "path4", RpcTypeEnum.TARS.getName(), "serviceName4", "method4",
+                "parameterTypes", rpcExt4, false, 
Constants.SYS_DEFAULT_NAMESPACE_ID);
+        List<MetaData> metaDataList = Lists.list(metaData1, metaData2, 
metaData3, metaData4);
+        assertThrows(NullPointerException.class, () -> {
+            ExecutorService executorService = Executors.newFixedThreadPool(4,
+                    
ShenyuThreadFactory.create("ApplicationConfigCache-tars-initPrx", false));
+            CountDownLatch countDownLatch = new CountDownLatch(4);
+            metaDataList.forEach(metaData -> executorService.execute(() -> {
+                applicationConfigCacheUnderTest.initPrx(metaData);
+                countDownLatch.countDown();
+            }));
+            countDownLatch.await();
+            assertEquals("promise_method1", 
applicationConfigCacheUnderTest.get("path1").getMethod().getName());
+            assertEquals("promise_method2", 
applicationConfigCacheUnderTest.get("path2").getMethod().getName());
+            assertEquals("promise_method3", 
applicationConfigCacheUnderTest.get("path3").getMethod().getName());
+            assertEquals("promise_method4", 
applicationConfigCacheUnderTest.get("path4").getMethod().getName());
+        });
+    }
+
+    @Test
+    public void testInitPrx() {
+        final MetaData metaData = new MetaData("id", "127.0.0.1:8080", 
"contextPath",
+                "path6", RpcTypeEnum.TARS.getName(), "serviceName6", "method1",
+                "parameterTypes", 
"{\"methodInfo\":[{\"methodName\":\"method1\",\"params\":[{\"left\":\"int\",\"right\":\"param1\"},"
+                + 
"{\"left\":\"java.lang.Integer\",\"right\":\"param2\"}],\"returnType\":\"java.lang.String\"}]}",
 false, Constants.SYS_DEFAULT_NAMESPACE_ID);
+        assertThrows(NullPointerException.class, () -> {
+            applicationConfigCacheUnderTest.initPrx(metaData);       
+            final TarsInvokePrxList result = 
applicationConfigCacheUnderTest.get("path6");
+            assertEquals("promise_method1", result.getMethod().getName());
+        });
+    }
+
+    @Test
+    public void testGetClassMethodKey() {
+        assertEquals("className_methodName", 
ApplicationConfigCache.getClassMethodKey("className", "methodName"));
+    }
+
+    @Test
+    public void testGetInstance() {
+        final ApplicationConfigCache result = 
ApplicationConfigCache.getInstance();
+        assertNotNull(result);
+    }
+}
diff --git 
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/test/java/org/apache/shenyu/plugin/tars/handler/TarsMetaDataHandlerTest.java
 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/test/java/org/apache/shenyu/plugin/tars/handler/TarsMetaDataHandlerTest.java
new file mode 100644
index 0000000..3377e78
--- /dev/null
+++ 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/test/java/org/apache/shenyu/plugin/tars/handler/TarsMetaDataHandlerTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.plugin.tars.handler;
+
+import org.apache.shenyu.common.constant.Constants;
+import org.apache.shenyu.common.dto.MetaData;
+import org.apache.shenyu.common.enums.RpcTypeEnum;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * Test case for {@link 
org.apache.shenyu.plugin.tars.handler.TarsMetaDataHandler}.
+ */
+@ExtendWith(MockitoExtension.class)
+public class TarsMetaDataHandlerTest {
+
+    private TarsMetaDataHandler tarsMetaDataHandler;
+
+    private MetaData metaData;
+
+    @BeforeEach
+    public void setUp() {
+        metaData = new MetaData("id", "127.0.0.1:8080", "contextPath",
+                "path", RpcTypeEnum.TARS.getName(), "serviceName", "method1",
+                "parameterTypes", 
"{\"methodInfo\":[{\"methodName\":\"method1\",\"params\":[{\"left\":\"int\",\"right\":\"param1\"},"
+                + 
"{\"left\":\"java.lang.Integer\",\"right\":\"param2\"}],\"returnType\":\"java.lang.String\"}]}",
 false, Constants.SYS_DEFAULT_NAMESPACE_ID);
+        tarsMetaDataHandler = new TarsMetaDataHandler();
+    }
+
+    @Test
+    public void testOnSubscribe() {
+        tarsMetaDataHandler.handle(metaData);
+        /**
+         * test for cache;
+         */
+        tarsMetaDataHandler.handle(metaData);
+    }
+
+    @Test
+    public void testUnSubscribe() {
+        tarsMetaDataHandler.remove(metaData);
+    }
+}
diff --git 
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/test/java/org/apache/shenyu/plugin/tars/handler/TarsPluginDataHandlerTest.java
 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/test/java/org/apache/shenyu/plugin/tars/handler/TarsPluginDataHandlerTest.java
new file mode 100644
index 0000000..a9791f1
--- /dev/null
+++ 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/test/java/org/apache/shenyu/plugin/tars/handler/TarsPluginDataHandlerTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.plugin.tars.handler;
+
+import org.apache.shenyu.common.dto.PluginData;
+import org.apache.shenyu.common.dto.convert.plugin.TarsRegisterConfig;
+import org.apache.shenyu.common.enums.PluginEnum;
+import org.apache.shenyu.common.utils.Singleton;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Test case for {@link TarsPluginDataHandler}.
+ */
+@ExtendWith(MockitoExtension.class)
+public final class TarsPluginDataHandlerTest {
+    
+    private TarsPluginDataHandler tarsPluginDataHandlerUnderTest;
+    
+    @BeforeEach
+    public void setUp() {
+        tarsPluginDataHandlerUnderTest = new TarsPluginDataHandler();
+    }
+    
+    @Test
+    public void testHandlerPlugin() {
+        final PluginData pluginData = new PluginData("id", "name", 
"{\"threadpool\":\"cached\",\"corethreads\":1,\"threads\":2,\"queues\":3}", 
"0", true, null);
+        tarsPluginDataHandlerUnderTest.handlerPlugin(pluginData);
+        assertTrue(pluginData.getName().endsWith("name"));
+        TarsRegisterConfig config = 
Singleton.INST.get(TarsRegisterConfig.class);
+        Assertions.assertEquals(config.getThreadpool(), "cached");
+        Assertions.assertEquals(config.getCorethreads(), 1);
+        Assertions.assertEquals(config.getThreads(), 2);
+        Assertions.assertEquals(config.getQueues(), 3);
+    }
+    
+    @Test
+    public void testPluginNamed() {
+        final String result = tarsPluginDataHandlerUnderTest.pluginNamed();
+        assertEquals(PluginEnum.TARS.getName(), result);
+    }
+}
diff --git 
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/test/java/org/apache/shenyu/plugin/tars/util/PrxInfoUtilTest.java
 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/test/java/org/apache/shenyu/plugin/tars/util/PrxInfoUtilTest.java
new file mode 100644
index 0000000..431756b
--- /dev/null
+++ 
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-tars/src/test/java/org/apache/shenyu/plugin/tars/util/PrxInfoUtilTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.plugin.tars.util;
+
+import org.apache.shenyu.common.constant.Constants;
+import org.apache.shenyu.common.dto.MetaData;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Test case for {@link PrxInfoUtil}.
+ */
+@ExtendWith(MockitoExtension.class)
+public class PrxInfoUtilTest {
+
+    @Test
+    public void testGetParamClass() throws Exception {
+        assertEquals(int.class, PrxInfoUtil.getParamClass("int"));
+        assertEquals(long.class, PrxInfoUtil.getParamClass("long"));
+        assertEquals(short.class, PrxInfoUtil.getParamClass("short"));
+        assertEquals(byte.class, PrxInfoUtil.getParamClass("byte"));
+        assertEquals(boolean.class, PrxInfoUtil.getParamClass("boolean"));
+        assertEquals(char.class, PrxInfoUtil.getParamClass("char"));
+        assertEquals(float.class, PrxInfoUtil.getParamClass("float"));
+        assertEquals(Integer.class, 
PrxInfoUtil.getParamClass("java.lang.Integer"));
+    }
+
+    @Test
+    public void testGetParamClassThrowsClassNotFoundException() throws 
Exception {
+        assertThrows(ClassNotFoundException.class, () -> 
PrxInfoUtil.getParamClass("className"));
+    }
+
+    @Test
+    public void testGetPrxName() {
+        final MetaData metaData = new MetaData("id", "appName", "contextPath", 
"/path",
+                "rpcType", "serviceName", "methodName", "parameterTypes",
+                "rpcExt", false, Constants.SYS_DEFAULT_NAMESPACE_ID);
+        final String result = PrxInfoUtil.getPrxName(metaData);
+        assertEquals("pathmethodNamePrx", result);
+    }
+
+    @Test
+    public void testGetMethodName() {
+        assertEquals("promise_methodName", 
PrxInfoUtil.getMethodName("methodName"));
+    }
+
+    @Test
+    public void testGetObjectName() {
+        final String result = PrxInfoUtil.getObjectName("127.0.0.1:8080", 
"serviceName");
+        assertEquals("serviceName@tcp -h 127.0.0.1 -p 8080", result);
+    }
+
+    @Test
+    public void testGetParamArray() {
+        assertArrayEquals(new Object[]{11, Double.valueOf("1.321321312"), 
Long.valueOf("131231312"), Short.valueOf("11"), Byte.valueOf("0"), false, 'a', 
1.321321312F},
+                PrxInfoUtil.getParamArray(new Class<?>[]{int.class, 
double.class, long.class, short.class, byte.class, boolean.class, char.class, 
float.class},
+                        new String[]{"int", "double", "long", "short", "byte", 
"boolean", "char", "float"},
+                        
"{\"int\":11,\"double\":1.321321312,\"long\":131231312,\"short\":11,\"byte\":0,\"boolean\":false,\"char\":'a',\"float\":1.321321312}"));
+    }
+}

Reply via email to