Skip to content

Commit b63e552

Browse files
committed
feat: add perMethod, perHost, perPath to RetryConfigMapping
1 parent a9e3e4c commit b63e552

File tree

8 files changed

+183
-77
lines changed

8 files changed

+183
-77
lines changed

core/src/main/java/com/linecorp/armeria/client/circuitbreaker/KeyedCircuitBreakerMapping.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
2-
* Copyright 2016 LINE Corporation
2+
* Copyright 2025 LY Corporation
33
*
4-
* LINE Corporation licenses this file to you under the Apache License,
4+
* LY Corporation licenses this file to you under the Apache License,
55
* version 2.0 (the "License"); you may not use this file except in compliance
66
* with the License. You may obtain a copy of the License at:
77
*
@@ -16,9 +16,9 @@
1616

1717
package com.linecorp.armeria.client.circuitbreaker;
1818

19-
import static com.linecorp.armeria.internal.common.circuitbreaker.CircuitBreakerMappingUtil.host;
20-
import static com.linecorp.armeria.internal.common.circuitbreaker.CircuitBreakerMappingUtil.method;
21-
import static com.linecorp.armeria.internal.common.circuitbreaker.CircuitBreakerMappingUtil.path;
19+
import static com.linecorp.armeria.internal.common.RequestContextUtil.host;
20+
import static com.linecorp.armeria.internal.common.RequestContextUtil.method;
21+
import static com.linecorp.armeria.internal.common.RequestContextUtil.path;
2222
import static java.util.Objects.requireNonNull;
2323
import static java.util.stream.Collectors.joining;
2424

core/src/main/java/com/linecorp/armeria/client/retry/KeyedRetryConfigMapping.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
2-
* Copyright 2020 LINE Corporation
2+
* Copyright 2025 LY Corporation
33
*
4-
* LINE Corporation licenses this file to you under the Apache License,
4+
* LY Corporation licenses this file to you under the Apache License,
55
* version 2.0 (the "License"); you may not use this file except in compliance
66
* with the License. You may obtain a copy of the License at:
77
*
@@ -16,11 +16,17 @@
1616

1717
package com.linecorp.armeria.client.retry;
1818

19+
import static com.linecorp.armeria.internal.common.RequestContextUtil.host;
20+
import static com.linecorp.armeria.internal.common.RequestContextUtil.method;
21+
import static com.linecorp.armeria.internal.common.RequestContextUtil.path;
1922
import static java.util.Objects.requireNonNull;
23+
import static java.util.stream.Collectors.joining;
2024

25+
import java.util.Objects;
2126
import java.util.concurrent.ConcurrentHashMap;
2227
import java.util.concurrent.ConcurrentMap;
2328
import java.util.function.BiFunction;
29+
import java.util.stream.Stream;
2430

2531
import com.linecorp.armeria.client.ClientRequestContext;
2632
import com.linecorp.armeria.common.Request;
@@ -38,6 +44,27 @@ final class KeyedRetryConfigMapping<T extends Response> implements RetryConfigMa
3844
this.retryConfigFactory = requireNonNull(retryConfigFactory, "retryConfigFactory");
3945
}
4046

47+
KeyedRetryConfigMapping(
48+
boolean perHost, boolean perMethod, boolean perPath, RetryConfigFactory retryConfigFactory) {
49+
requireNonNull(retryConfigFactory, "retryConfigFactory");
50+
51+
keyFactory = (ctx, req) -> {
52+
final String host = perHost ? host(ctx) : null;
53+
final String method = perMethod ? method(ctx) : null;
54+
final String path = perPath ? path(ctx) : null;
55+
return Stream.of(host, method, path)
56+
.filter(Objects::nonNull)
57+
.collect(joining("#"));
58+
};
59+
60+
this.retryConfigFactory = (ctx, req) -> {
61+
final String host = perHost ? host(ctx) : null;
62+
final String method = perMethod ? method(ctx) : null;
63+
final String path = perPath ? path(ctx) : null;
64+
return retryConfigFactory.apply(host, method, path);
65+
};
66+
}
67+
4168
@Override
4269
public RetryConfig<T> get(ClientRequestContext ctx, Request req) {
4370
final String key = keyFactory.apply(ctx, req);

core/src/main/java/com/linecorp/armeria/internal/common/circuitbreaker/package-info.java renamed to core/src/main/java/com/linecorp/armeria/client/retry/RetryConfigFactory.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
2-
* Copyright 2022 LINE Corporation
2+
* Copyright 2025 LY Corporation
33
*
4-
* LINE Corporation licenses this file to you under the Apache License,
4+
* LY Corporation licenses this file to you under the Apache License,
55
* version 2.0 (the "License"); you may not use this file except in compliance
66
* with the License. You may obtain a copy of the License at:
77
*
@@ -14,11 +14,12 @@
1414
* under the License.
1515
*/
1616

17-
/**
18-
* CircuitBreaker related classes used internally.
19-
* Anything in this package can be changed or removed at any time.
20-
*/
21-
@NonNullByDefault
22-
package com.linecorp.armeria.internal.common.circuitbreaker;
17+
package com.linecorp.armeria.client.retry;
18+
19+
import com.linecorp.armeria.common.Response;
20+
import com.linecorp.armeria.common.annotation.Nullable;
2321

24-
import com.linecorp.armeria.common.annotation.NonNullByDefault;
22+
@FunctionalInterface
23+
public interface RetryConfigFactory<T extends Response> {
24+
RetryConfig<T> apply(@Nullable String host, @Nullable String method, @Nullable String path);
25+
}

core/src/main/java/com/linecorp/armeria/client/retry/RetryConfigMapping.java

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
2-
* Copyright 2020 LINE Corporation
2+
* Copyright 2025 LY Corporation
33
*
4-
* LINE Corporation licenses this file to you under the Apache License,
4+
* LY Corporation licenses this file to you under the Apache License,
55
* version 2.0 (the "License"); you may not use this file except in compliance
66
* with the License. You may obtain a copy of the License at:
77
*
@@ -16,15 +16,18 @@
1616

1717
package com.linecorp.armeria.client.retry;
1818

19+
import static java.util.Objects.requireNonNull;
20+
1921
import java.util.function.BiFunction;
22+
import java.util.function.Function;
2023

2124
import com.linecorp.armeria.client.ClientRequestContext;
2225
import com.linecorp.armeria.common.Request;
2326
import com.linecorp.armeria.common.Response;
2427

2528
/**
2629
* Returns a {@link RetryConfig} given the {@link ClientRequestContext}.
27-
* Allows users to change retry behavior according to any context element, like host, method, path ...etc.
30+
* Allows users to change retry behavior according to any context element, like host, method, path, etc.
2831
*/
2932
@FunctionalInterface
3033
public interface RetryConfigMapping<T extends Response> {
@@ -49,13 +52,57 @@ public interface RetryConfigMapping<T extends Response> {
4952
* };
5053
* RetryConfigMapping mapping = RetryConfigMapping.of(keyFactory, configFactory);
5154
* } </pre>
55+
*
56+
* @deprecated Use {@link #perRequestAndContext(BiFunction, BiFunction)} instead.
5257
*/
58+
@Deprecated
5359
static <T extends Response> RetryConfigMapping<T> of(
5460
BiFunction<? super ClientRequestContext, Request, String> keyFactory,
5561
BiFunction<? super ClientRequestContext, Request, RetryConfig<T>> retryConfigFactory) {
5662
return new KeyedRetryConfigMapping<>(keyFactory, retryConfigFactory);
5763
}
5864

65+
static <T extends Response> RetryConfigMapping<T> perRequestAndContext(
66+
BiFunction<? super ClientRequestContext, Request, String> keyFactory,
67+
BiFunction<? super ClientRequestContext, Request, RetryConfig<T>> retryConfigFactory
68+
) {
69+
return new KeyedRetryConfigMapping<>(keyFactory, retryConfigFactory);
70+
}
71+
72+
static <T extends Response> RetryConfigMappingBuilder<T> builder() {
73+
return new RetryConfigMappingBuilder<>();
74+
}
75+
76+
static <T extends Response> RetryConfigMapping<T> perMethod(Function<String, RetryConfig<T>> factory) {
77+
requireNonNull(factory, "factory");
78+
return RetryConfigMapping.<T>builder()
79+
.perMethod()
80+
.build((host, method, path) -> factory.apply(method));
81+
}
82+
83+
static <T extends Response> RetryConfigMapping<T> perHost(Function<String, RetryConfig<T>> factory) {
84+
requireNonNull(factory, "factory");
85+
return RetryConfigMapping.<T>builder()
86+
.perHost()
87+
.build((host, method, path) -> factory.apply(host));
88+
}
89+
90+
static <T extends Response> RetryConfigMapping<T> perPath(Function<String, RetryConfig<T>> factory) {
91+
requireNonNull(factory, "factory");
92+
return RetryConfigMapping.<T>builder()
93+
.perPath()
94+
.build((host, method, path) -> factory.apply(path));
95+
}
96+
97+
static <T extends Response> RetryConfigMapping<T> perHostAndMethod(
98+
BiFunction<String, String, RetryConfig<T>> factory) {
99+
requireNonNull(factory, "factory");
100+
return RetryConfigMapping.<T>builder()
101+
.perHost()
102+
.perMethod()
103+
.build((host, method, path) -> factory.apply(host, method));
104+
}
105+
59106
/**
60107
* Returns the {@link RetryConfig} that maps to the given {@link ClientRequestContext} and {@link Request}.
61108
*/
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2025 LY Corporation
3+
*
4+
* LY Corporation licenses this file to you under the Apache License,
5+
* version 2.0 (the "License"); you may not use this file except in compliance
6+
* with the License. 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, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
package com.linecorp.armeria.client.retry;
18+
19+
import com.linecorp.armeria.common.Response;
20+
21+
public final class RetryConfigMappingBuilder<T extends Response> {
22+
private boolean perHost;
23+
private boolean perMethod;
24+
private boolean perPath;
25+
26+
public RetryConfigMappingBuilder<T> perHost() {
27+
perHost = true;
28+
return this;
29+
}
30+
31+
public RetryConfigMappingBuilder<T> perMethod() {
32+
perMethod = true;
33+
return this;
34+
}
35+
36+
public RetryConfigMappingBuilder<T> perPath() {
37+
perPath = true;
38+
return this;
39+
}
40+
41+
private boolean validateMappingKeys() {
42+
return perHost || perMethod || perPath;
43+
}
44+
45+
public RetryConfigMapping<T> build(RetryConfigFactory retryConfigFactory) {
46+
if (!validateMappingKeys()) {
47+
throw new IllegalStateException(
48+
"A RetryConfigMapping created by this builder must be per host, method and/or path");
49+
}
50+
51+
return new KeyedRetryConfigMapping<>(
52+
perHost, perMethod, perPath, retryConfigFactory
53+
);
54+
}
55+
}

core/src/main/java/com/linecorp/armeria/internal/common/RequestContextUtil.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
2-
* Copyright 2019 LINE Corporation
2+
* Copyright 2025 LY Corporation
33
*
4-
* LINE Corporation licenses this file to you under the Apache License,
4+
* LY Corporation licenses this file to you under the Apache License,
55
* version 2.0 (the "License"); you may not use this file except in compliance
66
* with the License. You may obtain a copy of the License at:
77
*
@@ -30,12 +30,15 @@
3030
import com.google.common.collect.MapMaker;
3131
import com.google.errorprone.annotations.MustBeClosed;
3232

33+
import com.linecorp.armeria.client.ClientRequestContext;
34+
import com.linecorp.armeria.client.Endpoint;
3335
import com.linecorp.armeria.common.ContextHolder;
3436
import com.linecorp.armeria.common.Flags;
3537
import com.linecorp.armeria.common.HttpRequest;
3638
import com.linecorp.armeria.common.RequestContext;
3739
import com.linecorp.armeria.common.RequestContextStorage;
3840
import com.linecorp.armeria.common.RequestContextStorageProvider;
41+
import com.linecorp.armeria.common.RpcRequest;
3942
import com.linecorp.armeria.common.annotation.Nullable;
4043
import com.linecorp.armeria.common.util.SafeCloseable;
4144
import com.linecorp.armeria.common.util.Sampler;
@@ -267,5 +270,29 @@ public static void ensureSameCtx(RequestContext ctx, ContextHolder contextHolder
267270
}
268271
}
269272

273+
public static String host(ClientRequestContext ctx) {
274+
final Endpoint endpoint = ctx.endpoint();
275+
if (endpoint == null) {
276+
return "UNKNOWN";
277+
} else {
278+
final String ipAddr = endpoint.ipAddr();
279+
if (ipAddr == null || endpoint.isIpAddrOnly()) {
280+
return endpoint.authority();
281+
} else {
282+
return endpoint.authority() + '/' + ipAddr;
283+
}
284+
}
285+
}
286+
287+
public static String method(ClientRequestContext ctx) {
288+
final RpcRequest rpcReq = ctx.rpcRequest();
289+
return rpcReq != null ? rpcReq.method() : ctx.method().name();
290+
}
291+
292+
public static String path(ClientRequestContext ctx) {
293+
final HttpRequest request = ctx.request();
294+
return request == null ? "" : request.path();
295+
}
296+
270297
private RequestContextUtil() {}
271298
}

core/src/main/java/com/linecorp/armeria/internal/common/circuitbreaker/CircuitBreakerMappingUtil.java

Lines changed: 0 additions & 51 deletions
This file was deleted.

resilience4j2/src/main/java/com/linecorp/armeria/resilience4j/circuitbreaker/client/KeyedResilience4jCircuitBreakerMapping.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
2-
* Copyright 2022 LINE Corporation
2+
* Copyright 2025 LY Corporation
33
*
4-
* LINE Corporation licenses this file to you under the Apache License,
4+
* LY Corporation licenses this file to you under the Apache License,
55
* version 2.0 (the "License"); you may not use this file except in compliance
66
* with the License. You may obtain a copy of the License at:
77
*
@@ -16,9 +16,9 @@
1616

1717
package com.linecorp.armeria.resilience4j.circuitbreaker.client;
1818

19-
import static com.linecorp.armeria.internal.common.circuitbreaker.CircuitBreakerMappingUtil.host;
20-
import static com.linecorp.armeria.internal.common.circuitbreaker.CircuitBreakerMappingUtil.method;
21-
import static com.linecorp.armeria.internal.common.circuitbreaker.CircuitBreakerMappingUtil.path;
19+
import static com.linecorp.armeria.internal.common.RequestContextUtil.host;
20+
import static com.linecorp.armeria.internal.common.RequestContextUtil.method;
21+
import static com.linecorp.armeria.internal.common.RequestContextUtil.path;
2222

2323
import com.google.common.base.MoreObjects;
2424

0 commit comments

Comments
 (0)