Skip to content

Commit 3a1f664

Browse files
authored
Merge pull request #1738 from chengyouling/develop-rpc
add FeignClient invoke tag transmission plugin
2 parents 9a1a189 + b37b09f commit 3a1f664

File tree

8 files changed

+410
-1
lines changed

8 files changed

+410
-1
lines changed

sermant-agentcore/sermant-agentcore-config/config/plugins.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
# 4. Bytecode enhancements at the same intercept point for different plugins take effect in the same order as the plugins load.
77
# 'plugins' is used to configure static plugins. The plugins take effect when Sermant starts by premain. Plugins that support static installation are configured and cannot be uninstalled
88
plugins:
9+
- tag-transmission
910
- flowcontrol
1011
- service-router
1112
- service-registry
@@ -16,7 +17,6 @@ plugins:
1617
- mq-consume-prohibition
1718
- service-removal
1819
- service-visibility
19-
- tag-transmission
2020
- database-write-prohibition
2121
- mq-grayscale
2222
# 'dynamicPlugins' is used to configure plugins that support dynamic installation. The plugins take effect when Sermant starts by agentmain. allows uninstallation:

sermant-plugins/sermant-tag-transmission/pom.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
<module>tag-transmission-dubbo2.7.x-plugin</module>
5454
<module>tag-transmission-dubbo3.x-plugin</module>
5555
<module>tag-transmission-service</module>
56+
<module>tag-transmission-feign11.x-plugin</module>
5657
</modules>
5758
</profile>
5859
<profile>
@@ -75,6 +76,7 @@
7576
<module>tag-transmission-dubbo2.7.x-plugin</module>
7677
<module>tag-transmission-dubbo3.x-plugin</module>
7778
<module>tag-transmission-service</module>
79+
<module>tag-transmission-feign11.x-plugin</module>
7880
</modules>
7981
</profile>
8082
<profile>
@@ -97,6 +99,7 @@
9799
<module>tag-transmission-dubbo2.7.x-plugin</module>
98100
<module>tag-transmission-dubbo3.x-plugin</module>
99101
<module>tag-transmission-service</module>
102+
<module>tag-transmission-feign11.x-plugin</module>
100103
</modules>
101104
</profile>
102105
</profiles>
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<groupId>io.sermant</groupId>
7+
<artifactId>sermant-tag-transmission</artifactId>
8+
<version>1.0.0</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>tag-transmission-feign11.x-plugin</artifactId>
13+
14+
<properties>
15+
<maven.compiler.source>8</maven.compiler.source>
16+
<maven.compiler.target>8</maven.compiler.target>
17+
<config.skip.flag>false</config.skip.flag>
18+
<package.plugin.type>plugin</package.plugin.type>
19+
<spring-web.version>5.3.30</spring-web.version>
20+
<feign-core.version>11.10</feign-core.version>
21+
</properties>
22+
23+
<dependencies>
24+
<dependency>
25+
<groupId>io.sermant</groupId>
26+
<artifactId>sermant-agentcore-core</artifactId>
27+
<scope>provided</scope>
28+
</dependency>
29+
<dependency>
30+
<groupId>io.sermant</groupId>
31+
<artifactId>tag-transmission-common</artifactId>
32+
</dependency>
33+
<dependency>
34+
<groupId>org.springframework</groupId>
35+
<artifactId>spring-web</artifactId>
36+
<version>${spring-web.version}</version>
37+
<scope>provided</scope>
38+
</dependency>
39+
<dependency>
40+
<groupId>io.github.openfeign</groupId>
41+
<artifactId>feign-core</artifactId>
42+
<version>${feign-core.version}</version>
43+
<scope>provided</scope>
44+
</dependency>
45+
<dependency>
46+
<groupId>junit</groupId>
47+
<artifactId>junit</artifactId>
48+
<scope>test</scope>
49+
</dependency>
50+
<dependency>
51+
<groupId>org.mockito</groupId>
52+
<artifactId>mockito-core</artifactId>
53+
<scope>test</scope>
54+
</dependency>
55+
<dependency>
56+
<groupId>org.mockito</groupId>
57+
<artifactId>mockito-inline</artifactId>
58+
<scope>test</scope>
59+
</dependency>
60+
<dependency>
61+
<groupId>org.apache.httpcomponents</groupId>
62+
<artifactId>httpclient</artifactId>
63+
<scope>test</scope>
64+
</dependency>
65+
</dependencies>
66+
67+
<build>
68+
<plugins>
69+
<plugin>
70+
<groupId>org.apache.maven.plugins</groupId>
71+
<artifactId>maven-shade-plugin</artifactId>
72+
</plugin>
73+
</plugins>
74+
</build>
75+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright (C) 2025-2025 Sermant Authors. All rights reserved.
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+
* http://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 io.sermant.tag.transmission.rpc.declarers;
18+
19+
import io.sermant.core.plugin.agent.declarer.AbstractPluginDeclarer;
20+
import io.sermant.core.plugin.agent.declarer.InterceptDeclarer;
21+
import io.sermant.core.plugin.agent.matcher.ClassMatcher;
22+
import io.sermant.core.plugin.agent.matcher.MethodMatcher;
23+
import io.sermant.tag.transmission.rpc.interceptors.FeignClientTagTransmissionInterceptor;
24+
25+
/**
26+
* The client enhancement class initiates the feign request method
27+
*
28+
* @author chengyouling
29+
* @since 2025-02-25
30+
*/
31+
public class FeignClientDeclarer extends AbstractPluginDeclarer {
32+
private static final String ENHANCE_CLASS = "feign.Client";
33+
34+
private static final String METHOD_NAME = "execute";
35+
36+
/**
37+
* Constructor
38+
*/
39+
@Override
40+
public ClassMatcher getClassMatcher() {
41+
return ClassMatcher.isExtendedFrom(ENHANCE_CLASS);
42+
}
43+
44+
@Override
45+
public InterceptDeclarer[] getInterceptDeclarers(ClassLoader classLoader) {
46+
return new InterceptDeclarer[]{
47+
InterceptDeclarer.build(MethodMatcher.nameEquals(METHOD_NAME),
48+
new FeignClientTagTransmissionInterceptor())
49+
};
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright (C) 2025-2025 Sermant Authors. All rights reserved.
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+
* http://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 io.sermant.tag.transmission.rpc.interceptors;
18+
19+
import feign.Request;
20+
import io.sermant.core.common.LoggerFactory;
21+
import io.sermant.core.plugin.agent.entity.ExecuteContext;
22+
import io.sermant.core.utils.CollectionUtils;
23+
import io.sermant.core.utils.ReflectUtils;
24+
import io.sermant.core.utils.tag.TrafficTag;
25+
import io.sermant.core.utils.tag.TrafficUtils;
26+
import io.sermant.tag.transmission.config.strategy.TagKeyMatcher;
27+
import io.sermant.tag.transmission.interceptors.AbstractClientInterceptor;
28+
29+
import java.util.Collection;
30+
import java.util.Collections;
31+
import java.util.HashMap;
32+
import java.util.List;
33+
import java.util.Map;
34+
import java.util.logging.Level;
35+
import java.util.logging.Logger;
36+
37+
/**
38+
* The client enhancement class, initiates the feign request method
39+
*
40+
* @author chengyouling
41+
* @since 2025-02-25
42+
*/
43+
public class FeignClientTagTransmissionInterceptor extends AbstractClientInterceptor<Map<String, Collection<String>>> {
44+
private static final Logger LOGGER = LoggerFactory.getLogger();
45+
46+
@Override
47+
protected ExecuteContext doBefore(ExecuteContext context) {
48+
Object argument = context.getArguments()[0];
49+
if (argument instanceof Request) {
50+
Request request = (Request) argument;
51+
LOGGER.log(Level.FINE, "feign client request url: {0}", request.url());
52+
Map<String, Collection<String>> headers = new HashMap<>(request.headers());
53+
injectTrafficTag2Carrier(headers);
54+
ReflectUtils.setFieldValue(request, "headers", Collections.unmodifiableMap(headers));
55+
LOGGER.log(Level.FINE, "after refactor feign client request headers: {0}", request.headers());
56+
}
57+
return context;
58+
}
59+
60+
@Override
61+
protected ExecuteContext doAfter(ExecuteContext context) {
62+
return context;
63+
}
64+
65+
@Override
66+
protected void injectTrafficTag2Carrier(Map<String, Collection<String>> headers) {
67+
TrafficTag trafficTag = TrafficUtils.getTrafficTag();
68+
for (Map.Entry<String, List<String>> entry : trafficTag.getTag().entrySet()) {
69+
String key = entry.getKey();
70+
if (!TagKeyMatcher.isMatch(key)) {
71+
continue;
72+
}
73+
List<String> values = entry.getValue();
74+
Collection<String> originValues = headers.get(key);
75+
if (originValues != null) {
76+
continue;
77+
}
78+
79+
// The server side converts the label value to list storage when it is not null. If it is null,
80+
// it directly puts null. Therefore, if the client side values are empty, they must be null.
81+
if (CollectionUtils.isEmpty(values)) {
82+
setHeaders(headers, key, Collections.emptyList());
83+
LOGGER.log(Level.FINE, "Traffic tag {0} has no values.", entry);
84+
continue;
85+
}
86+
setHeaders(headers, key, values);
87+
LOGGER.log(Level.FINE, "Traffic tag {0}={1} have been injected to feign client.",
88+
new Object[]{key, values});
89+
}
90+
}
91+
92+
private void setHeaders(Map<String, Collection<String>> headers, String key, List<String> values) {
93+
headers.put(key, values);
94+
}
95+
96+
@Override
97+
public ExecuteContext onThrow(ExecuteContext context) {
98+
return context;
99+
}
100+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#
2+
# Copyright (C) 2025-2025 Sermant Authors. All rights reserved.
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+
# http://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+
io.sermant.tag.transmission.rpc.declarers.FeignClientDeclarer
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright (C) 2025-2025 Sermant Authors. All rights reserved.
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+
* http://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 io.sermant.tag.transmission.rpc.interceptors;
18+
19+
import io.sermant.core.plugin.config.PluginConfigManager;
20+
import io.sermant.core.utils.tag.TrafficUtils;
21+
import io.sermant.tag.transmission.config.TagTransmissionConfig;
22+
23+
import org.junit.After;
24+
import org.junit.Before;
25+
import org.mockito.MockedStatic;
26+
import org.mockito.Mockito;
27+
28+
import java.util.ArrayList;
29+
import java.util.HashMap;
30+
import java.util.List;
31+
import java.util.Map;
32+
33+
/**
34+
* traffic label transparent ut basic test class
35+
*
36+
* @author chengyouling
37+
* @since 2025-02-25
38+
*/
39+
public class BaseInterceptorTest {
40+
public final TagTransmissionConfig tagTransmissionConfig = new TagTransmissionConfig();
41+
42+
public MockedStatic<PluginConfigManager> pluginConfigManagerMockedStatic;
43+
44+
public BaseInterceptorTest() {
45+
pluginConfigManagerMockedStatic = Mockito.mockStatic(PluginConfigManager.class);
46+
pluginConfigManagerMockedStatic.when(() -> PluginConfigManager.getPluginConfig(TagTransmissionConfig.class))
47+
.thenReturn(tagTransmissionConfig);
48+
}
49+
50+
@Before
51+
public void before() {
52+
tagTransmissionConfig.setEnabled(true);
53+
List<String> tagKeys = new ArrayList<>();
54+
tagKeys.add("x_lane_canary");
55+
Map<String, List<String>> matchRule = new HashMap<>();
56+
matchRule.put("exact", tagKeys);
57+
tagTransmissionConfig.setMatchRule(matchRule);
58+
TrafficUtils.removeTrafficTag();
59+
}
60+
61+
@After
62+
public void after() {
63+
TrafficUtils.removeTrafficTag();
64+
pluginConfigManagerMockedStatic.close();
65+
}
66+
}

0 commit comments

Comments
 (0)