[gev] 自定义协议支持

https://github.com/Allenxuxu/gev gev 是一个轻量、快速的基于 Reactor 模式的非阻塞 TCP 网络库,支持自定义协议,轻松快速搭建高性能服务器。 TCP 为什么会"粘包" TCP 本身就是面向流的协议,就是一串没有界限的数据。所以本质上来说 TCP 粘包是一个伪命题。 TCP 底层并不关心上层业务数据,会套接字缓冲区的实际情况进行包的划分,一个完整的业务数据可能会被拆分成多次进行发送,也可能会将多个小的业务数据封装成一个大的数据包发送(Nagle算法)。 gev 如何优雅处理 gev 通过回调函数 OnMessage 通知用户数据到来,回调函数中会将用户数据缓冲区(ringbuffer)通过参数传递过来。 用户通过对 ringbuffer 操作,来进行数据解包,获取到完整用户数据后再进行业务操作。这样又一个明显的缺点,就是会让业务操作和自定义协议解析代码堆在一起。 所以,最近对 gev 进行了一次较大改动,主要是为了能够以插件的形式支持各种自定义的数据协议,让使用者可以便捷处理 TCP 粘包问题,专注于业务逻辑。 做法如下,定义一个接口 Protocol // Protocol 自定义协议编解码接口 type Protocol interface { UnPacket(c *Connection, buffer *ringbuffer.RingBuffer) (interface{}, []byte) Packet(c *Connection, data []byte) []byte } 用户只需实现这个接口,并注册到 server 中,当客户端数据到来时,gev 会首先调用 UnPacket 方法,如果缓冲区中的数据足够组成一帧,则将数据解包,并返回真正的用户数据,然后在回调 OnMessage 函数并将数据通过参数传递。 下面,我们实现一个简单的自定义协议插件,来启动一个 Server : | 数据长度 n | payload | | 4字节 | n 字节 | // protocol....

October 31, 2019 · 3 min · 徐旭

Go 网络库并发吞吐量测试

https://github.com/Allenxuxu/gev 本文主要测试 gev 网络库和其他三方 Go 网络库以及标准库的吞吐量对比。 测试对象 gev :一个轻量、快速的基于 Reactor 模式的非阻塞 TCP 网络库 eviop :evio 的优化版本 evio :Fast event-loop networking for Go gnet :eviop 的网络模型替换版本 net 标准库 测试方法 采用陈硕测试 muduo 使用的 ping pong 协议来测试吞吐量。 简单地说,ping pong 协议是客户端和服务器都实现 echo 协议。当 TCP 连接建立时,客户端向服务器发送一些数据,服务器会 echo 回这些数据,然后客户端再 echo 回服务器。这些数据就会像乒乓球一样在客户端和服务器之间来回传送,直到有一方断开连接为止。这是用来测试吞吐量的常用办法。 测试的客户端代码: https://github.com/Allenxuxu/gev/blob/master/benchmarks/client/main.go 测试脚本:https://github.com/Allenxuxu/gev/blob/master/benchmarks/bench-pingpong.sh 主要做两项测试: 单线程单个 work 协程测试,测试并发连接数为 10/100/1000/10000 时的吞吐量 4线程4个 work 协程测试,测试并发连接数为 10/100/1000/10000 时的吞吐量 所有测试中,ping pong 消息的大小均为 4096 bytes,客户端始终是4线程运行。...

September 22, 2019 · 1 min · 徐旭

开源 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 · 徐旭

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 · 徐旭