{"id":13485769,"url":"https://github.com/mtakaki/dropwizard-circuitbreaker","last_synced_at":"2025-12-30T18:25:05.360Z","repository":{"id":57721668,"uuid":"43739496","full_name":"mtakaki/dropwizard-circuitbreaker","owner":"mtakaki","description":"A circuit breaker design pattern for dropwizard","archived":false,"fork":false,"pushed_at":"2019-12-31T09:22:17.000Z","size":94,"stargazers_count":43,"open_issues_count":0,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-10-30T20:45:44.824Z","etag":null,"topics":["circuit-breaker","dropwizard"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mtakaki.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":"2015-10-06T08:48:46.000Z","updated_at":"2024-10-02T17:46:09.000Z","dependencies_parsed_at":"2022-09-26T21:50:21.945Z","dependency_job_id":null,"html_url":"https://github.com/mtakaki/dropwizard-circuitbreaker","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtakaki%2Fdropwizard-circuitbreaker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtakaki%2Fdropwizard-circuitbreaker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtakaki%2Fdropwizard-circuitbreaker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtakaki%2Fdropwizard-circuitbreaker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mtakaki","download_url":"https://codeload.github.com/mtakaki/dropwizard-circuitbreaker/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245910888,"owners_count":20692513,"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":["circuit-breaker","dropwizard"],"created_at":"2024-07-31T18:00:31.383Z","updated_at":"2025-12-30T18:25:05.309Z","avatar_url":"https://github.com/mtakaki.png","language":"Java","funding_links":[],"categories":["Projects","项目","容错组件","Open Source"],"sub_categories":["Distributed Applications","分布式应用程序","微服务框架","Eclipse"],"readme":"### Status\n[![CircleCI](https://circleci.com/gh/mtakaki/dropwizard-circuitbreaker/tree/master.svg?style=svg)](https://circleci.com/gh/mtakaki/dropwizard-circuitbreaker/tree/master)\n[![Coverage Status](https://coveralls.io/repos/mtakaki/dropwizard-circuitbreaker/badge.svg?branch=master\u0026service=github)](https://coveralls.io/github/mtakaki/dropwizard-circuitbreaker?branch=master)\n[![Codacy Badge](https://api.codacy.com/project/badge/grade/039da749e34a4c46b297ddf1389d593a)](https://www.codacy.com/app/mitsuotakaki/dropwizard-circuitbreaker)\n[![Download](https://maven-badges.herokuapp.com/maven-central/com.github.mtakaki/dropwizard-circuitbreaker/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.mtakaki/dropwizard-circuitbreaker)\n[![Javadoc](http://javadoc.io/badge/com.github.mtakaki/dropwizard-circuitbreaker.svg)](http://www.javadoc.io/doc/com.github.mtakaki/dropwizard-circuitbreaker)\n![License](https://img.shields.io/github/license/mtakaki/dropwizard-circuitbreaker.svg)\n[![Dependency Status](https://www.versioneye.com/user/projects/57e784f5bd6fa600512e2134/badge.svg?style=flat-square)](https://www.versioneye.com/user/projects/57e784f5bd6fa600512e2134)\n\n# Circuit Breaker Library\n\nThis library provides a simple implementation of a [circuit breaker design pattern](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern).\n\nIt uses [dropwizard metrics](http://metrics.dropwizard.io/) to provide insights on the rate of failures and, with it, we can reliably assume a certain functionality is having issues. After a certain threshold is hit the circuit is opened and an exception is thrown, preventing from increasing the load on the failing code.\n\nThis library can be used as a stand-alone library or embedded into [dropwizard](http://www.dropwizard.io/), through the usage of annotations.\n\n[Project lombok](https://projectlombok.org/) was used to auto-generate the getter and setters, so you will need to have lombok plugin installed in your eclipse/intellij if you want to import the source code. The project also uses Java 8 features, such as `Optional` and lambda expressions, so it's not compatible with prior versions.\n\nThese are the supported versions of dropwizard:\n\n| Dropwizard  |  Circuit Breaker |\n|---|---|\n| 0.8.X  | 0.0.2  |\n| 0.9.X  | 0.0.6  |\n| 1.0.0  | 1.0.0  |\n| 1.0.5  | 1.0.5  |\n| 1.1.0  | 1.1.0  |\n| 1.3.8  | 1.3.8  |\n| 2.0.0  | 2.0.0  |\n\n## Stand-alone\n\nTo use this library as a stand-alone circuit breaker, you will need to build the object and pass a `MetricRegistry`. And it can be used to wrap code blocks, which can throw exceptions.\n\n```java\n// We build the CircuitBreakerManager with a threshold of 0.5 failure per second and we'll\n// be using a 1-minute average rate to determine if our circuit should be opened or not.\nCircuitBreakerManager circuitBreaker = new CircuitBreakerManager(metricRegistry, 0.5, CircuitBreakerManager.RateType.ONE_MINUTE);\n// When this exception below is thrown, the meter will be increased.\ncircuitBreaker.wrapCodeBlockWithCircuitBreaker(\"my.test.block\", (meter) -\u003e {\n    // This is where you should put your code.\n\n});\n```\n\nAfter several exceptions, and when the rate reaches the threshold, the method `wrapCodeBlockWithCircuitBreaker()` will thrown a `CircuitBreakerOpenedException` and won't run the code block anymore. Alternatively you can run it without throwing an exception, but you need to manually verify if the circuit is opened.\n\n```java\nCircuitBreakerManager circuitBreaker = new CircuitBreakerManager(metricRegistry, 0.5, CircuitBreakerManager.RateType.ONE_MINUTE);\n\nif (!circuitBreaker.isCircuitOpen(\"my.test.block\")) {\n    // This method will not throw an exception if our circuit is open.\n    circuitBreaker.wrapCodeBlock(\"my.test.block\", (meter) -\u003e {\n        // This is where you should put your code.\n\n    });\n}\n```\n\n## With dropwizard\n\nThis library seamlessly integrates with dropwizard and provides the annotation `@CircuitBreaker` and it can be used to decorate the resource methods that will use the circuit breaker. When the API has reached a certain rate of exceptions it will automatically return `503 Service Unavailable` until the rate drops below the threshold.\n\nThe usage of this design pattern can help alleviate the load when errors cascades from internal dependencies and surfaces into the outermost APIs. As the API starts returning `503` the clients will fail, either gracefully or not but that's outside of the scope of the back end, and the load will decrease on the internal dependencies that were causing the cascading failures.\n\nThis library also provides a configuration class (`CircuitBreaker`) that can be used in the application configuration class and a bundle class (`CircuitBreakerBundle`) to automatically register the circuit breaker with the application.\n\nThe annotation `@CircuitBreaker` should not be used in conjunction with `@ExceptionMetered` as they both will conflict when creating and registering the `Meter`. As they both are essentially measuring the same thing you won't need the `@ExceptionMetered` annotation.\n\n### Configuration\n\nThe configuration class can add reference to `CircuitBreaker` class, which holds the threshold and rate type:\n\n```java\npublic class MyAppConfiguration extends Configuration {\n    private CircuitBreakerConfiguration circuitBreaker;\n    \n    public CircuitBreakerConfiguration getCircuitBreaker() {\n        return this.circuitBreaker;\n    }\n}\n``` \n\nYour configuration YML will look like this. The `customThresholds` allows having custom thresholds for specific circuit breakers.\n\n```yaml\ncircuitBreaker:\n  threshold: 0.5\n  rateType: ONE_MINUTE\n  customThresholds:\n    com.mtakaki.testcb.TestResource.get.circuitBreaker: 0.2\n```\n\nTo register the bundle in the application:\n\n```java\npublic class MyApp extends Application {\n    public static void main(final String[] args) throws Exception {\n        new MyApp().run(args);\n    }\n\n    private final CircuitBreakerBundle\u003cMyAppConfiguration\u003e circuitBreakerBundle = new CircuitBreakerBundle\u003cMyAppConfiguration\u003e() {\n        @Override\n        protected CircuitBreakerConfiguration getConfiguration(\n                final MyAppConfiguration configuration) {\n            return configuration.getCircuitBreaker();\n        }\n    };\n\n    @Override\n    public void initialize(final Bootstrap\u003cMyAppConfiguration\u003e bootstrap) {\n        bootstrap.addBundle(this.circuitBreakerBundle);\n    };\n    \n    @Override\n    public void run(final MyAppConfiguration configuration, final Environment environment)\n            throws Exception {\n        environment.jersey().register(new TestResource());\n    }\n}\n```\n\nTo annotate the resource:\n\n```java\n@Path(\"/test\")\npublic class TestResource {\n    @GET\n    @CircuitBreaker\n    public Response get() throws Exception {\n        throw new Exception(\"We want this to fail\");\n    }\n\n    @GET\n    @Path(\"/custom\")\n    @CircuitBreaker(name = \"customName\")\n    public Response getCustom() throws Exception {\n        throw new Exception(\"We want this to fail\");\n    }\n}\n```\n\nNow the API `localhost:8080/test` will keep returning `500 Internal Server Error` until it reaches 0.5 exceptions per second (the rate set in our configuration) looking at the 1 minute average (also set in our configuration). Once that happens you will start getting `503 Service Unavailable`. After a couple of seconds the rate will decrease, even if you keep hitting the service and getting `503` responses, and you will be getting `500` once again.\n\nThe meter is available in the `metrics` page under the admin port like any other meter. The `circuitBreaker` one measures the exceptions in the API, while `circuiteBreaker.openCircuit` measures the requests that didn't hit your API due to the circuit being open. The latter is only available from version `0.0.3`.\n\n```json\n  \"meters\" : {\n    \"com.mtakaki.testcb.TestResource.get.circuitBreaker\" : {\n      \"count\" : 51,\n      \"m15_rate\" : 0.055135750452891936,\n      \"m1_rate\" : 0.564668417659197,\n      \"m5_rate\" : 0.15659880505693252,\n      \"mean_rate\" : 1.1303494869953181,\n      \"units\" : \"events/second\"\n    },\n    \"com.mtakaki.testcb.TestResource.get.circuitBreaker.openCircuit\" : {\n      \"count\" : 29,\n      \"m15_rate\" : 0.03204704240331378,\n      \"m1_rate\" : 0.44614897600789627,\n      \"m5_rate\" : 0.09510333717433071,\n      \"mean_rate\" : 0.8154557050860357,\n      \"units\" : \"events/second\"\n    }\n  }\n```\n\n## Maven\n\nThe library is available at the maven central, so just add dependency to `pom.xml`:\n\n```xml\n\u003cdependencies\u003e\n  \u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.mtakaki\u003c/groupId\u003e\n    \u003cartifactId\u003edropwizard-circuitbreaker\u003c/artifactId\u003e\n    \u003cversion\u003e2.0.0\u003c/version\u003e\n  \u003c/dependency\u003e\n\u003c/dependencies\u003e\n```\n\n## Benchmark\n\n**tl;dr**: Circuit breaker is very lean and there's barely any impact on performance.\n\nBenchmark tests were executed to assess if there is any cost of running an API with the circuit breaker always verifying if the circuit is open, before piping the request through. These tests were executed with these specs:\n\n- 30 concurrent clients\n- 2,000 requests\n\nOn average, **without** circuit breaker the benchmark test reached **179.97 requests/second**. While **with** circuit breaker it reached **180.77 requests/second**.\n\n### Failing API\n\nWith a constantly failing API, when using the circuit breaker it got **97.40 requests/second**, while without using the circuit breaker it got **92.29 requests/second**. On the other hand, out of the 2,000 requests, 1,396 requests returned `503 Service Unavailable` status.\n\nThe metric `CircuitBreakerApplicationEventListener.getCircuitBreakerName` shows how much overhead the circuit breaker adds to the requests. The average overhead, during these tests, were way below 1ms per request.\n\n```json\n  \"timers\" : {\n    \"com.github.mtakaki.dropwizard.circuitbreaker.jersey.CircuitBreakerApplicationEventListener.getCircuitBreakerName\" : {\n      \"count\" : 4015,\n      \"max\" : 0.001024921,\n      \"mean\" : 8.349680896258536E-6,\n      \"min\" : 1.038E-6,\n      \"p50\" : 4.683E-6,\n      \"p75\" : 7.406000000000001E-6,\n      \"p95\" : 1.6315000000000002E-5,\n      \"p98\" : 2.0050000000000003E-5,\n      \"p99\" : 2.7683000000000002E-5,\n      \"p999\" : 6.15457E-4,\n      \"stddev\" : 3.60241125613854E-5,\n      \"m15_rate\" : 6.589688969963449,\n      \"m1_rate\" : 13.102701736894172,\n      \"m5_rate\" : 11.54464183938005,\n      \"mean_rate\" : 30.191455659575904,\n      \"duration_units\" : \"seconds\",\n      \"rate_units\" : \"calls/second\"\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmtakaki%2Fdropwizard-circuitbreaker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmtakaki%2Fdropwizard-circuitbreaker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmtakaki%2Fdropwizard-circuitbreaker/lists"}