{"id":27185054,"url":"https://github.com/threefoldtech/zbus","last_synced_at":"2025-06-26T13:36:43.124Z","repository":{"id":40687544,"uuid":"176695812","full_name":"threefoldtech/zbus","owner":"threefoldtech","description":"Simple message bus","archived":false,"fork":false,"pushed_at":"2023-10-12T02:37:03.000Z","size":1599,"stargazers_count":4,"open_issues_count":8,"forks_count":0,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-06-17T21:51:09.993Z","etag":null,"topics":["go","message-broker","system"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/threefoldtech.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":"2019-03-20T09:09:45.000Z","updated_at":"2023-02-25T00:31:23.000Z","dependencies_parsed_at":"2023-10-12T12:19:33.378Z","dependency_job_id":null,"html_url":"https://github.com/threefoldtech/zbus","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/threefoldtech/zbus","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threefoldtech%2Fzbus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threefoldtech%2Fzbus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threefoldtech%2Fzbus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threefoldtech%2Fzbus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/threefoldtech","download_url":"https://codeload.github.com/threefoldtech/zbus/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/threefoldtech%2Fzbus/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262077152,"owners_count":23255129,"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":["go","message-broker","system"],"created_at":"2025-04-09T17:10:16.539Z","updated_at":"2025-06-26T13:36:43.099Z","avatar_url":"https://github.com/threefoldtech.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"![travis](https://travis-ci.com/threefoldtech/zbus.svg?branch=master) [![codecov](https://codecov.io/gh/threefoldtech/zbus/branch/master/graph/badge.svg)](https://codecov.io/gh/threefoldtech/zbus) [![GoDoc](https://godoc.org/github.com/threefoldtech/zbus?status.svg)](https://godoc.org/github.com/threefoldtech/zbus)\n\n# Motivation\nA light weight bus replacement for **local** inter-process communication. The main goal is to decouple separate\ncomponents from each other, by using a light-weight message bus (current implemented redis), to queue and\nsend message to the separate component that can serve it.\n\nThe keyword here is **local** zbus is not intended to be used over the network because it's intended ONLY for local inter process\ncommunication. Allows local processes to talk to each other.\n\nTo keep it light, the ZBUS does not do any authentication or permissions\n\nA public API then can expose a public API then internally make calls to other local components.\n\n\n# Overview\n- Each `module` has a name, a single `module` can host one or more `objects`\n- While it's not required an `object` can implement one or more `interfaces`\n- Each object must have a name and a version\n- `interfaces` are mainly used to generate client `stubs`, but it's totally fine to not have one. In that case the client\n  must know precisely the method `signature` (name and arguments number and types); same for the `return` value.\n- A consumer who has connection to the message broker can call methods on the remote objects, knowing only the `module` name, `object` name, method `name`, and `argument` list. The current implementation of the client supports only synchronous calls. In that matter it's similar to RPC.\n- A consumer of the component can use a `stub` to abstract the calls to the remote module\n- Support for events where clients can listen to announcements from different components\n\n`zbus` was built with `golang` only in mind so `zbusc` was only built to generate client stubs for `golang` from the service interface witch is `golang` interface! But the underlying protocol itself is simple enough to implement client and servers in other languages. For example `rust` hence we also have [rbus](https://github.com/threefoldtech/rbus) which is 100% compatible with the go implementation. Hence it's possible for services built in rust to be called from golang and the vise versa.\n\n\n# Installation\nInstalling the zbus compiler `zbusc`\n\n```bash\ngo install github.com/threefoldtech/zbus/zbusc\n```\n\nThe `zbusc` is only needed to generate `stub` code.\n\n# Walk-through\nLet's build a service from scratch say a `calculator` service.\nFirst we create a project and init it\n```bash\nmkdir calc\ncd calc\ngo mod init github.com/example/calc\ngo get github.com/threefoldtech/zbus\n```\n\nthis initialize the directory to be a go project (module)\n\u003e please use a proper module name when doing `mod init`\n\n\u003e All new files are created under the calc directory\nlet's create the service file\ncreate new file `api.go`\n```go\npackage calc\n\ntype Calculator interface {\n\tAdd(a, b float64) float64\n\tMultiply(n ...float64) float64\n\tDivide(a, b float64) (float64, error)\n}\n```\n\nwhile it's very simple, it shows that implementation supports variadic arguments, and also returning multiple arguments\n\n\u003e zbus can also return channels for event streams but let's leave that for another example\n\nThe next step is simple is to actually implement this interface and start our `zbus` server\n\nwe create file `server/server.go`\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/example/calc\"\n\t\"github.com/threefoldtech/zbus\"\n)\n\n// the service implementation structure\ntype myCalculator struct{}\n\n// this is just to verify that that the myCalculator actually\n// implements calc.Calculator interface ! if the interface\n// changes this should give a compile error\nvar _ calc.Calculator = (*myCalculator)(nil)\n\nfunc (c *myCalculator) Add(a, b float64) float64 {\n\treturn a + b\n}\n\nfunc (c *myCalculator) Multiply(n ...float64) float64 {\n\tvar v float64\n\tif len(n) \u003e 0 {\n\t\tv = n[0]\n\t}\n\n\tfor _, x := range n[1:] {\n\t\tv *= x\n\t}\n\n\treturn v\n}\n\nfunc (c *myCalculator) Divide(a, b float64) (float64, error) {\n\tif b == 0 {\n\t\treturn 0, fmt.Errorf(\"cannot divide by zero\")\n\t}\n\n\treturn a / b, nil\n}\n\nfunc app() error {\n\tconst module = \"calc\"\n\tconst address = \"tcp://localhost:6379\"\n\tserver, err := zbus.NewRedisServer(module, address, 10)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\timpl := \u0026myCalculator{}\n\n\t// a single module (in this case calc) can serve multiple objects\n\t// also it can serve multiple objects with same name but different version\n\t// hence it's important when u register an object to give it a name and a version\n\t// it's not possible to register the same name@version twice.\n\tserver.Register(zbus.ObjectIDFromString(\"calculator@1.0.0\"), impl)\n\n\t// once you are done registering ALL your objects it's time to start your server\n\n\treturn server.Run(context.Background())\n}\n\nfunc main() {\n\tif err := app(); err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"%v\", err)\n\t\tos.Exit(1)\n\t}\n}\n```\n\nYou can now run the zbus server simply by doing\n```bash\ngo run server/server.go\n```\n\n## Generating a stub client and using it\nWhile you still inside the `calc` project create a stubs directory\n```bash\nmkdir stubs\n```\nThen run the following command\n```bash\nzbusc -module calc -name calculator -version 1.0.0 -package stubs github.com/example/calc+Calculator stubs/calculator_stub.go\n```\nThe command line is simple it takes the module name, object name, object version, and the package name to use in the generated code. The it needs to know which interface to generate code for (in that case it's the `Calculator` interface) but it requires to know the full path hence it's provided as `github.com/example/calc+Calculator`. Finally where to output the generated code. We output the generated stub to `stubs/calculator_stubs.go`\n\nTo avoid typing this command every time you change the interface or you add new methods, instead edit the `api.go` file by adding this line above the Calculator interface\n\n```go\n\n//go:generate mkdir -p stubs\n//go:generate zbusc -module calc -name calculator -version 1.0.0 -package stubs github.com/example/calc+Calculator stubs/calculator_stub.go\n\ntype Calculator interface {\n```\n\nNow each time you want to regenerate the stubs do\n```bash\ngo generate ./...\n```\n\n### Testing the generated stub\nwhile under the `calc` project create a client directory\n```bash\nmkdir client\n```\nthen create file `client/client.go`\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/example/calc/stubs\"\n\t\"github.com/threefoldtech/zbus\"\n)\n\nfunc app(ctx context.Context) error {\n\tconst address = \"tcp://localhost:6379\"\n\n\tclient, err := zbus.NewRedisClient(address)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tstub := stubs.NewCalculatorStub(client)\n\n\t// calling the add function\n\tfmt.Printf(\"adding 2 numbers: %f \\n\", stub.Add(ctx, 20, 30))\n\n\t_, err = stub.Divide(ctx, 100, 0)\n\tif err != nil {\n\t\tfmt.Println(\"got error: \", err)\n\t}\n\n\treturn nil\n}\n\nfunc main() {\n\tif err := app(context.Background()); err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"%v\", err)\n\t\tos.Exit(1)\n\t}\n}\n```\n\nYou notice the following:\n- You create a generic low level client to zbus, then you can use that to create as many stubs (to other services and modules) as you want\n- The client does not have to know about the interface, just the stub and then it can do calls normally like any other service.\n- Generated stubs calls always take ctx as first argument which allows you to control timeouts and cancellation if call is taking to long (service down?!)\n\nTo test this first to this\n```bash\ngo run server/server.go\n```\n\nThen in another terminal do\n```bash\ngo run client/client.go\n```\nthis should output this\n```\nadding 2 numbers: 50.000000\ngot error:  cannot divide by zero\n```\n\n# Specs\nPlease check [specs](specs/readme.md) here\n\n# Usage\nIt's very simple, check the [examples](examples)\n\nThe [api.go](examples/server/api/api.go) have some `go generate` lines that runs the zbusc tool\n\n# Projects using zbus\n- [ZOS](https://github.com/threefoldtech/zos)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthreefoldtech%2Fzbus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthreefoldtech%2Fzbus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthreefoldtech%2Fzbus/lists"}