Slide 28
Slide 28 text
• Root Cause
在serve websocket连接时,先从sync.pool中拿到
bufio.Reader,再启⼀个协程来处理这个连接。
readBufPool = &sync.Pool{
New: func() interface{} {
return bufio.NewReaderSize(nil, BufSize)
},
}
func (s Server) serveWebSocket(w http.ResponseWriter, req
*http.Request) {
rwc, _, err := w.(http.Hijacker).Hijack()
rb := readBufPool.Get().(*bufio.Reader)
wb := writeBufPool.Get().(*bufio.Writer)
rb.Reset(rwc)
wb.Reset(rwc)
buf := bufio.NewReadWriter(rb, wb)
conn, err := newServerConn(rwc, buf, req, &s.Config,
s.Handshake)
……
conn = newHybiConn(&s.Config, buf, rwc, req)
go func() {
s.Handler(conn)
rwc.Close()
readBufPool.Put(rb)
}()
在s.Handler函数中,先设置⼼跳定时器,登录,启⼀个
读协程,本协程进⾏写操作。
func (sess *session) Serve() {
// 设置⼼跳超时定时器…...
go sess.SignIn() // 登录
<-sess.signInDone
if sess.id == 0 {
goto done
}
if sess.s.opts.Debug || sess.debug {
log.Infof(“sess: sign in success, %s”, sess)
}
sess.resetHB()
sess.s.putSession(sess)
go sess.loopRead() // 读协程
sess.loopWrite() // 写协程
done:
sess.clean()
}
注:读协程会⼀直使⽤传⼊的bufio.Reader。
虽然读协程和写协程使⽤了同步设施,保证有⼀⽅退出,
另⼀⽅也会退出,但不保证退出的时序。
如果本协程(写协程)先退出,会先将rb归还到sync.pool,
⽽读协程可能还在使⽤rb对象,就会产⽣Race
Condition。