Skip to content

Commit 7bc0897

Browse files
fix two problems in dubbo plugin (#2149)
* ensure that dubbo transaction will only be created at the provider side * fix the problem that the apm headers will be covered with unexpected value, which will be reproduced when the application invoke a dubbo api in a dubbo transaction. * update CHANGELOG.asciidoc for dubbo plugin bugfix * use random test ports * prevent few random port failures * refactor a bit dubbo tests Co-authored-by: Sylvain Juge <sylvain.juge@elastic.co>
1 parent e4f3eae commit 7bc0897

File tree

13 files changed

+259
-93
lines changed

13 files changed

+259
-93
lines changed

CHANGELOG.asciidoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ endif::[]
3737
* Fix slf4j-related `LinkageError` - {pull}2390[#2390]
3838
* Fix possible deadlock occurring when Byte Buddy reads System properties by warming up bytecode instrumentation code
3939
paths. The BCI warmup is on by default and may be disabled through the internal `warmup_byte_buddy` config option - {pull}2368[#2368]
40+
* Fixed few dubbo plugin issues - {pull}2149[#2149]
41+
** Dubbo transaction will should be created at the provider side
42+
** APM headers conversion issue within dubbo transaction
4043
4144
[[release-notes-1.x]]
4245
=== Java Agent version 1.x

apm-agent-core/src/test/java/co/elastic/apm/agent/testutils/TestPort.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public class TestPort {
3333
/**
3434
* @return random available TCP port
3535
*/
36-
public static int getAvailableRandomPort() {
36+
public static synchronized int getAvailableRandomPort() {
3737
int port;
3838
do {
3939
port = MIN_PORT + rand.nextInt(MAX_PORT - MIN_PORT + 1);
@@ -46,7 +46,7 @@ private static boolean isAvailablePort(int port) {
4646
ServerSocket serverSocket = ServerSocketFactory.getDefault().createServerSocket(port, 1, InetAddress.getByName("localhost"));
4747
serverSocket.close();
4848
return true;
49-
} catch (Exception var3) {
49+
} catch (Exception ignored) {
5050
return false;
5151
}
5252
}

apm-agent-plugins/apm-dubbo-plugin/src/main/java/co/elastic/apm/agent/dubbo/advice/AlibabaMonitorFilterAdvice.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,12 @@ public static Object onEnterFilterInvoke(@Advice.Argument(1) Invocation invocati
4848
Span span = DubboTraceHelper.createConsumerSpan(tracer, invocation.getInvoker().getInterface(),
4949
invocation.getMethodName(), context.getRemoteAddress());
5050
if (span != null) {
51-
span.propagateTraceContext(invocation, AlibabaDubboTextMapPropagator.INSTANCE);
51+
span.propagateTraceContext(context, AlibabaDubboTextMapPropagator.INSTANCE);
5252
return span;
5353
}
54-
} else if (active == null) {
54+
} else if (context.isProviderSide() && active == null) {
5555
// for provider side
56-
Transaction transaction = tracer.startChildTransaction(invocation, AlibabaDubboTextMapPropagator.INSTANCE, Invocation.class.getClassLoader());
56+
Transaction transaction = tracer.startChildTransaction(context, AlibabaDubboTextMapPropagator.INSTANCE, Invocation.class.getClassLoader());
5757
if (transaction != null) {
5858
transaction.activate();
5959
DubboTraceHelper.fillTransaction(transaction, invocation.getInvoker().getInterface(), invocation.getMethodName());

apm-agent-plugins/apm-dubbo-plugin/src/main/java/co/elastic/apm/agent/dubbo/advice/ApacheMonitorFilterAdvice.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,12 @@ public static Object onEnterFilterInvoke(@Advice.Argument(1) Invocation invocati
4949
Span span = DubboTraceHelper.createConsumerSpan(tracer, invocation.getInvoker().getInterface(),
5050
invocation.getMethodName(), context.getRemoteAddress());
5151
if (span != null) {
52-
span.propagateTraceContext(invocation, ApacheDubboTextMapPropagator.INSTANCE);
52+
span.propagateTraceContext(context, ApacheDubboTextMapPropagator.INSTANCE);
5353
return span;
5454
}
55-
} else if (active == null) {
55+
} else if (context.isProviderSide() && active == null) {
5656
// for provider side
57-
Transaction transaction = tracer.startChildTransaction(invocation, ApacheDubboTextMapPropagator.INSTANCE, Invocation.class.getClassLoader());
57+
Transaction transaction = tracer.startChildTransaction(context, ApacheDubboTextMapPropagator.INSTANCE, Invocation.class.getClassLoader());
5858
if (transaction != null) {
5959
transaction.activate();
6060
DubboTraceHelper.fillTransaction(transaction, invocation.getInvoker().getInterface(), invocation.getMethodName());

apm-agent-plugins/apm-dubbo-plugin/src/main/java/co/elastic/apm/agent/dubbo/helper/AlibabaDubboTextMapPropagator.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,27 @@
2020

2121
import co.elastic.apm.agent.impl.transaction.TextHeaderGetter;
2222
import co.elastic.apm.agent.impl.transaction.TextHeaderSetter;
23-
import com.alibaba.dubbo.rpc.Invocation;
23+
import com.alibaba.dubbo.rpc.RpcContext;
2424

2525
import javax.annotation.Nullable;
2626

27-
public enum AlibabaDubboTextMapPropagator implements TextHeaderGetter<Invocation>, TextHeaderSetter<Invocation> {
27+
public enum AlibabaDubboTextMapPropagator implements TextHeaderGetter<RpcContext>, TextHeaderSetter<RpcContext> {
2828

2929
INSTANCE;
3030

3131
@Nullable
3232
@Override
33-
public String getFirstHeader(String headerName, Invocation invocation) {
34-
return invocation.getAttachment(headerName);
33+
public String getFirstHeader(String headerName, RpcContext rpcContext) {
34+
return rpcContext.getAttachment(headerName);
3535
}
3636

3737
@Override
38-
public <S> void forEach(String headerName, Invocation invocation, S state, HeaderConsumer<String, S> consumer) {
39-
consumer.accept(invocation.getAttachment(headerName), state);
38+
public <S> void forEach(String headerName, RpcContext rpcContext, S state, HeaderConsumer<String, S> consumer) {
39+
consumer.accept(rpcContext.getAttachment(headerName), state);
4040
}
4141

4242
@Override
43-
public void setHeader(String headerName, String headerValue, Invocation invocation) {
44-
invocation.getAttachments().put(headerName, headerValue);
43+
public void setHeader(String headerName, String headerValue, RpcContext rpcContext) {
44+
rpcContext.setAttachment(headerName, headerValue);
4545
}
4646
}

apm-agent-plugins/apm-dubbo-plugin/src/main/java/co/elastic/apm/agent/dubbo/helper/ApacheDubboTextMapPropagator.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,28 @@
2020

2121
import co.elastic.apm.agent.impl.transaction.TextHeaderGetter;
2222
import co.elastic.apm.agent.impl.transaction.TextHeaderSetter;
23-
import org.apache.dubbo.rpc.Invocation;
23+
import org.apache.dubbo.rpc.RpcContext;
2424

2525
import javax.annotation.Nullable;
2626

27-
public enum ApacheDubboTextMapPropagator implements TextHeaderGetter<Invocation>, TextHeaderSetter<Invocation> {
27+
public enum ApacheDubboTextMapPropagator implements TextHeaderGetter<RpcContext>, TextHeaderSetter<RpcContext> {
2828

2929
INSTANCE;
3030

3131
@Nullable
3232
@Override
33-
public String getFirstHeader(String headerName, Invocation invocation) {
34-
return invocation.getAttachment(headerName);
33+
public String getFirstHeader(String headerName, RpcContext rpcContext) {
34+
return rpcContext.getAttachment(headerName);
3535
}
3636

3737
@Override
38-
public <S> void forEach(String headerName, Invocation invocation, S state, HeaderConsumer<String, S> consumer) {
39-
consumer.accept(invocation.getAttachment(headerName), state);
38+
public <S> void forEach(String headerName, RpcContext rpcContext, S state, HeaderConsumer<String, S> consumer) {
39+
//consumer.accept(invocation.getAttachment(headerName), state);
40+
consumer.accept(rpcContext.getAttachment(headerName), state);
4041
}
4142

4243
@Override
43-
public void setHeader(String headerName, String headerValue, Invocation invocation) {
44-
invocation.setAttachment(headerName, headerValue);
44+
public void setHeader(String headerName, String headerValue, RpcContext rpcContext) {
45+
rpcContext.setAttachment(headerName, headerValue);
4546
}
4647
}

apm-agent-plugins/apm-dubbo-plugin/src/test/java/co/elastic/apm/agent/dubbo/AbstractDubboInstrumentationTest.java

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,29 @@
2424
import co.elastic.apm.agent.dubbo.api.exception.BizException;
2525
import co.elastic.apm.agent.impl.context.Destination;
2626
import co.elastic.apm.agent.impl.error.ErrorCapture;
27+
import co.elastic.apm.agent.impl.transaction.AbstractSpan;
2728
import co.elastic.apm.agent.impl.transaction.Outcome;
2829
import co.elastic.apm.agent.impl.transaction.Span;
2930
import co.elastic.apm.agent.impl.transaction.Transaction;
31+
import co.elastic.apm.agent.testutils.TestPort;
3032
import org.junit.jupiter.api.AfterEach;
3133
import org.junit.jupiter.api.BeforeAll;
3234
import org.junit.jupiter.api.BeforeEach;
3335
import org.junit.jupiter.api.Test;
3436

3537
import javax.annotation.Nullable;
38+
import java.util.HashMap;
3639
import java.util.List;
40+
import java.util.Map;
3741

3842
import static org.assertj.core.api.Assertions.assertThat;
3943
import static org.mockito.Mockito.when;
4044

4145
public abstract class AbstractDubboInstrumentationTest extends AbstractInstrumentationTest {
4246

47+
private final int port = TestPort.getAvailableRandomPort();
48+
private final int anotherPort = TestPort.getAvailableRandomPort();
49+
4350
@Nullable
4451
private DubboTestApi dubboTestApi;
4552

@@ -171,5 +178,46 @@ public void validateDubboSpan(Span span, Class<?> apiClass, String methodName) {
171178
.isNotEqualTo(Outcome.UNKNOWN);
172179
}
173180

174-
abstract int getPort();
181+
protected int getPort() {
182+
return port;
183+
}
184+
185+
protected int getAnotherApiPort() {
186+
return anotherPort;
187+
}
188+
189+
@Test
190+
public void testBothProviderAndConsumer() {
191+
DubboTestApi dubboTestApi = getDubboTestApi();
192+
String arg = "hello, testBothProviderAndConsumer";
193+
String ret = dubboTestApi.willInvokeAnotherApi(arg);
194+
assertThat(ret).isEqualTo(arg);
195+
196+
assertThat(reporter.getFirstTransaction(5000)).isNotNull();
197+
assertThat(reporter.getTransactions()).hasSize(2);
198+
assertThat(reporter.getSpans()).hasSize(2);
199+
200+
Map<String, Transaction> transactionMap = buildMap(reporter.getTransactions());
201+
Map<String, Span> spanMap = buildMap(reporter.getSpans());
202+
203+
String testApiName = "DubboTestApi#willInvokeAnotherApi";
204+
String anotherApiName = "AnotherApi#echo";
205+
206+
assertThat(spanMap.get(testApiName).getTraceContext().getId().toString())
207+
.isEqualTo(transactionMap.get(testApiName).getTraceContext().getParentId().toString());
208+
209+
assertThat(transactionMap.get(testApiName).getTraceContext().getId().toString())
210+
.isEqualTo(spanMap.get(anotherApiName).getTraceContext().getParentId().toString());
211+
212+
assertThat(spanMap.get(anotherApiName).getTraceContext().getId().toString())
213+
.isEqualTo(transactionMap.get(anotherApiName).getTraceContext().getParentId().toString());
214+
}
215+
216+
public <T extends AbstractSpan<?>> Map<String, T> buildMap(List<T> list) {
217+
Map<String, T> map = new HashMap<>();
218+
for (T t : list) {
219+
map.put(t.getNameAsString(), t);
220+
}
221+
return map;
222+
}
175223
}

apm-agent-plugins/apm-dubbo-plugin/src/test/java/co/elastic/apm/agent/dubbo/AlibabaDubboInstrumentationTest.java

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
*/
1919
package co.elastic.apm.agent.dubbo;
2020

21+
import co.elastic.apm.agent.dubbo.api.AnotherApi;
2122
import co.elastic.apm.agent.dubbo.api.DubboTestApi;
2223
import co.elastic.apm.agent.dubbo.api.exception.BizException;
24+
import co.elastic.apm.agent.dubbo.api.impl.AnotherApiImpl;
2325
import co.elastic.apm.agent.dubbo.api.impl.DubboTestApiImpl;
2426
import co.elastic.apm.agent.impl.error.ErrorCapture;
2527
import co.elastic.apm.agent.impl.transaction.Span;
@@ -42,41 +44,27 @@
4244

4345
public class AlibabaDubboInstrumentationTest extends AbstractDubboInstrumentationTest {
4446

45-
private static ReferenceConfig<DubboTestApi> referenceConfig;
46-
47-
private static ServiceConfig<DubboTestApi> serviceConfig;
48-
4947
@Override
5048
protected DubboTestApi buildDubboTestApi() {
51-
ApplicationConfig providerAppConfig = new ApplicationConfig();
52-
providerAppConfig.setName("dubbo-provider");
49+
RegistryConfig registryConfig = createRegistryConfig();
50+
ApplicationConfig appConfig = createApplicationConfig();
5351

54-
ProtocolConfig protocolConfig = new ProtocolConfig();
55-
protocolConfig.setName("dubbo");
56-
protocolConfig.setPort(getPort());
57-
protocolConfig.setThreads(10);
52+
//build AnotherApi provider
53+
ProtocolConfig anotherApiProtocolConfig = createProtocolConfig(getAnotherApiPort());
54+
createAndExportServiceConfig(registryConfig, AnotherApi.class, new AnotherApiImpl(), appConfig, anotherApiProtocolConfig);
5855

59-
RegistryConfig registryConfig = new RegistryConfig();
60-
registryConfig.setAddress("N/A");
56+
//build AnotherApi consumer
57+
ReferenceConfig<AnotherApi> anotherApiReferenceConfig = createReferenceConfig(AnotherApi.class, appConfig, anotherApiProtocolConfig.getPort());
6158

62-
serviceConfig = new ServiceConfig<>();
63-
serviceConfig.setApplication(providerAppConfig);
64-
serviceConfig.setProtocol(protocolConfig);
65-
serviceConfig.setInterface(DubboTestApi.class);
66-
serviceConfig.setRef(new DubboTestApiImpl());
67-
serviceConfig.setRegistry(registryConfig);
68-
serviceConfig.export();
59+
// build DubboTestApi provider
60+
ProtocolConfig protocolConfig = createProtocolConfig(getPort());
61+
createAndExportServiceConfig(registryConfig, DubboTestApi.class, new DubboTestApiImpl(anotherApiReferenceConfig.get()), appConfig, protocolConfig);
6962

70-
ApplicationConfig consumerApp = new ApplicationConfig();
71-
consumerApp.setName("dubbo-consumer");
72-
referenceConfig = new ReferenceConfig<>();
73-
referenceConfig.setApplication(consumerApp);
74-
referenceConfig.setInterface(DubboTestApi.class);
75-
referenceConfig.setUrl("dubbo://localhost:" + getPort());
76-
referenceConfig.setTimeout(3000);
63+
// build DubboTestApi consumer
64+
ReferenceConfig<DubboTestApi> testApiReferenceConfig = createReferenceConfig(DubboTestApi.class, appConfig, protocolConfig.getPort());
7765

7866
List<MethodConfig> methodConfigList = new LinkedList<>();
79-
referenceConfig.setMethods(methodConfigList);
67+
testApiReferenceConfig.setMethods(methodConfigList);
8068
MethodConfig asyncConfig = new MethodConfig();
8169
asyncConfig.setName("async");
8270
asyncConfig.setAsync(true);
@@ -88,12 +76,51 @@ protected DubboTestApi buildDubboTestApi() {
8876
asyncNoReturnConfig.setReturn(false);
8977
methodConfigList.add(asyncNoReturnConfig);
9078

91-
return referenceConfig.get();
79+
return testApiReferenceConfig.get();
9280
}
9381

94-
@Override
95-
int getPort() {
96-
return 20880;
82+
private static RegistryConfig createRegistryConfig() {
83+
RegistryConfig registryConfig = new RegistryConfig();
84+
registryConfig.setAddress("N/A");
85+
return registryConfig;
86+
}
87+
88+
private static ApplicationConfig createApplicationConfig() {
89+
ApplicationConfig appConfig = new ApplicationConfig();
90+
appConfig.setName("all-in-one-app");
91+
return appConfig;
92+
}
93+
94+
private static ProtocolConfig createProtocolConfig(int port){
95+
ProtocolConfig protocolConfig = new ProtocolConfig();
96+
protocolConfig.setName("dubbo");
97+
protocolConfig.setPort(port);
98+
protocolConfig.setThreads(10);
99+
return protocolConfig;
100+
}
101+
102+
private static <T> void createAndExportServiceConfig(RegistryConfig registryConfig,
103+
Class<T> interfaceClass,
104+
T interfaceImpl,
105+
ApplicationConfig applicationConfig,
106+
ProtocolConfig protocolConfig) {
107+
108+
ServiceConfig<T> serviceConfig = new ServiceConfig<T>();
109+
serviceConfig.setApplication(applicationConfig);
110+
serviceConfig.setProtocol(protocolConfig);
111+
serviceConfig.setInterface(interfaceClass);
112+
serviceConfig.setRef(interfaceImpl);
113+
serviceConfig.setRegistry(registryConfig);
114+
serviceConfig.export();
115+
}
116+
117+
private static <T> ReferenceConfig<T> createReferenceConfig(Class<T> interfaceClass, ApplicationConfig applicationConfig, int port) {
118+
ReferenceConfig<T> referenceConfig = new ReferenceConfig<>();
119+
referenceConfig.setApplication(applicationConfig);
120+
referenceConfig.setInterface(interfaceClass);
121+
referenceConfig.setUrl(String.format("dubbo://localhost:%d", port));
122+
referenceConfig.setTimeout(3000);
123+
return referenceConfig;
97124
}
98125

99126
@Test

0 commit comments

Comments
 (0)