This is an automated email from the ASF dual-hosted git repository.
Aias00 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 4d33568dda perf(grpc): reuse parsed rpcExt metadata across requests
(#7831) (#7841)
4d33568dda is described below
commit 4d33568dda6bf40c6ce15f64340126fb50a69a54
Author: Harsh Raj <[email protected]>
AuthorDate: Mon Oct 19 13:21:18 2026 +0530
perf(grpc): reuse parsed rpcExt metadata across requests (#7831) (#7841)
Co-authored-by: Harsh <[email protected]>
---
.../org/apache/shenyu/plugin/grpc/GrpcPlugin.java | 39 +------
.../plugin/grpc/cache/ApplicationConfigCache.java | 127 ++++++++++++++++++++-
.../shenyu/plugin/grpc/cache/GrpcExtInfo.java | 102 +++++++++++++++++
.../plugin/grpc/handler/GrpcMetaDataHandler.java | 49 ++++++++
.../apache/shenyu/plugin/grpc/GrpcPluginTest.java | 24 ++++
.../grpc/cache/ApplicationConfigCacheTest.java | 77 +++++++++++++
.../grpc/handler/GrpcMetaDataHandlerTest.java | 76 ++++++++++++
.../plugin/grpc/GrpcPluginConfiguration.java | 12 ++
.../plugin/grpc/GrpcPluginConfigurationTest.java | 10 ++
9 files changed, 480 insertions(+), 36 deletions(-)
diff --git
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/GrpcPlugin.java
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/GrpcPlugin.java
index 2ff62f367a..d68910c9a8 100644
---
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/GrpcPlugin.java
+++
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/GrpcPlugin.java
@@ -19,7 +19,6 @@ package org.apache.shenyu.plugin.grpc;
import io.grpc.CallOptions;
import io.grpc.Context;
-import io.grpc.MethodDescriptor;
import org.apache.commons.lang3.StringUtils;
import org.apache.shenyu.common.constant.Constants;
import org.apache.shenyu.common.dto.MetaData;
@@ -29,7 +28,6 @@ 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.common.utils.GsonUtils;
import org.apache.shenyu.plugin.api.ShenyuPluginChain;
import org.apache.shenyu.plugin.api.context.ShenyuContext;
import org.apache.shenyu.plugin.api.result.ShenyuResultEnum;
@@ -37,7 +35,9 @@ 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.grpc.cache.ApplicationConfigCache;
import org.apache.shenyu.plugin.grpc.cache.GrpcClientCache;
+import org.apache.shenyu.plugin.grpc.cache.GrpcExtInfo;
import org.apache.shenyu.plugin.grpc.client.ShenyuGrpcClient;
import org.apache.shenyu.plugin.grpc.context.GrpcConstants;
import org.apache.shenyu.plugin.grpc.proto.ShenyuGrpcResponse;
@@ -104,15 +104,12 @@ public class GrpcPlugin extends AbstractShenyuPlugin {
.map(InetAddress::getHostAddress)
.orElse(StringUtils.EMPTY)).attach();
- GrpcExtInfo extInfo = StringUtils.isBlank(metaData.getRpcExt())
- ? new GrpcExtInfo()
- :
Optional.ofNullable(GsonUtils.getGson().fromJson(metaData.getRpcExt(),
GrpcExtInfo.class))
- .orElseGet(GrpcExtInfo::new);
- CallOptions callOptions =
CallOptions.DEFAULT.withDeadlineAfter(extInfo.timeout, TimeUnit.MILLISECONDS);
+ GrpcExtInfo extInfo =
ApplicationConfigCache.getInstance().obtainGrpcExtInfo(metaData);
+ CallOptions callOptions =
CallOptions.DEFAULT.withDeadlineAfter(extInfo.getTimeout(),
TimeUnit.MILLISECONDS);
Map<String, Map<String, String>> rpcContext =
exchange.getAttribute(Constants.GENERAL_CONTEXT);
Optional.ofNullable(rpcContext).map(context ->
context.get(PluginEnum.GRPC.getName())).ifPresent(
context -> Context.current().withValue(RPC_CONTEXT_KEY,
context).attach());
- CompletableFuture<ShenyuGrpcResponse> result = client.call(metaData,
callOptions, param, extInfo.methodType);
+ CompletableFuture<ShenyuGrpcResponse> result = client.call(metaData,
callOptions, param, extInfo.getMethodType());
Context.current().detach(Context.ROOT);
return Mono.fromFuture(result.thenApply(ret -> {
@@ -163,30 +160,4 @@ public class GrpcPlugin extends AbstractShenyuPlugin {
protected Mono<Void> handleRuleIfNull(final String pluginName, final
ServerWebExchange exchange, final ShenyuPluginChain chain) {
return WebFluxResultUtils.noRuleResult(pluginName, exchange);
}
-
- /**
- * The GrpcExt.
- */
- static class GrpcExtInfo {
-
- private Integer timeout = 5000;
-
- private MethodDescriptor.MethodType methodType =
MethodDescriptor.MethodType.UNARY;
-
- public Integer getTimeout() {
- return timeout;
- }
-
- public void setTimeout(final Integer timeout) {
- this.timeout = timeout;
- }
-
- public MethodDescriptor.MethodType getMethodType() {
- return methodType;
- }
-
- public void setMethodType(final MethodDescriptor.MethodType
methodType) {
- this.methodType = methodType;
- }
- }
}
diff --git
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/cache/ApplicationConfigCache.java
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/cache/ApplicationConfigCache.java
index bfd92f0a77..535b37f0a9 100644
---
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/cache/ApplicationConfigCache.java
+++
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/cache/ApplicationConfigCache.java
@@ -17,11 +17,15 @@
package org.apache.shenyu.plugin.grpc.cache;
+import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
+import io.grpc.MethodDescriptor;
import org.apache.commons.collections4.CollectionUtils;
+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.convert.rule.impl.GrpcRuleHandle;
import org.apache.shenyu.common.dto.convert.selector.GrpcUpstream;
import org.apache.shenyu.common.exception.ShenyuException;
@@ -30,8 +34,9 @@ import org.apache.shenyu.plugin.base.cache.CommonHandleCache;
import org.apache.shenyu.plugin.base.utils.BeanHolder;
import org.apache.shenyu.plugin.base.utils.CacheKeyUtils;
import org.apache.shenyu.plugin.grpc.resolver.ShenyuServiceInstanceLists;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.lang.NonNull;
-import org.springframework.util.StringUtils;
import java.util.List;
import java.util.Map;
@@ -46,6 +51,10 @@ import java.util.function.Supplier;
*/
public final class ApplicationConfigCache {
+ private static final Logger LOG =
LoggerFactory.getLogger(ApplicationConfigCache.class);
+
+ private static final GrpcExtInfo DEFAULT_EXT_INFO = new GrpcExtInfo(5000,
MethodDescriptor.MethodType.UNARY);
+
private final Supplier<CommonHandleCache<String, List<GrpcUpstream>>>
grpcUpstreamCachedHandle = new BeanHolder<>(CommonHandleCache::new);
private final Supplier<CommonHandleCache<String, GrpcRuleHandle>>
ruleCachedHandle = new BeanHolder<>(CommonHandleCache::new);
@@ -60,6 +69,10 @@ public final class ApplicationConfigCache {
}
});
+ private final Cache<String, CachedGrpcExtInfo> grpcExtCache =
CacheBuilder.newBuilder()
+ .maximumSize(Constants.CACHE_MAX_COUNT)
+ .build();
+
private final Map<String, Consumer<Object>> watchUpstreamListener = new
ConcurrentHashMap<>();
private ApplicationConfigCache() {
@@ -136,7 +149,7 @@ public final class ApplicationConfigCache {
* @param ruleHandle ruleHandle
*/
public void cacheRuleHandle(final String ruleDataKey, final String
ruleHandle) {
- final String handler = StringUtils.hasText(ruleHandle) ? ruleHandle :
"{}";
+ final String handler = StringUtils.isNotBlank(ruleHandle) ? ruleHandle
: "{}";
ruleCachedHandle.get().cachedHandle(ruleDataKey,
GsonUtils.getInstance().fromJson(handler, GrpcRuleHandle.class));
}
@@ -169,6 +182,116 @@ public final class ApplicationConfigCache {
return grpcUpstreamCachedHandle.get().obtainHandle(selectorId);
}
+ /**
+ * Obtains or parses GrpcExtInfo for metadata.
+ *
+ * @param metaData metaData
+ * @return parsed or cached GrpcExtInfo
+ */
+ public GrpcExtInfo obtainGrpcExtInfo(final MetaData metaData) {
+ if (Objects.isNull(metaData) ||
StringUtils.isBlank(metaData.getRpcExt())) {
+ return DEFAULT_EXT_INFO;
+ }
+ final String key = buildCacheKey(metaData);
+ final String rpcExt = metaData.getRpcExt();
+ CachedGrpcExtInfo cached = grpcExtCache.getIfPresent(key);
+ if (Objects.nonNull(cached) && Objects.equals(cached.rpcExt, rpcExt)) {
+ return cached.extInfo;
+ }
+ GrpcExtInfo parsed = parseRpcExt(rpcExt);
+ grpcExtCache.put(key, new CachedGrpcExtInfo(rpcExt, parsed));
+ return parsed;
+ }
+
+ /**
+ * Cache GrpcExtInfo for metadata on metadata update.
+ *
+ * @param metaData metaData
+ */
+ public void cacheGrpcExtInfo(final MetaData metaData) {
+ if (Objects.isNull(metaData)) {
+ return;
+ }
+ final String key = buildCacheKey(metaData);
+ final String rpcExt = metaData.getRpcExt();
+ if (StringUtils.isBlank(rpcExt)) {
+ grpcExtCache.put(key, new CachedGrpcExtInfo(rpcExt,
DEFAULT_EXT_INFO));
+ return;
+ }
+ try {
+ GrpcExtInfo extInfo = parseRpcExt(rpcExt);
+ grpcExtCache.put(key, new CachedGrpcExtInfo(rpcExt, extInfo));
+ } catch (Exception e) {
+ LOG.warn("Failed to parse gRPC rpcExt for metadata: {}", key, e);
+ }
+ }
+
+ /**
+ * Invalidate GrpcExtInfo for metadata on metadata removal.
+ *
+ * @param metaData metaData
+ */
+ public void invalidateGrpcExtInfo(final MetaData metaData) {
+ if (Objects.isNull(metaData)) {
+ return;
+ }
+ if (StringUtils.isNotBlank(metaData.getPath())) {
+ grpcExtCache.invalidate(metaData.getPath());
+ }
+ if (StringUtils.isNotBlank(metaData.getId())) {
+ grpcExtCache.invalidate(metaData.getId());
+ }
+ if (StringUtils.isNotBlank(metaData.getServiceName()) &&
StringUtils.isNotBlank(metaData.getMethodName())) {
+ grpcExtCache.invalidate(metaData.getServiceName() + "." +
metaData.getMethodName());
+ }
+ }
+
+ /**
+ * Clean GrpcExtInfo cache.
+ */
+ public void cleanGrpcExtInfo() {
+ grpcExtCache.invalidateAll();
+ }
+
+ private GrpcExtInfo parseRpcExt(final String rpcExt) {
+ if (StringUtils.isBlank(rpcExt)) {
+ return DEFAULT_EXT_INFO;
+ }
+ GrpcExtInfo extInfo = GsonUtils.getGson().fromJson(rpcExt,
GrpcExtInfo.class);
+ if (Objects.isNull(extInfo)) {
+ return DEFAULT_EXT_INFO;
+ }
+ if (Objects.isNull(extInfo.getTimeout())) {
+ extInfo.setTimeout(5000);
+ }
+ if (Objects.isNull(extInfo.getMethodType())) {
+ extInfo.setMethodType(MethodDescriptor.MethodType.UNARY);
+ }
+ return extInfo;
+ }
+
+ private String buildCacheKey(final MetaData metaData) {
+ if (StringUtils.isNotBlank(metaData.getPath())) {
+ return metaData.getPath();
+ }
+ if (StringUtils.isNotBlank(metaData.getId())) {
+ return metaData.getId();
+ }
+ return metaData.getServiceName() + "." + metaData.getMethodName();
+ }
+
+ static final class CachedGrpcExtInfo {
+
+ private final String rpcExt;
+
+ private final GrpcExtInfo extInfo;
+
+ CachedGrpcExtInfo(final String rpcExt, final GrpcExtInfo extInfo) {
+ this.rpcExt = rpcExt;
+ this.extInfo = extInfo;
+ }
+ }
+
/**
* The type Application config cache instance.
diff --git
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/cache/GrpcExtInfo.java
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/cache/GrpcExtInfo.java
new file mode 100644
index 0000000000..26be8e3fcc
--- /dev/null
+++
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/cache/GrpcExtInfo.java
@@ -0,0 +1,102 @@
+/*
+ * 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.grpc.cache;
+
+import io.grpc.MethodDescriptor;
+
+import java.util.Objects;
+
+/**
+ * The GrpcExtInfo.
+ */
+public class GrpcExtInfo {
+
+ private Integer timeout = 5000;
+
+ private MethodDescriptor.MethodType methodType =
MethodDescriptor.MethodType.UNARY;
+
+ /**
+ * Instantiates a new Grpc ext info.
+ */
+ public GrpcExtInfo() {
+ }
+
+ /**
+ * Instantiates a new Grpc ext info.
+ *
+ * @param timeout timeout
+ * @param methodType methodType
+ */
+ public GrpcExtInfo(final Integer timeout, final
MethodDescriptor.MethodType methodType) {
+ this.timeout = timeout;
+ this.methodType = methodType;
+ }
+
+ /**
+ * Gets timeout.
+ *
+ * @return timeout
+ */
+ public Integer getTimeout() {
+ return timeout;
+ }
+
+ /**
+ * Sets timeout.
+ *
+ * @param timeout timeout
+ */
+ public void setTimeout(final Integer timeout) {
+ this.timeout = timeout;
+ }
+
+ /**
+ * Gets methodType.
+ *
+ * @return methodType
+ */
+ public MethodDescriptor.MethodType getMethodType() {
+ return methodType;
+ }
+
+ /**
+ * Sets methodType.
+ *
+ * @param methodType methodType
+ */
+ public void setMethodType(final MethodDescriptor.MethodType methodType) {
+ this.methodType = methodType;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (Objects.isNull(o) || getClass() != o.getClass()) {
+ return false;
+ }
+ GrpcExtInfo that = (GrpcExtInfo) o;
+ return Objects.equals(timeout, that.timeout) && methodType ==
that.methodType;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(timeout, methodType);
+ }
+}
diff --git
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/handler/GrpcMetaDataHandler.java
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/handler/GrpcMetaDataHandler.java
new file mode 100644
index 0000000000..54a2cc40f7
--- /dev/null
+++
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/handler/GrpcMetaDataHandler.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.plugin.grpc.handler;
+
+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.grpc.cache.ApplicationConfigCache;
+
+/**
+ * The type grpc meta data handler.
+ */
+public class GrpcMetaDataHandler implements MetaDataHandler {
+
+ @Override
+ public void handle(final MetaData metaData) {
+ ApplicationConfigCache.getInstance().cacheGrpcExtInfo(metaData);
+ }
+
+ @Override
+ public void remove(final MetaData metaData) {
+ ApplicationConfigCache.getInstance().invalidateGrpcExtInfo(metaData);
+ }
+
+ @Override
+ public void refresh() {
+ ApplicationConfigCache.getInstance().cleanGrpcExtInfo();
+ }
+
+ @Override
+ public String rpcType() {
+ return RpcTypeEnum.GRPC.getName();
+ }
+}
diff --git
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/test/java/org/apache/shenyu/plugin/grpc/GrpcPluginTest.java
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/test/java/org/apache/shenyu/plugin/grpc/GrpcPluginTest.java
index 6ce28918b1..2a4048a3d7 100644
---
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/test/java/org/apache/shenyu/plugin/grpc/GrpcPluginTest.java
+++
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/test/java/org/apache/shenyu/plugin/grpc/GrpcPluginTest.java
@@ -17,6 +17,7 @@
package org.apache.shenyu.plugin.grpc;
+import io.grpc.CallOptions;
import io.grpc.MethodDescriptor;
import org.apache.shenyu.common.constant.Constants;
import org.apache.shenyu.common.dto.MetaData;
@@ -29,7 +30,9 @@ 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.grpc.cache.ApplicationConfigCache;
import org.apache.shenyu.plugin.grpc.cache.GrpcClientCache;
+import org.apache.shenyu.plugin.grpc.cache.GrpcExtInfo;
import org.apache.shenyu.plugin.grpc.client.ShenyuGrpcClient;
import org.apache.shenyu.plugin.grpc.context.GrpcConstants;
import org.apache.shenyu.plugin.grpc.proto.ShenyuGrpcResponse;
@@ -58,6 +61,8 @@ import java.util.concurrent.CompletableFuture;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -150,6 +155,9 @@ public class GrpcPluginTest {
.thenAnswer(invocation -> {
assertEquals(expectedRemoteAddress,
GrpcConstants.GRPC_REMOTE_ADDRESS.get());
assertEquals(expectedMethodType,
invocation.getArgument(3));
+ CallOptions callOptions = invocation.getArgument(1);
+ assertNotNull(callOptions);
+ assertNotNull(callOptions.getDeadline());
return CompletableFuture.completedFuture(response);
});
clientCacheMap.put("grpcId", mockClient);
@@ -159,6 +167,22 @@ public class GrpcPluginTest {
StepVerifier.create(grpcPlugin.doExecute(exchange, chain, selector,
data)).expectSubscription().verifyComplete();
}
+ @Test
+ public void testDoExecuteReusesParsedRpcExt()
+ throws ClassNotFoundException, NoSuchFieldException,
IllegalAccessException {
+ ServerWebExchange exchange1 = getServerWebExchange();
+ MetaData metaData = getMetaData();
+ executeRequest(exchange1, "", metaData,
MethodDescriptor.MethodType.SERVER_STREAMING);
+
+ GrpcExtInfo cached =
ApplicationConfigCache.getInstance().obtainGrpcExtInfo(metaData);
+
+ ServerWebExchange exchange2 = getServerWebExchange();
+ executeRequest(exchange2, "", metaData,
MethodDescriptor.MethodType.SERVER_STREAMING);
+
+ GrpcExtInfo second =
ApplicationConfigCache.getInstance().obtainGrpcExtInfo(metaData);
+ assertSame(cached, second);
+ }
+
@Test
public void testDoExecuteMetaDataError() {
ServerWebExchange exchange = getServerWebExchange();
diff --git
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/test/java/org/apache/shenyu/plugin/grpc/cache/ApplicationConfigCacheTest.java
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/test/java/org/apache/shenyu/plugin/grpc/cache/ApplicationConfigCacheTest.java
index 39be026b7c..a0b8ed2450 100644
---
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/test/java/org/apache/shenyu/plugin/grpc/cache/ApplicationConfigCacheTest.java
+++
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/test/java/org/apache/shenyu/plugin/grpc/cache/ApplicationConfigCacheTest.java
@@ -75,4 +75,81 @@ public class ApplicationConfigCacheTest {
public void testWatch() {
this.applicationConfigCache.watch(selector.getName(),
Assertions::assertNotNull);
}
+
+ @Test
+ public void testObtainGrpcExtInfo() {
+ org.apache.shenyu.common.dto.MetaData metaData =
org.apache.shenyu.common.dto.MetaData.builder()
+ .path("/grpc/echo")
+ .serviceName("echoService")
+ .methodName("echo")
+ .rpcExt("{\"methodType\":\"UNARY\",\"timeout\":2500}")
+ .build();
+
+ GrpcExtInfo info1 =
this.applicationConfigCache.obtainGrpcExtInfo(metaData);
+ org.junit.jupiter.api.Assertions.assertEquals(2500,
info1.getTimeout());
+
org.junit.jupiter.api.Assertions.assertEquals(io.grpc.MethodDescriptor.MethodType.UNARY,
info1.getMethodType());
+
+ GrpcExtInfo info2 =
this.applicationConfigCache.obtainGrpcExtInfo(metaData);
+ org.junit.jupiter.api.Assertions.assertSame(info1, info2);
+ }
+
+ @Test
+ public void testObtainGrpcExtInfoDefaults() {
+
org.junit.jupiter.api.Assertions.assertNotNull(this.applicationConfigCache.obtainGrpcExtInfo(null));
+
+ org.apache.shenyu.common.dto.MetaData blankMeta =
org.apache.shenyu.common.dto.MetaData.builder()
+ .path("/grpc/blank")
+ .rpcExt("")
+ .build();
+ GrpcExtInfo blankInfo =
this.applicationConfigCache.obtainGrpcExtInfo(blankMeta);
+ org.junit.jupiter.api.Assertions.assertEquals(5000,
blankInfo.getTimeout());
+
org.junit.jupiter.api.Assertions.assertEquals(io.grpc.MethodDescriptor.MethodType.UNARY,
blankInfo.getMethodType());
+
+ org.apache.shenyu.common.dto.MetaData emptyJsonMeta =
org.apache.shenyu.common.dto.MetaData.builder()
+ .path("/grpc/emptyJson")
+ .rpcExt("{}")
+ .build();
+ GrpcExtInfo emptyJsonInfo =
this.applicationConfigCache.obtainGrpcExtInfo(emptyJsonMeta);
+ org.junit.jupiter.api.Assertions.assertEquals(5000,
emptyJsonInfo.getTimeout());
+
org.junit.jupiter.api.Assertions.assertEquals(io.grpc.MethodDescriptor.MethodType.UNARY,
emptyJsonInfo.getMethodType());
+ }
+
+ @Test
+ public void testObtainGrpcExtInfoUpdate() {
+ org.apache.shenyu.common.dto.MetaData metaData =
org.apache.shenyu.common.dto.MetaData.builder()
+ .path("/grpc/update")
+ .rpcExt("{\"methodType\":\"UNARY\",\"timeout\":1000}")
+ .build();
+
+ GrpcExtInfo initial =
this.applicationConfigCache.obtainGrpcExtInfo(metaData);
+ org.junit.jupiter.api.Assertions.assertEquals(1000,
initial.getTimeout());
+
+
metaData.setRpcExt("{\"methodType\":\"SERVER_STREAMING\",\"timeout\":4000}");
+ GrpcExtInfo updated =
this.applicationConfigCache.obtainGrpcExtInfo(metaData);
+ org.junit.jupiter.api.Assertions.assertEquals(4000,
updated.getTimeout());
+
org.junit.jupiter.api.Assertions.assertEquals(io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING,
updated.getMethodType());
+ org.junit.jupiter.api.Assertions.assertNotSame(initial, updated);
+ }
+
+ @Test
+ public void testInvalidateAndCleanGrpcExtInfo() {
+ org.apache.shenyu.common.dto.MetaData metaData =
org.apache.shenyu.common.dto.MetaData.builder()
+ .id("meta-1")
+ .path("/grpc/invalidate")
+ .serviceName("service1")
+ .methodName("method1")
+ .rpcExt("{\"methodType\":\"UNARY\",\"timeout\":6000}")
+ .build();
+
+ GrpcExtInfo info1 =
this.applicationConfigCache.obtainGrpcExtInfo(metaData);
+ org.junit.jupiter.api.Assertions.assertSame(info1,
this.applicationConfigCache.obtainGrpcExtInfo(metaData));
+
+ this.applicationConfigCache.invalidateGrpcExtInfo(metaData);
+ GrpcExtInfo info2 =
this.applicationConfigCache.obtainGrpcExtInfo(metaData);
+ org.junit.jupiter.api.Assertions.assertNotSame(info1, info2);
+
+ this.applicationConfigCache.cleanGrpcExtInfo();
+ GrpcExtInfo info3 =
this.applicationConfigCache.obtainGrpcExtInfo(metaData);
+ org.junit.jupiter.api.Assertions.assertNotSame(info2, info3);
+ }
}
diff --git
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/test/java/org/apache/shenyu/plugin/grpc/handler/GrpcMetaDataHandlerTest.java
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/test/java/org/apache/shenyu/plugin/grpc/handler/GrpcMetaDataHandlerTest.java
new file mode 100644
index 0000000000..019977d493
--- /dev/null
+++
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/test/java/org/apache/shenyu/plugin/grpc/handler/GrpcMetaDataHandlerTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.grpc.handler;
+
+import io.grpc.MethodDescriptor;
+import org.apache.shenyu.common.dto.MetaData;
+import org.apache.shenyu.common.enums.RpcTypeEnum;
+import org.apache.shenyu.plugin.grpc.cache.ApplicationConfigCache;
+import org.apache.shenyu.plugin.grpc.cache.GrpcExtInfo;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+/**
+ * The Test Case For {@link GrpcMetaDataHandler}.
+ */
+public class GrpcMetaDataHandlerTest {
+
+ private GrpcMetaDataHandler grpcMetaDataHandler;
+
+ @BeforeEach
+ public void setUp() {
+ this.grpcMetaDataHandler = new GrpcMetaDataHandler();
+ ApplicationConfigCache.getInstance().cleanGrpcExtInfo();
+ }
+
+ @Test
+ public void testRpcType() {
+ assertEquals(RpcTypeEnum.GRPC.getName(),
grpcMetaDataHandler.rpcType());
+ }
+
+ @Test
+ public void testHandleAndRemoveAndRefresh() {
+ MetaData metaData = MetaData.builder()
+ .id("1")
+ .path("/grpc/test")
+ .serviceName("testService")
+ .methodName("testMethod")
+ .rpcExt("{\"methodType\":\"UNARY\",\"timeout\":3000}")
+ .build();
+
+ grpcMetaDataHandler.handle(metaData);
+ GrpcExtInfo info1 =
ApplicationConfigCache.getInstance().obtainGrpcExtInfo(metaData);
+ assertEquals(3000, info1.getTimeout());
+ assertEquals(MethodDescriptor.MethodType.UNARY, info1.getMethodType());
+
+ GrpcExtInfo info2 =
ApplicationConfigCache.getInstance().obtainGrpcExtInfo(metaData);
+ assertSame(info1, info2);
+
+ grpcMetaDataHandler.remove(metaData);
+ // After removal, next obtain re-parses and returns new instance
+ GrpcExtInfo info3 =
ApplicationConfigCache.getInstance().obtainGrpcExtInfo(metaData);
+ assertEquals(3000, info3.getTimeout());
+
+ grpcMetaDataHandler.refresh();
+ GrpcExtInfo info4 =
ApplicationConfigCache.getInstance().obtainGrpcExtInfo(metaData);
+ assertEquals(3000, info4.getTimeout());
+ }
+}
diff --git
a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-grpc/src/main/java/org/apache/shenyu/springboot/starter/plugin/grpc/GrpcPluginConfiguration.java
b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-grpc/src/main/java/org/apache/shenyu/springboot/starter/plugin/grpc/GrpcPluginConfiguration.java
index 0b4bb9da76..2be3f327c2 100644
---
a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-grpc/src/main/java/org/apache/shenyu/springboot/starter/plugin/grpc/GrpcPluginConfiguration.java
+++
b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-grpc/src/main/java/org/apache/shenyu/springboot/starter/plugin/grpc/GrpcPluginConfiguration.java
@@ -20,10 +20,12 @@ package org.apache.shenyu.springboot.starter.plugin.grpc;
import org.apache.shenyu.plugin.api.ShenyuPlugin;
import org.apache.shenyu.plugin.api.context.ShenyuContextDecorator;
import org.apache.shenyu.plugin.base.handler.DiscoveryUpstreamDataHandler;
+import org.apache.shenyu.plugin.base.handler.MetaDataHandler;
import org.apache.shenyu.plugin.base.handler.PluginDataHandler;
import org.apache.shenyu.plugin.grpc.GrpcPlugin;
import org.apache.shenyu.plugin.grpc.context.GrpcShenyuContextDecorator;
import org.apache.shenyu.plugin.grpc.handler.GrpcDiscoveryUpstreamDataHandler;
+import org.apache.shenyu.plugin.grpc.handler.GrpcMetaDataHandler;
import org.apache.shenyu.plugin.grpc.handler.GrpcPluginDataHandler;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
@@ -76,4 +78,14 @@ public class GrpcPluginConfiguration {
public ShenyuContextDecorator grpcShenyuContextDecorator() {
return new GrpcShenyuContextDecorator();
}
+
+ /**
+ * Grpc meta data handler.
+ *
+ * @return the meta data handler
+ */
+ @Bean
+ public MetaDataHandler grpcMetaDataHandler() {
+ return new GrpcMetaDataHandler();
+ }
}
diff --git
a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-grpc/src/test/java/org/apache/shenyu/springboot/starter/plugin/grpc/GrpcPluginConfigurationTest.java
b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-grpc/src/test/java/org/apache/shenyu/springboot/starter/plugin/grpc/GrpcPluginConfigurationTest.java
index 2927a188cd..a9811e03b6 100644
---
a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-grpc/src/test/java/org/apache/shenyu/springboot/starter/plugin/grpc/GrpcPluginConfigurationTest.java
+++
b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-grpc/src/test/java/org/apache/shenyu/springboot/starter/plugin/grpc/GrpcPluginConfigurationTest.java
@@ -19,6 +19,7 @@ package org.apache.shenyu.springboot.starter.plugin.grpc;
import org.apache.shenyu.plugin.api.ShenyuPlugin;
import org.apache.shenyu.plugin.api.context.ShenyuContextDecorator;
+import org.apache.shenyu.plugin.base.handler.MetaDataHandler;
import org.apache.shenyu.plugin.base.handler.PluginDataHandler;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -72,4 +73,13 @@ public class GrpcPluginConfigurationTest {
}
);
}
+
+ @Test
+ public void testGrpcMetaDataHandler() {
+ applicationContextRunner.run(context -> {
+ MetaDataHandler handler =
context.getBean("grpcMetaDataHandler", MetaDataHandler.class);
+ assertNotNull(handler);
+ }
+ );
+ }
}