{"id":36753473,"url":"https://github.com/easierway/service_decorators","last_synced_at":"2026-01-12T12:47:19.330Z","repository":{"id":57485754,"uuid":"137828253","full_name":"easierway/service_decorators","owner":"easierway","description":"Simplify your microservice development","archived":false,"fork":false,"pushed_at":"2020-07-06T03:38:59.000Z","size":924,"stargazers_count":355,"open_issues_count":1,"forks_count":71,"subscribers_count":18,"default_branch":"master","last_synced_at":"2024-06-18T17:07:09.375Z","etag":null,"topics":["chaos-engineering","circuit-break","microservice","rate-limit"],"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/easierway.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":"2018-06-19T02:08:18.000Z","updated_at":"2024-06-07T05:42:15.000Z","dependencies_parsed_at":"2022-09-11T16:01:14.959Z","dependency_job_id":null,"html_url":"https://github.com/easierway/service_decorators","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/easierway/service_decorators","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/easierway%2Fservice_decorators","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/easierway%2Fservice_decorators/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/easierway%2Fservice_decorators/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/easierway%2Fservice_decorators/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/easierway","download_url":"https://codeload.github.com/easierway/service_decorators/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/easierway%2Fservice_decorators/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28338983,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T12:22:26.515Z","status":"ssl_error","status_checked_at":"2026-01-12T12:22:10.856Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: 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":["chaos-engineering","circuit-break","microservice","rate-limit"],"created_at":"2026-01-12T12:47:19.265Z","updated_at":"2026-01-12T12:47:19.322Z","avatar_url":"https://github.com/easierway.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Simplify microservice development\n## What’s the most complicated part of implementing a microservice except the core business logic?\n\nYou might think it must be RPC end point part, which makes your business logic as a real service can be accessed from the network.\n\nBut, this is not true. By leveraging the opensource RPC packages, such as, apache thrift, gRPC, this part could be extremely easy except defining your service interface with some IDL.\n\nThe following codes are about starting a service server based on thrift.\n```Go\ntransportFactory := thrift.NewTFramedTransportFactory(thrift.NewTTransportFactory())\nprotocolFactory := thrift.NewTBinaryProtocolFactoryDefault()\nserverTransport, err := thrift.NewTServerSocket(NetworkAddr)\nif err != nil {\n\tos.Exit(1)\n}\nprocessor := calculator_thrift.NewCalculatorProcessor(\u0026calculatorHandler{})\nserver := thrift.NewTSimpleServer4(processor, serverTransport, transportFactory, protocolFactory)\nserver.Serve()\n```\nAs we all known, to build a robust and maintainable service is not an easy task. There are a lot of tricky work as the following:\n\n![image](https://github.com/easierway/service_decorators/blob/master/doc_pics/other_functions.jpg)\n\nMost of them are much complicated than RPC, in some cases they are even complicated than your core business logic.\n\nFor example, the code section of rate limit\n\n```Go\n...\nbucket := make(chan struct{}, tokenBucketSize)\n//fill the bucket firstly\nfor j := 0; j \u003c tokenBucketSize; j++ {\n\tbucket \u003c- struct{}{}\n}\ngo func() {\n\tfor _ = range time.Tick(interval) {\n\t\tfor i := 0; i \u003c numOfReqs; i++ {\n\t\t\tbucket \u003c- struct{}{}\n\t\t\tsleepTime := interval / time.Duration(numOfReqs)\n\t\t\ttime.Sleep(time.Nanosecond * sleepTime)\n\t\t}\n\t}\n}()\n...\n\nselect {\n\tcase \u003c-dec.tokenBucket:\n\t\treturn executeBusinessLog(req),nil\n\tdefault:\n\t\treturn errors.New(\"beyond the rate limit\")\n}\n\n...\n\n```\n\nJust as the following diagram shows normally what you have to do is much more than what you want to do when implementing a microservice.\n\n![image](https://github.com/easierway/service_decorators/blob/master/doc_pics/to_do.jpg)\n\nThe goal of the project is to reduce your work on what you have to do and let you only focus on what you want to do.\n\nAll these common functions (e.g. rate limit, circuit break, metric) are encapsulated in the components, and these common logics can be injected in your service implementation transparently by leveraging decorator pattern.\n\n![image](https://github.com/easierway/service_decorators/blob/master/doc_pics/decorator_pattern.jpg)\n\n![image](https://github.com/easierway/service_decorators/blob/master/doc_pics/decorators.jpg)\n\nThe following is an example of adding rate limit, circuit break and metrics functions with the prebuilt decorators.\n\n```Go\nif rateLimitDec, err = service_decorators.CreateRateLimitDecorator(time.Millisecond*1, 100); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif circuitBreakDec, err = service_decorators.CreateCircuitBreakDecorator().\n\t\tWithTimeout(time.Millisecond * 100).\n\t\tWithMaxCurrentRequests(1000).\n\t\tWithTimeoutFallbackFunction(addFallback).\n\t\tWithBeyondMaxConcurrencyFallbackFunction(addFallback).\n\t\tBuild(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tgmet := g_met.CreateGMetInstanceByDefault(\"g_met_config/gmet_config.xml\")\n\tif metricDec, err = service_decorators.CreateMetricDecorator(gmet).\n\t\tNeedsRecordingTimeSpent().Build(); err != nil {\n\t\treturn nil, err\n\t}\n\tdecFn := rateLimitDec.Decorate(circuitBreakDec.Decorate(metricDec.Decorate(innerFn)))\n  ...\n```\nRefer to the example: https://github.com/easierway/service_decorators_example/blob/master/example_service_test.go\n\n## Not only for building service\nYou definitely can use this project in the cases besides building a service. For example, when invoking a remote service, you have to consider on fault-over and metrics. In this case, you can leverage the decorators to simplify your code as following:\n```Go\nfunc originalFunction(a int, b int) (int, error) {\n\treturn a + b, nil\n}\n\nfunc Example() {\n\t// Encapsulate the original function as service_decorators.serviceFunc method signature\n\ttype encapsulatedReq struct {\n\t\ta int\n\t\tb int\n\t}\n\n\tencapsulatedFn := func(req Request) (Response, error) {\n\t\tinnerReq, ok := req.(encapsulatedReq)\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"invalid parameters\")\n\t\t}\n\t\treturn originalFunction(innerReq.a, innerReq.b)\n\t}\n\n\t// Add the logics with decorators\n\t// 1. Create the decorators\n\tvar (\n\t\tretryDec        *RetryDecorator\n\t\tcircuitBreakDec *CircuitBreakDecorator\n\t\tmetricDec       *MetricDecorator\n\t\terr             error\n\t)\n\n\tif retryDec, err = CreateRetryDecorator(3 /*max retry times*/, time.Second*1,\n\t\ttime.Second*1, retriableChecker); err != nil {\n\t\tpanic(err)\n\t}\n\n\tif circuitBreakDec, err = CreateCircuitBreakDecorator().\n\t\tWithTimeout(time.Millisecond * 100).\n\t\tWithMaxCurrentRequests(1000).\n\t\tBuild(); err != nil {\n\t\tpanic(err)\n\t}\n\n\tgmet := g_met.CreateGMetInstanceByDefault(\"g_met_config/gmet_config.xml\")\n\tif metricDec, err = CreateMetricDecorator(gmet).\n\t\tNeedsRecordingTimeSpent().Build(); err != nil {\n\t\tpanic(err)\n\t}\n\n\t// 2. decorate the encapsulted function with decorators\n\t// be careful of the order of the decorators\n\tdecFn := circuitBreakDec.Decorate(metricDec.Decorate(retryDec.Decorator(encapsulatedFn)))\n\tret, err := decFn(encapsulatedReq{1, 2})\n\tfmt.Println(ret, err)\n\t//Output: 3 \u003cnil\u003e\n}\n```\n## Decorators\n### Decorators List\n1. Rate Limit Decorator\n2. Circuit Break Decorator\n3. Advanced Circuit Break Decorator\n4. Metric Decorator\n5. Retry Decorator\n6. Chaos Engineering Decorator\n\n### CircuitBreakDecorator\nCircuit breaker is the essential part of fault tolerance and recovery oriented solution. Circuit breaker is to stop cascading failure and enable resilience in complex distributed systems where failure is inevitable.\n\nStop cascading failures. Fallbacks and graceful degradation. Fail fast and rapid recovery.\n\nCircuitBreakDecorator would interrupt client invoking when its time spent being longer than the expectation. A timeout exception or the result coming from the degradation method will be return to client.\nThe similar logic is also for the concurrency limit.\nhttps://github.com/easierway/service_decorators_example/blob/master/example_service_test.go#L46\n\n### AdvancedCircuitBreakDecorator\nAdvancedCircuitBreakDecorator is a stateful circuit breaker. Not like CircuitBreakDecorator, which each client call will invoke the service function wrapped by the decorators finally, AdvancedCircuitBreakDecorator is rarely invoked the service function when it's in \"OPEN\" state. Refer to the following state flow.\n![image](https://github.com/easierway/service_decorators/blob/master/doc_pics/circuit_breaker_states_transtion.png)\n\nTo use AdvancedCircuitBreakDecorator to handle the timeout and max concurrency limit, the service will be decorated by both CircuitBreakDecorator and AdvancedCircuitBreakDecorator.\n\nBe careful:\n1 AdvancedCircuitBreakDecorator should be put out of CircuitBreakDecorator to get timeout or beyond max concurrency errors.\n2 To let AdvancedCircuitBreakDecorator catch the errors and process the faults, not setting the fallback methods for CircuitBreakDecorator.\n![image](https://github.com/easierway/service_decorators/blob/master/doc_pics/AdvancedCircuitBreaker.png)\n\n####\n\n\n\n### ChaosEngineeringDecorator\n#### What is Chaos Engineering?\nAccording to the principles of chaos engineering, chaos engineering is “the discipline of experimenting on a distributed system in order to build confidence in the system’s capability to withstand turbulent conditions in production.”\n\n#### Why Chaos Engineering?\nYou learn how to fix the things that often break.  \nYou don’t learn how to fix the things that rarely break.  \nIf something hurts, do it more often!\nFor today’s large scale distributed system, normally it is impossible to simulate all the cases (including the failure modes) in a nonproductive environment. Chaos engineering is about experimenting with continuous low level of breakage to make sure the system can handle the big things.\n\n#### What can ChaosEngineeringDecorator help in your chaos engineering practice?\nBy ChaosEngineeringDecorator you can inject the failures (such as, slow response, error response) into the distributed system under control. This would help you to test and improve the resilience of your system.\n![image](https://github.com/easierway/service_decorators/blob/master/doc_pics/chaos_engineering_dec.png)\n\n#### How to ChaosEngineeringDecorator?\n##### Configurations\nThe following is the configuration about ChaosEngineeringDecorator.\n```Javascript\n{\n\t \"IsToInjectChaos\" : true, // Is it to start chaos injection, if it is false, all chaos injects (chaos function, slow response) will be stopped\n\t \"AdditionalResponseTime\" : 500, // Inject additional time spent (milseconds) to simulate slow response.\n\t \"ChaosRate\" : 40 // The proportion of the chaos response, the range is 0-100\n }\n ```\n The configuration is stored in the storage that can be accessed with [\"ConfigStorage\"](https://github.com/easierway/service_decorators/blob/master/config_storage.go#L6) interface.\n\n##### Inject Slow Response\n```Go\n// Example_ChaosEngineeringDecorator_InjectError is the example for slow response injection.\n// To run the example, put the following configuration into Consul KV storage with the key \"ChaosExample\"\n// {\n//   \"IsToInjectChaos\" : true,\n//   \"AdditionalResponseTime\" : 100, // response time will be increased 100ms\n//   \"ChaosRate\" : 10\n// }\nfunc Example_ChaosEngineeringDecorator_InjectSlowResponse() {\n\tserviceFn := func(req Request) (Response, error) {\n\t\treturn \"Service is invoked\", nil\n\t}\n\tErrorInjectionFn := func(req Request) (Response, error) {\n\t\treturn \"Error Injection\", errors.New(\"Failed to process.\")\n\t}\n\tstorage, err := CreateConsulConfigStorage(\u0026api.Config{})\n\tif err != nil {\n\t\tfmt.Println(\"You might need to start Cousul server.\", err)\n\t\treturn\n\t}\n\tchaosDec, err := CreateChaosEngineeringDecorator(storage, \"ChaosExample\",\n\t\tErrorInjectionFn, 10*time.Millisecond)\n\tif err != nil {\n\t\tfmt.Println(\"You might need to start Cousul server.\", err)\n\t\treturn\n\t}\n\tdecFn := chaosDec.Decorate(serviceFn)\n\tfor i := 0; i \u003c 10; i++ {\n\t\ttStart := time.Now()\n\t\tret, _ := decFn(\"\")\n\t\tfmt.Printf(\"Output is %s. Time escaped: %f ms\\n\", ret,\n\t\t\ttime.Since(tStart).Seconds()*1000)\n\t}\n\n\t//You have 10% probability to get slow response.\n\n}\n```\n##### Inject Error Response\n```Go\n// Example_ChaosEngineeringDecorator_InjectError is the example for error injection.\n// To run the example, put the following configuration into Consul KV storage with the key \"ChaosExample\"\n// {\n//   \"IsToInjectChaos\" : true,\n//   \"AdditionalResponseTime\" : 0,\n//   \"ChaosRate\" : 10\n// }\nfunc Example_ChaosEngineeringDecorator_InjectError() {\n\tserviceFn := func(req Request) (Response, error) {\n\t\treturn \"Service is invoked\", nil\n\t}\n\tErrorInjectionFn := func(req Request) (Response, error) {\n\t\treturn \"Error Injection\", errors.New(\"Failed to process.\")\n\t}\n\tstorage, err := CreateConsulConfigStorage(\u0026api.Config{})\n\tif err != nil {\n\t\tfmt.Println(\"You might need to start Cousul server.\", err)\n\t\treturn\n\t}\n\tchaosDec, err := CreateChaosEngineeringDecorator(storage, \"ChaosExample\",\n\t\tErrorInjectionFn, 10*time.Millisecond)\n\tif err != nil {\n\t\tfmt.Println(\"You might need to start Cousul server.\", err)\n\t\treturn\n\t}\n\tdecFn := chaosDec.Decorate(serviceFn)\n\tfor i := 0; i \u003c 10; i++ {\n\t\tret, _ := decFn(\"\")\n\t\tfmt.Printf(\"Output is %s\\n\", ret)\n\t}\n\t//You have 10% probability to get \"Error Injection\"\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feasierway%2Fservice_decorators","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feasierway%2Fservice_decorators","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feasierway%2Fservice_decorators/lists"}