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

earthchen pushed a commit to branch 3.3
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.3 by this push:
     new 947c061a7a gRPC support context path (#14509)
947c061a7a is described below

commit 947c061a7a3857a404869d398561e24900c9deac
Author: Sean Yang <[email protected]>
AuthorDate: Mon Aug 12 10:29:39 2024 +0800

    gRPC support context path (#14509)
---
 .../rpc/protocol/tri/servlet/TripleFilter.java     | 45 +++-------
 .../java/org/apache/dubbo/rpc/PathResolver.java    |  8 +-
 .../dubbo/rpc/protocol/tri/RequestMetadata.java    |  2 +-
 .../apache/dubbo/rpc/protocol/tri/RequestPath.java | 75 +++++++++++++++++
 .../dubbo/rpc/protocol/tri/TriplePathResolver.java | 97 +++++++++++++++++++---
 .../dubbo/rpc/protocol/tri/TripleProtocol.java     | 91 ++++++++------------
 .../tri/h12/grpc/GrpcRequestHandlerMapping.java    | 39 ++++-----
 .../mapping/DefaultRequestMappingRegistry.java     |  3 +
 .../transport/TripleIsolationExecutorSupport.java  | 37 +++++----
 .../rpc/protocol/tri/TriplePathResolverTest.java   | 42 ++++++++--
 .../tri/service/TriBuiltinServiceTest.java         |  2 +-
 11 files changed, 296 insertions(+), 145 deletions(-)

diff --git 
a/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/TripleFilter.java
 
b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/TripleFilter.java
index 4b5ffdf145..c95f177b57 100644
--- 
a/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/TripleFilter.java
+++ 
b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/TripleFilter.java
@@ -16,21 +16,18 @@
  */
 package org.apache.dubbo.rpc.protocol.tri.servlet;
 
-import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.io.StreamUtils;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.remoting.http12.h2.Http2InputMessageFrame;
 import org.apache.dubbo.remoting.http12.h2.Http2ServerTransportListenerFactory;
 import org.apache.dubbo.remoting.http12.h2.Http2TransportListener;
-import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.PathResolver;
 import org.apache.dubbo.rpc.TriRpcStatus.Code;
 import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.protocol.tri.RequestPath;
 import org.apache.dubbo.rpc.protocol.tri.ServletExchanger;
-import org.apache.dubbo.rpc.protocol.tri.TripleConstant;
 import org.apache.dubbo.rpc.protocol.tri.TripleHeaderEnum;
-import org.apache.dubbo.rpc.protocol.tri.TripleProtocol;
 import org.apache.dubbo.rpc.protocol.tri.h12.grpc.GrpcHeaderNames;
 import 
org.apache.dubbo.rpc.protocol.tri.h12.grpc.GrpcHttp2ServerTransportListener;
 import org.apache.dubbo.rpc.protocol.tri.h12.grpc.GrpcUtils;
@@ -73,31 +70,31 @@ public class TripleFilter implements Filter {
     }
 
     @Override
-    public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain)
+    public void doFilter(ServletRequest servletRequest, ServletResponse 
servletResponse, FilterChain chain)
             throws ServletException, IOException {
-        HttpServletRequest hRequest = (HttpServletRequest) request;
-        HttpServletResponse hResponse = (HttpServletResponse) response;
+        HttpServletRequest request = (HttpServletRequest) servletRequest;
+        HttpServletResponse response = (HttpServletResponse) servletResponse;
 
-        if (!hasServiceMapping(hRequest) && 
!mappingRegistry.exists(hRequest.getRequestURI(), hRequest.getMethod())) {
+        if (!hasGrpcMapping(request) && 
!mappingRegistry.exists(request.getRequestURI(), request.getMethod())) {
             chain.doFilter(request, response);
             return;
         }
 
         AsyncContext context = request.startAsync(request, response);
-        ServletStreamChannel channel = new ServletStreamChannel(hRequest, 
hResponse, context);
+        ServletStreamChannel channel = new ServletStreamChannel(request, 
response, context);
         try {
             Http2TransportListener listener = 
determineHttp2ServerTransportListenerFactory(request.getContentType())
                     .newInstance(channel, ServletExchanger.getUrl(), 
FrameworkModel.defaultModel());
 
             boolean isGrpc = listener instanceof 
GrpcHttp2ServerTransportListener;
             channel.setGrpc(isGrpc);
-            context.setTimeout(resolveTimeout(hRequest, isGrpc));
+            context.setTimeout(resolveTimeout(request, isGrpc));
             context.addListener(new TripleAsyncListener(channel));
             ServletInputStream is = request.getInputStream();
             is.setReadListener(new TripleReadListener(listener, channel, is));
             response.getOutputStream().setWriteListener(new 
TripleWriteListener(channel));
 
-            listener.onMetadata(new HttpMetadataAdapter(hRequest));
+            listener.onMetadata(new HttpMetadataAdapter(request));
         } catch (Throwable t) {
             LOGGER.info("Failed to process request", t);
             channel.writeError(Code.UNKNOWN.code, t);
@@ -107,30 +104,14 @@ public class TripleFilter implements Filter {
     @Override
     public void destroy() {}
 
-    private boolean hasServiceMapping(HttpServletRequest request) {
-        String uri = request.getRequestURI();
-
-        int index = uri.indexOf('/', 1);
-        if (index == -1) {
-            return false;
-        }
-        if (uri.indexOf('/', index + 1) != -1) {
+    private boolean hasGrpcMapping(HttpServletRequest request) {
+        RequestPath path = RequestPath.parse(request.getRequestURI());
+        if (path == null) {
             return false;
         }
-
-        String serviceName = uri.substring(1, index);
-        String version = 
request.getHeader(TripleHeaderEnum.SERVICE_VERSION.getHeader());
         String group = 
request.getHeader(TripleHeaderEnum.SERVICE_GROUP.getHeader());
-        String key = URL.buildKey(serviceName, group, version);
-        Invoker<?> invoker = pathResolver.resolve(key);
-        if (invoker == null && TripleProtocol.RESOLVE_FALLBACK_TO_DEFAULT) {
-            invoker = pathResolver.resolve(URL.buildKey(serviceName, group, 
TripleConstant.DEFAULT_VERSION));
-            if (invoker == null) {
-                return pathResolver.resolve(serviceName) != null;
-            }
-        }
-
-        return true;
+        String version = 
request.getHeader(TripleHeaderEnum.SERVICE_VERSION.getHeader());
+        return pathResolver.resolve(path.getPath(), group, version) != null;
     }
 
     private Http2ServerTransportListenerFactory 
determineHttp2ServerTransportListenerFactory(String contentType) {
diff --git 
a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/PathResolver.java 
b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/PathResolver.java
index ba8d17cdcc..815229c4b4 100644
--- 
a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/PathResolver.java
+++ 
b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/PathResolver.java
@@ -21,16 +21,20 @@ import org.apache.dubbo.common.extension.ExtensionScope;
 import org.apache.dubbo.common.extension.SPI;
 
 /**
- * PathResolver maintains a mapping between request path and Invoker for 
multiple protocols.
+ * PathResolver maintains a mapping between request paths and invokers for 
multiple protocols.
  */
 @SPI(value = CommonConstants.TRIPLE, scope = ExtensionScope.FRAMEWORK)
 public interface PathResolver {
 
+    void register(Invoker<?> invoker);
+
+    void unregister(Invoker<?> invoker);
+
     Invoker<?> add(String path, Invoker<?> invoker);
 
     Invoker<?> addIfAbsent(String path, Invoker<?> invoker);
 
-    Invoker<?> resolve(String path);
+    Invoker<?> resolve(String path, String group, String version);
 
     boolean hasNativeStub(String path);
 
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/RequestMetadata.java
 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/RequestMetadata.java
index 63e1339625..52794f146e 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/RequestMetadata.java
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/RequestMetadata.java
@@ -55,7 +55,7 @@ public class RequestMetadata {
         header.scheme(scheme)
                 .authority(address)
                 .method(HttpMethod.POST.asciiName())
-                .path("/" + service + "/" + method.getMethodName())
+                .path(RequestPath.toFullPath(service, method.getMethodName()))
                 .set(TripleHeaderEnum.CONTENT_TYPE_KEY.getHeader(), 
MediaType.APPLICATION_GRPC_PROTO.getName())
                 .set(HttpHeaderNames.TE, HttpHeaderValues.TRAILERS);
         setIfNotNull(header, TripleHeaderEnum.TIMEOUT.getHeader(), timeout);
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/RequestPath.java
 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/RequestPath.java
new file mode 100644
index 0000000000..22e79ca102
--- /dev/null
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/RequestPath.java
@@ -0,0 +1,75 @@
+/*
+ * 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.dubbo.rpc.protocol.tri;
+
+public final class RequestPath {
+
+    private final String path;
+    private final String stubPath;
+    private final String serviceInterface;
+    private final String methodName;
+
+    private RequestPath(String path, String stubPath, String serviceInterface, 
String methodName) {
+        this.path = path;
+        this.stubPath = stubPath;
+        this.serviceInterface = serviceInterface;
+        this.methodName = methodName;
+    }
+
+    // Request path patten:
+    //     '{interfaceName}/{methodName}' or 
'{contextPath}/{interfaceName}/{methodName}'
+    //      └─── path ────┘ └─ method ─┘      └────────── path ───────────┘ └─ 
method ─┘
+    public static RequestPath parse(String fullPath) {
+        int i = fullPath.lastIndexOf('/');
+        if (i < 1) {
+            return null;
+        }
+
+        String path = fullPath.substring(1, i);
+        int j = path.lastIndexOf('/');
+        if (j == -1) {
+            return new RequestPath(path, fullPath, path, fullPath.substring(i 
+ 1));
+        } else {
+            return new RequestPath(path, fullPath.substring(j), 
path.substring(j + 1), fullPath.substring(i + 1));
+        }
+    }
+
+    public static String toFullPath(String path, String methodName) {
+        return '/' + path + '/' + methodName;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public String getStubPath() {
+        return stubPath;
+    }
+
+    public String getServiceInterface() {
+        return serviceInterface;
+    }
+
+    public String getMethodName() {
+        return methodName;
+    }
+
+    @Override
+    public String toString() {
+        return path + '/' + methodName;
+    }
+}
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TriplePathResolver.java
 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TriplePathResolver.java
index be291bd865..49192076bb 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TriplePathResolver.java
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TriplePathResolver.java
@@ -16,48 +16,121 @@
  */
 package org.apache.dubbo.rpc.protocol.tri;
 
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.PathResolver;
 
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.Map;
 
 public class TriplePathResolver implements PathResolver {
 
-    private final ConcurrentHashMap<String, Invoker<?>> path2Invoker = new 
ConcurrentHashMap<>();
-    private final ConcurrentHashMap<String, Object> nativeStub = new 
ConcurrentHashMap<>();
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(TripleProtocol.class);
+
+    private final Map<String, Invoker<?>> mapping = 
CollectionUtils.newConcurrentHashMap();
+    private final Map<String, Boolean> nativeStubs = 
CollectionUtils.newConcurrentHashMap();
+
+    @Override
+    public void register(Invoker<?> invoker) {
+        URL url = invoker.getUrl();
+        String serviceKey = url.getServiceKey();
+        String serviceInterface = 
url.getServiceModel().getServiceModel().getInterfaceName();
+
+        register0(serviceKey, serviceInterface, invoker, url);
+
+        // Path patten: '{interfaceName}' or '{contextPath}/{interfaceName}'
+        String path = url.getPath();
+        int index = path.lastIndexOf('/');
+        if (index == -1) {
+            return;
+        }
+        String fallbackPath = path.substring(0, index + 1) + serviceInterface;
+        register0(URL.buildKey(path, url.getGroup(), url.getVersion()), 
fallbackPath, invoker, url);
+    }
+
+    private void register0(String path, String fallbackPath, Invoker<?> 
invoker, URL url) {
+        // register default mapping
+        Invoker<?> previous = mapping.put(path, invoker);
+        if (previous != null) {
+            if (path.equals(fallbackPath)) {
+                LOGGER.info(
+                        "Already exists an invoker[{}] on path[{}], dubbo will 
override with invoker[{}]",
+                        previous.getUrl(),
+                        path,
+                        url);
+            } else {
+                throw new IllegalStateException(String.format(
+                        "Already exists an invoker[%s] on path[%s], failed to 
add invoker[%s], please use a unique path.",
+                        previous.getUrl(), path, url));
+            }
+        } else {
+            LOGGER.debug("Register triple grpc mapping: '{}' -> invoker{}", 
path, url);
+        }
+
+        // register fallback mapping
+        if (TripleProtocol.RESOLVE_FALLBACK_TO_DEFAULT && 
!path.equals(fallbackPath)) {
+            previous = mapping.putIfAbsent(fallbackPath, invoker);
+            if (previous != null) {
+                LOGGER.info(
+                        "Already exists an invoker[{}] on path[{}], dubbo will 
skip override with invoker[{}]",
+                        previous.getUrl(),
+                        fallbackPath,
+                        url);
+            } else {
+                LOGGER.info("Register fallback triple grpc mapping: '{}' -> 
invoker{}", fallbackPath, url);
+            }
+        }
+    }
+
+    @Override
+    public void unregister(Invoker<?> invoker) {
+        URL url = invoker.getUrl();
+        mapping.remove(url.getServiceKey());
+        if (TripleProtocol.RESOLVE_FALLBACK_TO_DEFAULT) {
+            
mapping.remove(url.getServiceModel().getServiceModel().getInterfaceName());
+        }
+    }
 
     @Override
     public Invoker<?> add(String path, Invoker<?> invoker) {
-        return path2Invoker.put(path, invoker);
+        return mapping.put(path, invoker);
     }
 
     @Override
     public Invoker<?> addIfAbsent(String path, Invoker<?> invoker) {
-        return path2Invoker.putIfAbsent(path, invoker);
+        return mapping.putIfAbsent(path, invoker);
     }
 
     @Override
-    public Invoker<?> resolve(String path) {
-        return path2Invoker.get(path);
+    public Invoker<?> resolve(String path, String group, String version) {
+        Invoker<?> invoker = mapping.get(URL.buildKey(path, group, version));
+        if (invoker == null && TripleProtocol.RESOLVE_FALLBACK_TO_DEFAULT) {
+            invoker = mapping.get(URL.buildKey(path, group, 
TripleConstant.DEFAULT_VERSION));
+            if (invoker == null) {
+                invoker = mapping.get(path);
+            }
+        }
+        return invoker;
     }
 
-    @Override
     public boolean hasNativeStub(String path) {
-        return nativeStub.containsKey(path);
+        return nativeStubs.containsKey(path);
     }
 
     @Override
     public void addNativeStub(String path) {
-        nativeStub.put(path, 0);
+        nativeStubs.put(path, Boolean.TRUE);
     }
 
     @Override
     public void remove(String path) {
-        path2Invoker.remove(path);
+        mapping.remove(path);
     }
 
     @Override
     public void destroy() {
-        path2Invoker.clear();
+        mapping.clear();
     }
 }
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocol.java
 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocol.java
index 7c04bf914f..a7f37a0be5 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocol.java
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocol.java
@@ -19,8 +19,6 @@ package org.apache.dubbo.rpc.protocol.tri;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.config.Configuration;
 import org.apache.dubbo.common.config.ConfigurationUtils;
-import org.apache.dubbo.common.logger.Logger;
-import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
 import org.apache.dubbo.common.utils.ExecutorUtil;
 import org.apache.dubbo.common.utils.NetUtils;
@@ -45,7 +43,6 @@ import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 
-import io.grpc.health.v1.HealthCheckResponse;
 import io.grpc.health.v1.HealthCheckResponse.ServingStatus;
 
 import static 
org.apache.dubbo.common.constants.CommonConstants.DEFAULT_CLIENT_THREADPOOL;
@@ -65,8 +62,6 @@ import static org.apache.dubbo.rpc.Constants.HTTP3_KEY;
 
 public class TripleProtocol extends AbstractProtocol {
 
-    private static final Logger logger = 
LoggerFactory.getLogger(TripleProtocol.class);
-
     private final PathResolver pathResolver;
     private final RequestMappingRegistry mappingRegistry;
     private final TriBuiltinService triBuiltinService;
@@ -110,72 +105,60 @@ public class TripleProtocol extends AbstractProtocol {
     public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
         URL url = invoker.getUrl();
         String key = serviceKey(url);
+
+        // create exporter
         AbstractExporter<T> exporter = new AbstractExporter<T>(invoker) {
             @Override
             public void afterUnExport() {
-                pathResolver.remove(url.getServiceKey());
-                
pathResolver.remove(url.getServiceModel().getServiceModel().getInterfaceName());
+                // unregister grpc request mapping
+                pathResolver.unregister(invoker);
+
                 // unregister rest request mapping
                 mappingRegistry.unregister(invoker);
-                // set service status
-                if (triBuiltinService.enable()) {
-                    triBuiltinService
-                            .getHealthStatusManager()
-                            .setStatus(url.getServiceKey(), 
ServingStatus.NOT_SERVING);
-                    triBuiltinService
-                            .getHealthStatusManager()
-                            .setStatus(url.getServiceInterface(), 
ServingStatus.NOT_SERVING);
-                }
+
+                // set service status to NOT_SERVING
+                setServiceStatus(url, ServingStatus.NOT_SERVING);
+
                 exporterMap.remove(key);
             }
         };
-
         exporterMap.put(key, exporter);
 
+        // add invoker
         invokers.add(invoker);
 
-        Invoker<?> previous = pathResolver.add(url.getServiceKey(), invoker);
-        if (previous != null) {
-            if (url.getServiceKey()
-                    
.equals(url.getServiceModel().getServiceModel().getInterfaceName())) {
-                logger.info("Already exists an invoker[" + previous.getUrl() + 
"] on path[" + url.getServiceKey()
-                        + "], dubbo will override with invoker[" + url + "]");
-            } else {
-                throw new IllegalStateException(
-                        "Already exists an invoker[" + previous.getUrl() + "] 
on path[" + url.getServiceKey()
-                                + "], failed to add invoker[" + url + "] , 
please use unique serviceKey.");
-            }
-        }
-        if (RESOLVE_FALLBACK_TO_DEFAULT) {
-            previous = pathResolver.addIfAbsent(
-                    
url.getServiceModel().getServiceModel().getInterfaceName(), invoker);
-            if (previous != null) {
-                logger.info("Already exists an invoker[" + previous.getUrl() + 
"] on path["
-                        + 
url.getServiceModel().getServiceModel().getInterfaceName()
-                        + "], dubbo will skip override with invoker[" + url + 
"]");
-            } else {
-                logger.info("Add fallback triple invoker[" + url + "] to path["
-                        + 
url.getServiceModel().getServiceModel().getInterfaceName() + "] with invoker[" 
+ url + "]");
-            }
-        }
+        // register grpc path mapping
+        pathResolver.register(invoker);
 
         // register rest request mapping
         mappingRegistry.register(invoker);
 
-        // set service status
-        if (triBuiltinService.enable()) {
-            triBuiltinService
-                    .getHealthStatusManager()
-                    .setStatus(url.getServiceKey(), 
HealthCheckResponse.ServingStatus.SERVING);
-            triBuiltinService
-                    .getHealthStatusManager()
-                    .setStatus(url.getServiceInterface(), 
HealthCheckResponse.ServingStatus.SERVING);
-        }
-        // init
+        // set service status to SERVING
+        setServiceStatus(url, ServingStatus.SERVING);
+
+        // init server executor
         ExecutorRepository.getInstance(url.getOrDefaultApplicationModel())
                 .createExecutorIfAbsent(ExecutorUtil.setThreadName(url, 
SERVER_THREAD_POOL_NAME));
 
+        // bind server port
+        bindServerPort(url);
+
+        // optimize serialization
+        optimizeSerialization(url);
+
+        return exporter;
+    }
+
+    private void setServiceStatus(URL url, ServingStatus status) {
+        if (triBuiltinService.enable()) {
+            
triBuiltinService.getHealthStatusManager().setStatus(url.getServiceKey(), 
status);
+            
triBuiltinService.getHealthStatusManager().setStatus(url.getServiceInterface(), 
status);
+        }
+    }
+
+    private void bindServerPort(URL url) {
         boolean bindPort = true;
+
         if (SERVLET_ENABLED) {
             int port = url.getParameter(BIND_PORT_KEY, url.getPort());
             Integer serverPort = ServletExchanger.getServerPort();
@@ -188,6 +171,7 @@ public class TripleProtocol extends AbstractProtocol {
             }
             ServletExchanger.bind(url);
         }
+
         if (bindPort) {
             PortUnificationExchanger.bind(url, new DefaultPuHandler());
         }
@@ -196,9 +180,6 @@ public class TripleProtocol extends AbstractProtocol {
             Http3Exchanger.bind(url);
             http3Bound = true;
         }
-
-        optimizeSerialization(url);
-        return exporter;
     }
 
     @Override
@@ -231,7 +212,7 @@ public class TripleProtocol extends AbstractProtocol {
     @Override
     public void destroy() {
         if (logger.isInfoEnabled()) {
-            logger.info("Destroying protocol [" + getClass().getSimpleName() + 
"] ...");
+            logger.info("Destroying protocol [{}] ...", 
getClass().getSimpleName());
         }
         PortUnificationExchanger.close();
         if (http3Bound) {
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcRequestHandlerMapping.java
 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcRequestHandlerMapping.java
index c2e8d9d3ae..b5467f2d50 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcRequestHandlerMapping.java
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcRequestHandlerMapping.java
@@ -20,15 +20,17 @@ import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.remoting.http12.HttpRequest;
 import org.apache.dubbo.remoting.http12.HttpResponse;
+import org.apache.dubbo.remoting.http12.HttpStatus;
+import org.apache.dubbo.remoting.http12.exception.HttpStatusException;
 import org.apache.dubbo.remoting.http12.message.HttpMessageCodec;
 import org.apache.dubbo.remoting.http12.message.MediaType;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.PathResolver;
 import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.apache.dubbo.rpc.protocol.tri.DescriptorUtils;
+import org.apache.dubbo.rpc.protocol.tri.RequestPath;
 import org.apache.dubbo.rpc.protocol.tri.TripleConstant;
 import org.apache.dubbo.rpc.protocol.tri.TripleHeaderEnum;
-import org.apache.dubbo.rpc.protocol.tri.TripleProtocol;
 import org.apache.dubbo.rpc.protocol.tri.route.RequestHandler;
 import org.apache.dubbo.rpc.protocol.tri.route.RequestHandlerMapping;
 
@@ -52,33 +54,22 @@ public final class GrpcRequestHandlerMapping implements 
RequestHandlerMapping {
             return null;
         }
 
-        String uri = request.uri();
-        int index = uri.indexOf('/', 1);
-        if (index == -1) {
-            return null;
-        }
-        if (uri.indexOf('/', index + 1) != -1) {
-            return null;
+        RequestPath path = RequestPath.parse(request.uri());
+        if (path == null) {
+            throw notFound();
         }
 
-        String serviceName = uri.substring(1, index);
-        String version = 
request.header(TripleHeaderEnum.SERVICE_VERSION.getHeader());
         String group = 
request.header(TripleHeaderEnum.SERVICE_GROUP.getHeader());
-        String key = URL.buildKey(serviceName, group, version);
-        Invoker<?> invoker = pathResolver.resolve(key);
-        if (invoker == null && TripleProtocol.RESOLVE_FALLBACK_TO_DEFAULT) {
-            invoker = pathResolver.resolve(URL.buildKey(serviceName, group, 
TripleConstant.DEFAULT_VERSION));
-            if (invoker == null) {
-                invoker = pathResolver.resolve(serviceName);
-                if (invoker == null) {
-                    return null;
-                }
-            }
+        String version = 
request.header(TripleHeaderEnum.SERVICE_VERSION.getHeader());
+        Invoker<?> invoker = pathResolver.resolve(path.getPath(), group, 
version);
+        if (invoker == null) {
+            throw notFound();
         }
 
         RequestHandler handler = new RequestHandler(invoker);
-        handler.setHasStub(pathResolver.hasNativeStub(uri));
-        handler.setMethodName(uri.substring(index + 1));
+        handler.setHasStub(pathResolver.hasNativeStub(path.getStubPath()));
+        handler.setMethodName(path.getMethodName());
+        String serviceName = path.getServiceInterface();
         
handler.setServiceDescriptor(DescriptorUtils.findServiceDescriptor(invoker, 
serviceName, handler.isHasStub()));
         HttpMessageCodec codec = CODEC_FACTORY.createCodec(url, 
frameworkModel, request.contentType());
         handler.setHttpMessageDecoder(codec);
@@ -86,6 +77,10 @@ public final class GrpcRequestHandlerMapping implements 
RequestHandlerMapping {
         return handler;
     }
 
+    private static HttpStatusException notFound() {
+        return new HttpStatusException(HttpStatus.NOT_FOUND.getCode(), 
"Invoker for gRPC not found");
+    }
+
     @Override
     public String getType() {
         return TripleConstant.TRIPLE_HANDLER_TYPE_GRPC;
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/DefaultRequestMappingRegistry.java
 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/DefaultRequestMappingRegistry.java
index 159b1e7179..3168e23c5a 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/DefaultRequestMappingRegistry.java
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/DefaultRequestMappingRegistry.java
@@ -184,6 +184,9 @@ public final class DefaultRequestMappingRegistry implements 
RequestMappingRegist
 
     @Override
     public void unregister(Invoker<?> invoker) {
+        if (tree == null) {
+            return;
+        }
         lock.writeLock().lock();
         try {
             tree.remove(mapping -> mapping.meta.getInvoker() == invoker);
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleIsolationExecutorSupport.java
 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleIsolationExecutorSupport.java
index cd70a2a3b5..81ec798f35 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleIsolationExecutorSupport.java
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleIsolationExecutorSupport.java
@@ -21,9 +21,11 @@ import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.remoting.http12.HttpHeaders;
-import org.apache.dubbo.remoting.http12.h2.Http2Header;
+import org.apache.dubbo.remoting.http12.RequestMetadata;
 import org.apache.dubbo.rpc.executor.AbstractIsolationExecutorSupport;
+import org.apache.dubbo.rpc.protocol.tri.RequestPath;
 import org.apache.dubbo.rpc.protocol.tri.TripleHeaderEnum;
+import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
 
 public class TripleIsolationExecutorSupport extends 
AbstractIsolationExecutorSupport {
     private static final ErrorTypeAwareLogger logger =
@@ -35,21 +37,28 @@ public class TripleIsolationExecutorSupport extends 
AbstractIsolationExecutorSup
 
     @Override
     protected ServiceKey getServiceKey(Object data) {
-        if (!(data instanceof Http2Header)) {
+        if (!(data instanceof RequestMetadata)) {
             return null;
         }
 
-        Http2Header http2Metadata = (Http2Header) data;
-        HttpHeaders headers = http2Metadata.headers();
-        String path = http2Metadata.path();
-        String[] parts = path.split("/"); // path like 
/{interfaceName}/{methodName}
-        String interfaceName = parts[1];
-        String version = 
headers.containsKey(TripleHeaderEnum.SERVICE_VERSION.getHeader())
-                ? 
headers.getFirst(TripleHeaderEnum.SERVICE_VERSION.getHeader())
-                : null;
-        String group = 
headers.containsKey(TripleHeaderEnum.SERVICE_GROUP.getHeader())
-                ? headers.getFirst(TripleHeaderEnum.SERVICE_GROUP.getHeader())
-                : null;
-        return new ServiceKey(interfaceName, version, group);
+        RequestMetadata httpMetadata = (RequestMetadata) data;
+        RequestPath path = RequestPath.parse(httpMetadata.path());
+        if (path == null) {
+            return null;
+        }
+
+        HttpHeaders headers = httpMetadata.headers();
+        return new ServiceKey(
+                path.getServiceInterface(),
+                getHeader(headers, TripleHeaderEnum.SERVICE_VERSION, 
RestConstants.HEADER_SERVICE_VERSION),
+                getHeader(headers, TripleHeaderEnum.SERVICE_GROUP, 
RestConstants.HEADER_SERVICE_GROUP));
+    }
+
+    private static String getHeader(HttpHeaders headers, TripleHeaderEnum en, 
String key) {
+        String value = headers.getFirst(en.getHeader());
+        if (value == null) {
+            value = headers.getFirst(key);
+        }
+        return value;
     }
 }
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/TriplePathResolverTest.java
 
b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/TriplePathResolverTest.java
index 5e9d212bf0..9ec9532681 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/TriplePathResolverTest.java
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/TriplePathResolverTest.java
@@ -17,27 +17,41 @@
 package org.apache.dubbo.rpc.protocol.tri;
 
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.PathResolver;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ServiceDescriptor;
+import org.apache.dubbo.rpc.model.ServiceModel;
 
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
 
 class TriplePathResolverTest {
 
     private static final PathResolver PATH_RESOLVER =
-            
ExtensionLoader.getExtensionLoader(PathResolver.class).getDefaultExtension();
+            
FrameworkModel.defaultModel().getDefaultExtension(PathResolver.class);
+
+    private static final String SERVICE_NAME = "DemoService";
 
     private static final Invoker<Object> INVOKER = new Invoker<Object>() {
         @Override
         public URL getUrl() {
-            return null;
+            ServiceDescriptor serviceDescriptor = 
Mockito.mock(ServiceDescriptor.class);
+            
Mockito.when(serviceDescriptor.getInterfaceName()).thenReturn(SERVICE_NAME);
+            ServiceModel serviceModel = Mockito.mock(ServiceModel.class);
+            
Mockito.when(serviceModel.getServiceModel()).thenReturn(serviceDescriptor);
+            return URL.valueOf("tri://localhost/demo/" + SERVICE_NAME)
+                    .setServiceInterface(SERVICE_NAME)
+                    .addParameter(CommonConstants.GROUP_KEY, "g1")
+                    .addParameter(CommonConstants.VERSION_KEY, "1.0.1")
+                    .setServiceModel(serviceModel);
         }
 
         @Override
@@ -61,8 +75,8 @@ class TriplePathResolverTest {
 
     @BeforeEach
     public void init() {
-
         PATH_RESOLVER.add("/abc", INVOKER);
+        PATH_RESOLVER.register(INVOKER);
     }
 
     @AfterEach
@@ -75,6 +89,22 @@ class TriplePathResolverTest {
         Assertions.assertEquals(INVOKER, getInvokerByPath("/abc"));
     }
 
+    @Test
+    void testResolveWithContextPath() {
+        String path = "demo/" + SERVICE_NAME;
+        Assertions.assertEquals(INVOKER, PATH_RESOLVER.resolve(SERVICE_NAME, 
null, null));
+        Assertions.assertEquals(INVOKER, PATH_RESOLVER.resolve(path, null, 
null));
+        Assertions.assertEquals(INVOKER, PATH_RESOLVER.resolve(SERVICE_NAME, 
"g1", "1.0.1"));
+        Assertions.assertEquals(INVOKER, PATH_RESOLVER.resolve(path, "g1", 
"1.0.1"));
+        Assertions.assertEquals(INVOKER, PATH_RESOLVER.resolve(SERVICE_NAME, 
"g2", "1.0.2"));
+        Assertions.assertEquals(INVOKER, PATH_RESOLVER.resolve(path, "g2", 
"1.0.2"));
+        TripleProtocol.RESOLVE_FALLBACK_TO_DEFAULT = false;
+        Assertions.assertEquals(INVOKER, PATH_RESOLVER.resolve(SERVICE_NAME, 
"g1", "1.0.1"));
+        Assertions.assertEquals(INVOKER, PATH_RESOLVER.resolve(path, "g1", 
"1.0.1"));
+        Assertions.assertNull(PATH_RESOLVER.resolve(SERVICE_NAME, "g2", 
"1.0.2"));
+        Assertions.assertNull(PATH_RESOLVER.resolve(path, "g2", "1.0.2"));
+    }
+
     @Test
     void testRemove() {
         Assertions.assertEquals(INVOKER, getInvokerByPath("/abc"));
@@ -102,7 +132,7 @@ class TriplePathResolverTest {
         Assertions.assertNull(getInvokerByPath("/bcd"));
     }
 
-    private Invoker getInvokerByPath(String path) {
-        return PATH_RESOLVER.resolve(path);
+    private Invoker<?> getInvokerByPath(String path) {
+        return PATH_RESOLVER.resolve(path, null, null);
     }
 }
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/service/TriBuiltinServiceTest.java
 
b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/service/TriBuiltinServiceTest.java
index d4d45a8e4a..ed731db3c0 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/service/TriBuiltinServiceTest.java
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/service/TriBuiltinServiceTest.java
@@ -47,7 +47,7 @@ class TriBuiltinServiceTest {
         Assertions.assertNotNull(triBuiltinService.getHealthStatusManager());
         PathResolver pathResolver =
                 
frameworkModel.getExtensionLoader(PathResolver.class).getDefaultExtension();
-        Assertions.assertNotNull(pathResolver.resolve(serviceName));
+        Assertions.assertNotNull(pathResolver.resolve(serviceName, null, 
null));
         ModuleServiceRepository repository =
                 
frameworkModel.getInternalApplicationModel().getInternalModule().getServiceRepository();
         Assertions.assertFalse(repository.getAllServices().isEmpty());


Reply via email to