forked from resilience4j/resilience4j
-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
FeignDecorators.java
125 lines (110 loc) · 4.52 KB
/
FeignDecorators.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
*
* Copyright 2018
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*
*
*/
package io.github.resilience4j.feign;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import feign.InvocationHandlerFactory.MethodHandler;
import feign.Target;
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.ratelimiter.RateLimiter;
import io.vavr.CheckedFunction1;
/**
* Builder to help build stacked decorators. <br>
*
* <pre>
* {@code
* CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("backendName");
* RateLimiter rateLimiter = RateLimiter.ofDefaults("backendName");
* FeignDecorators decorators = FeignDecorators.builder()
* .withCircuitBreaker(circuitBreaker)
* .withRateLimiter(rateLimiter)
* .build();
* MyService myService = Resilience4jFeign.builder(decorators).target(MyService.class, "http://localhost:8080/");
* }
* </pre>
*
* The order in which decorators are applied correspond to the order in which they are declared. For
* example, calling {@link FeignDecorators.Builder#withFallback(Object)} before
* {@link FeignDecorators.Builder#withCircuitBreaker(CircuitBreaker)} would mean that the fallback
* is called when the HTTP request fails, but would no longer be reachable if the CircuitBreaker
* were open. However, reversing the order would mean that the fallback is called both when the HTTP
* request fails and when the CircuitBreaker is open. <br>
* So be wary of this when designing your "resilience" strategy.
*/
public class FeignDecorators implements FeignDecorator {
private final List<FeignDecorator> decorators;
private FeignDecorators(List<FeignDecorator> decorators) {
this.decorators = decorators;
}
@Override
public CheckedFunction1<Object[], Object> decorate(CheckedFunction1<Object[], Object> fn,
Method method, MethodHandler methodHandler, Target<?> target) {
CheckedFunction1<Object[], Object> decoratedFn = fn;
for (final FeignDecorator decorator : decorators) {
decoratedFn = decorator.decorate(decoratedFn, method, methodHandler, target);
}
return decoratedFn;
}
public static Builder builder() {
return new Builder();
}
public static final class Builder {
private final List<FeignDecorator> decorators = new ArrayList<>();
/**
* Adds a {@link CircuitBreaker} to the decorator chain.
*
* @param rateLimiter a fully configured {@link CircuitBreaker}.
* @return the builder
*/
public Builder withCircuitBreaker(CircuitBreaker circuitBreaker) {
decorators.add((fn, m, mh, t) -> CircuitBreaker.decorateCheckedFunction(circuitBreaker, fn));
return this;
}
/**
* Adds a {@link RateLimiter} to the decorator chain.
*
* @param rateLimiter a fully configured {@link RateLimiter}.
* @return the builder
*/
public Builder withRateLimiter(RateLimiter rateLimiter) {
decorators.add((fn, m, mh, t) -> RateLimiter.decorateCheckedFunction(rateLimiter, fn));
return this;
}
/**
* Adds a fallback to the decorator chain. Multiple fallbacks can be applied with the next
* fallback being called when the previous one fails.
*
* @param fallback must match the feign interface, i.e. the interface specified when calling
* {@link Resilience4jFeign.Builder#target(Class, String)}.
* @return the builder
*/
public Builder withFallback(Object fallback) {
decorators.add(new FallbackDecorator<Object>(fallback));
return this;
}
/**
* Builds the decorator chain. This can then be used to setup an instance of
* {@link Resilience4jFeign}.
*
* @return the decorators.
*/
public FeignDecorators build() {
return new FeignDecorators(decorators);
}
}
}