-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
动手写RPC框架 - GeeRPC第一天 服务端与消息编码 | 极客兔兔 #91
Comments
非常感谢这个教程 |
@gaodansoft 笔芯~ |
说点什么,那当然是赞了😏 |
@JYFiaueng 感谢认可~ 😯 |
请问下面这一行是什么意思呢? var _ Codec = (*GobCodec)(nil) |
@wy-ei 参考 7days-golang 有价值的问题讨论汇总贴 |
写的太好了 |
@wangwenjunfromlanzhou 感谢认可~ 😊😊😊 |
直接这样可以吗defer func() { conn.Close() }() 而不是 defer func() { _ = conn.Close() }() |
client.sending.Lock() |
应该是 defer conn.Close() |
再一次被大佬精湛的技术 按在地板上摩擦 |
刚才照着大佬的代码敲了一遍,发现main函数中的循环发送请求如果不执行readBody或者执行出错,我们的sendResponse就会只打印其中的两三个请求,有时甚至还会报EOF错误: 代码如下:
是什么原因阻塞了我们的请求呢,又或者执行什么超时导致主线程退出了 |
就是检查结构体是否实现了这个接口 |
因为你的主协程 发送完5次请求后就退出了 此时运行服务器的协程还没有打印完这5次请求也退出了 |
我直接使用的day-1的代码,在windows上可以运行,到我虚拟机的ubuntu上,会阻塞在readRequestHeader,这可能是什么原因呢? |
geektutu/7days-golang#34 不知道是不是这个问题 |
我想问问如果不在header里指定body长度,会有粘包拆包的问题吗? |
json 字符串是有数据的边界的即 "{" 和 "}"所以这里并不会出现粘包的问题 |
问下 这行代码 // send options |
学到了 |
秒啊enc使用新的buffer,我一开始没注意使用的gob.NewEncoder(conn),导致可能会Option和header一起传过去,就会导致用body的解析为header就报错了 |
请问这里可以解释一下,没太明白buf := bufio.NewWriter(conn)以及enc: gob.NewEncoder(buf),这两句 |
感谢这个教程!实现这些项目我一个校招生终于春招找到了大厂工作!确实认认真真的熟悉了golang的编程思想和一些设计模式!十分感谢博主! |
encoder 因为是要往 conn 中写入内容, 这里文章中说明了要使用 buffer 来优化写入效率, 所以我们先写入到 buffer 中, 然后我们再调用 buffer.Flush() 来将 buffer 中的全部内容写入到 conn 中, 从而优化效率. 对于读则不需要这方面的考虑, 所以直接在 conn 中读内容即可. |
type GobCodec struct { cannot use (*GobCodec)(nil) (value of type *GobCodec) as Codec value in variable declaration: missing method |
json包里面的encode 和 decode只是简单调用的net包的read和write,没有处理tcp的数据边界问题,这样不会有问题吗? |
for循环后加一句time.Sleep(2 * time.Second),在运行就可以看到5个请求都处理了,所以应该就是主程序提前退出导致的 |
为什么会导致Option和Header一起传过去啊?使用bufio不是为了提高写入的效率吗?按理说使用gob.NewEncoder(conn)也是可以的呀 |
day1-codec/main/main.go |
这里就是读取服务端返回的response的header |
但是涉及到json的只有前面的option,后面的header和body是gob编解码的,会有上述的问题吗? |
|
这个是go的一个小技巧,确定GobCodec实现了Codec的接口,java里面不需要这一行 |
我想请问一下main.go中这里client端通过Write函数写request之后就通过ReadHeader,ReadBody读取Response了。但是这里没有任何的同步点,是否可能出现client端ReadBody的时候server端还没有写入Response的情况呢? for i := 0; i < 5; i++ {
h := &codec.Header{
ServiceMethod: "Foo.Sum",
Seq: uint64(i),
}
_ = cc.Write(h, fmt.Sprintf("geerpc req %d", h.Seq))
_ = cc.ReadHeader(h)
var reply string
_ = cc.ReadBody(&reply)
log.Println("reply:", reply)
} |
我尝试注销cc.write部分,会发现cc.ReadHeader会阻塞住,应该是conn内如果还没有写入数据的话,就不会读出来,直到有数据写入 |
谢谢。 我之前理解错了,conn应该是本身在client端和server端就有独立的数据缓冲区的,所以如果server端还没写入,client端这边就读不到就会被block |
这里应该存在发送options的时候,服务端读取到options后面header的内容吧。导致再次读取后面的内容是不完整的,导致gob编码失败 |
请教各位大佬一个问题,例如在 main.go 中,大量存在 _ = cc.Write(h, fmt.Sprintf("geerpc req %d", h.Seq))
_ = cc.ReadHeader(h) 像这样的返回值既然要抛弃,为何不直接写成这样呢? cc.Write(h, fmt.Sprintf("geerpc req %d", h.Seq))
cc.ReadHeader(h) |
当我尝试将Options 通过gob 编码发送后(服务端也使用 gob 解码)代码会在readRequestHeader 这个函数阻塞,这是为什么呢 main.go time.Sleep(time.Second)
// 告知服务端本次链接的options
//_ = json.NewEncoder(conn).Encode(server.DefaultOption)
_ = gob.NewEncoder(conn).Encode(server.DefaultOption)
//使用GobCodec 作为编辑编解码器
cc := codec.NewGobCodec(conn)
// send request & receive response
for i := 0; i < 5; i++ {
h := &codec.Header{
ServiceMethod: "Foo.Sum",
Seq: uint64(i),
}
_ = cc.Write(h, fmt.Sprintf("geerpc req %d", h.Seq))
_ = cc.ReadHeader(h)
var reply string
_ = cc.ReadBody(&reply)
log.Println("reply:", reply)
} server.go // ServeConn runs the server on a single connection.
// ServeConn blocks, serving the connection until the client hangs up.
func (server *Server) ServeConn(conn io.ReadWriteCloser) {
defer func() { _ = conn.Close() }()
var opt Option
//if err := json.NewDecoder(conn).Decode(&opt); err != nil {
if err := gob.NewDecoder(conn).Decode(&opt); err != nil {
log.Println("rpc server: options error: ", err)
return
}
if opt.MagicNumber != MagicNumber {
log.Printf("rpc server: invalid magic number %x", opt.MagicNumber)
return
}
f := codec.NewCodecFuncMap[opt.CodecType]
if f == nil {
log.Printf("rpc server: invalid codec type %s", opt.CodecType)
return
}
server.serveCodec(f(conn))
} |
请教下,为什么注释掉ReadHeader,ReadBody会读不到东西,已经加了time.sleep,我理解的不是很清楚。
|
我出现了一个rpc server: invalid codec type 的报错,代码都是一摸一样的,请问这是哪里出现了问题,我调试出来主要是,没有接收到对应默认option的codectype |
已经解决, Option属性私有化了,非常的愚蠢 |
数据格式|header|body|heder|body 把reply类型改一下 var reply codec.Header |
time.Sleep(time.Second) 为什么这地方要阻塞 1s |
请问兄弟用的什么软件画的图? |
解析是要安装顺序的,序列化的时候是先header然后body。读取的时候进行反序列化,所以也得安装序列化时候的顺序去解析,这就得先读取header再读body |
用_接收不会有警告 为了好看 |
同问,不过结合之前大佬们的回复,这里是解决粘包的问题吗? |
确保连接成功建立 |
粘包是靠在header里指定body长度解决 |
这个只是为了让IDE忽略没有错误处理的警告 |
为了解决tcp粘包,一般报文设计都会有先是header,在里面指定后面body的长度,所以要先读取header,判断报文类型和后续要读取的长度等。其实这个写的不太严谨,这里应该需要判断错误的,而不是直接忽略。 |
想请教一下,将header和body通过buffer缓冲一起进行发送,在client或server中进行读取的时候会出现粘包问题吗? 这里header好像没有固定大小? |
Although the header and body are sent together, when the header and body are sent separately, the length of the bytes is marked. source code: go/src/encoding/gob/encoder.go // writeMessage sends the data item preceded by an unsigned count of its length.
func (enc *Encoder) writeMessage(w io.Writer, b *encBuffer) {
// Space has been reserved for the length at the head of the message.
// This is a little dirty: we grab the slice from the bytes.Buffer and massage
// it by hand.
message := b.Bytes()
messageLen := len(message) - maxLength
// Length cannot be bigger than the decoder can handle.
if messageLen >= tooBig {
enc.setError(errors.New("gob: encoder: message too big"))
return
}
// Encode the length.
enc.countState.b.Reset()
enc.countState.encodeUint(uint64(messageLen))
// Copy the length to be a prefix of the message.
offset := maxLength - enc.countState.b.Len()
copy(message[offset:], enc.countState.b.Bytes())
// Write the data.
_, err := w.Write(message[offset:])
// Drain the buffer and restore the space at the front for the count of the next message.
b.Reset()
b.Write(spaceForLength)
if err != nil {
enc.setError(err)
}
}
|
https://geektutu.com/post/geerpc-day1.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 等多种传输协议。第一天实现了一个简单的服务端和消息的编码与解码。
The text was updated successfully, but these errors were encountered: