mosn / mosn

The Cloud-Native Network Proxy Platform
https://mosn.io
Apache License 2.0
4.45k stars 798 forks source link

xprotocol协议代理出现了serverCallbacks为空的panic问题 #2338

Open zzqCh opened 1 year ago

zzqCh commented 1 year ago

Your question

MOSN的config文件如下:

{
    "disable_upgrade": true,
    "servers":[
        {
            "default_log_path":"stdout",
            "default_log_level": "TRACE",
            "routers":[
                {
                    "router_config_name":"server_router",
                    "virtual_hosts":[{
                        "name":"serverHost",
                        "domains": ["*"],
                        "routers": [
                            {
                                "route":{"cluster_name":"serverCluster"}
                            }
                        ]
                    }]
                }
            ],
            "listeners": [
                {
                    "name":"serverListener",
                    "address": "0.0.0.0:2048",
                    "bind_port": true,
                    "filter_chains": [{
                        "filters": [
                            {
                                "type": "proxy",
                                "config": {
                                    "downstream_protocol": "protocolA",
                                    "upstream_protocol": "protocolA",
                                    "router_config_name":"server_router"
                                }
                            }
                        ]
                    }]
                }
            ]
        }
    ],
    "cluster_manager":{
        "clusters":[
            {
                "name":"serverCluster",
                "type": "SIMPLE",
                "lb_type": "LB_RANDOM",
                "max_request_per_conn": 2048,
                "conn_buffer_limit_bytes":32768,
                "cluster_pool_enable": true,
                "circuit_breakers": [
                    {
                        "max_connections": 100000,
                        "max_pending_requests": 1000000000,
                        "max_requests": 1000000000,
                        "max_retries": 2
                    }
                ],
                "hosts":[
                    {"address":"168.1.1.1:9999"}
                ]
            }
        ]
    },
    "admin": {
        "address": {
            "socket_address": {
                "address": "0.0.0.0",
                "port_value": 34923
            }
        }
    },
    "pprof": {
        "debug": true,
        "port_value": 34922
    }
}

访问运行出现了panic,问题截图:

image

跟踪代码发现:

image image image

Environment

taoyuanyuan commented 1 year ago

protocolA 你自己实现的协议吗

zzqCh commented 1 year ago

protocolA 你自己实现的协议吗

是的。是在xrpotcol的框架下实现的

taoyuanyuan commented 1 year ago

https://github.com/mosn/mosn/blob/07be2d0d6b9cee511262f85132277fcf937be23b/pkg/stream/xprotocol/conn.go#L254C11-L254C19 看你的流程,应该是返回response,你这儿返回了request,走入了错误的流程

zzqCh commented 1 year ago

https://github.com/mosn/mosn/blob/07be2d0d6b9cee511262f85132277fcf937be23b/pkg/stream/xprotocol/conn.go#L254C11-L254C19 看你的流程,应该是返回response,你这儿返回了request,走入了错误的流程

好的。 谢谢您。我再研究研究

zzqCh commented 1 year ago

https://github.com/mosn/mosn/blob/07be2d0d6b9cee511262f85132277fcf937be23b/pkg/stream/xprotocol/conn.go#L254C11-L254C19 看你的流程,应该是返回response,你这儿返回了request,走入了错误的流程

您好,我这边定位到了,发现是协议中没有字段表明是request还是response,因此返回的都是requst,请问有什么好的办法区分么?莫非要加个filter(filter中把mosn自带的direction设置到variable中??)

taoyuanyuan commented 1 year ago

https://github.com/mosn/mosn/blob/07be2d0d6b9cee511262f85132277fcf937be23b/pkg/protocol/xprotocol/dubbo/protocol.go#L81 你从ctx里面可以感知到时downstream还是upstream,你可以在frame里面直接保存是request,还是response

zzqCh commented 1 year ago

https://github.com/mosn/mosn/blob/07be2d0d6b9cee511262f85132277fcf937be23b/pkg/protocol/xprotocol/dubbo/protocol.go#L81

你从ctx里面可以感知到时downstream还是upstream,你可以在frame里面直接保存是request,还是response

image

而且我打印了下ctx,发现里面都是 {"context":{"context":{"context":0}}}

taoyuanyuan commented 1 year ago

这儿你可以不根据协议(你的协议没有字段判断),你可以根据你现在是downstream和upstream的连接来判断,downstream是request,upstream是reponse。 ctx不可能为空吧?日志打出来看看呢

zzqCh commented 1 year ago

这儿你可以不根据协议(你的协议没有字段判断),你可以根据你现在是downstream和upstream的连接来判断,downstream是request,upstream是reponse。 ctx不可能为空吧?日志打出来看看呢

2023-08-15 13:58:04,410 [DEBUG] [network] [server idle checker] connection have read/write data before this read timeout: 308, 314, 0, 0 -------------------->>>>>>context信息>>>>>>>>> {"Context":{"Context":{"Context":0}}} -------------------->>>>>>context信息>>>>>>>>> {"Context":{"Context":{"Context":0}}} 2023-08-15 13:58:12,427 [DEBUG] [2,-,-] [stream] [xprotocol] new stream detect, requestId = 0 2023-08-15 13:58:12,427 [DEBUG] [2,-,-] [proxy] [downstream] new stream, proxyId = 2 , requestId =0, oneway=false 2023-08-15 13:58:12,427 [DEBUG] [2,-,-] [proxy] [downstream] OnReceive 2023-08-15 13:58:12,427 [TRACE] [2,-,-] [proxy] [downstream] OnReceive

中间省略 中间省略 中间省略 中间省略 中间省略

下面是接收到返回后,打印的日志 2023-08-15 13:58:12,431 [DEBUG] [2,4,-] [stream] [xprotocol] appendData, direction = 0, requestId = 1 2023-08-15 13:58:12,431 [DEBUG] [2,4,-] [stream] [xprotocol] connection 4 endStream, direction = 0, requestId = 1 2023-08-15 13:58:14,108 [DEBUG] [2,4,-] [proxy] [downstream] enter phase WaitNotify[11], proxyId = 2
2023-08-15 13:58:14,108 [DEBUG] [2,4,-] [proxy] [downstream] waitNotify begin 0xc0001e9340, proxyId = 2 -------------------->>>>>>context信息>>>>>>>>> {"Context":{"Context":0}} -------------------->>>>>>context信息>>>>>>>>> {"Context":{"Context":0}} 2023-08-15 13:58:20,159 [DEBUG] [-,-,-] [stream] [xprotocol] new stream detect, requestId = 0 2023-08-15 13:58:20,161 goroutine panic: runtime error: invalid memory address or nil pointer dereference

taoyuanyuan commented 1 year ago

你用的什么connpool?方便看看你的代码吗

zzqCh commented 1 year ago

你用的什么connpool?方便看看你的代码吗

connpool_multiplex.go. 使用的是这个,具体是哪块的代码呢?我打印context是在decode中的开头,也是看的example中的方法

image image
taoyuanyuan commented 1 year ago

直接 %+v 打印 ctx 看看

zzqCh commented 1 year ago

直接 %+v 打印 ctx 看看

这么确实打印出来了,但是好像根本看不懂的呢。 &{0xc0006dc540 [0xc0006c1800 [{false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {true 0} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>} {false <nil>}]]}

另外,您能告诉下为啥会替换requestId的么?

image image

我理解的流程如下:

image image
taoyuanyuan commented 1 year ago

因为client是有多个, 如果这儿不换成新的唯一id,发送给服务端的时候会有冲突。 比如两个client都同时发送了200的id到mosn,mosn不替换的话,会给服务端发送两个200的请求。 你的协议里面有唯一的id标识吗?

taoyuanyuan commented 1 year ago

如果你的协议不支持多路复用,可以选择pingpong的connpool,类似http1.

zzqCh commented 1 year ago

因为client是有多个, 如果这儿不换成新的唯一id,发送给服务端的时候会有冲突。 比如两个client都同时发送了200的id到mosn,mosn不替换的话,会给服务端发送两个200的请求。 你的协议里面有唯一的id标识吗?

有唯一id的。 这个怎么确认协议是否是支持多路复用的呢?我理解实现requestid的两个接口就能多路复用的吧? http1.1的坏处就是client-->proxy和proxy-->server的连接数是一对一的,这样server也需要部署好多机器,比较浪费资源,如果能够多路复用,那么proxy--->server的连接就会很少。

目前发现收到服务端的响应后,因为找不到clientStream,导致mosn终止了给客户端的响应,您知道是哪块的问题么?

taoyuanyuan commented 1 year ago
  1. 能不能支持多路复用还是要看你的协议以及服务端的实现哟,比如dubbo,http2就是支持多路复用的,http1就不支持,只能pingpong,一来一回的。
  2. http1虽然是pingpong的,但是proxy-server的连接是可以复用的哈,一个请求结束了,就是放入连接池,下次请求来了继续用。
  3. 找不到clientstream,是不是id替换不对呀,你server返回的id和你替换的id一样吗?
taoyuanyuan commented 1 year ago

那是不是你实现的setrequestid方法不对?

zzqCh commented 1 year ago

那是不是你实现的setrequestid方法不对?

是的,我修改了该方法,处理response的时候设置成发送时候的Requestid,非常感谢您的耐心指导。

对了,顺便问下您,您知道取ctx中的哪个key是代表direction的么?我目前没发现,因此自己手动设置了下。

taoyuanyuan commented 1 year ago

你暂时用 types.VariableListenerName 试试,能获取到这个变量就是downstream,后面可以搞个通用的变量。 variable.Get(ctx, types.VariableListenerName)

zzqCh commented 1 year ago

你暂时用 types.VariableListenerName 试试,能获取到这个变量就是downstream,后面可以搞个通用的变量。 variable.Get(ctx, types.VariableListenerName)

好的,谢谢您。

zzqCh commented 1 year ago

您好,请问下,今天压测过程中发现数据出现了交叉,只有在压测过程中才会出现数据交叉的情况,请问能从哪几个方面去查询的呢? 场景: 一、sever端是一个echo服务,即返回内容就是客户端的请求内容 二、压测客户端client1发送数据到server,比如发送的是aaacccddd 三、此时,重新启动一个客户端client2,发送数据到server,发现接收到的数据错乱了,接收到的数据是client2发送的部分数据和client1发送的部分数据。

taoyuanyuan commented 1 year ago

意思就是server收到了client1和client2的部分数据,就是不完整的请求吗? 你们是支持多路复用的吗?也就是server可以并发处理多个请求?

zzqCh commented 1 year ago

意思就是server收到了client1和client2的部分数据,就是不完整的请求吗? 你们是支持多路复用的吗?也就是server可以并发处理多个请求?

是多路复用,而且xprotocol只支持多路复用的吧?之前改成pingpong的时候服务起不来。server是可以支持多路复用的呢,因为server端每接收到一个请求,就会启动一个协程。 目前从表现上看是client收到了错乱的数据,但不清楚是哪块的问题。

taoyuanyuan commented 1 year ago

你可以打一个debug日志出来看看,有tracelog,能关联出来的,是不是id错乱了

zzqCh commented 1 year ago

你可以打一个debug日志出来看看,有tracelog,能关联出来的,是不是id错乱了

image

这个哪个字段是关联的呢?日志有点多的呢。

zzqCh commented 1 year ago

你可以打一个debug日志出来看看,有tracelog,能关联出来的,是不是id错乱了

您好,请问client到mosn发现有请求id为空,我这边写的请求id为空,那么setRequestID默认是0,是不是这个会导致数据错乱?如果是的话,怎么避免的呢?

taoyuanyuan commented 1 year ago

那肯定有问题,你发送id为空的两个请求,没办法判断回包是属于那个请求啊

taoyuanyuan commented 1 year ago

你如果请求id为空,就证明你不支持多路复用啊,只支持pingpong模型了,或者pipeline。

zzqCh commented 1 year ago

你如果请求id为空,就证明你不支持多路复用啊,只支持pingpong模型了,或者pipeline。

好的。谢谢您,目前定位到确实也是这个问题

taoyuanyuan commented 1 year ago

那你可以用pingpong的connpool试试,串行的,就不会乱了

zzqCh commented 1 year ago

那你可以用pingpong的connpool试试,串行的,就不会乱了

您好,这里我还有个问题,我发现错乱的场景是这样的: 场景: 1)客户端:有三个客户端,其中一个客户端发送的请求中有uuid(我们命名为客户端A),但是另外两个发送的请求中无uuid(命名为客户端B和客户端C),即可认为requestid是0 2)服务端:简单的echo服务,即响应内容就是客户端的请求内容

测试方式: 1.客户端A,一直发送数据,比如发送的数据{"data":"test","iphone":"13pro","time":"123"} 2.客户端B开始发送数据,要求服务端延迟3秒,比如发送的数据{"name":"a","code":"b"} 3.客户端C开始发送数据,要求服务端延迟10秒,比如发送的数据{"name":"a","code":"b"} 4.客户端B开始发送数据,要求服务端延迟3秒,比如发送的数据{"name":"a","code":"b"}

在这个场景下,客户端B和客户端C接收到的数据中会夹杂着客户端A发送的数据,比如可能接收到的是{"name":"a","iphone":"13pro"}或者是{"nameipho":"13a"}(只是为了说明数据错乱了,而且无规则) 但是按照我对mosn的理解,无论客户端B和客户端C怎么发送数据,数据错乱也只会影响到客户端B和客户端C的吧?也就是说客户端B和客户端C收到的数据中不应该存在客户端A收到的数据,但是目前看到客户端B或者C中却存在了客户端A的响应数据。

请求无uuid的情况下,这个case是合理的么?

taoyuanyuan commented 1 year ago

这儿的uuid是协议的唯一id吗 你们server端会返回这个uuid吗 最好有一个出问题的debug日志

zzqCh commented 1 year ago

这儿的uuid是协议的唯一id吗 你们server端会返回这个uuid吗 最好有一个出问题的debug日志

uuid是协议的唯一id,server端也会返回的这个唯一id的呢。

按理来说客户端B和C不会出现客户端A中的数据吧?

taoyuanyuan commented 1 year ago

那其实server收到的请求都是有uuid的钱,因为mosn会生成的,有没有可能server端有问题?最好有个debug日志