apache / dubbo

The java implementation of Apache Dubbo. An RPC and microservice framework.
https://dubbo.apache.org/
Apache License 2.0
40.43k stars 26.41k forks source link

why send flow control error when connection window is 0 #13676

Open iJIAJIA opened 8 months ago

iJIAJIA commented 8 months ago

对于dubbo自定义的http2 流控窗口逻辑有点疑惑.

复现步骤: dubbo version: 3.2.x

  1. 调整peer initialize window size from 8M to 64k(65535) 越小越容易复现.
  2. provider实现一个大数据返参接口.(如 listUser)

TriHttp2RemoteFlowController

// FlowState
int writeAllocatedBytes(int allocated) {
            ...
            try {
                ...
            } finally {
                ...
                // 当connection window为0时, 此时会直接发送flow control error, 阻断连接. 
                if(monitor.isOverFlowControl()){
                    logger.info(String.format("[HTTP2] connection window throttling, stream:%d overflow flow controlling", stream.id()));
//                    cause = new Throwable();
//                    cancel(FLOW_CONTROL_ERROR,cause);
                }
            }
            return writtenBytes;
        }

provider报:

Caused by: io.netty.handler.codec.http2.Http2Exception$StreamException: TotalPendingBytes size overflow for stream: 3 at io.netty.handler.codec.http2.Http2Exception.streamError(Http2Exception.java:153) at org.apache.dubbo.rpc.protocol.tri.TriHttp2RemoteFlowController$ListenerWritabilityMonitor.checkConnectionThenStreamWritabilityChanged(TriHttp2RemoteFlowController.java:794) at org.apache.dubbo.rpc.protocol.tri.TriHttp2RemoteFlowController$ListenerWritabilityMonitor.stateCancelled(TriHttp2RemoteFlowController.java:755) ... 35 common frames omitted

这里很好奇, http2 官方协议并没有规定, 当 connection window为0 时 直接发送 flow_control_error. 常规做法不应该是暂停发送, 等待 窗口更新后 继续传输么?

iJIAJIA commented 8 months ago

对于dubbo自定义的http2 流控窗口逻辑有点疑惑.

复现步骤: dubbo version: 3.2.x

  1. 调整peer initialize window size from 8M to 64k(65535) 越小越容易复现.
  2. provider实现一个大数据返参接口.(如 listUser)

TriHttp2RemoteFlowController

// FlowState
int writeAllocatedBytes(int allocated) {
            ...
            try {
                ...
            } finally {
                ...
                // 当connection window为0时, 此时会直接发送flow control error, 阻断连接. 
                if(monitor.isOverFlowControl()){
                    logger.info(String.format("[HTTP2] connection window throttling, stream:%d overflow flow controlling", stream.id()));
//                    cause = new Throwable();
//                    cancel(FLOW_CONTROL_ERROR,cause);
                }
            }
            return writtenBytes;
        }

provider报:

Caused by: io.netty.handler.codec.http2.Http2Exception$StreamException: TotalPendingBytes size overflow for stream: 3 at io.netty.handler.codec.http2.Http2Exception.streamError(Http2Exception.java:153) at org.apache.dubbo.rpc.protocol.tri.TriHttp2RemoteFlowController$ListenerWritabilityMonitor.checkConnectionThenStreamWritabilityChanged(TriHttp2RemoteFlowController.java:794) at org.apache.dubbo.rpc.protocol.tri.TriHttp2RemoteFlowController$ListenerWritabilityMonitor.stateCancelled(TriHttp2RemoteFlowController.java:755) ... 35 common frames omitted

这里很好奇, http2 官方协议并没有规定, 当 connection window为0 时 直接发送 flow_control_error. 常规做法不应该是暂停发送, 等待 窗口更新后 继续传输么?

@AlbumenJ 能否帮忙联系下作者回复下? java to java因为默认窗口设置的8M, 所以暂时未触发此异常. 但我们跨语言调用碰到了. golang那边的初始化窗口大小只有64k. 这里的逻辑, 给不到远端做窗口更新的机会.

AlbumenJ commented 8 months ago

https://github.com/apache/dubbo/pull/11029

AlbumenJ commented 8 months ago

@EarthChen PTAL

EarthChen commented 8 months ago

这里其实是反压逻辑没做彻底,为了保护让consumer 中断,从而不再发请求

iJIAJIA commented 8 months ago

这里其实是反压逻辑没做彻底,为了保护让consumer 中断,从而不再发请求

那我先将我们内部版本调整回netty的默认流控策略了. 现在在跨语言调用下, 实际体验并不好.

EarthChen commented 8 months ago

这里其实是反压逻辑没做彻底,为了保护让consumer 中断,从而不再发请求

那我先将我们内部版本调整回netty的默认流控策略了. 现在在跨语言调用下, 实际体验并不好.

默认策略是放入队列等待,当 consumer 速率大于 provider 时,可能导致 oom