geektutu / blog

极客兔兔的博客,Coding Coding 创建有趣的开源项目。
https://geektutu.com
Apache License 2.0
166 stars 21 forks source link

动手写RPC框架 - GeeRPC第六天 负载均衡(load balance) | 极客兔兔 #97

Open geektutu opened 3 years ago

geektutu commented 3 years ago

https://geektutu.com/post/geerpc-day6.html

7天用 Go语言/golang 从零实现 RPC 框架 GeeRPC 教程(7 days implement golang remote procedure call framework from scratch tutorial),动手写 RPC 框架,参照 golang 标准库 net/rpc 的实现,实现了服务端(server)、支持异步和并发的客户端(client)、消息编码与解码(message encoding and decoding)、服务注册(service register)、支持 TCP/Unix/HTTP 等多种传输协议。第六天实现了2种简单的负载均衡(load balance)算法,随机选择和 Round Robin 轮询调度算法。

ppd0705 commented 3 years ago

没看错的话,Broadcast方法里面的 goroutine 使用了loop iterator variable rpcAddr,这个可能会有问题吧?

geektutu commented 3 years ago

@ppd0705 感谢指出问题,会尽快修正:

for _, rpcAddr := range servers {
    wg.Add(1)
    go func(rpcAddr string) {
        defer wg.Done()
        // ...
    }(rpcAddr)
}
ppd0705 commented 3 years ago

不客气,golang我还不太熟,所以有点不确定。

gnehcein commented 3 years ago

话说
if err == nil && !replyDone { reflect.ValueOf(reply).Elem().Set(reflect.ValueOf(clonedReply).Elem()) replyDone = true } 中的reflect.valueof(reply).Elem()...这一句 如果改成reply=clonedeReply main也能正常运行,请问这两者有什么区别?好像只是变成了闭包效果而已。。

whitebluepants commented 2 years ago

话说 if err == nil && !replyDone { reflect.ValueOf(reply).Elem().Set(reflect.ValueOf(clonedReply).Elem()) replyDone = true } 中的reflect.valueof(reply).Elem()...这一句 如果改成reply=clonedeReply main也能正常运行,请问这两者有什么区别?好像只是变成了闭包效果而已。。

@gnehcein 我自己的猜测,会不会是reply=clonedReply会导致函数结束后,clonedReply变量不会被回收呢

winterszhangdong commented 2 years ago

@gnehcein 话说
if err == nil && !replyDone { reflect.ValueOf(reply).Elem().Set(reflect.ValueOf(clonedReply).Elem()) replyDone = true } 中的reflect.valueof(reply).Elem()...这一句 如果改成reply=clonedeReply main也能正常运行,请问这两者有什么区别?好像只是变成了闭包效果而已。。

我也有一样的疑问,似乎也不是不行?

yuanweining commented 2 years ago

@whitebluepants

话说 if err == nil && !replyDone { reflect.ValueOf(reply).Elem().Set(reflect.ValueOf(clonedReply).Elem()) replyDone = true } 中的reflect.valueof(reply).Elem()...这一句 如果改成reply=clonedeReply main也能正常运行,请问这两者有什么区别?好像只是变成了闭包效果而已。。

@gnehcein 我自己的猜测,会不会是reply=clonedReply会导致函数结束后,clonedReply变量不会被回收呢

我认为,兔兔的写法是不改变reply的指针,在reply指向的那块内存写数据。如果使用reply = cloneReply,那就把reply换了一个地址,指向clonedReply指向的变量,所以会导致内存逃逸(也就是clonedReply变量不会被回收)。

yan0327 commented 2 years ago

想请教一个问题。大家在测试Day6这个例子时,会不会出现发送消息卡死的现象。(具体体现为RPC服务器无法处理客户端发的请求,从而导致客户端由于未收到响应从而阻塞导致程序无法正常进行处理)

klzhu-zz commented 2 years ago

发现Day6的代码执行多次后会出现 rpc server: read header error: gob: unknow type id or corrupted data 以及 wsarecv: An existing connecion was forcibly closed by remote host的错误 本来以为是自己哪里写错了,但是拿教程里的代码来跑也会有这个问题,有没有人知道是为什么啊

pandaye commented 1 year ago

@winterszhangdong

@gnehcein 话说
if err == nil && !replyDone { reflect.ValueOf(reply).Elem().Set(reflect.ValueOf(clonedReply).Elem()) replyDone = true } 中的reflect.valueof(reply).Elem()...这一句 如果改成reply=clonedeReply main也能正常运行,请问这两者有什么区别?好像只是变成了闭包效果而已。。

我也有一样的疑问,似乎也不是不行?

不行的吧,reply 是指针,在函数里面改变不会影响到外面,直接 reply = clonedReply 函数外面就的不到计算结果了

pandaye commented 1 year ago

@USER-ZKL 发现Day6的代码执行多次后会出现 rpc server: read header error: gob: unknow type id or corrupted data 以及 wsarecv: An existing connecion was forcibly closed by remote host的错误 本来以为是自己哪里写错了,但是拿教程里的代码来跑也会有这个问题,有没有人知道是为什么啊

应该是 TCP 粘包了,这个教程里没有处理粘包问题

abcdhope commented 1 year ago

Broadcast方法中,定义了 replyDone := reply == nil

if err == nil && !replyDone {
 reflect.ValueOf(reply).Elem().Set(reflect.ValueOf(clonedReply).Elem())
 replyDone = true
}

我想问的是为什么要在reply不为空的时候进行赋值呢

Inuyasha-Monster commented 1 year ago

gee-rpc/day6-load-balance/xclient/discovery.go:50 可以替换为

d.mu.RLock()
defer d.mu.RUnlock()

提升锁的并发度

China-zhang-hui commented 1 year ago

@yan0327 想请教一个问题。大家在测试Day6这个例子时,会不会出现发送消息卡死的现象。(具体体现为RPC服务器无法处理客户端发的请求,从而导致客户端由于未收到响应从而阻塞导致程序无法正常进行处理)

会的,而且大可能会,好像是个bug,没有得到解决方案。

kaioji commented 2 weeks ago

@China-zhang-hui

@yan0327 想请教一个问题。大家在测试Day6这个例子时,会不会出现发送消息卡死的现象。(具体体现为RPC服务器无法处理客户端发的请求,从而导致客户端由于未收到响应从而阻塞导致程序无法正常进行处理)

会的,而且大可能会,好像是个bug,没有得到解决方案。 可以把serveCodec中_ = cc.Close()删除,在ServeConn中已经处理了连接关闭