Skip to content

Commit 271af4f

Browse files
author
Ryan Baxter
committed
Provide an alternate naming convention for CB ids to allow for configuration via configuration properties
Configuration properties cannot contain characters like hash, parens, or commas
1 parent 765431d commit 271af4f

File tree

3 files changed

+139
-0
lines changed

3 files changed

+139
-0
lines changed

docs/src/main/asciidoc/spring-cloud-openfeign.adoc

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,47 @@ public class FooConfiguration {
372372

373373
To enable Spring Cloud CircuitBreaker group set the `feign.circuitbreaker.group.enabled` property to `true` (by default `false`).
374374

375+
[[spring-clou-feign-circuitbreaker-configurationproperties]]
376+
=== Configuring CircuitBreakers With Configuration Properties
377+
378+
You can configure CircuitBreakers via configuration properties. To do set
379+
`feign.circuitbreaker.alphanumericCircuitBreakerNameResolver.enabled` to `true`. Since
380+
you cannot use characters like `#`, `(`, `)` `,` in configuration property names we need to
381+
change the naming convention for the ids of the circuit breakers generated by OpenFeign. The above
382+
property will do this for you.
383+
384+
For example, if you had this Feign client
385+
386+
[source,java,indent=0]
387+
----
388+
@FeignClienturl = "http://localhost:8080")
389+
public interface DemoClient {
390+
391+
@GetMapping("demo")
392+
String getDemo();
393+
}
394+
----
395+
396+
You could configure it using configuration properties by doing the following
397+
398+
[source,yaml,indent=0]
399+
----
400+
feign:
401+
circuitbreaker:
402+
enabled: true
403+
alphanumericCircuitBreakerNameResolver:
404+
enabled: true
405+
resilience4j:
406+
circuitbreaker:
407+
instances:
408+
DemoClientgetDemo:
409+
minimumNumberOfCalls: 69
410+
timelimiter:
411+
instances:
412+
DemoClientgetDemo:
413+
timeoutDuration: 10s
414+
----
415+
375416

376417
[[spring-cloud-feign-circuitbreaker-fallback]]
377418
=== Feign Spring Cloud CircuitBreaker Fallbacks

spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,10 +166,20 @@ public Targeter defaultFeignTargeter() {
166166

167167
@Bean
168168
@ConditionalOnMissingBean(CircuitBreakerNameResolver.class)
169+
@ConditionalOnProperty(value = "feign.circuitbreaker.alphanumericNames.enabled",
170+
havingValue = "false", matchIfMissing = true)
169171
public CircuitBreakerNameResolver circuitBreakerNameResolver() {
170172
return new DefaultCircuitBreakerNameResolver();
171173
}
172174

175+
@Bean
176+
@ConditionalOnMissingBean(CircuitBreakerNameResolver.class)
177+
@ConditionalOnProperty(value = "feign.circuitbreaker.alphanumericNames.enabled",
178+
havingValue = "true")
179+
public CircuitBreakerNameResolver alphanumericCircuitBreakerNameResolver() {
180+
return new AlphanumericCircuitBreakerNameResolver();
181+
}
182+
173183
@Bean
174184
@ConditionalOnMissingBean
175185
@ConditionalOnBean(CircuitBreakerFactory.class)
@@ -189,6 +199,15 @@ public String resolveCircuitBreakerName(String feignClientName, Target<?> target
189199

190200
}
191201

202+
static class AlphanumericCircuitBreakerNameResolver extends DefaultCircuitBreakerNameResolver {
203+
204+
@Override
205+
public String resolveCircuitBreakerName(String feignClientName, Target<?> target, Method method) {
206+
return super.resolveCircuitBreakerName(feignClientName, target, method).replaceAll("[^a-zA-Z0-9]", "");
207+
}
208+
209+
}
210+
192211
}
193212

194213
// the following configuration is for alternate feign clients if
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright 2013-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.openfeign.circuitbreaker;
18+
19+
import feign.Target;
20+
import org.junit.jupiter.api.Nested;
21+
import org.junit.jupiter.api.Test;
22+
23+
import org.springframework.beans.factory.annotation.Autowired;
24+
import org.springframework.boot.test.context.SpringBootTest;
25+
import org.springframework.cloud.openfeign.CircuitBreakerNameResolver;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
import static org.mockito.Mockito.mock;
29+
import static org.mockito.Mockito.when;
30+
31+
/**
32+
* @author Ryan Baxter
33+
*/
34+
public class CircuitBreakerAutoConfigurationTests {
35+
36+
@SpringBootTest(classes = CircuitBreakerTests.Application.class,
37+
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
38+
value = { "spring.application.name=springcircuittest", "spring.jmx.enabled=false",
39+
"feign.circuitbreaker.enabled=true" })
40+
@Nested
41+
class DefaultNamingStrategy {
42+
43+
@Autowired
44+
CircuitBreakerNameResolver nameResolver;
45+
46+
@Test
47+
public void assertDefaultNamingStrategy() throws Exception {
48+
Target target = mock(Target.class);
49+
when(target.type()).thenReturn(CircuitBreakerTests.TestClientWithFactory.class);
50+
assertThat(nameResolver.resolveCircuitBreakerName("foo", target,
51+
CircuitBreakerTests.TestClientWithFactory.class.getMethod("getHello")))
52+
.isEqualTo("TestClientWithFactory#getHello()");
53+
}
54+
55+
}
56+
57+
@SpringBootTest(classes = CircuitBreakerTests.Application.class,
58+
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
59+
value = { "spring.application.name=springcircuittest", "spring.jmx.enabled=false",
60+
"feign.circuitbreaker.enabled=true",
61+
"feign.circuitbreaker.alphanumericNames.enabled=true" })
62+
@Nested
63+
class AlphanumericNamingStrategy {
64+
65+
@Autowired
66+
CircuitBreakerNameResolver nameResolver;
67+
68+
@Test
69+
public void assertAlphanumericNamingStrategy() throws Exception {
70+
Target target = mock(Target.class);
71+
when(target.type()).thenReturn(CircuitBreakerTests.TestClientWithFactory.class);
72+
assertThat(nameResolver.resolveCircuitBreakerName("foo", target,
73+
CircuitBreakerTests.TestClientWithFactory.class.getMethod("getHello")))
74+
.isEqualTo("TestClientWithFactorygetHello");
75+
}
76+
77+
}
78+
79+
}

0 commit comments

Comments
 (0)