{"id":21878985,"url":"https://github.com/nitro/ringman","last_synced_at":"2025-04-15T03:06:57.697Z","repository":{"id":57608275,"uuid":"88056602","full_name":"Nitro/ringman","owner":"Nitro","description":"Consistent hash ring with automatic cluster management","archived":false,"fork":false,"pushed_at":"2019-05-10T10:14:41.000Z","size":37,"stargazers_count":22,"open_issues_count":1,"forks_count":3,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-08-12T10:14:32.413Z","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/Nitro.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}},"created_at":"2017-04-12T13:44:20.000Z","updated_at":"2024-03-02T03:40:31.000Z","dependencies_parsed_at":"2022-09-03T10:21:45.062Z","dependency_job_id":null,"html_url":"https://github.com/Nitro/ringman","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nitro%2Fringman","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nitro%2Fringman/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nitro%2Fringman/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nitro%2Fringman/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Nitro","download_url":"https://codeload.github.com/Nitro/ringman/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226881023,"owners_count":17696894,"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-11-28T08:14:18.060Z","updated_at":"2024-11-28T08:14:18.773Z","avatar_url":"https://github.com/Nitro.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"Ringman\n=======\n\nThis is a consistent hash ring implementation backed by either [our fork of\nHashicorp's Memberlist library](https://github.com/Nitro/memberlist), or\n[Sidecar service discovery platform](https://github.com/Nitro/sidecar), and the\n[hashring](https://github.com/serialx/hashring) library.\n\nIt sets up an automatic consistent hash ring across multiple nodes. The nodes\nare discovered and health validated either over Memberlist's implementation of\nthe SWIM gossip protocol or via Sidecar. It manages adding and removing nodes\nfrom the ring based on events from Memberlist or Sidecar.\n\nIn addition, the package provides some HTTP handlers and a Mux that can be\nmounted anywhere in your application if you want to expose the hashring.\n\nThe Problem This Solves\n-----------------------\n\nIf you're building a cluster of nodes and need to distribute data amongst them,\nyou have a bunch of choices. One good choice is to build a consistent hash and\nback it with a list of cluster nodes. When distributing work/data/etc you can\nthen do a lookup against the hash to identify which node(s) should handle the\nwork. But now you need to maintain the list of nodes, and identify when they\ncome and go from the cluster, and other details of clustering that don't really\nhave much to do with the application you're trying to write.  Sometimes a\nspecialized load balancer can solve this problem for you. Sometimes you need to\nwrite your own mechanism.\n\nThis is where ringman comes in. It maintains a consistent hash and the\nclustering mechanism underneath it via one of two different provided backends.\nIt also offers an optional queryable web API so you or other services can\ninspect the state of the cluster.\n\nIt takes one line of code to set up the cluster, and one line of code to\nquery it! The cluster currently expects equal weighting for all\nnodes.\n\nMemberlist Ring\n---------------\n\nIf you just want to get information from the ring you can query it in your code\ndirectly like:\n\n```go\nring, err := ringman.NewDefaultMemberlistRing([]string{\"127.0.0.1\"}, \"8000\")\nif err != nil {\n    log.Fatalf(\"Unable to establish memberlist ring: %s\", err)\n}\n\nprintln(ring.Manager().GetNode(\"mykey\"))\n```\n\nThe following would set up a Memberlist-backed consistent hash ring and serve\nthe node information over HTTP:\n\n```go\nring, err := ringman.NewDefaultMemberlistRing([]string{\"127.0.0.1\"}, \"8000\")\nif err != nil {\n    log.Fatalf(\"Unable to establish memberlist ring: %s\", err)\n}\n\nhttp.HandleFunc(\"/your_stuff\", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(\"OK\")) })\nhttp.Handle(\"/hashring/\", http.StripPrefix(\"/hashring\", ring.HttpMux()))\nerr = http.ListenAndServe(\":8080\", http.DefaultServeMux)\nif err != nil {\n\tlog.Fatalf(\"Unable to start HTTP server: %s\", err)\n}\n```\n\nWe can then query it with:\n\n```\n$ curl http://localhost:8000/hashring/nodes\n```\n\nWhich will return output like:\n\n```\n[\n  {\n    \"Name\": \"ubuntu\",\n    \"Addr\": \"127.0.0.1\",\n    \"Port\": 7946,\n    \"Meta\": null,\n    \"PMin\": 1,\n    \"PMax\": 4,\n    \"PCur\": 2,\n    \"DMin\": 0,\n    \"DMax\": 0,\n    \"DCur\": 0\n  }\n]\n```\n\nOr we can find the node for a specific key like:\n\n```\n$ curl http://docker1:8000/hashring/nodes/get?key=somekey\n```\n\nWhich returns output like:\n\n```\n{\n  \"Node\": \"ubuntu\",\n  \"Key\": \"somekey\"\n}\n```\n\n### More About Memberlist\nIf you are going to set up the Memberlist ring, it may be helpful to read up on\n[Memberlist](https://github.com/hashicorp/memberlist) and the [SWIM\npaper](https://www.cs.cornell.edu/~asdas/research/dsn02-swim.pdf) that explains\nroughly how the underlying algorithm works.\n\nSidecar Ring\n------------\n\nAn alternate implementation backed by New Relic/Nitro's\n[Sidecar](https://github.com/Nitro/sidecar) is available as well. Underneath\nSidecar also lies Memberlist, but Sidecar provides a lot of features on top of\nit. This implementation of Ringman assumes it will be subscribed to incoming\nSidecar events on the listener port. See the Sidecar\n[README](https://github.com/Nitro/sidecar) for how to set that up. Once that is\nestablished, you can do the following:\n\n```go\nring, err := ringman.NewSidecarRing(\"http://localhost:7777/api/state.json\")\nif err != nil {\n    log.Fatalf(\"Unable to establish sidecar ring: %s\", err)\n}\n\nprintln(ring.Manager().GetNode(\"mykey\"))\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnitro%2Fringman","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnitro%2Fringman","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnitro%2Fringman/lists"}