目前 Go 圈有很多款异步的网络框架:
排名不分先后。
这里面最早的实现是 evio 。evio 也存在一些问题,之前也写过evio文章介绍过。 其他比如 nbio 和 gnet 也写过一些源码分析。
为什么会出现这些框架?之前也提到过,由于标准库 netpoll 的一些特性:
这些框架在应用层上做了很多优化,比如:Worker Pool,Buffer,Ring Buffer,NoCopy......。
都分析了好几篇的代码了,那么咋么说也得自己动手搞一个来达成学习目的。
没错,这就是easyio的由来。
它是一个最小化的 IO 框架,只实现最核心的部分,加起来不超过 500 行代码。
也没有用户端上层应用的优化,且目前只实现了 linux 的 epoll ,以及只能运行 tcp 协议。
简单的 demo ,
服务端:
package main
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"github.com/wuqinqiang/easyio"
)
var _ easyio.EventHandler = (*Handler)(nil)
type Handler struct{}
type EasyioKey struct{}
type Message struct{ Msg string }
var CtxKey EasyioKey
func (h Handler) OnOpen(c easyio.Conn) context.Context {
return context.WithValue(context.Background(), CtxKey, Message{Msg: "helloword"})
}
func (h Handler) OnRead(ctx context.Context, c easyio.Conn) {
_, ok := ctx.Value(CtxKey).(Message)
if !ok {
return
}
var b = make([]byte, 100)
_, err := c.Read(b)
if err != nil {
fmt.Println("err:", err)
}
fmt.Println("[Handler] read data:", string(b))
if _, err = c.Write(b); err != nil {
panic(err)
}
}
func (h Handler) OnClose(_ context.Context, c easyio.Conn) {
fmt.Println("[Handler] closed", c.Fd())
}
func main() {
e := easyio.New("tcp", ":8090",easyio.WithNumPoller(4), easyio.WithEventHandler(Handler{}))
if err := e.Start(); err != nil {
panic(err)
}
defer e.Stop()
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT)
<-c
}
上面的代码,初始化一个 easyio ,启动一个 tcp 服务,监听端口 8090 ,options 里面设置 epoll 的数量,以及设置事件处理器。
当一个新连接到来时会回调 OnOpen 函数,此时你可以设置自定义的 ctx ,那么当对应连接读事件到来 OnRead 回调,你可以拿到之前设置的 ctx ,调用 conn.Read 读取数据,且通过 Write 向对端写数据。
这里需要注意的是,一个连接如果数据没读完,当 OnRead 执行结束,下一轮会继续触发回调代码,因为底层 epoll 采用的是 LT 触发方式。
简单的客户端
package main
import (
"fmt"
"net"
"os"
"os/signal"
"syscall"
)
func main() {
conn, err := net.Dial("tcp", ":8090")
if err != nil {
panic(err)
}
n, err := conn.Write([]byte("hello world"))
if err != nil {
panic(err)
}
go func() {
b := make([]byte, 100)
if n, err = conn.Read(b); err != nil {
panic(err)
}
fmt.Println("read data:", n, string(b))
}()
defer conn.Close()
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
<-ch
}
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.