You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by al...@apache.org on 2024/07/30 13:00:17 UTC

(dubbo) branch 3.3 updated: Support stream in servlet mode (#14469)

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

albumenj 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 3a72599c9c Support stream in servlet mode (#14469)
3a72599c9c is described below

commit 3a72599c9c08ead011f69c55eac84fe23ad034e5
Author: Sean Yang <ox...@gmail.com>
AuthorDate: Tue Jul 30 21:00:11 2024 +0800

    Support stream in servlet mode (#14469)
    
    * Support stream in servlet mode
    
    * Compatible with netty version 4.1.101.Final
---
 .../apache/dubbo/config/nested/Http3Config.java    |  12 +-
 .../apache/dubbo/config/nested/ServletConfig.java  |  27 +---
 .../dubbo-demo-spring-boot-provider/pom.xml        |   5 -
 .../src/main/resources/application.yml             |  14 +--
 .../pom.xml                                        |  77 ++++--------
 .../dubbo/springboot/demo/servlet/ApiConsumer.java | 107 ++++++++++++++++
 .../springboot/demo/servlet/GreeterService.java    |  44 +++++++
 .../demo/servlet/GreeterServiceImpl.java           |  82 ++++++++++++
 .../dubbo/springboot/demo/servlet/HelloReply.java  |  34 +++++
 .../springboot/demo/servlet/HelloRequest.java      |  34 +++++
 .../demo/servlet/ProviderApplication.java          |  32 +++++
 .../src/main/resources/application.yml             |  19 +--
 .../src/main/resources/log4j2.xml                  |  29 +++++
 dubbo-demo/dubbo-demo-spring-boot/pom.xml          |   1 +
 dubbo-plugin/dubbo-triple-servlet/pom.xml          |  31 +++++
 .../protocol/tri/rest/support/servlet/Helper.java  |  12 ++
 .../servlet/ServletHttpMessageAdapterFactory.java  |   4 +-
 .../protocol/tri/servlet/ServletStreamChannel.java | 106 ++++++++++++----
 .../rpc/protocol/tri/servlet/TripleFilter.java     | 139 +++++++++++++++------
 .../http12/AbstractServerHttpChannelObserver.java  |  10 +-
 .../rpc/protocol/tri/TripleHttp2Protocol.java      |  21 ++--
 .../dubbo-spring-boot-3-autoconfigure/pom.xml      |  59 +--------
 .../DubboTriple3AutoConfiguration.java             |   8 +-
 .../dubbo-spring-boot-autoconfigure/pom.xml        |  16 +--
 .../DubboTripleAutoConfiguration.java              |   4 -
 .../dubbo-spring-boot-starter/pom.xml              |   5 -
 dubbo-spring-boot/pom.xml                          |  18 ++-
 27 files changed, 671 insertions(+), 279 deletions(-)

diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/Http3Config.java b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/Http3Config.java
index 757516196f..1d6a87767f 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/Http3Config.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/Http3Config.java
@@ -23,10 +23,10 @@ public class Http3Config implements Serializable {
     private static final long serialVersionUID = -4443828713331129834L;
 
     /**
-     * Enable http3 support
+     * Whether to enable HTTP/3 support
      * <p>The default value is false.
      */
-    private Boolean enable;
+    private Boolean enabled;
 
     /**
      * See <a href="https://docs.rs/quiche/0.6.0/quiche/struct.Config.html#method.set_initial_max_data">set_initial_max_data</a>.
@@ -115,12 +115,12 @@ public class Http3Config implements Serializable {
      */
     private String ccAlgorithm;
 
-    public Boolean getEnable() {
-        return enable;
+    public Boolean getEnabled() {
+        return enabled;
     }
 
-    public void setEnable(Boolean enable) {
-        this.enable = enable;
+    public void setEnabled(Boolean enabled) {
+        this.enabled = enabled;
     }
 
     public Integer getInitialMaxData() {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/ServletConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/ServletConfig.java
index 30b67ebf57..6bcbe50b60 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/ServletConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/ServletConfig.java
@@ -23,17 +23,10 @@ public class ServletConfig implements Serializable {
     private static final long serialVersionUID = 1091478303358670173L;
 
     /**
-     * Enable servlet support, requests are transport through the servlet container,
-     * which only supports unary calls due to protocol limitations
+     * Whether to enable servlet support, requests are transport through the servlet container
      * <p>The default value is false.
      */
-    private Boolean enable;
-
-    /**
-     * The timeout in milliseconds for the servlet async timeout.
-     * <p>The default value is 180_000.
-     */
-    private Integer timeout;
+    private Boolean enabled;
 
     /**
      * The URL patterns that the servlet filter will be registered for.
@@ -47,20 +40,12 @@ public class ServletConfig implements Serializable {
      */
     private Integer filterOrder;
 
-    public Boolean getEnable() {
-        return enable;
-    }
-
-    public void setEnable(Boolean enable) {
-        this.enable = enable;
-    }
-
-    public Integer getTimeout() {
-        return timeout;
+    public Boolean getEnabled() {
+        return enabled;
     }
 
-    public void setTimeout(Integer timeout) {
-        this.timeout = timeout;
+    public void setEnabled(Boolean enabled) {
+        this.enabled = enabled;
     }
 
     public String[] getFilterUrlPatterns() {
diff --git a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/pom.xml b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/pom.xml
index 88b6160ba4..2c0d523449 100644
--- a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/pom.xml
+++ b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/pom.xml
@@ -92,11 +92,6 @@
       <version>${project.version}</version>
     </dependency>
 
-    <dependency>
-      <groupId>org.springframework.boot</groupId>
-      <artifactId>spring-boot-starter-web</artifactId>
-    </dependency>
-
     <dependency>
       <groupId>org.apache.dubbo</groupId>
       <artifactId>dubbo-spring-boot-starter</artifactId>
diff --git a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/src/main/resources/application.yml b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/src/main/resources/application.yml
index e2642b6612..b1789be60f 100644
--- a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/src/main/resources/application.yml
+++ b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/src/main/resources/application.yml
@@ -21,14 +21,9 @@ spring:
 dubbo:
   application:
     name: ${spring.application.name}
-    qos-enable: false
   protocol:
-    name: tri
+    name: dubbo
     port: -1
-    #port: ${server.port}
-    triple:
-      servlet:
-        enable: true
   registry:
     id: zk-registry
     address: zookeeper://127.0.0.1:2181
@@ -46,10 +41,3 @@ dubbo:
       exporter:
         enabled: true
     enable-metadata: true
-
-server:
-  port: 8081
-  http2:
-    enabled: true
-  tomcat:
-    keep-alive-timeout: 180000
diff --git a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/pom.xml b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/pom.xml
similarity index 67%
copy from dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/pom.xml
copy to dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/pom.xml
index 88b6160ba4..33c0414d0b 100644
--- a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/pom.xml
+++ b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/pom.xml
@@ -24,96 +24,65 @@
     <relativePath>../pom.xml</relativePath>
   </parent>
 
-  <artifactId>dubbo-demo-spring-boot-provider</artifactId>
-
-  <properties>
-    <skip_maven_deploy>true</skip_maven_deploy>
-  </properties>
+  <artifactId>dubbo-demo-spring-boot-servlet</artifactId>
 
   <dependencies>
-    <dependency>
-      <groupId>org.apache.dubbo</groupId>
-      <artifactId>dubbo-demo-spring-boot-interface</artifactId>
-      <version>${project.parent.version}</version>
-    </dependency>
-
     <dependency>
       <groupId>org.apache.dubbo</groupId>
       <artifactId>dubbo-registry-zookeeper</artifactId>
       <version>${project.version}</version>
     </dependency>
-
-    <dependency>
-      <groupId>org.apache.dubbo</groupId>
-      <artifactId>dubbo-configcenter-zookeeper</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-
-    <dependency>
-      <groupId>org.apache.dubbo</groupId>
-      <artifactId>dubbo-metadata-report-zookeeper</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-
     <dependency>
       <groupId>org.apache.dubbo</groupId>
-      <artifactId>dubbo-config-spring</artifactId>
+      <artifactId>dubbo-rpc-triple</artifactId>
       <version>${project.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>com.google.protobuf</groupId>
+          <artifactId>protobuf-java-util</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>com.google.protobuf</groupId>
+          <artifactId>protobuf-java</artifactId>
+        </exclusion>
+      </exclusions>
     </dependency>
-
     <dependency>
       <groupId>org.apache.dubbo</groupId>
-      <artifactId>dubbo-remoting-netty4</artifactId>
+      <artifactId>dubbo-triple-servlet</artifactId>
       <version>${project.version}</version>
     </dependency>
-
     <dependency>
       <groupId>org.apache.dubbo</groupId>
       <artifactId>dubbo-serialization-hessian2</artifactId>
       <version>${project.version}</version>
     </dependency>
-
     <dependency>
       <groupId>org.apache.dubbo</groupId>
-      <artifactId>dubbo-serialization-fastjson2</artifactId>
+      <artifactId>dubbo-config-spring</artifactId>
       <version>${project.version}</version>
     </dependency>
 
-    <!-- Observability -->
     <dependency>
       <groupId>org.apache.dubbo</groupId>
-      <artifactId>dubbo-tracing-otel-zipkin-spring-boot-starter</artifactId>
+      <artifactId>dubbo-spring-boot-autoconfigure</artifactId>
       <version>${project.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.apache.dubbo</groupId>
+          <artifactId>dubbo</artifactId>
+        </exclusion>
+      </exclusions>
     </dependency>
-
     <dependency>
       <groupId>org.apache.dubbo</groupId>
-      <artifactId>dubbo-qos</artifactId>
+      <artifactId>dubbo-spring-boot-3-autoconfigure</artifactId>
       <version>${project.version}</version>
     </dependency>
-
     <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
     </dependency>
-
-    <dependency>
-      <groupId>org.apache.dubbo</groupId>
-      <artifactId>dubbo-spring-boot-starter</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-
-    <dependency>
-      <groupId>org.springframework.boot</groupId>
-      <artifactId>spring-boot-starter</artifactId>
-      <exclusions>
-        <exclusion>
-          <groupId>org.springframework.boot</groupId>
-          <artifactId>spring-boot-starter-logging</artifactId>
-        </exclusion>
-      </exclusions>
-    </dependency>
-
     <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-log4j2</artifactId>
@@ -125,7 +94,6 @@
       <plugin>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-maven-plugin</artifactId>
-        <version>${spring-boot-maven-plugin.version}</version>
         <executions>
           <execution>
             <goals>
@@ -136,5 +104,4 @@
       </plugin>
     </plugins>
   </build>
-
 </project>
diff --git a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/ApiConsumer.java b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/ApiConsumer.java
new file mode 100644
index 0000000000..a16fdd0722
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/ApiConsumer.java
@@ -0,0 +1,107 @@
+/*
+ * 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.springboot.demo.servlet;
+
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.config.ProtocolConfig;
+import org.apache.dubbo.config.ReferenceConfig;
+import org.apache.dubbo.config.RegistryConfig;
+import org.apache.dubbo.config.bootstrap.DubboBootstrap;
+
+import java.util.concurrent.CompletableFuture;
+
+public class ApiConsumer {
+
+    public static void main(String[] args) throws InterruptedException {
+        ReferenceConfig<GreeterService> referenceConfig = new ReferenceConfig<>();
+        referenceConfig.setInterface(GreeterService.class);
+        referenceConfig.setCheck(false);
+        referenceConfig.setProtocol(CommonConstants.TRIPLE);
+        referenceConfig.setLazy(true);
+        referenceConfig.setTimeout(100000);
+
+        DubboBootstrap bootstrap = DubboBootstrap.getInstance();
+        bootstrap
+                .application(new ApplicationConfig("dubbo-demo-triple-api-consumer"))
+                .registry(new RegistryConfig("zookeeper://127.0.0.1:2181"))
+                .protocol(new ProtocolConfig(CommonConstants.TRIPLE, -1))
+                .reference(referenceConfig)
+                .start();
+
+        GreeterService greeterService = referenceConfig.get();
+        System.out.println("dubbo referenceConfig started");
+        System.out.println("Call sayHello");
+        HelloReply reply = greeterService.sayHello(buildRequest("triple"));
+        System.out.println("sayHello reply: " + reply.getMessage());
+
+        System.out.println("Call sayHelloAsync");
+        CompletableFuture<String> sayHelloAsync = greeterService.sayHelloAsync("triple");
+        sayHelloAsync.thenAccept(value -> System.out.println("sayHelloAsync reply: " + value));
+
+        StreamObserver<HelloReply> responseObserver = new StreamObserver<HelloReply>() {
+            @Override
+            public void onNext(HelloReply reply) {
+                System.out.println("sayHelloServerStream onNext: " + reply.getMessage());
+            }
+
+            @Override
+            public void onError(Throwable t) {
+                System.out.println("sayHelloServerStream onError: " + t.getMessage());
+            }
+
+            @Override
+            public void onCompleted() {
+                System.out.println("sayHelloServerStream onCompleted");
+            }
+        };
+        System.out.println("Call sayHelloServerStream");
+        greeterService.sayHelloServerStream(buildRequest("triple"), responseObserver);
+
+        StreamObserver<HelloReply> biResponseObserver = new StreamObserver<HelloReply>() {
+            @Override
+            public void onNext(HelloReply reply) {
+                System.out.println("biRequestObserver onNext: " + reply.getMessage());
+            }
+
+            @Override
+            public void onError(Throwable t) {
+                System.out.println("biResponseObserver onError: " + t.getMessage());
+            }
+
+            @Override
+            public void onCompleted() {
+                System.out.println("biResponseObserver onCompleted");
+            }
+        };
+        System.out.println("Call biRequestObserver");
+        StreamObserver<HelloRequest> biRequestObserver = greeterService.sayHelloBiStream(biResponseObserver);
+        for (int i = 0; i < 5; i++) {
+            biRequestObserver.onNext(buildRequest("triple" + i));
+        }
+        biRequestObserver.onCompleted();
+
+        Thread.sleep(2000);
+    }
+
+    private static HelloRequest buildRequest(String name) {
+        HelloRequest request = new HelloRequest();
+        request.setName(name);
+        return request;
+    }
+}
diff --git a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/GreeterService.java b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/GreeterService.java
new file mode 100644
index 0000000000..e777429e71
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/GreeterService.java
@@ -0,0 +1,44 @@
+/*
+ * 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.springboot.demo.servlet;
+
+import org.apache.dubbo.common.stream.StreamObserver;
+
+import java.util.concurrent.CompletableFuture;
+
+public interface GreeterService {
+
+    /**
+     * Sends a greeting
+     */
+    HelloReply sayHello(HelloRequest request);
+
+    /**
+     * Sends a greeting asynchronously
+     */
+    CompletableFuture<String> sayHelloAsync(String request);
+
+    /**
+     * Sends a greeting with server streaming
+     */
+    void sayHelloServerStream(HelloRequest request, StreamObserver<HelloReply> responseObserver);
+
+    /**
+     * Sends greetings with bi streaming
+     */
+    StreamObserver<HelloRequest> sayHelloBiStream(StreamObserver<HelloReply> responseObserver);
+}
diff --git a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/GreeterServiceImpl.java b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/GreeterServiceImpl.java
new file mode 100644
index 0000000000..a9f3ac608f
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/GreeterServiceImpl.java
@@ -0,0 +1,82 @@
+/*
+ * 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.springboot.demo.servlet;
+
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.config.annotation.DubboService;
+
+import java.util.concurrent.CompletableFuture;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@DubboService
+public class GreeterServiceImpl implements GreeterService {
+
+    private static final Logger LOG = LoggerFactory.getLogger(GreeterServiceImpl.class);
+
+    @Override
+    public HelloReply sayHello(HelloRequest request) {
+        LOG.info("Received sayHello request: {}", request.getName());
+        return toReply("Hello " + request.getName());
+    }
+
+    @Override
+    public CompletableFuture<String> sayHelloAsync(String name) {
+        LOG.info("Received sayHelloAsync request: {}", name);
+        return CompletableFuture.supplyAsync(() -> "Hello " + name);
+    }
+
+    @Override
+    public void sayHelloServerStream(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
+        LOG.info("Received sayHelloServerStream request");
+        for (int i = 1; i < 6; i++) {
+            LOG.info("sayHelloServerStream onNext: {} {} times", request.getName(), i);
+            responseObserver.onNext(toReply("Hello " + request.getName() + ' ' + i + " times"));
+        }
+        LOG.info("sayHelloServerStream onCompleted");
+        responseObserver.onCompleted();
+    }
+
+    @Override
+    public StreamObserver<HelloRequest> sayHelloBiStream(StreamObserver<HelloReply> responseObserver) {
+        LOG.info("Received sayHelloBiStream request");
+        return new StreamObserver<HelloRequest>() {
+            @Override
+            public void onNext(HelloRequest request) {
+                LOG.info("sayHelloBiStream onNext: {}", request.getName());
+                responseObserver.onNext(toReply("Hello " + request.getName()));
+            }
+
+            @Override
+            public void onError(Throwable throwable) {
+                LOG.error("sayHelloBiStream onError", throwable);
+            }
+
+            @Override
+            public void onCompleted() {
+                LOG.info("sayHelloBiStream onCompleted");
+            }
+        };
+    }
+
+    private static HelloReply toReply(String message) {
+        HelloReply reply = new HelloReply();
+        reply.setMessage(message);
+        return reply;
+    }
+}
diff --git a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/HelloReply.java b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/HelloReply.java
new file mode 100644
index 0000000000..580d5336c5
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/HelloReply.java
@@ -0,0 +1,34 @@
+/*
+ * 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.springboot.demo.servlet;
+
+import java.io.Serializable;
+
+public class HelloReply implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private String message;
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+}
diff --git a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/HelloRequest.java b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/HelloRequest.java
new file mode 100644
index 0000000000..a17fb810dd
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/HelloRequest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.springboot.demo.servlet;
+
+import java.io.Serializable;
+
+public class HelloRequest implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private String name;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}
diff --git a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/ProviderApplication.java b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/ProviderApplication.java
new file mode 100644
index 0000000000..a3876c7e7d
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/java/org/apache/dubbo/springboot/demo/servlet/ProviderApplication.java
@@ -0,0 +1,32 @@
+/*
+ * 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.springboot.demo.servlet;
+
+import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+@EnableDubbo
+public class ProviderApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(ProviderApplication.class, args);
+        System.out.println("dubbo service started");
+    }
+}
diff --git a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/src/main/resources/application.yml b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/resources/application.yml
similarity index 73%
copy from dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/src/main/resources/application.yml
copy to dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/resources/application.yml
index e2642b6612..e03758b7cc 100644
--- a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-provider/src/main/resources/application.yml
+++ b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/resources/application.yml
@@ -16,7 +16,7 @@
 
 spring:
   application:
-    name: dubbo-springboot-demo-provider
+    name: dubbo-springboot-demo-servlet
 
 dubbo:
   application:
@@ -24,28 +24,13 @@ dubbo:
     qos-enable: false
   protocol:
     name: tri
-    port: -1
-    #port: ${server.port}
+    port: ${server.port}
     triple:
       servlet:
         enable: true
   registry:
     id: zk-registry
     address: zookeeper://127.0.0.1:2181
-  config-center:
-    address: zookeeper://127.0.0.1:2181
-  metadata-report:
-    address: zookeeper://127.0.0.1:2181
-  metrics:
-    protocol: prometheus
-    enable-jvm: true
-    enable-registry: true
-    aggregation:
-      enabled: true
-    prometheus:
-      exporter:
-        enabled: true
-    enable-metadata: true
 
 server:
   port: 8081
diff --git a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/resources/log4j2.xml b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/resources/log4j2.xml
new file mode 100644
index 0000000000..69e1321d22
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/resources/log4j2.xml
@@ -0,0 +1,29 @@
+<?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.
+  -->
+<Configuration status="WARN">
+    <Appenders>
+        <Console name="Console" target="SYSTEM_OUT" follow="true">
+            <PatternLayout pattern="%style{%d{HH:mm:ss.SSS}}{Magenta} %style{|-}{White}%highlight{%-5p} [%t] %style{%40.40c}{Cyan}:%style{%-3L}{Blue} %style{-|}{White} %m%n%rEx{filters(jdk.internal.reflect,java.lang.reflect,sun.reflect)}" disableAnsi="false" charset="UTF-8"/>
+        </Console>
+    </Appenders>
+    <Loggers>
+        <Root level="info">
+            <AppenderRef ref="Console"/>
+        </Root>
+    </Loggers>
+</Configuration>
diff --git a/dubbo-demo/dubbo-demo-spring-boot/pom.xml b/dubbo-demo/dubbo-demo-spring-boot/pom.xml
index 5396c1e0fc..126f18552a 100644
--- a/dubbo-demo/dubbo-demo-spring-boot/pom.xml
+++ b/dubbo-demo/dubbo-demo-spring-boot/pom.xml
@@ -29,6 +29,7 @@
     <module>dubbo-demo-spring-boot-consumer</module>
     <module>dubbo-demo-spring-boot-provider</module>
     <module>dubbo-demo-spring-boot-interface</module>
+    <module>dubbo-demo-spring-boot-servlet</module>
   </modules>
 
   <properties>
diff --git a/dubbo-plugin/dubbo-triple-servlet/pom.xml b/dubbo-plugin/dubbo-triple-servlet/pom.xml
index ab55ef6942..f15f9a1332 100644
--- a/dubbo-plugin/dubbo-triple-servlet/pom.xml
+++ b/dubbo-plugin/dubbo-triple-servlet/pom.xml
@@ -106,6 +106,7 @@
     }]]></replacevalue>
                     </replace>
                     <replace file="${sources_directory}/rest/support/servlet/jakarta/ServletHttpMessageAdapterFactory.java" token="-100" value="-200" />
+                    <replace file="${sources_directory}/rest/support/servlet/jakarta/FilterAdapter.java" token="&quot;servlet" value="&quot;jakarta-servlet" />
                   </target>
                 </configuration>
               </execution>
@@ -132,5 +133,35 @@
         </plugins>
       </build>
     </profile>
+    <profile>
+      <id>release</id>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-enforcer-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>enforce-generate-jakarta</id>
+                <goals>
+                  <goal>enforce</goal>
+                </goals>
+                <phase>prepare-package</phase>
+                <configuration>
+                  <rules>
+                    <requireFilesExist>
+                      <files>
+                        <file>${sources_directory}/servlet/jakarta/TripleFilter.java</file>
+                      </files>
+                    </requireFilesExist>
+                  </rules>
+                  <fail>true</fail>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
   </profiles>
 </project>
diff --git a/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/Helper.java b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/Helper.java
index b7c6c218e0..a341fa686a 100644
--- a/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/Helper.java
+++ b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/Helper.java
@@ -20,8 +20,11 @@ import org.apache.dubbo.common.io.StreamUtils;
 import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.remoting.http12.HttpCookie;
 import org.apache.dubbo.remoting.http12.HttpRequest.FileUpload;
+import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.apache.dubbo.rpc.protocol.tri.rest.RestException;
 
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
 import javax.servlet.http.Part;
 
 import java.io.FileOutputStream;
@@ -142,4 +145,13 @@ final class Helper {
             return Collections.emptyList();
         }
     }
+
+    public static ServletContext createDummyServletContext(FrameworkModel frameworkModel) {
+        return new DummyServletContext(frameworkModel);
+    }
+
+    public static FilterConfig createDummyFilterConfig(
+            String filterName, FrameworkModel frameworkModel, ServletContext servletContext) {
+        return new DummyFilterConfig(filterName, frameworkModel, servletContext);
+    }
 }
diff --git a/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpMessageAdapterFactory.java b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpMessageAdapterFactory.java
index 1862593203..b10579ad81 100644
--- a/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpMessageAdapterFactory.java
+++ b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpMessageAdapterFactory.java
@@ -37,7 +37,7 @@ public final class ServletHttpMessageAdapterFactory
 
     public ServletHttpMessageAdapterFactory(FrameworkModel frameworkModel) {
         this.frameworkModel = frameworkModel;
-        servletContext = new DummyServletContext(frameworkModel);
+        servletContext = Helper.createDummyServletContext(frameworkModel);
         httpSessionFactory = getHttpSessionFactory(frameworkModel);
     }
 
@@ -61,6 +61,6 @@ public final class ServletHttpMessageAdapterFactory
     }
 
     public FilterConfig adaptFilterConfig(String filterName) {
-        return new DummyFilterConfig(filterName, frameworkModel, servletContext);
+        return Helper.createDummyFilterConfig(filterName, frameworkModel, servletContext);
     }
 }
diff --git a/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/ServletStreamChannel.java b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/ServletStreamChannel.java
index d5f0c7d39f..d6beb3a567 100644
--- a/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/ServletStreamChannel.java
+++ b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/ServletStreamChannel.java
@@ -16,17 +16,21 @@
  */
 package org.apache.dubbo.rpc.protocol.tri.servlet;
 
-import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.remoting.http12.HttpHeaderNames;
 import org.apache.dubbo.remoting.http12.HttpHeaders;
 import org.apache.dubbo.remoting.http12.HttpMetadata;
 import org.apache.dubbo.remoting.http12.HttpOutputMessage;
 import org.apache.dubbo.remoting.http12.HttpVersion;
+import org.apache.dubbo.remoting.http12.exception.HttpStatusException;
 import org.apache.dubbo.remoting.http12.h2.H2StreamChannel;
 import org.apache.dubbo.remoting.http12.h2.Http2Header;
 import org.apache.dubbo.remoting.http12.h2.Http2OutputMessage;
 import org.apache.dubbo.remoting.http12.h2.Http2OutputMessageFrame;
+import org.apache.dubbo.rpc.TriRpcStatus;
+import org.apache.dubbo.rpc.TriRpcStatus.Code;
+import org.apache.dubbo.rpc.protocol.tri.TripleHeaderEnum;
 
 import javax.servlet.AsyncContext;
 import javax.servlet.ServletOutputStream;
@@ -42,38 +46,81 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.concurrent.CompletableFuture;
 
-import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_IO_EXCEPTION;
-
 final class ServletStreamChannel implements H2StreamChannel {
 
-    private static final ErrorTypeAwareLogger LOG = LoggerFactory.getErrorTypeAwareLogger(ServletStreamChannel.class);
+    private static final Logger LOG = LoggerFactory.getLogger(ServletStreamChannel.class);
 
     private final HttpServletRequest request;
     private final HttpServletResponse response;
     private final AsyncContext context;
 
+    private boolean isGrpc;
+
     ServletStreamChannel(HttpServletRequest request, HttpServletResponse response, AsyncContext context) {
         this.request = request;
         this.response = response;
         this.context = context;
     }
 
+    public void setGrpc(boolean isGrpc) {
+        this.isGrpc = isGrpc;
+    }
+
+    public void writeError(int code, Throwable throwable) {
+        if (response.isCommitted() && code == Code.DEADLINE_EXCEEDED.code) {
+            return;
+        }
+        try {
+            if (isGrpc) {
+                response.setTrailerFields(() -> {
+                    Map<String, String> map = new HashMap<>();
+                    map.put(TripleHeaderEnum.STATUS_KEY.getHeader(), String.valueOf(code));
+                    return map;
+                });
+                return;
+            }
+            try {
+                if (throwable instanceof HttpStatusException) {
+                    response.setStatus(((HttpStatusException) throwable).getStatusCode());
+                    response.getOutputStream().close();
+                } else {
+                    response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+                }
+            } catch (Throwable t) {
+                LOG.info("Failed to send response", t);
+            }
+        } finally {
+            context.complete();
+        }
+    }
+
     @Override
     public CompletableFuture<Void> writeResetFrame(long errorCode) {
+        if (isGrpc) {
+            writeError(TriRpcStatus.httpStatusToGrpcCode((int) errorCode).code, null);
+            return completed();
+        }
+
         try {
             if (errorCode == 0L) {
                 response.getOutputStream().close();
-            } else if (errorCode >= 300 && errorCode < 600) {
+                return completed();
+            }
+            if (response.isCommitted()) {
+                return completed();
+            }
+            if (errorCode >= 300 && errorCode < 600) {
                 response.sendError((int) errorCode);
             } else {
                 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
             }
         } catch (Throwable t) {
-            LOG.error(COMMON_IO_EXCEPTION, "", "", "Failed to close response", t);
+            LOG.info("Failed to close response", t);
         } finally {
             context.complete();
         }
-        return CompletableFuture.completedFuture(null);
+
+        return completed();
     }
 
     @Override
@@ -94,31 +141,36 @@ final class ServletStreamChannel implements H2StreamChannel {
                     }
                     return map;
                 });
-            } else {
-                for (Entry<String, List<String>> entry : headers.entrySet()) {
-                    String key = entry.getKey();
-                    List<String> values = entry.getValue();
-                    if (HttpHeaderNames.STATUS.getName().equals(key)) {
-                        response.setStatus(Integer.parseInt(values.get(0)));
-                        continue;
-                    }
-                    if (values.size() == 1) {
-                        response.setHeader(key, values.get(0));
-                    } else {
-                        for (int i = 0, size = values.size(); i < size; i++) {
-                            response.addHeader(key, values.get(i));
-                        }
+                return completed();
+            }
+
+            if (response.isCommitted()) {
+                return completed();
+            }
+
+            for (Entry<String, List<String>> entry : headers.entrySet()) {
+                String key = entry.getKey();
+                List<String> values = entry.getValue();
+                if (HttpHeaderNames.STATUS.getName().equals(key)) {
+                    response.setStatus(Integer.parseInt(values.get(0)));
+                    continue;
+                }
+                if (values.size() == 1) {
+                    response.setHeader(key, values.get(0));
+                } else {
+                    for (int i = 0, size = values.size(); i < size; i++) {
+                        response.addHeader(key, values.get(i));
                     }
                 }
             }
         } catch (Throwable t) {
-            LOG.error(COMMON_IO_EXCEPTION, "", "", "Failed to write header", t);
+            LOG.info("Failed to write header", t);
         } finally {
             if (endStream) {
                 context.complete();
             }
         }
-        return CompletableFuture.completedFuture(null);
+        return completed();
     }
 
     @Override
@@ -133,13 +185,13 @@ final class ServletStreamChannel implements H2StreamChannel {
             bos.writeTo(out);
             out.flush();
         } catch (Throwable t) {
-            LOG.error(COMMON_IO_EXCEPTION, "", "", "Failed to write message", t);
+            LOG.info("Failed to write message", t);
         } finally {
             if (endStream) {
                 context.complete();
             }
         }
-        return CompletableFuture.completedFuture(null);
+        return completed();
     }
 
     @Override
@@ -154,4 +206,8 @@ final class ServletStreamChannel implements H2StreamChannel {
 
     @Override
     public void flush() {}
+
+    private static CompletableFuture<Void> completed() {
+        return CompletableFuture.completedFuture(null);
+    }
 }
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 b009588853..18af427d1f 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
@@ -18,15 +18,14 @@ 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.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.common.utils.StringUtils;
-import org.apache.dubbo.remoting.http12.h2.H2StreamChannel;
 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.ServletExchanger;
 import org.apache.dubbo.rpc.protocol.tri.TripleConstant;
@@ -40,38 +39,37 @@ import org.apache.dubbo.rpc.protocol.tri.rest.mapping.DefaultRequestMappingRegis
 import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingRegistry;
 
 import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
+import javax.servlet.ReadListener;
 import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
+import javax.servlet.WriteListener;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.Set;
 
-import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_IO_EXCEPTION;
-import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR;
-
 public class TripleFilter implements Filter {
 
-    private static final ErrorTypeAwareLogger LOG = LoggerFactory.getErrorTypeAwareLogger(TripleFilter.class);
+    private static final Logger LOG = LoggerFactory.getLogger(TripleFilter.class);
 
     private PathResolver pathResolver;
     private RequestMappingRegistry mappingRegistry;
-    private int defaultTimeout;
 
     @Override
     public void init(FilterConfig config) {
         FrameworkModel frameworkModel = FrameworkModel.defaultModel();
         pathResolver = frameworkModel.getDefaultExtension(PathResolver.class);
         mappingRegistry = frameworkModel.getBeanFactory().getOrRegisterBean(DefaultRequestMappingRegistry.class);
-        String timeoutString = config.getInitParameter("timeout");
-        defaultTimeout = StringUtils.isEmpty(timeoutString) ? 180_000 : Integer.parseInt(timeoutString);
     }
 
     @Override
@@ -85,37 +83,24 @@ public class TripleFilter implements Filter {
             return;
         }
 
-        AsyncContext context = request.startAsync();
+        AsyncContext context = request.startAsync(request, response);
+        ServletStreamChannel channel = new ServletStreamChannel(hRequest, hResponse, context);
         try {
-            H2StreamChannel streamChannel = new ServletStreamChannel(hRequest, hResponse, context);
             Http2TransportListener listener = determineHttp2ServerTransportListenerFactory(request.getContentType())
-                    .newInstance(streamChannel, ServletExchanger.getUrl(), FrameworkModel.defaultModel());
+                    .newInstance(channel, ServletExchanger.getUrl(), FrameworkModel.defaultModel());
 
-            context.setTimeout(resolveTimeout(hRequest, listener instanceof GrpcHttp2ServerTransportListener));
+            boolean isGrpc = listener instanceof GrpcHttp2ServerTransportListener;
+            channel.setGrpc(isGrpc);
+            context.setTimeout(resolveTimeout(hRequest, 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));
-
-            ByteArrayOutputStream os;
-            try {
-                os = new ByteArrayOutputStream(1024);
-                StreamUtils.copy(request.getInputStream(), os);
-            } catch (Throwable t) {
-                LOG.error(COMMON_IO_EXCEPTION, "", "", "Failed to read input", t);
-                try {
-                    hResponse.sendError(HttpServletResponse.SC_BAD_REQUEST);
-                } finally {
-                    context.complete();
-                }
-                return;
-            }
-            listener.onData(new Http2InputMessageFrame(new ByteArrayInputStream(os.toByteArray()), true));
         } catch (Throwable t) {
-            LOG.error(INTERNAL_ERROR, "", "", "Failed to process request", t);
-            try {
-                hResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-            } finally {
-                context.complete();
-            }
+            LOG.info("Failed to process request", t);
+            channel.writeError(Code.UNKNOWN.code, t);
         }
     }
 
@@ -160,7 +145,7 @@ public class TripleFilter implements Filter {
         return GenericHttp2ServerTransportListenerFactory.INSTANCE;
     }
 
-    private int resolveTimeout(HttpServletRequest request, boolean isGrpc) {
+    private static int resolveTimeout(HttpServletRequest request, boolean isGrpc) {
         try {
             if (isGrpc) {
                 String timeoutString = request.getHeader(GrpcHeaderNames.GRPC_TIMEOUT.getName());
@@ -178,6 +163,84 @@ public class TripleFilter implements Filter {
             }
         } catch (Throwable ignored) {
         }
-        return defaultTimeout;
+        return 0;
+    }
+
+    private static final class TripleAsyncListener implements AsyncListener {
+
+        private final ServletStreamChannel streamChannel;
+
+        TripleAsyncListener(ServletStreamChannel streamChannel) {
+            this.streamChannel = streamChannel;
+        }
+
+        @Override
+        public void onComplete(AsyncEvent event) {}
+
+        @Override
+        public void onTimeout(AsyncEvent event) {
+            streamChannel.writeError(Code.DEADLINE_EXCEEDED.code, event.getThrowable());
+        }
+
+        @Override
+        public void onError(AsyncEvent event) {
+            streamChannel.writeError(Code.CANCELLED.code, event.getThrowable());
+        }
+
+        @Override
+        public void onStartAsync(AsyncEvent event) {}
+    }
+
+    private static final class TripleReadListener implements ReadListener {
+
+        private final Http2TransportListener listener;
+        private final ServletStreamChannel channel;
+        private final ServletInputStream input;
+        private final byte[] buffer = new byte[4 * 1024];
+
+        TripleReadListener(Http2TransportListener listener, ServletStreamChannel channel, ServletInputStream input) {
+            this.listener = listener;
+            this.channel = channel;
+            this.input = input;
+        }
+
+        @Override
+        public void onDataAvailable() throws IOException {
+            while (input.isReady()) {
+                int length = input.read(buffer);
+                if (length == -1) {
+                    return;
+                }
+                byte[] copy = Arrays.copyOf(buffer, length);
+                listener.onData(new Http2InputMessageFrame(new ByteArrayInputStream(copy), false));
+            }
+        }
+
+        @Override
+        public void onAllDataRead() {
+            listener.onData(new Http2InputMessageFrame(StreamUtils.EMPTY, true));
+        }
+
+        @Override
+        public void onError(Throwable t) {
+            channel.writeError(Code.CANCELLED.code, t);
+        }
+    }
+
+    private static final class TripleWriteListener implements WriteListener {
+
+        private final ServletStreamChannel channel;
+
+        TripleWriteListener(ServletStreamChannel channel) {
+            this.channel = channel;
+        }
+
+        @Override
+        public void onWritePossible() {}
+
+        @Override
+        public void onError(Throwable t) {
+            channel.writeError(Code.CANCELLED.code, t);
+        }
     }
 }
diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/AbstractServerHttpChannelObserver.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/AbstractServerHttpChannelObserver.java
index 3cb9f90e49..1ed6b3e450 100644
--- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/AbstractServerHttpChannelObserver.java
+++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/AbstractServerHttpChannelObserver.java
@@ -188,9 +188,13 @@ public abstract class AbstractServerHttpChannelObserver implements CustomizableH
     }
 
     protected final String resolveStatusCode(Throwable throwable) {
-        return throwable instanceof HttpStatusException
-                ? String.valueOf(((HttpStatusException) throwable).getStatusCode())
-                : HttpStatus.INTERNAL_SERVER_ERROR.getStatusString();
+        if (throwable == null) {
+            return HttpStatus.OK.getStatusString();
+        }
+        if (throwable instanceof HttpStatusException) {
+            return String.valueOf(((HttpStatusException) throwable).getStatusCode());
+        }
+        return HttpStatus.INTERNAL_SERVER_ERROR.getStatusString();
     }
 
     protected final ErrorResponse buildErrorResponse(String statusCode, Throwable throwable) {
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2Protocol.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2Protocol.java
index 011881ddc9..aeb3e7f3be 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2Protocol.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2Protocol.java
@@ -47,7 +47,6 @@ import java.util.List;
 import io.netty.channel.ChannelDuplexHandler;
 import io.netty.channel.ChannelInitializer;
 import io.netty.channel.ChannelPipeline;
-import io.netty.handler.codec.http.HttpDecoderConfig;
 import io.netty.handler.codec.http.HttpObjectAggregator;
 import io.netty.handler.codec.http.HttpServerCodec;
 import io.netty.handler.codec.http.HttpServerUpgradeHandler;
@@ -89,7 +88,7 @@ public class TripleHttp2Protocol extends AbstractWireProtocol implements ScopeMo
     @Override
     public void configClientPipeline(URL url, ChannelOperator operator, ContextOperator contextOperator) {
         TripleConfig tripleConfig = ConfigManager.getProtocol(url).getTriple();
-        final Http2FrameCodec codec = Http2FrameCodecBuilder.forClient()
+        Http2FrameCodec codec = Http2FrameCodecBuilder.forClient()
                 .gracefulShutdownTimeoutMillis(10000)
                 .initialSettings(new Http2Settings()
                         .headerTableSize(tripleConfig.getHeaderTableSize())
@@ -131,13 +130,15 @@ public class TripleHttp2Protocol extends AbstractWireProtocol implements ScopeMo
         }
     }
 
+    @SuppressWarnings("deprecation")
     private void configurerHttp1Handlers(URL url, List<ChannelHandler> handlers) {
         TripleConfig tripleConfig = ConfigManager.getProtocol(url).getTriple();
-        final HttpServerCodec sourceCodec = new HttpServerCodec(new HttpDecoderConfig()
-                .setMaxChunkSize(tripleConfig.getMaxChunkSize())
-                .setMaxHeaderSize(tripleConfig.getMaxHeaderSize())
-                .setMaxInitialLineLength(tripleConfig.getMaxInitialLineLength())
-                .setInitialBufferSize(tripleConfig.getInitialBufferSize()));
+        HttpServerCodec sourceCodec = new HttpServerCodec(
+                tripleConfig.getMaxInitialLineLength(),
+                tripleConfig.getMaxHeaderSize(),
+                tripleConfig.getMaxChunkSize(),
+                false,
+                tripleConfig.getInitialBufferSize());
         handlers.add(new ChannelHandlerPretender(sourceCodec));
         // Triple protocol http1 upgrade support
         handlers.add(new ChannelHandlerPretender(new HttpServerUpgradeHandler(
@@ -169,7 +170,7 @@ public class TripleHttp2Protocol extends AbstractWireProtocol implements ScopeMo
         return new Http2MultiplexHandler(new ChannelInitializer<Http2StreamChannel>() {
             @Override
             protected void initChannel(Http2StreamChannel ch) {
-                final ChannelPipeline p = ch.pipeline();
+                ChannelPipeline p = ch.pipeline();
                 p.addLast(new NettyHttp2FrameCodec());
                 p.addLast(new NettyHttp2ProtocolSelectorHandler(
                         url, frameworkModel, tripleConfig, GenericHttp2ServerTransportListenerFactory.INSTANCE));
@@ -179,8 +180,8 @@ public class TripleHttp2Protocol extends AbstractWireProtocol implements ScopeMo
 
     private void configurerHttp2Handlers(URL url, List<ChannelHandler> handlers) {
         TripleConfig tripleConfig = ConfigManager.getProtocol(url).getTriple();
-        final Http2FrameCodec codec = buildHttp2FrameCodec(tripleConfig);
-        final Http2MultiplexHandler handler = buildHttp2MultiplexHandler(url, tripleConfig);
+        Http2FrameCodec codec = buildHttp2FrameCodec(tripleConfig);
+        Http2MultiplexHandler handler = buildHttp2MultiplexHandler(url, tripleConfig);
         handlers.add(new ChannelHandlerPretender(new HttpWriteQueueHandler()));
         handlers.add(new ChannelHandlerPretender(codec));
         handlers.add(new ChannelHandlerPretender(new FlushConsolidationHandler(64, true)));
diff --git a/dubbo-spring-boot/dubbo-spring-boot-3-autoconfigure/pom.xml b/dubbo-spring-boot/dubbo-spring-boot-3-autoconfigure/pom.xml
index 0e8435743a..e65cf6bf5f 100644
--- a/dubbo-spring-boot/dubbo-spring-boot-3-autoconfigure/pom.xml
+++ b/dubbo-spring-boot/dubbo-spring-boot-3-autoconfigure/pom.xml
@@ -60,70 +60,17 @@
       <optional>true</optional>
     </dependency>
 
-    <dependency>
-      <groupId>jakarta.servlet</groupId>
-      <artifactId>jakarta.servlet-api</artifactId>
-      <optional>true</optional>
-    </dependency>
-
     <dependency>
       <groupId>org.apache.dubbo</groupId>
-      <artifactId>dubbo-rpc-triple</artifactId>
+      <artifactId>dubbo-triple-servlet</artifactId>
       <version>${project.version}</version>
       <optional>true</optional>
     </dependency>
 
     <dependency>
-      <groupId>org.apache.dubbo</groupId>
-      <artifactId>dubbo-triple-servlet</artifactId>
-      <version>${project.version}</version>
+      <groupId>jakarta.servlet</groupId>
+      <artifactId>jakarta.servlet-api</artifactId>
       <optional>true</optional>
     </dependency>
-
   </dependencies>
-
-  <build>
-    <resources>
-      <resource>
-        <directory>src/main/resources</directory>
-        <excludes>
-          <exclude>**</exclude>
-        </excludes>
-      </resource>
-    </resources>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-compiler-plugin</artifactId>
-        <configuration>
-          <skipMain>true</skipMain>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
-
-  <profiles>
-    <profile>
-      <id>jdk-version-ge-17</id>
-      <activation>
-        <jdk>[17,)</jdk>
-      </activation>
-      <build>
-        <resources>
-          <resource>
-            <directory>src/main/resources</directory>
-          </resource>
-        </resources>
-        <plugins>
-          <plugin>
-            <groupId>org.apache.maven.plugins</groupId>
-            <artifactId>maven-compiler-plugin</artifactId>
-            <configuration>
-              <skipMain>false</skipMain>
-            </configuration>
-          </plugin>
-        </plugins>
-      </build>
-    </profile>
-  </profiles>
 </project>
diff --git a/dubbo-spring-boot/dubbo-spring-boot-3-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple3AutoConfiguration.java b/dubbo-spring-boot/dubbo-spring-boot-3-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple3AutoConfiguration.java
index 08df8da168..81ef0b0582 100644
--- a/dubbo-spring-boot/dubbo-spring-boot-3-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple3AutoConfiguration.java
+++ b/dubbo-spring-boot/dubbo-spring-boot-3-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple3AutoConfiguration.java
@@ -19,8 +19,6 @@ package org.apache.dubbo.spring.boot.autoconfigure;
 import org.apache.dubbo.rpc.protocol.tri.ServletExchanger;
 import org.apache.dubbo.rpc.protocol.tri.servlet.jakarta.TripleFilter;
 
-import java.util.Collections;
-
 import jakarta.servlet.Filter;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -46,13 +44,11 @@ public class DubboTriple3AutoConfiguration {
 
         @Bean
         public FilterRegistrationBean<TripleFilter> tripleProtocolFilter(
-                @Value("${" + PREFIX + ".timeout:}") String timeout,
-                @Value("${" + PREFIX + ".servlet-filter-url-patterns:/*}") String[] urlPatterns,
-                @Value("${" + PREFIX + ".servlet-filter-order:-1000000}") int order,
+                @Value("${" + PREFIX + ".filter-url-patterns:/*}") String[] urlPatterns,
+                @Value("${" + PREFIX + ".filter-order:-1000000}") int order,
                 @Value("${server.port:8080}") int serverPort) {
             ServletExchanger.bindServerPort(serverPort);
             FilterRegistrationBean<TripleFilter> registrationBean = new FilterRegistrationBean<>();
-            registrationBean.setInitParameters(Collections.singletonMap("timeout", timeout));
             registrationBean.setFilter(new TripleFilter());
             registrationBean.addUrlPatterns(urlPatterns);
             registrationBean.setOrder(order);
diff --git a/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/pom.xml b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/pom.xml
index cca1fdfafc..4d85060d46 100644
--- a/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/pom.xml
+++ b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/pom.xml
@@ -71,24 +71,17 @@
       <optional>true</optional>
     </dependency>
 
-    <dependency>
-      <groupId>javax.servlet</groupId>
-      <artifactId>javax.servlet-api</artifactId>
-      <scope>provided</scope>
-    </dependency>
-
     <dependency>
       <groupId>org.apache.dubbo</groupId>
-      <artifactId>dubbo-rpc-triple</artifactId>
+      <artifactId>dubbo-triple-servlet</artifactId>
       <version>${project.version}</version>
       <optional>true</optional>
     </dependency>
 
     <dependency>
-      <groupId>org.apache.dubbo</groupId>
-      <artifactId>dubbo-triple-servlet</artifactId>
-      <version>${project.version}</version>
-      <optional>true</optional>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+      <scope>provided</scope>
     </dependency>
 
     <!-- Test Dependencies -->
@@ -97,6 +90,5 @@
       <artifactId>spring-boot-starter-test</artifactId>
       <scope>test</scope>
     </dependency>
-
   </dependencies>
 </project>
diff --git a/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTripleAutoConfiguration.java b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTripleAutoConfiguration.java
index 9f6139a7ba..c750552f68 100644
--- a/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTripleAutoConfiguration.java
+++ b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTripleAutoConfiguration.java
@@ -21,8 +21,6 @@ import org.apache.dubbo.rpc.protocol.tri.servlet.TripleFilter;
 
 import javax.servlet.Filter;
 
-import java.util.Collections;
-
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -47,13 +45,11 @@ public class DubboTripleAutoConfiguration {
 
         @Bean
         public FilterRegistrationBean<TripleFilter> tripleProtocolFilter(
-                @Value("${" + PREFIX + ".timeout:}") String timeout,
                 @Value("${" + PREFIX + ".filter-url-patterns:/*}") String[] urlPatterns,
                 @Value("${" + PREFIX + ".filter-order:-1000000}") int order,
                 @Value("${server.port:8080}") int serverPort) {
             ServletExchanger.bindServerPort(serverPort);
             FilterRegistrationBean<TripleFilter> registrationBean = new FilterRegistrationBean<>();
-            registrationBean.setInitParameters(Collections.singletonMap("timeout", timeout));
             registrationBean.setFilter(new TripleFilter());
             registrationBean.addUrlPatterns(urlPatterns);
             registrationBean.setOrder(order);
diff --git a/dubbo-spring-boot/dubbo-spring-boot-starter/pom.xml b/dubbo-spring-boot/dubbo-spring-boot-starter/pom.xml
index a52cd9e2c0..b5f8958714 100644
--- a/dubbo-spring-boot/dubbo-spring-boot-starter/pom.xml
+++ b/dubbo-spring-boot/dubbo-spring-boot-starter/pom.xml
@@ -41,10 +41,5 @@
       <artifactId>dubbo-spring-boot-autoconfigure</artifactId>
       <version>${project.version}</version>
     </dependency>
-    <dependency>
-      <groupId>org.apache.dubbo</groupId>
-      <artifactId>dubbo-spring-boot-3-autoconfigure</artifactId>
-      <version>${project.version}</version>
-    </dependency>
   </dependencies>
 </project>
diff --git a/dubbo-spring-boot/pom.xml b/dubbo-spring-boot/pom.xml
index 96fb40c057..551725b991 100644
--- a/dubbo-spring-boot/pom.xml
+++ b/dubbo-spring-boot/pom.xml
@@ -32,7 +32,6 @@
   <modules>
     <module>dubbo-spring-boot-actuator</module>
     <module>dubbo-spring-boot-autoconfigure</module>
-    <module>dubbo-spring-boot-3-autoconfigure</module>
     <module>dubbo-spring-boot-compatible</module>
     <module>dubbo-spring-boot-starter</module>
     <module>dubbo-spring-boot-starters</module>
@@ -190,5 +189,22 @@
         <spring-boot.version>2.2.8.RELEASE</spring-boot.version>
       </properties>
     </profile>
+
+    <profile>
+      <id>jdk-version-ge-17</id>
+      <activation>
+        <jdk>[17,)</jdk>
+      </activation>
+      <modules>
+        <module>dubbo-spring-boot-3-autoconfigure</module>
+      </modules>
+    </profile>
+
+    <profile>
+      <id>release</id>
+      <modules>
+        <module>dubbo-spring-boot-3-autoconfigure</module>
+      </modules>
+    </profile>
   </profiles>
 </project>