{"id":15028458,"url":"https://github.com/allenxuxu/gev","last_synced_at":"2025-05-15T05:07:32.729Z","repository":{"id":41293871,"uuid":"205671865","full_name":"Allenxuxu/gev","owner":"Allenxuxu","description":"🚀Gev is a lightweight, fast non-blocking TCP network library / websocket server based on Reactor mode. Support custom protocols to quickly and easily build high-performance servers. ","archived":false,"fork":false,"pushed_at":"2023-02-25T07:15:08.000Z","size":585,"stargazers_count":1746,"open_issues_count":14,"forks_count":196,"subscribers_count":41,"default_branch":"master","last_synced_at":"2025-05-15T05:07:25.509Z","etag":null,"topics":["epoll","epoll-tcp-server","event-driven","evio","gev","go","golang","gomaxprocs","goroutine","kqueue","network-programming","nonblocking","protocol","reactor","websocket"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Allenxuxu.png","metadata":{"files":{"readme":"README-ZH.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2019-09-01T12:16:18.000Z","updated_at":"2025-05-11T10:03:08.000Z","dependencies_parsed_at":"2023-10-11T01:11:06.663Z","dependency_job_id":"37a32404-9837-482b-9c23-92158837520b","html_url":"https://github.com/Allenxuxu/gev","commit_stats":{"total_commits":237,"total_committers":12,"mean_commits":19.75,"dds":0.07594936708860756,"last_synced_commit":"343acc7a79b3f0ccf57f9bc9ef14e1d5c47c9ce5"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Allenxuxu%2Fgev","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Allenxuxu%2Fgev/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Allenxuxu%2Fgev/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Allenxuxu%2Fgev/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Allenxuxu","download_url":"https://codeload.github.com/Allenxuxu/gev/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254276447,"owners_count":22043867,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["epoll","epoll-tcp-server","event-driven","evio","gev","go","golang","gomaxprocs","goroutine","kqueue","network-programming","nonblocking","protocol","reactor","websocket"],"created_at":"2024-09-24T20:08:22.826Z","updated_at":"2025-05-15T05:07:27.696Z","avatar_url":"https://github.com/Allenxuxu.png","language":"Go","funding_links":["https://www.paypal.me/AllenXuxu"],"categories":[],"sub_categories":[],"readme":"# gev\n\n[![Github Actions](https://github.com/Allenxuxu/gev/workflows/CI/badge.svg)](https://github.com/Allenxuxu/gev/actions)\n[![Go Report Card](https://goreportcard.com/badge/github.com/Allenxuxu/gev)](https://goreportcard.com/report/github.com/Allenxuxu/gev)\n[![Codacy Badge](https://api.codacy.com/project/badge/Grade/a2a55fe9c0c443e198f588a6c8026cd0)](https://www.codacy.com/manual/Allenxuxu/gev?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=Allenxuxu/gev\u0026amp;utm_campaign=Badge_Grade)\n[![GoDoc](https://godoc.org/github.com/Allenxuxu/gev?status.svg)](https://godoc.org/github.com/Allenxuxu/gev)\n[![LICENSE](https://img.shields.io/badge/LICENSE-MIT-blue)](https://github.com/Allenxuxu/gev/blob/master/LICENSE)\n[![Code Size](https://img.shields.io/github/languages/code-size/Allenxuxu/gev.svg?style=flat)](https://img.shields.io/github/languages/code-size/Allenxuxu/gev.svg?style=flat)\n[![Sourcegraph](https://sourcegraph.com/github.com/Allenxuxu/gev/-/badge.svg)](https://sourcegraph.com/github.com/Allenxuxu/gev?badge)\n\n#### 中文 | [English](README.md)\n\n`gev` 是一个轻量、快速的基于 Reactor 模式的非阻塞 TCP 网络库 / websocket server，支持自定义协议，轻松快速搭建高性能服务器。\n\n## 特点\n\n- 基于 epoll 和 kqueue 实现的高性能事件循环\n- 支持多核多线程\n- 动态扩容 Ring Buffer 实现的读写缓冲区\n- 异步读写\n- 自动清理空闲连接\n- SO_REUSEPORT 端口重用支持\n- 支持 WebSocket/Protobuf, 自定义协议\n- 支持定时任务，延时任务\n- 开箱即用的高性能 websocket server\n\n## 网络模型\n\n`gev` 只使用极少的 goroutine, 一个 goroutine 负责监听客户端连接，其他 goroutine （work 协程）负责处理已连接客户端的读写事件，work 协程数量可以配置，默认与运行主机 CPU 数量相同。\n\n\u003cdiv align=center\u003e\n\u003cimg src=\"benchmarks/out/reactor.png\" height=\"300\"/\u003e\n\u003c/div\u003e\n\n## 性能测试\n\n\u003cdetails\u003e\n  \u003csummary\u003e 📈 测试数据 \u003c/summary\u003e\n\n\u003e 测试环境 Ubuntu18.04 | 4 Virtual CPUs | 4.0 GiB\n\n### 吞吐量测试\n\n限制 GOMAXPROCS=1（单线程），1 个 work 协程\n\n![image](benchmarks/out/gev11.png)\n\n限制 GOMAXPROCS=4，4 个 work 协程\n\n![image](benchmarks/out/gev44.png)\n\n### 其他测试\n\n\u003cdetails\u003e\n  \u003csummary\u003e 速度测试 \u003c/summary\u003e\n\n和同类库的简单性能比较, 压测方式与 evio 项目相同。\n\n- gnet\n- eviop\n- evio\n- net (标准库)\n\n限制 GOMAXPROCS=1，1 个 work 协程\n\n![image](benchmarks/out/echo-1c-1loops.png)\n\n限制 GOMAXPROCS=1，4 个 work 协程\n\n![image](benchmarks/out/echo-1c-4loops.png)\n\n限制 GOMAXPROCS=4，4 个 work 协程\n\n![image](benchmarks/out/echo-4c-4loops.png)\n\n\u003c/details\u003e\n\n\u003c/details\u003e\n\n## 安装\n\n```bash\ngo get -u github.com/Allenxuxu/gev\n```\n\n## 快速入门\n\n### echo demo\n\n```go\npackage main\n\nimport (\n\t\"flag\"\n\t\"net/http\"\n\t_ \"net/http/pprof\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/Allenxuxu/gev\"\n\t\"github.com/Allenxuxu/gev/log\"\n\t\"github.com/Allenxuxu/toolkit/sync/atomic\"\n)\n\ntype example struct {\n\tCount atomic.Int64\n}\n\nfunc (s *example) OnConnect(c *gev.Connection) {\n\ts.Count.Add(1)\n\t//log.Println(\" OnConnect ： \", c.PeerAddr())\n}\nfunc (s *example) OnMessage(c *gev.Connection, ctx interface{}, data []byte) (out interface{}) {\n\t//log.Println(\"OnMessage\")\n\tout = data\n\treturn\n}\n\nfunc (s *example) OnClose(c *gev.Connection) {\n\ts.Count.Add(-1)\n\t//log.Println(\"OnClose\")\n}\n\nfunc main() {\n\tgo func() {\n\t\tif err := http.ListenAndServe(\":6060\", nil); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}()\n\n\thandler := new(example)\n\tvar port int\n\tvar loops int\n\n\tflag.IntVar(\u0026port, \"port\", 1833, \"server port\")\n\tflag.IntVar(\u0026loops, \"loops\", -1, \"num loops\")\n\tflag.Parse()\n\n\ts, err := gev.NewServer(handler,\n\t\tgev.Network(\"tcp\"),\n\t\tgev.Address(\":\"+strconv.Itoa(port)),\n\t\tgev.NumLoops(loops),\n\t\tgev.MetricsServer(\"\", \":9091\"),\n\t)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\ts.RunEvery(time.Second*2, func() {\n\t\tlog.Info(\"connections :\", handler.Count.Get())\n\t})\n\n\ts.Start()\n}\n```\n\nHandler 是一个接口，我们的程序必须实现它。\n\n```go\ntype CallBack interface {\n\tOnMessage(c *Connection, ctx interface{}, data []byte) interface{}\n\tOnClose(c *Connection)\n}\n\ntype Handler interface {\n\tCallBack\n\tOnConnect(c *Connection)\n}\n```\n\nOnMessage 会在一个完整的数据帧到来时被回调。用户可此可以拿到数据，处理业务逻辑，并返回需要发送的数据。\n\n在有数据到来时，gev 并非立刻回调 OnMessage ，而是会先回调一个 UnPacket 函数。大概执行逻辑如下：\n\n```go\nctx, receivedData := c.protocol.UnPacket(c, buffer)\nfor ctx != nil || len(receivedData) != 0 {\n\tsendData := c.callBack.OnMessage(c, ctx, receivedData)\n\tif sendData != nil {\n\t\t*tmpBuffer = append(*tmpBuffer, c.protocol.Packet(c, sendData)...)\n\t}\n\n\tctx, receivedData = c.protocol.UnPacket(c, buffer)\n}\n```\n\n![protocol](benchmarks/out/protocol.png)\n\nUnPacket 函数中会查看 ringbuffer 中的数据是否是一个完整的数据帧，如果是则会将数据拆包并返回 payload 数据；如果还不是一个完整的数据帧，则直接返回。\n\nUnPacket 的返回值 `(interface{}, []byte)` 会作为 OnMessage 的入参 `ctx interface{}, data []byte` 被传入并回调。`ctx` 被设计用来传递在 UnPacket 函数中解析数据帧时生成的特殊信息（复杂的数据帧协议会需要），`data` 则是用来传递 payload 数据。\n\n```go\ntype Protocol interface {\n\tUnPacket(c *Connection, buffer *ringbuffer.RingBuffer) (interface{}, []byte)\n\tPacket(c *Connection, data interface{}) []byte\n}\n\n\ntype DefaultProtocol struct{}\n\nfunc (d *DefaultProtocol) UnPacket(c *Connection, buffer *ringbuffer.RingBuffer) (interface{}, []byte) {\n\ts, e := buffer.PeekAll()\n\tif len(e) \u003e 0 {\n\t\tsize := len(s) + len(e)\n\t\tuserBuffer := *c.UserBuffer()\n\t\tif size \u003e cap(userBuffer) {\n\t\t\tuserBuffer = make([]byte, size)\n\t\t\t*c.UserBuffer() = userBuffer\n\t\t}\n\n\t\tcopy(userBuffer, s)\n\t\tcopy(userBuffer[len(s):], e)\n\n\t\treturn nil, userBuffer\n\t} else {\n\t\tbuffer.RetrieveAll()\n\n\t\treturn nil, s\n\t}\n}\n\nfunc (d *DefaultProtocol) Packet(c *Connection, data interface{}) []byte {\n\treturn data.([]byte)\n}\n```\n\n如上，`gev` 提供一个默认的 Protocol 实现，会将接受缓冲区中( ringbuffer )的所有数据取出。\n在实际使用中，通常会有自己的数据帧协议，`gev` 可以以插件的形式来设置：在创建 Server 的时候通过可变参数设置。\n\n```go\ns, err := gev.NewServer(handler,gev.Protocol(\u0026ExampleProtocol{}))\n```\n\n更详细的使用方式可以参考示例：[自定义协议](example/protocol)\n\nConnection 还提供 Send 方法来发送数据。Send 并不会立刻发送数据，而是先添加到 event loop 的任务队列中，然后唤醒 event loop 去发送。\n\n更详细的使用方式可以参考示例：[服务端定时推送](example/pushmessage/main.go)\n\n```go\nfunc (c *Connection) Send(data interface{}, opts ...ConnectionOption) error\n```\n\nConnection ShutdownWrite 会关闭写端，从而断开连接。\n\n更详细的使用方式可以参考示例：[限制最大连接数](example/maxconnection/main.go)\n\n```go\nfunc (c *Connection) ShutdownWrite() error\n```\n\n[RingBuffer](https://github.com/Allenxuxu/ringbuffer) 是一个动态扩容的循环缓冲区实现。\n\n### WebSocket 支持\n\nWebSocket 协议构建在 TCP 协议之上的，所以 `gev` 无需内置它，而是通过插件的形式提供支持，在 `plugins/websocket` 目录。\n\n\u003cdetails\u003e\n  \u003csummary\u003e code \u003c/summary\u003e\n\n```go\ntype Protocol struct {\n\tupgrade *ws.Upgrader\n}\n\nfunc New(u *ws.Upgrader) *Protocol {\n\treturn \u0026Protocol{upgrade: u}\n}\n\nfunc (p *Protocol) UnPacket(c *connection.Connection, buffer *ringbuffer.RingBuffer) (ctx interface{}, out []byte) {\n\tupgraded := c.Context()\n\tif upgraded == nil {\n\t\tvar err error\n\t\tout, _, err = p.upgrade.Upgrade(buffer)\n\t\tif err != nil {\n\t\t\tlog.Println(\"Websocket Upgrade :\", err)\n\t\t\treturn\n\t\t}\n\t\tc.SetContext(true)\n\t} else {\n\t\theader, err := ws.VirtualReadHeader(buffer)\n\t\tif err != nil {\n\t\t\tlog.Println(err)\n\t\t\treturn\n\t\t}\n\t\tif buffer.VirtualLength() \u003e= int(header.Length) {\n\t\t\tbuffer.VirtualFlush()\n\n\t\t\tpayload := make([]byte, int(header.Length))\n\t\t\t_, _ = buffer.Read(payload)\n\n\t\t\tif header.Masked {\n\t\t\t\tws.Cipher(payload, header.Mask, 0)\n\t\t\t}\n\n\t\t\tctx = \u0026header\n\t\t\tout = payload\n\t\t} else {\n\t\t\tbuffer.VirtualRevert()\n\t\t}\n\t}\n\treturn\n}\n\nfunc (p *Protocol) Packet(c *connection.Connection, data []byte) []byte {\n\treturn data\n}\n```\n\n\u003c/details\u003e\n\n详细实现可以插件实现查看 [源码](plugins/websocket)，使用方式可以查看 websocket [示例](example/websocket)\n\n## 示例\n\n- [Echo Server](example/echo)\n- [主动断开空闲连接](example/idleconnection)\n- [限制最大连接数](example/maxconnection)\n- [服务端定时推送](example/pushmessage)\n- [WebSocket](example/websocket)\n- [Protobuf](example/protobuf)\n- [...](example)\n\n## 请我喝杯咖啡\n\n\u003cimg src=\"https://raw.githubusercontent.com/Allenxuxu/doc/master/alipay.jpeg\" width = \"200\" height=\"300\" /\u003e\n\u003cimg src=\"https://raw.githubusercontent.com/Allenxuxu/doc/master/wechat.jpeg\" width = \"200\" height=\"300\" /\u003e\n\n**Paypal**: [Paypal/AllenXuxu](https://www.paypal.me/AllenXuxu)\n\n## 致谢\n\n感谢 JetBrains 提供的免费开源 License\n\n\u003ca href=\"https://www.jetbrains.com/?from=gev\" target=\"_blank\"\u003e\n\t\u003cimg src=\"https://raw.githubusercontent.com/Allenxuxu/doc/master/jetbrains.png\" width = \"260\" align=center /\u003e\n\u003c/a\u003e\n\n## 参考\n\n本项目受 evio 启发，参考 muduo 实现。\n\n- [evio](https://github.com/tidwall/evio)\n- [muduo](https://github.com/chenshuo/muduo)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fallenxuxu%2Fgev","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fallenxuxu%2Fgev","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fallenxuxu%2Fgev/lists"}