Skip to content

xing393939/netpoll-study

Repository files navigation

netpoll-study

一些细节

  • epoll_create1(EPOLL_CLOEXEC)比之epoll_create1(0),fork出来的子进程这些pd是关闭的,参考
  • epoll_pwait除了接收fd的读写事件,还能接收signalfd的信号,参考
  • BIO和NIO的对比:IO多路复用与Go网络库的实现
    • BIO的缺点是在et模式下需要一个线程对一个连接;内核不停的切换线程的开销大。
    • NIO一般由少量event-dispatching线程和用户线程组成,缺点是实现复杂。
  • 本人总结网络模型的两个原则:
    1. 不要让io任务阻塞了线程/协程,例如:1系统调用read设置为non-blocking,2使用自研网络库gnet、netpoll
    2. 尽早的提交io任务给io设备,例如:有了新的read事件后开启协程来处理read->decode->process->encode->write,可以改成一个协程处理read、write,一个协程处理decode->process->encode
  • 关注点:
    1. Netpoll是lt(不会for循环accept、read),net包是et(for循环accept、read)
    2. Netpoll是NIO模式,net包是BIO模式:参考
    • net包提供的api(conn.Read)会挂起协程;Netpool提供的是创建eventPoll(传入onRequest方法)
    1. Netpoll增加了连接的活性检查,net包没有:参考
    • Netpoll的conn有IsActive()方法
    1. NoCopy API:Netpoll有零拷贝技术
    • 1业务层的零拷贝:如果只使用了一个linkBufferNode直接使用该地址;2对socket的读写使用了零拷贝
    1. LinkBuffer:多个tcp连接读写同一块内存池,减少内存分配开销
    • LinkBuffer池和mcache分配
    1. 如果有栈扩张问题,使用协程池gopool:没有worker创建worker,worker执行完task后若还有task则继续执行,没有则exit
    • 减少栈扩张的开销
    1. tcp Nagle算法的缺点:一般要等收到足够的数据包后才ack。解决:开启TCP_NODELAY,缺点是ack包变多(但是现在网络环境好)
    2. tcp send之前先合并包:为啥不用tcp本身的合并功能?
    3. 连接多路复用:假设上游的pv是M个,每个pv需要调用N次下游,那么下游流量就是MxN(多路复用后是M)

两个问题

  1. readv调用时并没有使用多个内存块向量
  2. 没有栈扩张问题为什么建议关闭gopool
  3. Next是无拷贝的,readBinary和readString是有拷贝
  4. 改内核让sendmsg变成同步调用
  5. listener fd 和 client fd 是共享 epoll 的
// 读取 n 字节, 返回底层缓存切片, 同时缓存减少 n 字节
conn.Reader().Next(n)
// 预读取 n 字节, 返回底层缓存切片, 缓存大小不变, 可重复预读
conn.Reader().Peek(n)
// 丢弃缓存最前的 n 字节, 不可找回
conn.Reader().Skip(n)
// 释放已读部分的底层缓存, (在此之前读取的)上层读缓存切片将全部失效
conn.Reader().Release()
// 在连接写缓存区顺序分配 n 字节
conn.Writer().Malloc(n)
// 将已分配的写缓存全部发送到连接对端, (在此之前分配的)上层写缓存切片将全部失效
conn.Writer().Flush()

// 不使用linkbuffer
a := "res"
conn.Write(a) // 后续由运行时gc掉

// 使用linkbuffer
writer := conn.Writer()
buf, _ := writer.Malloc(3)
copy(buf, []byte("res"))
writer.Flush() // 标记这3个字节为free,可以重新利用,避免gc

字节跳动在 Go 网络库上的实践

img

高性能 NIO 网络库 Netpoll-开源说

Go 原生同步网络模型解析-Go夜读

netFD    // net包的网络描述符
pollDesc // 运行时的一个poller的描述符

// 运行时网络轮询器的接口
func netpollinit()
func netpollopen(fd uintptr, pd *pollDesc) int32
func netpoll(delta int64) gList
 
// 运行时网络轮询器在linux下的底层系统调用
int epoll_create1(int flags);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_pwait(int epfd, struct epoll_event *events, int maxevents, int timeout, const sigset_t *sigmask);

实例浅析epoll的LT和ET模式,ET模式为何要使用非阻塞IO

epool的lt是只要事件没有被处理就一直触发,et则只触发一次,导致et必须每次要循环处理完所有的事件,六个测试场景如下:

1. sockfd是lt模式+非阻塞:能正常accept所有请求
2. sockfd是et模式+非阻塞:会漏处理accept一些请求,因为每次epoll_wait返回时只处理了一个请求
3. connfd是lt模式+阻塞:能正常处理所有连接的read
4. connfd是lt模式+非阻塞:能正常处理所有连接的read
5. connfd是et模式+阻塞:如果epoll_wait返回时只处理一次read,那么数据可能没有读完;如果循环处理read,当没有读事件时会阻塞线程。
6. connfd是et模式+非阻塞:如果epoll_wait返回时只处理一次read,那么数据可能没有读完;如果循环处理read,能正常处理所有连接的read

Netpoll 模型的抽象和问题-曹大

  • Netpoll 模型的抽象和问题-曹大
  • pull.FD的Read和Write方法是有锁的,保证同一时间只有一个协程读或者写,见源码
  • 社区的netpoll的努力:改变Go的1个连接1个协程,在有读事件的时候才开启协程

img img img img img img

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published