{"id":13412506,"url":"https://github.com/lesismal/arpc","last_synced_at":"2025-05-14T17:06:21.755Z","repository":{"id":40452906,"uuid":"265228655","full_name":"lesismal/arpc","owner":"lesismal","description":"More effective network communication, two-way calling, notify and broadcast supported.","archived":false,"fork":false,"pushed_at":"2025-02-20T18:06:18.000Z","size":583,"stargazers_count":1025,"open_issues_count":0,"forks_count":77,"subscribers_count":28,"default_branch":"master","last_synced_at":"2025-04-13T10:04:21.094Z","etag":null,"topics":[],"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/lesismal.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":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-05-19T11:30:05.000Z","updated_at":"2025-04-10T03:37:55.000Z","dependencies_parsed_at":"2023-11-20T04:31:09.018Z","dependency_job_id":"ae7159a7-a7ab-4d4e-bd4e-023438ce1802","html_url":"https://github.com/lesismal/arpc","commit_stats":{"total_commits":312,"total_committers":7,"mean_commits":44.57142857142857,"dds":0.3141025641025641,"last_synced_commit":"37c02e7d49b44dd4bf4e0e265ef85ba81c7605c6"},"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lesismal%2Farpc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lesismal%2Farpc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lesismal%2Farpc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lesismal%2Farpc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lesismal","download_url":"https://codeload.github.com/lesismal/arpc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254190396,"owners_count":22029632,"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":[],"created_at":"2024-07-30T20:01:25.492Z","updated_at":"2025-05-14T17:06:21.729Z","avatar_url":"https://github.com/lesismal.png","language":"Go","funding_links":[],"categories":["Go","Distributed Systems","分布式系统","Relational Databases"],"sub_categories":["Search and Analytic Databases","检索及分析资料库","SQL 查询语句构建库","Advanced Console UIs"],"readme":"# ARPC - More Effective Network Communication \n\n[![Slack][1]][2]\n\n[![Mentioned in Awesome Go][3]][4] [![MIT licensed][5]][6] [![Build Status][7]][8] [![Go Report Card][9]][10] [![Coverage Statusd][11]][12]\n\n[1]: https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true\u0026logo=slack\u0026colorB=green\n[2]: https://join.slack.com/t/arpcnbio/shared_invite/zt-1esuz619h-SbKacrYHNdbkNmO9SV~X1A\n[3]: https://awesome.re/mentioned-badge-flat.svg\n[4]: https://github.com/avelino/awesome-go#distributed-systems\n[5]: https://img.shields.io/badge/license-MIT-blue.svg\n[6]: LICENSE\n[7]: https://img.shields.io/github/actions/workflow/status/lesismal/arpc/build_linux.yml?branch=master\u0026style=flat-square\u0026logo=github-actions\n[8]: https://github.com/lesismal/arpc/actions?query=workflow%3build-linux\n[9]: https://goreportcard.com/badge/github.com/lesismal/arpc\n[10]: https://goreportcard.com/report/github.com/lesismal/arpc\n[11]: https://codecov.io/gh/lesismal/arpc/branch/master/graph/badge.svg\n[12]: https://codecov.io/gh/lesismal/arpc\n[13]: https://godoc.org/github.com/lesismal/arpc?status.svg\n[14]: https://godoc.org/github.com/lesismal/arpc\n\n\n\n## Contents\n\n- [ARPC - More Effective Network Communication](#arpc---more-effective-network-communication)\n\t- [Contents](#contents)\n\t- [Features](#features)\n\t- [Performance](#performance)\n\t- [Header Layout](#header-layout)\n\t- [Installation](#installation)\n\t- [Quick Start](#quick-start)\n\t- [API Examples](#api-examples)\n\t\t- [Register Routers](#register-routers)\n\t\t- [Router Middleware](#router-middleware)\n\t\t- [Coder Middleware](#coder-middleware)\n\t\t- [Client Call, CallAsync, Notify](#client-call-callasync-notify)\n\t\t- [Server Call, CallAsync, Notify](#server-call-callasync-notify)\n\t\t- [Broadcast - Notify](#broadcast---notify)\n\t\t- [Async Response](#async-response)\n\t\t- [Handle New Connection](#handle-new-connection)\n\t\t- [Handle Disconnected](#handle-disconnected)\n\t\t- [Handle Client's send queue overstock](#handle-clients-send-queue-overstock)\n\t\t- [Custom Net Protocol](#custom-net-protocol)\n\t\t- [Custom Codec](#custom-codec)\n\t\t- [Custom Logger](#custom-logger)\n\t\t- [Custom operations before conn's recv and send](#custom-operations-before-conns-recv-and-send)\n\t\t- [Custom arpc.Client's Reader by wrapping net.Conn](#custom-arpcclients-reader-by-wrapping-netconn)\n\t\t- [Custom arpc.Client's send queue capacity](#custom-arpcclients-send-queue-capacity)\n\t- [JS Client](#js-client)\n\t- [Web Chat Examples](#web-chat-examples)\n\t- [Pub/Sub Examples](#pubsub-examples)\n\t- [More Examples](#more-examples)\n\n## Features\n- [x] Two-Way Calling\n- [x] Two-Way Notify\n- [x] Sync and Async Calling\n- [x] Sync and Async Response\n- [x] Batch Write | Writev | net.Buffers \n- [x] Broadcast\n- [x] Middleware\n- [x] Pub/Sub\n- [x] [Opentracing](https://github.com/opentracing/opentracing-go)\n\n| Pattern | Interactive Directions       | Description              |\n| ------- | ---------------------------- | ------------------------ |\n| call    | two-way:\u003cbr\u003ec -\u003e s\u003cbr\u003es -\u003e c | request and response     |\n| notify  | two-way:\u003cbr\u003ec -\u003e s\u003cbr\u003es -\u003e c | request without response |\n\n\n## Performance\n\nHere are some thirdparty benchmark including arpc, **although these repos have provide the performance report, but I suggest you run the code yourself and get the real result, other than just believe other people's doc**:\n- [go-rpc-framework-benchmark](https://github.com/micro-svc/go-rpc-framework-benchmark)\n- [rpcx-benchmark](https://github.com/rpcxio/rpcx-benchmark)\n- [kitex-benchmark](https://github.com/cloudwego/kitex-benchmark)\n\n\n## Header Layout\n\n- LittleEndian\n\n| bodyLen | reserved | cmd    | flag    | methodLen | sequence | method          | body                    |\n| ------- | -------- | ------ | ------- | --------- | -------- | --------------- | ----------------------- |\n| 4 bytes | 1 byte   | 1 byte | 1 bytes | 1 bytes   | 8 bytes  | methodLen bytes | bodyLen-methodLen bytes |\n\n\n\n## Installation\n\n1. Get and install arpc\n\n```sh\n$ go get -u github.com/lesismal/arpc\n```\n\n2. Import in your code:\n\n```go\nimport \"github.com/lesismal/arpc\"\n```\n\n\n## Quick Start\n \n- start a [server](https://github.com/lesismal/arpc/blob/master/examples/rpc/server/server.go)\n\n```go\npackage main\n\nimport \"github.com/lesismal/arpc\"\n\nfunc main() {\n\tserver := arpc.NewServer()\n\n\t// register router\n\tserver.Handler.Handle(\"/echo\", func(ctx *arpc.Context) {\n\t\tstr := \"\"\n\t\tif err := ctx.Bind(\u0026str); err == nil {\n\t\t\tctx.Write(str)\n\t\t}\n\t})\n\n\tserver.Run(\"localhost:8888\")\n}\n```\n\n- start a [client](https://github.com/lesismal/arpc/blob/master/examples/rpc/client/client.go)\n\n```go\npackage main\n\nimport (\n\t\"log\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/lesismal/arpc\"\n)\n\nfunc main() {\n\tclient, err := arpc.NewClient(func() (net.Conn, error) {\n\t\treturn net.DialTimeout(\"tcp\", \"localhost:8888\", time.Second*3)\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer client.Stop()\n\n\treq := \"hello\"\n\trsp := \"\"\n\terr = client.Call(\"/echo\", \u0026req, \u0026rsp, time.Second*5)\n\tif err != nil {\n\t\tlog.Fatalf(\"Call failed: %v\", err)\n\t} else {\n\t\tlog.Printf(\"Call Response: \\\"%v\\\"\", rsp)\n\t}\n}\n```\n\n\n\n## API Examples\n\n### Register Routers\n\n```golang\nvar handler arpc.Handler\n\n// package\nhandler = arpc.DefaultHandler\n// server\nhandler = server.Handler\n// client\nhandler = client.Handler\n\n// message would be default handled one by one  in the same conn reader goroutine\nhandler.Handle(\"/route\", func(ctx *arpc.Context) { ... })\nhandler.Handle(\"/route2\", func(ctx *arpc.Context) { ... })\n\n// this make message handled by a new goroutine\nasync := true\nhandler.Handle(\"/asyncResponse\", func(ctx *arpc.Context) { ... }, async)\n```\n\n### Router Middleware\n\nSee [router middleware](https://github.com/lesismal/arpc/tree/master/extension/middleware/router), it's easy to implement middlewares yourself\n\n```golang\nimport \"github.com/lesismal/arpc/extension/middleware/router\"\n\nvar handler arpc.Handler\n\n// package\nhandler = arpc.DefaultHandler\n// server\nhandler = server.Handler\n// client\nhandler = client.Handler\n\nhandler.Use(router.Recover())\nhandler.Use(router.Logger())\nhandler.Use(func(ctx *arpc.Context) { ... })\nhandler.Handle(\"/echo\", func(ctx *arpc.Context) { ... })\nhandler.Use(func(ctx *arpc.Context) { ... })\n```\n\n\n### Coder Middleware\n\n- Coder Middleware is used for converting a message data to your designed format, e.g encrypt/decrypt and compress/uncompress\n\n```golang\nimport \"github.com/lesismal/arpc/extension/middleware/coder/gzip\"\n\nvar handler arpc.Handler\n\n// package\nhandler = arpc.DefaultHandler\n// server\nhandler = server.Handler\n// client\nhandler = client.Handler\n\nhandler.UseCoder(gzip.New())\nhandler.Handle(\"/echo\", func(ctx *arpc.Context) { ... })\n```\n\n\n\n### Client Call, CallAsync, Notify\n\n1. Call (Block, with timeout/context)\n\n```golang\nrequest := \u0026Echo{...}\nresponse := \u0026Echo{}\ntimeout := time.Second*5\nerr := client.Call(\"/call/echo\", request, response, timeout)\n// ctx, cancel := context.WithTimeout(context.Background(), time.Second)\n// defer cancel()\n// err := client.CallWith(ctx, \"/call/echo\", request, response)\n```\n\n2. CallAsync (Nonblock, with callback and timeout/context)\n\n```golang\nrequest := \u0026Echo{...}\n\ntimeout := time.Second*5\nerr := client.CallAsync(\"/call/echo\", request, func(ctx *arpc.Context) {\n\tresponse := \u0026Echo{}\n\tctx.Bind(response)\n\t...\t\n}, timeout)\n```\n\n3. Notify (same as CallAsync with timeout/context, without callback)\n\n```golang\ndata := \u0026Notify{...}\nclient.Notify(\"/notify\", data, time.Second)\n// ctx, cancel := context.WithTimeout(context.Background(), time.Second)\n// defer cancel()\n// client.NotifyWith(ctx, \"/notify\", data)\n```\n\n### Server Call, CallAsync, Notify\n\n1. Get client and keep it in your application\n\n```golang\nvar client *arpc.Client\nserver.Handler.Handle(\"/route\", func(ctx *arpc.Context) {\n\tclient = ctx.Client\n\t// release client\n\tclient.OnDisconnected(func(c *arpc.Client){\n\t\tclient = nil\n\t})\n})\n\ngo func() {\n\tfor {\n\t\ttime.Sleep(time.Second)\n\t\tif client != nil {\n\t\t\tclient.Call(...)\n\t\t\tclient.CallAsync(...)\n\t\t\tclient.Notify(...)\n\t\t}\n\t}\n}()\n```\n\n2. Then Call/CallAsync/Notify\n\n- [See Previous](#client-call-callasync-notify)\n\n### Broadcast - Notify\n\n- for more details:\t[**server**](https://github.com/lesismal/arpc/blob/master/examples/broadcast/server/server.go) [**client**](https://github.com/lesismal/arpc/blob/master/examples/broadcast/client/client.go)\n\n```golang\nvar mux = sync.RWMutex{}\nvar clientMap = make(map[*arpc.Client]struct{})\n\nfunc broadcast() {\n\tvar svr *arpc.Server = ... \n\tmsg := svr.NewMessage(arpc.CmdNotify, \"/broadcast\", fmt.Sprintf(\"broadcast msg %d\", i))\n\tmux.RLock()\n\tfor client := range clientMap {\n\t\tclient.PushMsg(msg, arpc.TimeZero)\n\t}\n\tmux.RUnlock()\n}\n```\n\n### Async Response\n\n```golang\nvar handler arpc.Handler\n\n// package\nhandler = arpc.DefaultHandler\n// server\nhandler = server.Handler\n// client\nhandler = client.Handler\n\nasyncResponse := true // default is true, or set false\nhandler.Handle(\"/echo\", func(ctx *arpc.Context) {\n\treq := ...\n\terr := ctx.Bind(req)\n\tif err == nil {\n\t\tctx.Write(data)\n\t}\n}, asyncResponse)\n```\n\n\n### Handle New Connection\n\n```golang\n// package\narpc.DefaultHandler.HandleConnected(func(c *arpc.Client) {\n\t...\n})\n\n// server\nsvr := arpc.NewServer()\nsvr.Handler.HandleConnected(func(c *arpc.Client) {\n\t...\n})\n\n// client\nclient, err := arpc.NewClient(...)\nclient.Handler.HandleConnected(func(c *arpc.Client) {\n\t...\n})\n```\n\n### Handle Disconnected\n\n```golang\n// package\narpc.DefaultHandler.HandleDisconnected(func(c *arpc.Client) {\n\t...\n})\n\n// server\nsvr := arpc.NewServer()\nsvr.Handler.HandleDisconnected(func(c *arpc.Client) {\n\t...\n})\n\n// client\nclient, err := arpc.NewClient(...)\nclient.Handler.HandleDisconnected(func(c *arpc.Client) {\n\t...\n})\n```\n\n### Handle Client's send queue overstock\n\n```golang\n// package\narpc.DefaultHandler.HandleOverstock(func(c *arpc.Client) {\n\t...\n})\n\n// server\nsvr := arpc.NewServer()\nsvr.Handler.HandleOverstock(func(c *arpc.Client) {\n\t...\n})\n\n// client\nclient, err := arpc.NewClient(...)\nclient.Handler.HandleOverstock(func(c *arpc.Client) {\n\t...\n})\n```\n\n### Custom Net Protocol\n\n```golang\n// server\nvar ln net.Listener = ...\nsvr := arpc.NewServer()\nsvr.Serve(ln)\n\n// client\ndialer := func() (net.Conn, error) { \n\treturn ... \n}\nclient, err := arpc.NewClient(dialer)\n```\n \n### Custom Codec\n\n```golang\nimport \"github.com/lesismal/arpc/codec\"\n\nvar codec arpc.Codec = ...\n\n// package\ncodec.Defaultcodec = codec\n\n// server\nsvr := arpc.NewServer()\nsvr.Codec = codec\n\n// client\nclient, err := arpc.NewClient(...)\nclient.Codec = codec\n```\n\n### Custom Logger\n\n```golang\nimport \"github.com/lesismal/arpc/log\"\n\nvar logger arpc.Logger = ...\nlog.SetLogger(logger) // log.DefaultLogger = logger\n``` \n\n### Custom operations before conn's recv and send\n\n```golang\narpc.DefaultHandler.BeforeRecv(func(conn net.Conn) error) {\n\t// ...\n})\n\narpc.DefaultHandler.BeforeSend(func(conn net.Conn) error) {\n\t// ...\n})\n```\n\n### Custom arpc.Client's Reader by wrapping net.Conn \n\n```golang\narpc.DefaultHandler.SetReaderWrapper(func(conn net.Conn) io.Reader) {\n\t// ...\n})\n```\n\n### Custom arpc.Client's send queue capacity \n\n```golang\narpc.DefaultHandler.SetSendQueueSize(4096)\n```\n\n## JS Client \n\n- See [arpc.js](https://github.com/lesismal/arpc/blob/master/extension/jsclient/arpc.js)\n\n## Web Chat Examples\n\n- See [webchat](https://github.com/lesismal/arpc/tree/master/examples/webchat)\n\n\n## Pub/Sub Examples\n\n- start a server\n```golang\nimport \"github.com/lesismal/arpc/extension/pubsub\"\n\nvar (\n\taddress = \"localhost:8888\"\n\n\tpassword = \"123qwe\"\n\n\ttopicName = \"Broadcast\"\n)\n\nfunc main() {\n\ts := pubsub.NewServer()\n\ts.Password = password\n\n\t// server publish to all clients\n\tgo func() {\n\t\tfor i := 0; true; i++ {\n\t\t\ttime.Sleep(time.Second)\n\t\t\ts.Publish(topicName, fmt.Sprintf(\"message from server %v\", i))\n\t\t}\n\t}()\n\n\ts.Run(address)\n}\n```\n\n- start a subscribe client\n```golang\nimport \"github.com/lesismal/arpc/log\"\nimport \"github.com/lesismal/arpc/extension/pubsub\"\n\nvar (\n\taddress = \"localhost:8888\"\n\n\tpassword = \"123qwe\"\n\n\ttopicName = \"Broadcast\"\n)\n\nfunc onTopic(topic *pubsub.Topic) {\n\tlog.Info(\"[OnTopic] [%v] \\\"%v\\\", [%v]\",\n\t\ttopic.Name,\n\t\tstring(topic.Data),\n\t\ttime.Unix(topic.Timestamp/1000000000, topic.Timestamp%1000000000).Format(\"2006-01-02 15:04:05.000\"))\n}\n\nfunc main() {\n\tclient, err := pubsub.NewClient(func() (net.Conn, error) {\n\t\treturn net.DialTimeout(\"tcp\", address, time.Second*3)\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tclient.Password = password\n\n\t// authentication\n\terr = client.Authenticate()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// subscribe topic\n\tif err := client.Subscribe(topicName, onTopic, time.Second); err != nil {\n\t\tpanic(err)\n\t}\n\n\t\u003c-make(chan int)\n}\n```\n\n- start a publish client\n```golang\nimport \"github.com/lesismal/arpc/extension/pubsub\"\n\nvar (\n\taddress = \"localhost:8888\"\n\n\tpassword = \"123qwe\"\n\n\ttopicName = \"Broadcast\"\n)\n\nfunc main() {\n\tclient, err := pubsub.NewClient(func() (net.Conn, error) {\n\t\treturn net.DialTimeout(\"tcp\", address, time.Second*3)\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tclient.Password = password\n\n\t// authentication\n\terr = client.Authenticate()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfor i := 0; true; i++ {\n\t\tif i%5 == 0 {\n\t\t\t// publish msg to all clients\n\t\t\tclient.Publish(topicName, fmt.Sprintf(\"message from client %d\", i), time.Second)\n\t\t} else {\n\t\t\t// publish msg to only one client\n\t\t\tclient.PublishToOne(topicName, fmt.Sprintf(\"message from client %d\", i), time.Second)\n\t\t}\n\t\ttime.Sleep(time.Second)\n\t}\n}\n```\n\n\n## More Examples\n\n- See [examples](https://github.com/lesismal/arpc/tree/master/examples)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flesismal%2Farpc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flesismal%2Farpc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flesismal%2Farpc/lists"}