用go实现了一个数据库访问的代理,即客户端发数据到proxy proxy转发至后端数据库并将数据返回给客户端,项目使用的go net包;
pprof分析cpu耗时,发现syscall (28%)和epollwait(50%)占比相当高,syscall主要是net的read和write耗时,已经优化合并了部分读写操作,epollwait高的吓人,请大神帮助分析下,感激不尽
![4069.png](https://static.golangjob.cn/221118/1856f1ef5ac4a76cff83e64d99e67269.png)
程序是实现了一个数据库代理服务:
客户端 <---> 代理服务 <--> 数据库
代码逻辑大概如下:
// 建立监听
s.listener, err = net.Listen(netProto, s.addr)
//循环accept
for s.running {
conn, err := s.listener.Accept()
if err != nil {
...
continue
}
// 协程处理connection
go s.onConn(conn, dbType)
}
//
func (s *Server) onConn(c net.Conn, dbType string) {
...
// 循环监听读connection消息
for {
// 使用 io.ReadFull读消息;RB是 bufio.Reader; 若没消息程序会停在这里,我理解这就是epoll的挂起 并不消耗cpu
header := make([]byte, 5)
if _, err := io.ReadFull(cc.pkg.Rb, header); err != nil {
return nil, nil, err
}
....
//收到消息后 会通过后端数据库链接将消息写入数据库连接中;Wb是 io.Writer
// 其中后端链接通过连接池维护,轮询选取,选取时有一个锁控制选取哪个节点;此方法cpu耗时很少
err = cc.backendConn.Conn.pkg.Wb.Write(data)
...
//for循环监听数据库端返回消息
for {
header := make([]byte, 5)
data, err = io.ReadFull(cc.backendConn.Conn.pkg.Rb, header )
// 处理消息
.....
//写消息到客户端
c.pkg.Wb.Write(data)
}
// 回收数据库连接到资源池
c.closeConn(c.backendConn, false)
}
...
}
#3
更多评论
![image.png](https://static.golangjob.cn/221121/a2d9dc67c22104138ef82a2a681acbe6.png)
#1