开源 gev: Go 实现基于 Reactor 模式的非阻塞 TCP 网络库

gev 轻量、快速的 Golang 网络库 gev 是一个轻量、快速的基于 Reactor 模式的非阻塞 TCP 网络库,支持自定义协议,轻松快速搭建高性能服务器。 为什么有 gev Golang 的 goroutine 虽然非常轻量,但是每启动一个 goroutine 仍需要 4k 左右的内存。读了鸟窝大佬的文章【百万 Go TCP 连接的思考: epoll方式减少资源占用】后,便去研究了了下 evio。 evio 虽然非常快,但是仍然存在一些问题,便尝试去优化它,于是有了 eviop 项目。关于 evio 的问题可以看我的另一篇博文 【Golang 网络库evio一些问题/bug和思考】。在优化 evio 完成 eviop 的过程中,因为其网络模型的缘故,愈加感觉修改它非常麻烦,成本比重新搞一个还高。 最终决定自己重搞一个,更加轻量,不需要的全去掉。加上大学时学习过 muduo ,便参考 muduo 的使用的 Reactor 模型实现 gev 。 在 linux 环境下,gev 底层使用 epoll ,这是 gev 会专注优化的地方。在 mac 下底层使用 kqueue,可能不会过多关注这部分的优化,毕竟很少有用 mac 做服务器的(Windows 环境"暂"不支持)。 特点 基于 epoll 和 kqueue 实现的高性能事件循环 支持多核多线程 动态扩容 Ring Buffer 实现的读写缓冲区 异步读写 SO_REUSEPORT 端口重用支持 支持 WebSocket 支持定时任务,延时任务 支持自定义协议,处理 TCP 粘包 网络模型 gev 只使用极少的 goroutine, 一个 goroutine 负责监听客户端连接,其他 goroutine (work 协程)负责处理已连接客户端的读写事件,work 协程数量可以配置,默认与运行主机 CPU 数量相同。...

September 19, 2019 · 1 min · 徐旭

Go net/http 浅析

GO HTTP Server 使用标准库构建 HTTP 服务 Go 语言标准库自带一个完善的 net/http 包,可以很方便编写一个可以直接运行的 Web 服务。 package main import ( "log" "net/http" ) func hello(w http.ResponseWriter, r *http.Request) { log.Println(r.Method, r.Host, r.RequestURI) w.Write([]byte("hello")) } func main() { http.HandleFunc("/hello", hello) //设置访问的路由 // http.Handle("/hello", http.HandlerFunc(hello)) // 和上面写法等价 err := http.ListenAndServe(":9090", nil) //设置监听的端口并启动 HTTP 服务 if err != nil { log.Fatal("ListenAndServe: ", err) } } $ curl -v 127.0.0.1:9090/hello * Trying 127.0.0.1... * TCP_NODELAY set * Connected to 127....

September 15, 2019 · 6 min · 徐旭

Go channel 拷贝问题

Go 的 channel 使用非常方便,但是总听说 channel 会拷贝传递的数据,生怕频繁拷贝影响效率。 究竟是怎么个拷贝法呢,下面会有两个 demo 验证下。 先说结论: Go channel 的发送接收数据的拷贝和 Go 的函数传参道理是一样的,都是默认的值拷贝。 如果你传递一个值,那么 Go 会复制一份新的;如果传递一个指针,则会拷贝这个指针,不会去拷贝这个指针所指的变量(这一点 C++ 选手可能会理解比较深)。 所以,如果你需要通过 channel 传递一个很大的 struct ,那么应该传递 指针。但是,要非常注意通过 channel 发送后,不要修改这个指,这会导致线程间潜在的竞争。 下面是两个验证的小 demo: 通过 channel 传递指针 package main import ( "fmt" "time" ) func recv(ch <-chan *int) { time.Sleep(1 * time.Second) out := <-ch fmt.Println("recv : ", out, *out) } func main() { i := 1 ch := make(chan *int, 2) fmt....

August 21, 2019 · 1 min · 徐旭

拥抱 Go module

go get 拉包一直时国内选手头疼的问题,虽然梯子可以解决问题,但是总是有很慢的时候,而且需要每台电脑都配置,特别是 CI 的服务器等,很烦人。 七牛云开源了 goproxy ,还免费提供 https://goproxy.cn 作为代理来拉包。 不过 GOPROXY 只有在 Go module 下才能使用,索性全面拥抱 Go module 一劳永逸。 修改一下配置文件,即可: sudo vi /etc/profile 在最后添加如下内容,开启 Go module 和代理: export GO111MODULE=on export GOPROXY=https://goproxy.cn 让配置文件立即生效 source /etc/profile 接下来就可以畅快 Go 了! PS: Go 1.16 已经默认开启 go moudle了。

August 20, 2019 · 1 min · 徐旭

Golang 网络库 evio 一些问题/bug和思考

Fast event-loop networking for Go 最近翻了 evio 的源码,发现一些问题,主要集中在 linux 平台 epoll 上和读写的处理。 用来唤醒 epoll 的 eventfd 写入数据没有读出 listen 的 fd 注册到所有事件循环,epoll 的惊群问题 loopWrite 在内核缓冲区满,无法一次写入时,出现写入数据丢失 eventfd 的使用问题 在 internal/internal_linux.go 中封装了 epoll 的使用 API 。 // Poll ... type Poll struct { fd int // epoll fd wfd int // wake fd notes noteQueue } 在 OpenPoll 时,会创建一个 eventfd 并将 fd 赋值给 Poll 的 wfd 成员, 并且注册到 epoll 监听可读事件。...

August 15, 2019 · 3 min · 徐旭

Golang 高性能网络库 evio 源码解析

阅读前提:了解 epoll evio 是一个基于事件驱动的网络框架,它非常轻量而且相比 Go net 标准库更快。其底层使用epoll 和 kqueue 系统调度实现。 原理 evio 是 Reactor 模式的简单实现。Reactor 本质就是“non-blocking IO + IO multiplexing”,通过非阻塞IO+ IO 多路复用来处理并发。程序运行一个或者多个事件循环,通过在事件循环中注册回调的方式实现业务逻辑。 evio 将所有文件描述符设为非阻塞,并注册到事件循环( epoll / kqueue )中。相较于传统的 per thread per connection 的处理方法,线程使用更少,线程资源利用率更高。 evio 需要在服务启动前,注册回调函数,当事件循环中有事件到来时,会调用回调函数处理。 使用示例 先从一个简单的 echo server 的例子来了解 evio 。 package main import ( "flag" "fmt" "log" "strings" "github.com/tidwall/evio" ) func main() { var port int var loops int var udp bool var trace bool var reuseport bool var stdlib bool flag....

August 6, 2019 · 11 min · 徐旭

Golang 极简入门教程

Hello World 我们以传统的“hello world”案例开始吧。 package main import "fmt" func main() { fmt.Println("Hello World") } Go的源文件以 .go 为后缀名,这些文件名均由小写字母(推荐做法)组成且不包含空格和其他特殊字符,如 main.go 。如果文件名由多个部分组成,则使用下划线 _ 对它们进行分隔,如 main_test.go 。 Go是一门编译型语言,Go语言的工具链将源代码及其依赖转换成计算机的机器指令。Go语言提供的工具都通过一个单独的命令 go 调用,go 命令有一系列子命令。 $ go help Go is a tool for managing Go source code. Usage: go <command> [arguments] The commands are: bug start a bug report build compile packages and dependencies clean remove object files and cached files doc show documentation for package or symbol env print Go environment information fix update packages to use new APIs fmt gofmt (reformat) package sources generate generate Go files by processing source get download and install packages and dependencies install compile and install packages and dependencies list list packages or modules mod module maintenance run compile and run Go program test test packages tool run specified go tool version print Go version vet report likely mistakes in packages ....

August 4, 2019 · 13 min · 徐旭

Golang实现默认参数

在golang 中是不支持默认参数的,micro中有一种优雅的实现方法(并非 micro 首创),叫做 Functional Options Patter。Functional Options 可以用来实现简洁的支持默认参数的函数方法。 options package server import ( "time" ) type Options struct { ConnectTimeOut time.Duration Name string Address string } type Option func(*Options) func newOptions(opt ...Option) Options { opts := Options{} for _, o := range opt { o(&opts) } if len(opts.Address) == 0 { opts.Address = DefaultAddress } if len(opts.Name) == 0 { opts.Name = DefaultName } if opts.ConnectTimeOut == time.Duration(0) { opts....

June 27, 2019 · 2 min · 徐旭