AlexStocks commented on code in PR #3235:
URL: https://github.com/apache/dubbo-go/pull/3235#discussion_r2969086748
##########
filter/graceful_shutdown/consumer_filter.go:
##########
@@ -50,27 +54,61 @@ func init() {
}
type consumerGracefulShutdownFilter struct {
- shutdownConfig *global.ShutdownConfig
+ shutdownConfig *global.ShutdownConfig
+ closingEventHandler gracefulshutdown.ClosingEventHandler
+ closingInvokers sync.Map // map[string]time.Time (url key -> expire
time)
}
+const consumerCountMarkedKey = "dubbo-go-graceful-shutdown-consumer-counted"
+
func newConsumerGracefulShutdownFilter() filter.Filter {
if csf == nil {
csfOnce.Do(func() {
Review Comment:
## consumer_filter.go:33 - 常量定义位置不一致
`consumerCountMarkedKey` 在 consumer_filter.go 第 33 行定义,但类似的
`providerCountMarkedKey` 在 provider_filter.go 第 16 行定义。
**建议**: 将这些常量统一放到 compat.go 或单独的 constants.go 文件中,便于维护。
##########
graceful_shutdown/shutdown.go:
##########
@@ -125,31 +140,125 @@ func totalTimeout(shutdown *global.ShutdownConfig)
time.Duration {
}
func beforeShutdown(shutdown *global.ShutdownConfig) {
- destroyRegistries()
+ // 1. mark closing state
+ logger.Info("Graceful shutdown --- Mark closing state.")
+ shutdown.Closing.Store(true)
+
+ // 2. unregister services from registries
+ unregisterRegistries()
+
+ // 3. notify long connection consumers
+ notifyLongConnectionConsumers(shutdown)
+
+ // 4. wait and accept new requests
// waiting for a short time so that the clients have enough time to get
the notification that server shutdowns
// The value of configuration depends on how long the clients will get
notification.
waitAndAcceptNewRequests(shutdown)
+ // 5. reject new requests and wait for in-flight requests
// reject sending/receiving the new request but keeping waiting for
accepting requests
waitForSendingAndReceivingRequests(shutdown)
- // destroy all protocols
+ // 6. destroy protocols
destroyProtocols()
- logger.Info("Graceful shutdown --- Execute the custom callbacks.")
- customCallbacks := extension.GetAllCustomShutdownCallbacks()
- for callback := customCallbacks.Front(); callback != nil; callback =
callback.Next() {
- callback.Value.(func())()
- }
+ // 7. execute custom callbacks
+ executeCustomShutdownCallbacks(shutdown)
}
-// destroyRegistries destroys RegistryProtocol directly.
-func destroyRegistries() {
- logger.Info("Graceful shutdown --- Destroy all registriesConfig. ")
- registryProtocol := extension.GetProtocol(constant.RegistryProtocol)
+// unregisterRegistries unregisters exported services from registries during
graceful shutdown.
+// If the registry protocol does not expose a narrower unregister capability,
it falls back to Destroy.
+func unregisterRegistries() {
+ logger.Info("Graceful shutdown --- Unregister exported services from
registries.")
+ registryProtocol, ok := getProtocolSafely(constant.RegistryProtocol)
+ if !ok {
+ logger.Warnf("Graceful shutdown --- Registry protocol %s is not
registered, skip unregistering registries.", constant.RegistryProtocol)
+ return
+ }
+
+ if unregisterer, ok :=
registryProtocol.(protocolbase.RegistryUnregisterer); ok {
+ unregisterer.UnregisterRegistries()
+ return
+ }
+
+ logger.Warnf("Graceful shutdown --- Registry protocol %s does not
support unregister-only shutdown, falling back to Destroy().",
constant.RegistryProtocol)
registryProtocol.Destroy()
}
+// notifyLongConnectionConsumers notifies all connected consumers via long
connections
+func notifyLongConnectionConsumers(shutdown *global.ShutdownConfig) {
+ logger.Info("Graceful shutdown --- Notify long connection consumers.")
+
+ notifyTimeout := parseDuration(shutdown.NotifyTimeout,
notifyTimeoutDesc, defaultNotifyTimeout)
+ callbacks := extension.GracefulShutdownCallbacks()
+ var wg sync.WaitGroup
+ for name, callback := range callbacks {
+ wg.Add(1)
+ go func(name string, callback
extension.GracefulShutdownCallback) {
+ defer wg.Done()
+ ctx, cancel :=
context.WithTimeout(context.Background(), notifyTimeout)
+ defer cancel()
+ notifyWithRetry(ctx, name, callback)
+ }(name, callback)
+ }
+ wg.Wait()
+}
Review Comment:
## shutdown.go:140 - notifyWithRetry 中 ctx 超时后重试可能继续
当 context 超时(notifyTimeout)后,backoff.RetryNotify 可能会继续执行正在等待的重试。虽然代码在第
172-174 行检查了 ctx.Err(),但如果在重试等待期间 context 取消,goroutine 可能无法及时退出。
**建议**: 在 notifyWithRetry 返回前再次检查 ctx.Err(),确保超时后立即退出。
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]