-
Notifications
You must be signed in to change notification settings - Fork 936
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add resilience4j integration for circuitbreaker #4139
Conversation
Looking forward to progress in this PR ❤️ |
@jrhee17 hi, jrhee :) I am interested in this feature. Do you have any updates by any chance? |
@heesuk-ahn Hi! Sorry about the late reply. Let me increase priority in getting this feature merged 🙇 |
1a3cf23
to
cc37a71
Compare
Codecov ReportBase: 74.08% // Head: 74.06% // Decreases project coverage by
Additional details and impacted files@@ Coverage Diff @@
## master #4139 +/- ##
============================================
- Coverage 74.08% 74.06% -0.03%
- Complexity 18185 18241 +56
============================================
Files 1537 1549 +12
Lines 67469 67629 +160
Branches 8537 8545 +8
============================================
+ Hits 49987 50089 +102
- Misses 13419 13466 +47
- Partials 4063 4074 +11
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report at Codecov. |
71b5bc5
to
3c067d9
Compare
1dbbc11
to
864330b
Compare
1aa99f7
to
29126d7
Compare
Really sorry about the delay all 😅 I think this PR is finally in a reviewable state 🙏 |
I believe this PR is (finally) ready for review. Please take a look at your convenience 🙏 |
build.gradle
Outdated
if (task.path.startsWith(':examples:')) { | ||
// Target Java 11 for examples. | ||
options.release.set(11) | ||
if (task.path.startsWith(':examples:') || task.path.contains(':resilience4j')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is out of scope of this PR but we need a general solution to tell a module needs Java 17 to compile.
What do you think of introducing java17
flag for it? We may set both java
and java17
or only java17
to Spring Boot 3 and Resilience4J projects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed! Let me work on this in a separate PR 😄
.../com/linecorp/armeria/resilience4j/circuitbreaker/FailedCircuitBreakerDecisionException.java
Show resolved
Hide resolved
.../com/linecorp/armeria/resilience4j/circuitbreaker/FailedCircuitBreakerDecisionException.java
Outdated
Show resolved
Hide resolved
...java/com/linecorp/armeria/resilience4j/circuitbreaker/Resilience4jCircuitBreakerFactory.java
Show resolved
Hide resolved
...n/java/com/linecorp/armeria/resilience4j/circuitbreaker/Resilience4jCircuitBreakerUtils.java
Outdated
Show resolved
Hide resolved
...corp/armeria/resilience4j/circuitbreaker/client/Resilience4JCircuitBreakerClientHandler.java
Outdated
Show resolved
Hide resolved
logger.warn("Failed to get a circuit breaker from mapping", t); | ||
return null; | ||
} | ||
circuitBreaker.acquirePermission(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think of catching CallNotPermittedException
and wrapping it with FailFastException
and UnprocessedRequestException
?
FailFastException
would be useful to set a retry rule and I found that there are some logics relying on FailFastException
.
I guess we also need to wrap UnprocessedRequestException
for
Line 48 in e495f0d
throw new FailFastException(circuitBreaker); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've wrapped with UnprocessedRequestException
, but am unsure if we should generalize FailFastException
to accept a resilience4j
circuitbreaker..
- I believe the general direction is to move away from custom circuitbreaker implementations to
resilience4j
, which will eventually makeFailFastException
obsolete - I think wrapping with
UnprocessedRequestException
is enough for the current logic/retry rules.
Let me know if you feel differently though 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sense. I found only one place where FailFastException
is used internally that might be converted by FailFastException
.
Lines 341 to 345 in fee87f8
if (cause instanceof TException || | |
cause instanceof UnprocessedRequestException || | |
cause instanceof FailFastException) { | |
return (Exception) cause; | |
} |
By the way, there might be a certain reason that FailFastException
isn't wrapped by UnprocessedRequestException
. @minwoox Do you know the historical reason? Otherwise, is it a bug as I thought?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had a chat internally and we decided to consider the change in Armeria 2.0.
Sorry for the incorrect direction. Could you revert the wrapping code? 🙇♂️
#4623
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reverted for DefaultCircuitBreakerClientHandler
🙇
* | ||
* // for RpcRequest | ||
* CircuitBreakerRuleWithContent rule = ...; | ||
* CircuitBreakerClientHandler<RpcRequest> handler = Resilience4JCircuitBreakerClientHandler.of( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if the T
type parameter for Request
makes the API type-safe. It looks a bit verbose to use. ClientCircuitBreakerGenerator
already takes Request
as a parameter. How about removing the T
type parameter from CircuitBreakerClientHandler
and inline Request
to the method argument?
CircuitBreakerCallback tryRequest(ClientRequestContext ctx, Request req);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm guessing if we do this we can't use the following pattern 😅 I've updated the javadocs though 🙏
final Function<? super HttpClient, CircuitBreakerClient> circuitBreakerDecorator =
CircuitBreakerClient.newDecorator( // expects HttpRequest as the parameterized type, but is Request
Resilience4JCircuitBreakerClientHandler.of(mapping), rule);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couldn't we change CircuitBreakerClientHandler<HttpRequest>
to CircuitBreakerClientHandler
? It is still an unstable API.
armeria/core/src/main/java/com/linecorp/armeria/client/circuitbreaker/CircuitBreakerClient.java
Line 116 in e495f0d
newDecorator(CircuitBreakerClientHandler<HttpRequest> handler, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you're right, looks much cleaner 👍 Good idea as always 🙇
bf421a0
to
06f0d41
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome work! 🚀🥳
Left a question on `UnprcoessedRequestException. All other things are minor.
...esilience4j-spring/src/test/java/example/armeria/resilience4j/spring/CircuitBreakerTest.java
Show resolved
Hide resolved
...java/com/linecorp/armeria/resilience4j/circuitbreaker/Resilience4jCircuitBreakerFactory.java
Show resolved
Hide resolved
...corp/armeria/resilience4j/circuitbreaker/client/Resilience4JCircuitBreakerClientHandler.java
Outdated
Show resolved
Hide resolved
107446a
to
2ff1064
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly looking good! 👍 👍 👍
core/src/main/java/com/linecorp/armeria/client/circuitbreaker/CircuitBreakerClientHandler.java
Show resolved
Hide resolved
.../com/linecorp/armeria/resilience4j/circuitbreaker/FailedCircuitBreakerDecisionException.java
Outdated
Show resolved
Hide resolved
...java/com/linecorp/armeria/resilience4j/circuitbreaker/Resilience4jCircuitBreakerFactory.java
Show resolved
Hide resolved
...n/java/com/linecorp/armeria/resilience4j/circuitbreaker/Resilience4jCircuitBreakerUtils.java
Outdated
Show resolved
Hide resolved
.../linecorp/armeria/resilience4j/circuitbreaker/client/Resilience4JCircuitBreakerCallback.java
Show resolved
Hide resolved
...corp/armeria/resilience4j/circuitbreaker/client/Resilience4JCircuitBreakerClientHandler.java
Outdated
Show resolved
Hide resolved
...m/linecorp/armeria/resilience4j/circuitbreaker/client/Resilience4jCircuitBreakerMapping.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work, @jrhee17! 👍 👍 👍
...java/com/linecorp/armeria/resilience4j/circuitbreaker/Resilience4jCircuitBreakerFactory.java
Show resolved
Hide resolved
Still looks good. 👍 |
Thanks for your hard work. You finally made it! 🚀🥳 |
wow ! 👀 thank you for your effort ! I will check this feature latest version :) 👍 |
The changes in line/armeria#4139 were not integrated back to upstream. I've verified that there aren't any more inconsistencies: ``` ~/Projects/gradle-scripts on main 1 % diff -r . ~/Projects/centraldogma/gradle/scripts Only in .: .git Only in /Users/jrhee17/Projects/centraldogma/gradle/scripts: .gitrepo Only in .: .gradle Only in .: .idea ```
Motivation:
Resilience4j provides utility patterns that are useful especially for microservices.
This PR attempts to integrate Resilience4j's circuit breaker utility into Armeria.
Modifications:
jdk 17
is required forresilience4j2
.:examples
,:resilience4j
modules are now built to target>= jre 17
< jre 17
.shadow-gradle-plugin
has been bumped to7.1.2
, and the module name has been updated togradle.plugin.com.github.johnrengelman:shadow
.gradle-scripts
now must define a dependency ofgradle.plugin.com.github.johnrengelman:shadow
instead ofcom.github.jengelman.gradle.plugins:shadow
to shade dependencies.CircuitBreakerRpcClient#newDecorator(CircuitBreakerClientHandler<RpcRequest> handler, CircuitBreakerRuleWithContent<RpcResponse> ruleWithContent
) so thatresilience4j2
module can support rpc decorators.resilience4j2
module has been introduced.Resilience4jCircuitBreakerMappingBuilder
which is analogous toCircuitBreakerMappingBuilder
has been introduced.Resilience4jCircuitBreakerMapping
which is analogous toCircuitBreakerMapping
has been introduced.Resilience4JCircuitBreakerClientHandler
andResilience4JCircuitBreakerCallback
have been introduced to useresilience4j
'sCircuitBreaker
in conjunction withCircuitBreakerClient
.Resilience4jCircuitBreakerFactory
has been introduced for users who may want to customize howCircuitBreaker
is generated based on host, path, and method.Resilience4jCircuitBreakerUtils
is added to hide the default implementation ofResilience4jCircuitBreakerFactory
FailedCircuitBreakerDecisionException
has been added to use as the default exception when aThrowable
is not available forCircuitBreaker#onError
.:examples:resilience4j-spring
module has been introducedresilience4j-spring-boot2
andresilience4j-micrometer
.it:resilience4j
module has been added to verify rpc integration.Result: