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

Alanxtl pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/dubbo-go-samples.git


The following commit(s) were added to refs/heads/main by this push:
     new a435cdef fix(graceful_shutdown): use WithShutdown API in sample (#1133)
a435cdef is described below

commit a435cdef3d89d72afcf2e7dd0bab93e7ae5f8c0d
Author: XiaoFei <[email protected]>
AuthorDate: Sat Aug 22 22:05:23 2026 +0800

    fix(graceful_shutdown): use WithShutdown API in sample (#1133)
    
    * fix(graceful_shutdown): use WithShutdown API in sample
    
    * fix:ci
    
    * test: add graceful shutdown integration assertions
    
    * fix:ci
    
    * test: align graceful shutdown rejection assertion with reject phase
    
    * test(graceful_shutdown): move assertions into sample code
    
    * test(graceful_shutdown): validate shutdown behavior in sample code
    
    * fix(graceful_shutdown): reject new RPCs after shutdown begins
    
    * style(graceful_shutdown): format imports
    
    * fix(graceful_shutdown): verify framework reject path
    
    * fix:ci
    
    * test:restore github-actions.yml
    
    * fix(graceful_shutdown): simplify shutdown config and clean test hooks
    
    * fix(graceful_shutdown): stabilize framework reject test
    
    * fix:CI
    
    * fix:CI
---
 graceful_shutdown/README.md             |  19 ++++--
 graceful_shutdown/README_CN.md          |  17 +++--
 graceful_shutdown/go-server/cmd/main.go |  65 ++++++++++++------
 integrate_test.sh                       | 114 +++++++++++++++++++++++++-------
 4 files changed, 159 insertions(+), 56 deletions(-)

diff --git a/graceful_shutdown/README.md b/graceful_shutdown/README.md
index c58026d6..06775778 100644
--- a/graceful_shutdown/README.md
+++ b/graceful_shutdown/README.md
@@ -8,7 +8,7 @@ It is useful for verifying these behaviors:
 - active notice for long connections on Triple
 - passive closing behavior on the consumer side
 - waiting for in-flight provider requests during shutdown
-- the effect of `timeout`, `step-timeout`, `consumer-update-wait`, and 
`offline-window`
+- the effect of `timeout`, `step-timeout`, `notify-timeout`, 
`consumer-update-wait`, and `offline-window`
 
 This sample does **not** include a registry. That means you can test 
protocol-level active notice and request draining, but you cannot directly 
observe registry unregister propagation in this sample alone.
 
@@ -58,11 +58,16 @@ If you omit the protocol prefix and only pass 
`127.0.0.1:20000`, the direct refe
 - `-port=20000`
 - `-timeout=60s`
 - `-step-timeout=3s`
+- `-notify-timeout=5s`
 - `-consumer-update-wait=3s`
 - `-offline-window=3s`
 - `-delay=0s`
+- `-ignore-context-cancel=false`
+- `-reject-request=false`
 
 `-delay` adds artificial processing delay to every request so you can verify 
in-flight request draining.
+`-ignore-context-cancel` is used by the automated integration scenario to keep 
the in-flight request running after shutdown begins.
+`-reject-request` is used by the automated integration scenario to start the 
framework reject path without relying on the short natural shutdown window.
 
 ## Client Flags
 
@@ -179,17 +184,19 @@ This sample is wired into the root integration test flow:
 ./integrate_test.sh graceful_shutdown
 ```
 
-The script starts the Triple server, runs the client in the background, waits 
until at least one request succeeds, and then sends an interrupt signal to 
trigger graceful shutdown.
+The script validates two behaviors separately. First, it starts the Triple 
server with the built-in integration flags, starts an in-flight Go client 
request, waits until that request enters the provider, and then sends an 
interrupt signal to trigger graceful shutdown. Second, it starts a fresh server 
with framework request rejection enabled and runs a separate short-connection 
probe to verify new requests are rejected by the framework.
 
-Before the client exits, it must observe:
+The integration asserts that:
 
-- at least one successful request
-- at least one failed request during shutdown
+- the in-flight first request completes successfully after shutdown starts
+- a separate request is rejected by the framework provider filter without 
entering the `Greet` handler
+- the server exits within the configured shutdown timeout
 
-If those expectations are not met, the client panics so CI fails immediately.
+If the expected success and failure counts are not met, or if the reject probe 
reaches the `Greet` handler, CI fails immediately.
 
 ## Practical Notes
 
 - Triple is the intended protocol for manual verification in this sample.
 - This sample is intentionally Triple-only so it focuses on the active notice 
path implemented in the current graceful shutdown flow.
+- The server configures graceful shutdown through `dubbo.WithShutdown(...)`, 
which is the public instance-level configuration path used by the current API.
 - Because this sample has no registry, the "unregister from registry" phase is 
only part of the core implementation flow, not something you can fully observe 
here.
diff --git a/graceful_shutdown/README_CN.md b/graceful_shutdown/README_CN.md
index b84c902f..e2efd47a 100644
--- a/graceful_shutdown/README_CN.md
+++ b/graceful_shutdown/README_CN.md
@@ -9,7 +9,7 @@
 - 长连接消费者的主动通知
 - 消费端在停机期间的被动关闭表现
 - Provider 停机时对进行中请求的等待与排空
-- `timeout`、`step-timeout`、`consumer-update-wait` 和 `offline-window` 等参数的影响
+- `timeout`、`step-timeout`、`notify-timeout`、`consumer-update-wait` 和 
`offline-window` 等参数的影响
 
 该示例**不包含注册中心**。因此你可以验证协议层的主动通知和请求排空行为,但不能直接观察“从注册中心摘除并传播”的完整链路。
 
@@ -62,11 +62,16 @@ go run ./graceful_shutdown/go-client/cmd 
-addr=tri://127.0.0.1:20000 -concurrenc
 - `-port=20000`
 - `-timeout=60s`
 - `-step-timeout=3s`
+- `-notify-timeout=5s`
 - `-consumer-update-wait=3s`
 - `-offline-window=3s`
 - `-delay=0s`
+- `-ignore-context-cancel=false`
+- `-reject-request=false`
 
 其中 `-delay` 会给每次请求增加固定处理延迟,用于观察停机时的在途请求排空效果。
+`-ignore-context-cancel` 用于自动化集成测试场景,使在途请求在停机开始后仍继续执行。
+`-reject-request` 用于自动化集成测试场景,在不依赖自然停机短暂窗口的情况下进入框架拒绝路径。
 
 ## 客户端参数
 
@@ -197,14 +202,16 @@ go run ./graceful_shutdown/go-client/cmd 
-addr=tri://127.0.0.1:20000 -short=true
 ./integrate_test.sh graceful_shutdown
 ```
 
-脚本会启动 Triple 服务端,后台运行客户端,在观察到至少一次成功请求后向服务端发送中断信号,并要求客户端在退出前同时观察到:
+脚本会分两段验证行为。第一段使用内置集成测试参数启动 Triple 服务端,先启动一个在途 Go client 请求,等待该请求进入 Provider 
后由脚本发送中断信号触发优雅停机;第二段会重新启动一个启用框架请求拒绝的服务端,再启动一个独立的短连接探针,验证新请求会被框架拒绝。
 
-- 至少一次成功请求
-- 至少一次停机期间的失败请求
+- 停机开始后,第一个进行中的请求仍能成功完成
+- 独立探针请求会被框架 provider filter 拒绝,且不会进入 `Greet` handler
+- 服务端会在配置的停机超时时间内退出
 
-如果这些条件没有满足,客户端会直接 `panic`,从而使 CI 失败。
+如果预期的成功和失败次数不满足,或拒绝探针进入了 `Greet` handler,CI 会立即失败。
 
 ## 补充说明
 
 - 该示例以 Triple 协议为主,用于聚焦当前优雅停机流程中的主动通知路径。
+- 服务端通过 `dubbo.WithShutdown(...)` 配置优雅停机,这是当前 API 使用的实例级公开配置方式。
 - 因为没有注册中心,这里只能覆盖协议层停机行为,不能完整覆盖注册中心摘除传播。
diff --git a/graceful_shutdown/go-server/cmd/main.go 
b/graceful_shutdown/go-server/cmd/main.go
index 8867f507..334ac308 100644
--- a/graceful_shutdown/go-server/cmd/main.go
+++ b/graceful_shutdown/go-server/cmd/main.go
@@ -19,16 +19,18 @@ package main
 
 import (
        "context"
+       "errors"
        "flag"
        "fmt"
+       "net/http"
        "time"
 )
 
 import (
+       "dubbo.apache.org/dubbo-go/v3"
        "dubbo.apache.org/dubbo-go/v3/graceful_shutdown"
        _ "dubbo.apache.org/dubbo-go/v3/imports"
        "dubbo.apache.org/dubbo-go/v3/protocol"
-       "dubbo.apache.org/dubbo-go/v3/server"
 
        "github.com/dubbogo/gost/log/logger"
 )
@@ -38,7 +40,8 @@ import (
 )
 
 type GreetProvider struct {
-       fixedDelay time.Duration
+       fixedDelay          time.Duration
+       ignoreContextCancel bool
 }
 
 func (p *GreetProvider) Greet(ctx context.Context, req *greet.GreetRequest) 
(*greet.GreetResponse, error) {
@@ -46,14 +49,18 @@ func (p *GreetProvider) Greet(ctx context.Context, req 
*greet.GreetRequest) (*gr
        logger.Infof("Handling greet request, name=%s delay=%s", req.Name, 
p.fixedDelay)
 
        if p.fixedDelay > 0 {
-               timer := time.NewTimer(p.fixedDelay)
-               defer timer.Stop()
-
-               select {
-               case <-timer.C:
-               case <-ctx.Done():
-                       logger.Warnf("Greet request canceled before completion, 
name=%s err=%v", req.Name, ctx.Err())
-                       return nil, ctx.Err()
+               if p.ignoreContextCancel {
+                       time.Sleep(p.fixedDelay)
+               } else {
+                       timer := time.NewTimer(p.fixedDelay)
+                       defer timer.Stop()
+
+                       select {
+                       case <-timer.C:
+                       case <-ctx.Done():
+                               logger.Warnf("Greet request canceled before 
completion, name=%s err=%v", req.Name, ctx.Err())
+                               return nil, ctx.Err()
+                       }
                }
        }
 
@@ -68,40 +75,56 @@ func main() {
        port := flag.Int("port", 20000, "triple listen port")
        timeout := flag.Duration("timeout", 60*time.Second, "overall graceful 
shutdown timeout budget")
        stepTimeout := flag.Duration("step-timeout", 3*time.Second, "timeout 
for waiting provider and consumer in-flight requests")
+       notifyTimeout := flag.Duration("notify-timeout", 5*time.Second, 
"timeout for notifying consumers during graceful shutdown")
        consumerUpdateWait := flag.Duration("consumer-update-wait", 
3*time.Second, "time to wait for consumers to observe instance changes")
        offlineWindow := flag.Duration("offline-window", 3*time.Second, "time 
window for observing late requests after offline")
        requestDelay := flag.Duration("delay", 0, "artificial delay added to 
each greet request")
+       ignoreContextCancel := flag.Bool("ignore-context-cancel", false, 
"continue the artificial delay even if the request context is canceled")
+       rejectRequest := flag.Bool("reject-request", false, "start with 
framework request rejection enabled for integration validation")
        flag.Parse()
 
-       graceful_shutdown.Init(
+       shutdownOpts := []graceful_shutdown.Option{
                graceful_shutdown.WithTimeout(*timeout),
                graceful_shutdown.WithStepTimeout(*stepTimeout),
+               graceful_shutdown.WithNotifyTimeout(*notifyTimeout),
                
graceful_shutdown.WithConsumerUpdateWaitTime(*consumerUpdateWait),
                
graceful_shutdown.WithOfflineRequestWindowTimeout(*offlineWindow),
-       )
-       logger.Infof("Graceful shutdown initialized, timeout=%s step-timeout=%s 
consumer-update-wait=%s offline-window=%s request-delay=%s",
-               timeout.String(), stepTimeout.String(), 
consumerUpdateWait.String(), offlineWindow.String(), requestDelay.String())
+       }
+       if *rejectRequest {
+               shutdownOpts = append(shutdownOpts, 
graceful_shutdown.WithRejectRequest())
+       }
 
-       srv, err := server.NewServer(
-               server.WithServerProtocol(
+       ins, err := dubbo.NewInstance(
+               dubbo.WithShutdown(shutdownOpts...),
+               dubbo.WithProtocol(
                        protocol.WithProtocol("tri"),
                        protocol.WithPort(*port),
                        protocol.WithID("tri"),
                ),
        )
        if err != nil {
-               logger.Fatalf("failed to create server: %v", err)
+               panic(fmt.Sprintf("failed to create dubbo instance: %v", err))
+       }
+       logger.Infof("Graceful shutdown configured, timeout=%s step-timeout=%s 
notify-timeout=%s consumer-update-wait=%s offline-window=%s request-delay=%s 
ignore-context-cancel=%v reject-request=%v",
+               timeout.String(), stepTimeout.String(), notifyTimeout.String(), 
consumerUpdateWait.String(), offlineWindow.String(), requestDelay.String(), 
*ignoreContextCancel, *rejectRequest)
+
+       srv, err := ins.NewServer()
+       if err != nil {
+               panic(fmt.Sprintf("failed to create server: %v", err))
        }
        logger.Infof("Exposing Triple on port %d", *port)
 
-       provider := &GreetProvider{fixedDelay: *requestDelay}
+       provider := &GreetProvider{
+               fixedDelay:          *requestDelay,
+               ignoreContextCancel: *ignoreContextCancel,
+       }
        if err := greet.RegisterGreetServiceHandler(srv, provider); err != nil {
-               logger.Fatalf("failed to register greet service handler: %v", 
err)
+               panic(fmt.Sprintf("failed to register greet service handler: 
%v", err))
        }
 
        logger.Info("Triple server started, press Ctrl+C to trigger graceful 
shutdown")
 
-       if err := srv.Serve(); err != nil {
-               logger.Fatalf("failed to serve: %v", err)
+       if err := srv.Serve(); err != nil && !errors.Is(err, 
http.ErrServerClosed) {
+               panic(fmt.Sprintf("failed to serve: %v", err))
        }
 }
diff --git a/integrate_test.sh b/integrate_test.sh
index 6d999f7a..77387618 100755
--- a/integrate_test.sh
+++ b/integrate_test.sh
@@ -635,9 +635,10 @@ start_java_server_if_present() {
 }
 
 run_graceful_shutdown_sample() {
-  local client_log="/tmp/.${PROJECT_NAME}.go-client.log"
+  local inflight_client_log="/tmp/.${PROJECT_NAME}.go-client.inflight.log"
+  local reject_client_log="/tmp/.${PROJECT_NAME}.go-client.reject.log"
+  local inflight_client_pid=""
   local server_pid=""
-  local client_pid=""
   local server_bin="/tmp/.${PROJECT_NAME}.go-server.bin"
   local client_bin="/tmp/.${PROJECT_NAME}.go-client.bin"
 
@@ -660,7 +661,13 @@ run_graceful_shutdown_sample() {
   echo "Starting graceful_shutdown Go server..."
   (
     cd "$P_DIR"
-    exec "$server_bin" -timeout=15s -step-timeout=2s -consumer-update-wait=0s 
-delay=2s
+    exec "$server_bin" \
+      -timeout=25s \
+      -step-timeout=20s \
+      -consumer-update-wait=0s \
+      -offline-window=0s \
+      -delay=3s \
+      -ignore-context-cancel=true
   ) >"$GO_SERVER_LOG" 2>&1 &
   server_pid="$!"
   echo "$server_pid" >"$PID_FILE"
@@ -677,35 +684,42 @@ run_graceful_shutdown_sample() {
     return 1
   fi
 
-  echo "Running graceful_shutdown Go client..."
+  echo "Running graceful_shutdown in-flight request validation..."
   (
     cd "$P_DIR"
     exec "$client_bin" \
       -addr=tri://127.0.0.1:20000 \
-      -concurrency=2 \
-      -interval=200ms \
-      -short \
-      -request-timeout=6s \
-      -max-requests=12 \
+      -short=true \
+      -max-requests=1 \
       -min-successes=1 \
-      -min-failures=1 \
-      -name-prefix=integration
-  ) >"$client_log" 2>&1 &
-  client_pid="$!"
-
-  if ! wait_for_log_pattern "$client_log" "succeeded" 30; then
-    echo "graceful_shutdown client did not observe a successful request before 
shutdown"
-    cat "$client_log" || true
+      -request-timeout=10s \
+      -name-prefix=integration-inflight
+  ) >"$inflight_client_log" 2>&1 &
+  inflight_client_pid="$!"
+
+  if ! wait_for_log_pattern "$GO_SERVER_LOG" "Handling greet request, 
name=integration-inflight-1" 30; then
+    echo "graceful_shutdown in-flight request did not enter the provider"
+    kill_if_running "$inflight_client_pid"
+    wait "$inflight_client_pid" 2>/dev/null || true
+    cat "$inflight_client_log" || true
     cat "$GO_SERVER_LOG" || true
     return 1
   fi
 
-  echo "Triggering graceful shutdown..."
+  echo "Triggering graceful_shutdown after in-flight request entered 
provider..."
   kill -INT "$server_pid" 2>/dev/null || true
 
-  if ! wait "$client_pid"; then
-    echo "graceful_shutdown client exited with failure"
-    cat "$client_log" || true
+  if ! wait "$inflight_client_pid"; then
+    echo "graceful_shutdown in-flight request validation failed"
+    cat "$inflight_client_log" || true
+    cat "$GO_SERVER_LOG" || true
+    return 1
+  fi
+  inflight_client_pid=""
+
+  if ! wait_for_log_pattern "$GO_SERVER_LOG" "Greet request finished, 
name=integration-inflight-1" 30; then
+    echo "graceful_shutdown in-flight request did not finish in the provider"
+    cat "$inflight_client_log" || true
     cat "$GO_SERVER_LOG" || true
     return 1
   fi
@@ -717,13 +731,65 @@ run_graceful_shutdown_sample() {
   fi
 
   wait "$server_pid" 2>/dev/null || true
+  server_pid=""
+  rm -f "$PID_FILE"
+
+  echo "Starting graceful_shutdown reject-stage validation server..."
+  : >"$GO_SERVER_LOG"
+  (
+    cd "$P_DIR"
+    exec "$server_bin" \
+      -timeout=25s \
+      -step-timeout=20s \
+      -consumer-update-wait=0s \
+      -offline-window=0s \
+      -reject-request=true
+  ) >"$GO_SERVER_LOG" 2>&1 &
+  server_pid="$!"
+  echo "$server_pid" >"$PID_FILE"
+
+  if ! wait_for_tcp_port "127.0.0.1" "20000" 30; then
+    echo "graceful_shutdown reject-stage server did not become ready on 
127.0.0.1:20000"
+    cat "$GO_SERVER_LOG" || true
+    return 1
+  fi
+
+  echo "Running graceful_shutdown reject-stage probe..."
+  (
+    cd "$P_DIR"
+    exec "$client_bin" \
+      -addr=tri://127.0.0.1:20000 \
+      -short=true \
+      -max-requests=1 \
+      -min-failures=1 \
+      -request-timeout=5s \
+      -name-prefix=integration-reject-probe
+  ) >"$reject_client_log" 2>&1 || {
+    echo "graceful_shutdown reject-stage probe failed"
+    cat "$reject_client_log" || true
+    cat "$GO_SERVER_LOG" || true
+    return 1
+  }
+
+  if grep -q "Handling greet request, name=integration-reject-probe" 
"$GO_SERVER_LOG"; then
+    echo "graceful_shutdown reject-stage probe reached the Greet handler"
+    cat "$reject_client_log" || true
+    cat "$GO_SERVER_LOG" || true
+    return 1
+  fi
 
-  if ! grep -q "failed" "$client_log"; then
-    echo "graceful_shutdown client did not observe request failures during 
shutdown"
-    cat "$client_log" || true
+  if ! wait_for_log_pattern "$GO_SERVER_LOG" "application is closing, new 
request will be rejected" 30; then
+    echo "graceful_shutdown reject-stage probe was not rejected by the 
framework provider filter"
+    cat "$reject_client_log" || true
+    cat "$GO_SERVER_LOG" || true
     return 1
   fi
 
+  kill_if_running "$server_pid"
+  wait "$server_pid" 2>/dev/null || true
+  server_pid=""
+  rm -f "$PID_FILE"
+
   echo "graceful_shutdown integration completed"
 }
 

Reply via email to