{"id":26604707,"url":"https://github.com/brickingsoft/rio","last_synced_at":"2026-01-12T05:06:32.840Z","repository":{"id":260102527,"uuid":"878487989","full_name":"brickingsoft/rio","owner":"brickingsoft","description":"IOURING network library for go","archived":false,"fork":false,"pushed_at":"2025-09-01T07:51:43.000Z","size":3121,"stargazers_count":10,"open_issues_count":4,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-12T04:19:59.510Z","etag":null,"topics":["iouring","network","tcp"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brickingsoft.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"security/dialer.go","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-10-25T13:38:31.000Z","updated_at":"2025-11-14T21:36:36.000Z","dependencies_parsed_at":"2024-11-20T14:22:16.278Z","dependency_job_id":"8caad474-76f3-4aa2-bcc7-91c4c328f1b7","html_url":"https://github.com/brickingsoft/rio","commit_stats":null,"previous_names":["brickingsoft/rio"],"tags_count":47,"template":false,"template_full_name":null,"purl":"pkg:github/brickingsoft/rio","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brickingsoft%2Frio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brickingsoft%2Frio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brickingsoft%2Frio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brickingsoft%2Frio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brickingsoft","download_url":"https://codeload.github.com/brickingsoft/rio/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brickingsoft%2Frio/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28335154,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T00:36:25.062Z","status":"online","status_checked_at":"2026-01-12T02:00:08.677Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["iouring","network","tcp"],"created_at":"2025-03-23T20:01:45.126Z","updated_at":"2026-01-12T05:06:32.834Z","avatar_url":"https://github.com/brickingsoft.png","language":"Go","funding_links":[],"categories":["Libraries"],"sub_categories":["Golang"],"readme":"# RIO ([中文](https://github.com/brickingsoft/rio/tree/main/README_zh.md))\n\nAn `AIO` network library based on `IOURING`, without using `CGO`, and following the design pattern of the standard library.\n\nSupported protocols: `TCP`, `UDP`, `UNIX`, `UNIXGRAM` (`IP` is the proxy standard library).\n\n`RIO` is a library that follows the usage pattern of the standard library and can be put into use very conveniently. Therefore, it is not a toy and can replace `NET` at a very low cost.\n\n## NOTE\n* Linux kernel version must be `\u003e= 6.13`.\n* Scenarios that only use `Dial` require `PIN` and `UNPIN` to pin the kernel thread of `IOURING`.\n* `NetworkingMode=mirrored` cannot be enabled in `WSL2`.\n* Since `DIRECT FD` does not support `CLOEXEC`, it is necessary to close all `FD` when the program exits (close all links when both net.Http and fasthttp implement closure).\n\n\n## Features\n* Based on the implementation of `net.Listener`, `net.Conn` and `net.PacketConn`.\n* Use `BATCH` to reduce the overhead of `SYSTEM CALL`.\n* Support `TLS`.\n* Support `MULTISHOT_ACCEPT` `MULTISHOT_RECV` and `MULTISHOT_RECV_FROM`.\n* Support `SEND_ZC` and `SENDMSG_ZC`.\n* Support `NAPI`.\n* Support `PERSIONALITY`.\n* Supports `CURVE` to dynamically adjust the timeout of `WAIT CQE` to fit different scenarios.\n\n\n## [Performances](https://github.com/brickingsoft/rio_examples/tree/main/benchmark) \n\n***TCP*** \n\n\u003cimg src=\"benchmark/benchmark_tcpkali_C50T10s.png\" width=\"336\" height=\"144\" alt=\"echo benchmark\"\u003e\n\u003cimg src=\"benchmark/benchmark_tcpkali_C50R5K.png\" width=\"336\" height=\"144\" alt=\"echo benchmark\"\u003e\n\n***HTTP***\n\n\u003cimg src=\"benchmark/benchmark_k6.png\" width=\"336\" height=\"144\" alt=\"echo benchmark\"\u003e\n\n\n| Endpoint | Platform | IP              | OS                                           | SKU     |\n|----------|----------|-----------------|----------------------------------------------|---------|\n| Client   | WSL2     | 192.168.100.1   | Ubuntu22.04 (6.13.6-microsoft-standard-WSL2) | 4C 16G  |\n| Server   | Hyper-V  | 192.168.100.120 | Ubuntu24.10 (6.13.12-061312-generic)         | 4C 0.5G |\n\n\n***Syscall***\n\n![syscall_rio_sqpoll.png](benchmark/syscall_rio_sqpoll.png)\n\n![syscall_rio_single.png](benchmark/syscall_rio_single.png)\n\n![syscall_net.png](benchmark/syscall_net.png)\n\n| Lib | Proportion | Desc                                                          |\n|-----|------------|---------------------------------------------------------------|\n| RIO | 33% (3%)   | 33% is the single publisher mode, and 3% is the SQ-POLL mode. |\n| NET | 74%        | Reading, writing, Epoll, etc. account for a total of 74%.     | \n\n\n## Usage\n\n```shell\ngo get -u github.com/brickingsoft/rio\n```\n\nFor basic use, replace `net` with `github.com/brickingsoft/rio`.\n```go\n// replace net.Listen() with rio.Listen() \nln, lnErr := rio.Listen(\"tcp\", \":9000\")\n// replace net.Dial() with rio.Dial() \nconn, dialErr := rio.Dial(\"tcp\", \"127.0.0.1:9000\")\n```\n\n### SocketOPT\n\nUse `rio.Conn` to set or get socketopt. \n```go\nrc := conn.(rio.Conn)\nsetErr := rc.SetSocketOptInt(level, name, value)\nvalue, getErr := rc.GetSocketOptInt(level, name)\n```\n\n### TLS\n\nUse the built-in `security` approach.\n```go\n// server(\"github.com/brickingsoft/rio/security\")\nln, _ = security.Listen(\"tcp\", \":9000\", config)\n\n// client(\"github.com/brickingsoft/rio/security\")\nconn, _ = security.Dial(\"tcp\", \"127.0.0.1:9000\", config)\n```\n\n### HTTP\nFor server, use `http.Server.Serve()` replace `Listener`.\n```go\nrio.Preset(\n    aio.WithNAPIBusyPollTimeout(time.Microsecond * 50),\n)\n\nln, lnErr := rio.Listen(\"tcp\", \":9000\")\nif lnErr != nil {\n    panic(lnErr)\n    return\n}\n\nsrv := \u0026http.Server{\n    Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n        w.Header().Set(\"Content-Type\", \"text/html; charset=utf-8\")\n        w.WriteHeader(http.StatusOK)\n        _, _ = w.Write([]byte(\"hello world\"))\n    }),\n}\n\ndone := make(chan struct{}, 1)\ngo func(ln net.Listener, srv *http.Server, done chan\u003c- struct{}) {\n    if srvErr := srv.Serve(ln); srvErr != nil {\n        if errors.Is(srvErr, http.ErrServerClosed) {\n            close(done)\n            return\n        }\n        panic(srvErr)\n        return\n\t}\n\tclose(done)\n}(ln, srv, done)\n\nsignalCh := make(chan os.Signal, 1)\nsignal.Notify(signalCh, syscall.SIGINT, syscall.SIGKILL, syscall.SIGQUIT, syscall.SIGABRT, syscall.SIGTERM)\n\u003c-signalCh\n\nif shutdownErr := srv.Shutdown(context.Background()); shutdownErr != nil {\n    panic(shutdownErr)\n}\n\u003c-done\n```\n\nFor client, reset `http.DefaultTransport`.\n```go\nhttp.DefaultTransport = \u0026http.Transport{\n    Proxy: http.ProxyFromEnvironment,\n    DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n        dialer := rio.Dialer{\n            Timeout:   30 * time.Second,\n            KeepAlive: 30 * time.Second,\n        }\n        return dialer.DialContext(ctx, network, addr)\n    },\n    ForceAttemptHTTP2:     true,\n    MaxIdleConns:          100,\n    IdleConnTimeout:       90 * time.Second,\n    TLSHandshakeTimeout:   10 * time.Second,\n    ExpectContinueTimeout: 1 * time.Second,\n}\n\nresp, getErr := http.Get(\"http://127.0.0.1:9000/\")\n```\n\n### Types\n\n```go\ntcpConn, ok := conn.(*rio.TCPConn)\nudpConn, ok := conn.(*rio.UDPConn)\nunixConn, ok := conn.(*rio.UnixConn)\nrioConn, ok := conn.(rio.Conn)\n```\n\n### Config\n\n`rio.ListenConfig` is similar to `net.ListenConfig` and listens by configuration.\n\n```go\nconfig := rio.ListenConfig{\n    Control:            nil,                    \n    KeepAlive:          0,                       \n    KeepAliveConfig:    net.KeepAliveConfig{},   \n    MultipathTCP:       false,                   \n    ReusePort:          false,                  \n}\nln, lnErr := config.Listen(context.Background(), \"tcp\", \":9000\")\n```\n\n`rio.Dialer` is similar to `net.Dialer` and dials by configuration.\n\n```go\ndialer := rio.Dialer{\n    Timeout:            0,                          \n    Deadline:           time.Time{},                \n    KeepAlive:          0,                          \n    KeepAliveConfig:    net.KeepAliveConfig{},      \n    LocalAddr:          nil,                        \n    FallbackDelay:      0,                           \n    MultipathTCP:       false,                      \n    Control:            nil,                       \n    ControlContext:     nil,                        \n}\nconn, dialErr := dialer.DialContext(context.Background(), \"tcp\", \"127.0.0.1:9000\")\n```\n\n### PIN and UNPIN\n\nBecause `IOURING` has a resource handling step in its setup and shutdown process, and its lifecycle is tied to the user's maximum lifecycle.\n\nTo prevent an instance from shutting down when it shouldn't, its lifecycle can be manually controlled via `PIN` and `UNPIN`, generally for scenarios where there is only `DIAL` or where there is more than one `LISTEN`.\n\n```go\n// Calling before presetting and launching a link\nrio.Pin()\n// Called after all links are closed\nrio.Unpin()\n```\n\n### SQL\nFor postgres example, create a `Dialer` and use the `Connector` to open database.\n\n```go\ntype RIOPQDialer struct{}\n\nfunc (d *RIOPQDialer) Dial(network, address string) (net.Conn, error) {\n\treturn rio.Dial(network, address)\n}\n\nfunc (d *RIOPQDialer) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) {\n\treturn rio.DialTimeout(network, address, timeout)\n}\n```\n\n```go\nconnector, connectorErr := pq.NewConnector(\"{dsn}\")\nif connectorErr != nil {\n    panic(connectorErr)\n}\nconnector.Dialer(\u0026RIOPQDialer{})\ndb := sql.OpenDB(connector)\n```\n\n### CQE Wait Timeout Curve\n\nPredefined `aio.NCurve` `aio.SCurve` and `aio.LCurve`.\n\n| Name   | Desc  | Scenes                                   |  \n|--------|-------|------------------------------------------|\n| NCurve | Nil   | For non-single publishers only           | \n| SCurve | Short | For short links under single publisher   | \n| LCurve | Long  | For long links under multiple publishers | \n\n\n### Preset\n\nCustomize `IOURING` with presets.\n\n```go\nrio.Peset(\n    // Set the size of the IOURING, default is 16384, maximum is 32768.\n    aio.WithEntries(liburing.DefaultEntries),\n    // Set the Flags of the IOURING.\n    // Optimized for single threading by default, how you need to turn on SQPOLL can be set.\n    aio.WithFlags(liburing.IORING_SETUP_SINGLE_ISSUER),\n    // Whether to enable SEND ZERO COPY.\n    // Not turned on by default. Note: Some pressure testing tools cannot detect the return value.\n    aio.WithSendZCEnabled(false),\n    // Whether to disable multishot mode.\n    // Not disabled by default.\n    // Multishots can significantly reduce SQE casts, but will require additional resources such as registering and deregistering BufferAndRing.\n    // Disable multishot mode is typically used in conjunction with enabling SQPOLL to significantly reduce the overhead of SYSCALL.\n    aio.WithMultishotDisabled(false),\n    // Set BufferAndRing config.\n    // Effective in non-prohibited multishot mode.\n    // A BufferAndRing serves only one Fd.\n    // The parameter size is the size of the buffer, which is recommended to be a page size.\n    // The parameter count is the number of buffer nodes in the ring.\n    // The parameter idle timeout is the amount of time to idle before logging out when it is no longer in use.\n    aio.WithBufferAndRingConfig(4096, 32, 5*time.Second),\n    // Set the CQE wait timeout curve.\n    aio.WithWaitCQETimeoutCurve(aio.SCurve),\n    // Set the NAPI.\n    // The minimum unit of timeout time is microsecond, which is not turned on by default.\n    aio.WithNAPIBusyPollTimeout(50*time.Microsecond),\n)\n```\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrickingsoft%2Frio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrickingsoft%2Frio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrickingsoft%2Frio/lists"}