{"id":49273815,"url":"https://github.com/bethibande/messaging","last_synced_at":"2026-04-25T15:04:51.268Z","repository":{"id":319112332,"uuid":"1055603511","full_name":"Bethibande/messaging","owner":"Bethibande","description":"Low-latency in-memory message broker","archived":false,"fork":false,"pushed_at":"2025-10-16T14:22:08.000Z","size":40,"stargazers_count":0,"open_issues_count":3,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-10-17T17:24:19.656Z","etag":null,"topics":["java"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Bethibande.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-12T14:09:28.000Z","updated_at":"2025-09-12T18:27:24.000Z","dependencies_parsed_at":"2025-10-18T05:06:42.444Z","dependency_job_id":"1cd4e578-2a2d-458b-9c0c-afae74285909","html_url":"https://github.com/Bethibande/messaging","commit_stats":null,"previous_names":["bethibande/messaging"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/Bethibande/messaging","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bethibande%2Fmessaging","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bethibande%2Fmessaging/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bethibande%2Fmessaging/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bethibande%2Fmessaging/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Bethibande","download_url":"https://codeload.github.com/Bethibande/messaging/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bethibande%2Fmessaging/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32266009,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-25T09:15:33.318Z","status":"ssl_error","status_checked_at":"2026-04-25T09:15:31.997Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["java"],"created_at":"2026-04-25T15:04:31.186Z","updated_at":"2026-04-25T15:04:51.262Z","avatar_url":"https://github.com/Bethibande.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Messaging\nThis is an implementation of a very simple MQTT like high-throughput, low-latency, in-memory message broker.\nThis router can deliver messages to subscribers with sub-microsecond latency at hundreds of millions of messages per second.\nThe primary factor limiting performance at this point is the JVMs ability to iterate over and invoke the subscribers of each node.\nPerformance may also drastically vary depending on the JVM used.\n\nEach key is broken into individual nodes which are assembled into a tree structure by the router.\nA message must be posted using a concrete key like `entities/user/ID` whereas a subscriber can subscribe to individual topics like `entities/user/ID`.\nSubscribers may also use wildcards in their keys like `entities/user/*` or `entities/*/someValue`.\n\n### Example\n```java\nvoid main() {\n    final MessageRouter router = new MessageRouter();\n    final PreComputedKey key = router.createKey(\"entities\", \"user\");\n    router.subscribe((subscription, actualRoute, message) -\u003e {/* do something */}, key);\n\n    final PreComputedKey postKey = router.createKey(\"entities\", \"user\", \"12345\");\n    router.post(postKey, \"test\");\n}\n```\n\n## Benchmarks\nThese benchmarks are run on an AMD Ryzen 9 5950x with 64 GB of RAM.\nBefore the benchmark, a router with n subscribers is initialized. The subscribers are randomly generated using a hard-coded seed.\nEach subscriber is subscribed to a random topic. Each generated topic is 2 or 3 tokens long. The 2nd and 3rd tokens\nhave a chance of being wildcards.\nThe benchmark then posts messages using a static key to ensure we don't perform any heap allocations during the benchmark.\nWith this setup, we ensure each posted message triggers a large portion of all registered subscribers.\nThere is no optimization for repeated posts to the same key as such we can assume that posting messages to any available topic\nwill be roughly the same in terms of throughput and latency as long as they are not the top-level keys.\nDoing this optimizes CPU cache usage and avoids heap allocations. For a better real-world scenario, it may be necessary\nto run these benchmarks again in the future with randomly chosen keys to ensure we limit CPU cache usage as much as possible.\n\nAll benchmarks are run on GraalVM 24.0.1 and 32 threads unless states otherwise. Depending on the benchmark type different GC settings are used.\nSee the description of each section for more details. GC settings can greatly affect throughput and latency\neven though the benchmarks perform little to no heap allocations after warmup.\n\nThe blow table indicates the number of subscribers along with the number of total generated nodes\nand how many subscribers are hit for each message posted in our benchmark.\n\n| subscribers | nodes | hits  |\n|-------------|-------|-------|\n| 10          | 16    | 1     |\n| 100         | 67    | 15    |\n| 1k          | 463   | 155   |\n| 10k         | 3385  | 1624  |\n| 100k        | 12121 | 16048 |\n\n### Throughput\nThe below tests are all run using the default GC provided by GraalVM. Using ZGC yields ~50% less throughput.\n\n| subscribers | messages per second |\n|-------------|---------------------|\n| 10          | 673.6 million       |\n| 100         | 302 million         |\n| 1k          | 48 million          |\n| 10k         | 3 million           |\n| 100k        | 421.9 thousand      |\n\n\n### Latency\nThe below tests are all run using ZGC as it yields much better and stable latencies.\n\n| subscribers | p0.00   | p0.50    | p0.90    | p0.95    | p0.99    | p0.999   | p0.9999  | p1.00   |\n|-------------|---------|----------|----------|----------|----------|----------|----------|---------|\n| 10          | ?       | 100 ns   | 100 ns   | 100 ns   | 100 ns   | 100 ns   | 3700 ns  | 14.8 ms |\n| 100         | ?       | 200 ns   | 200 ns   | 200 ns   | 200 ns   | 200 ns   | 5000 ns  | 31.5 ms |\n| 1k          | 300 ns  | 700 ns   | 800 ns   | 800 ns   | 800 ns   | 900 ns   | 16.6 µs  | 23.2 ms |\n| 10k         | 5800 ns | 12800 ns | 12992 ns | 12992 ns | 14000 ns | 24800 ns | 129.2 µs | 35.3 ms |\n\nFor peak latencies it's recommended to not run the benchmark on all available CPUs. The following benchmarks are run on 16 instead of 32 threads.\n\n| subscribers | p0.00   | p0.50   | p0.90   | p0.95   | p0.99   | p0.999   | p0.9999 | p1.00    |\n|-------------|---------|---------|---------|---------|---------|----------|---------|----------|\n| 10          | 0 ns    | 0 ns    | 100 ns  | 100 ns  | 100 ns  | 100 ns   | 1200 ns | 108.3 µs |\n| 100         | 0 ns    | 100 ns  | 100 ns  | 100 ns  | 100 ns  | 200 ns   | 2800 ns | 145.2 µs |\n| 1k          | 200 ns  | 400 ns  | 400 ns  | 400 ns  | 400 ns  | 700 ns   | 4896 ns | 1.7 ms   |\n| 10k         | 4096 ns | 4896 ns | 5000 ns | 5096 ns | 8896 ns | 14688 ns | 27.1 µs | 2.7 ms   |\n\n### Conclusion\nThe key takeaway from these benchmarks is that the router is able to deliver messages to subscribers with sub-microsecond latency at hundreds of millions of messages per second under ideal conditions.\nHowever, an increased routing table size, including larger subscriber counts, will diminish the throughput of the router.\nOne key reason for this is the likely contention around L1 and L2 CPU caches. That's why latency improves a lot when running only on thread per core instead of one per processor.\nAt the same time invoking the listeners is quite expensive since they are interface method invocations. Though this likely depends on the JVM used.\nBased on some other benchmarks I've run, I think it is safe to say, that subscriber count usually matters more than node count.\nThe routing table internally is a tree that makes heavy use of hash map lookups, as such node count even per parent node does not matter much — not nearly as much as listener count.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbethibande%2Fmessaging","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbethibande%2Fmessaging","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbethibande%2Fmessaging/lists"}