{"id":20136978,"url":"https://github.com/txix-open/grmq","last_synced_at":"2025-04-09T17:44:15.655Z","repository":{"id":45051798,"uuid":"439859008","full_name":"txix-open/grmq","owner":"txix-open","description":"High abstraction wrapper for Golang Rabbit MQ Client","archived":false,"fork":false,"pushed_at":"2025-01-15T15:08:37.000Z","size":99,"stargazers_count":6,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-23T19:45:20.505Z","etag":null,"topics":["consumer","dlq","go","rabbitmq","rabbitmq-client","reconnect"],"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/txix-open.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2021-12-19T12:35:17.000Z","updated_at":"2025-01-15T15:02:48.000Z","dependencies_parsed_at":"2024-04-01T17:44:52.576Z","dependency_job_id":null,"html_url":"https://github.com/txix-open/grmq","commit_stats":null,"previous_names":["integration-system/gmrq","txix-open/grmq","integration-system/grmq"],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/txix-open%2Fgrmq","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/txix-open%2Fgrmq/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/txix-open%2Fgrmq/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/txix-open%2Fgrmq/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/txix-open","download_url":"https://codeload.github.com/txix-open/grmq/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248080347,"owners_count":21044486,"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":["consumer","dlq","go","rabbitmq","rabbitmq-client","reconnect"],"created_at":"2024-11-13T21:24:40.362Z","updated_at":"2025-04-09T17:44:15.647Z","avatar_url":"https://github.com/txix-open.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GRMQ\n## Go Rabbit MQ\n![Build and test](https://github.com/txix-open/grmq/actions/workflows/main.yml/badge.svg)\n[![codecov](https://codecov.io/gh/txix-open/grmq/branch/main/graph/badge.svg?token=JMTTJ5O6WB)](https://codecov.io/gh/txix-open/grmq)\n[![Go Report Card](https://goreportcard.com/badge/github.com/txix-open/grmq)](https://goreportcard.com/report/github.com/txix-open/grmq)\n\nWhat are the typical use-cases for RabbitMQ broker ?\n* We create a durable topology (exchanges, queues, bindings).\n* Begin queue consuming (commonly in several goroutines with prefetch count) and use [DLQ](https://www.rabbitmq.com/dlx.html) to avoid poison messages.\n* If we can't handle message at this time, we can retry a bit later (some external service is not available for instance)\n* Also, we expect that if something happens with connection, we can reestablish it and continue our work transparently.\n* Graceful shutdown to reduce probability of message duplication.\n\nAll of those commonly used cases are implemented in the package.\n\nHigh abstraction wrapper for [amqp091-go](https://github.com/rabbitmq/amqp091-go). Inspired by http package and [cony](https://github.com/assembla/cony)\n\n## Features\n* re-connection support\n* graceful shutdown support\n* flexible `context.Context` based api\n* middlewares for publishers and consumers\n* DLQ declaration out of the box\n* [flexible retries](#retries)\n\n## Complete Example\n```go\ntype LogObserver struct {\n\tgrmq.NoopObserver\n}\n\nfunc (o LogObserver) ClientError(err error) {\n\tlog.Printf(\"rmq client error: %v\", err)\n}\n\nfunc (o LogObserver) ConsumerError(consumer consumer.Consumer, err error) {\n\tlog.Printf(\"unexpected consumer error (queue=%s): %v\", consumer.Queue, err)\n}\n\nfunc main() {\n\turl := amqpUrl()\n\n\tpub := publisher.New(\n\t\t\"exchange\",\n\t\t\"test\",\n\t\tpublisher.WithMiddlewares(publisher.PersistentMode()),\n\t)\n\n\tsimpleHandler := consumer.HandlerFunc(func(ctx context.Context, delivery *consumer.Delivery) {\n\t\tlog.Printf(\"message body: %s, queue: %s\", delivery.Source().Body, delivery.Source().RoutingKey)\n\t\terr := delivery.Ack()\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t})\n\tsimpleConsumer := consumer.New(\n\t\tsimpleHandler,\n\t\t\"queue\",\n\t\tconsumer.WithConcurrency(32),   //default 1\n\t\tconsumer.WithPrefetchCount(32), //default 1\n\t)\n\n\tretryPolicy := retry.NewPolicy(\n\t\ttrue, //move to dlq after last failed try\n\t\tretry.WithDelay(500*time.Millisecond, 1),\n\t\tretry.WithDelay(1*time.Second, 1),\n\t\tretry.WithDelay(2*time.Second, 1),\n\t)\n\tretryHandler := consumer.HandlerFunc(func(ctx context.Context, delivery *consumer.Delivery) {\n\t\tlog.Printf(\"message body: %s, queue: %s\", delivery.Source().Body, delivery.Source().RoutingKey)\n\t\terr := delivery.Retry()\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t})\n\tretryConsumer := consumer.New(\n\t\tretryHandler,\n\t\t\"retryQueue\",\n\t\tconsumer.WithRetryPolicy(retryPolicy),\n\t)\n\n\tcli := grmq.New(\n\t\turl,\n\t\tgrmq.WithPublishers(pub),\n\t\tgrmq.WithConsumers(simpleConsumer, retryConsumer),\n\t\tgrmq.WithTopologyBuilding(\n\t\t\ttopology.WithQueue(\"queue\", topology.WithDLQ(true)),\n\t\t\t//you MUST declare queue with the same retry policy\n\t\t\ttopology.WithQueue(\"retryQueue\", topology.WithRetryPolicy(retryPolicy)),\n\t\t\ttopology.WithDirectExchange(\"exchange\"),\n\t\t\ttopology.WithBinding(\"exchange\", \"queue\", \"test\"),\n\t\t),\n\t\tgrmq.WithReconnectTimeout(3*time.Second), //default 1s\n\t\tgrmq.WithObserver(LogObserver{}),\n\t)\n\t//it tries to connect\n\t//declare topology\n\t//init publishers and consumers\n\t//returns first occurred error or nil \n\t//or you can use  cli.Serve(context.Background()), which is completely non-blocking\n\terr := cli.Run(context.Background())\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\terr = pub.Publish(context.Background(), \u0026amqp091.Publishing{Body: []byte(\"hello world\")})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t//you may use any publisher to send message to any exchange\n\terr = pub.PublishTo(context.Background(), \"\", \"retryQueue\", \u0026amqp091.Publishing{Body: []byte(\"retry me\")})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\ttime.Sleep(10 * time.Second)\n\n\tcli.Shutdown()\n}\n```\n\n## Retries\nThis is quite fresh feature implemented in `1.4.0`.\nBefore using it you must know how it works under the hood.\nIt combines two mechanisms: [DLQ](https://www.rabbitmq.com/dlx.html) + [TTL](https://www.rabbitmq.com/ttl.html)\n\nLets say we use policy below for queue `test`\n```go\nretryPolicy := retry.NewPolicy(\n\ttrue,\n\tretry.WithDelay(500*time.Millisecond, 1),\n\tretry.WithDelay(1*time.Second, 1),\n\tretry.WithDelay(2*time.Second, 1), \n)\n```\nThis configuration will create\n* exchange with name `default-dead-letter`\n* 4 extra queues\n  * `test.DLQ`\n  * `test.retry.500`\n  * `test.retry.1000`\n  * `test.retry.2000`\n* each retry queue will have `x-message-ttl` property equal to its delay\n* each retry queue will have DLX routing to the original queue `test`\n* `consumer.Delivery.Retry()` will find a suitable queue by `grmq-retry-count` header(0 by default), increment `grmq-retry-count` header, directly publish with confirmation to the queue, manually acknowledge the delivery\n* if there is no suitable retry option and `moveToDql` is `true`, it moves the message to `test.DLQ`\n* otherwise, it performs ack\n\n**Recommendation**: If you want to change retry policy for a queue, before doing it, ensure there is no messages in retry queues.\n\nDon't forget to delete old retry queues. \n\n## State and road map\n* the package is used in production (reconnection works perfect)\n* more tests need to be implemented\n* add `go doc`\n* add supporting for publishing confirmation to achieve more reliable publishing\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftxix-open%2Fgrmq","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftxix-open%2Fgrmq","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftxix-open%2Fgrmq/lists"}