[ISSUE#8481] Add Trace Event SPI for Naming. (#9336)

* Add Trace plugin spi.

* Add NacosCombinedTraceSubscriber

* Subscribe NamingTraceEvent when start up cluster.

* Fix test error.
This commit is contained in:
杨翊 SionYang 2022-10-18 09:30:37 +08:00 committed by GitHub
parent ebe81f86c0
commit b1ac88be78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 511 additions and 19 deletions

View File

@ -30,9 +30,9 @@ import java.util.List;
public abstract class SmartSubscriber extends Subscriber {
/**
* Returns which event type are smartsubscriber interested in.
* Returns which event type are smart subscriber interested in.
*
* @return The interestd event types.
* @return The interested event types.
*/
public abstract List<Class<? extends Event>> subscribeTypes();

View File

@ -103,7 +103,6 @@ public class TraceEventPublisher extends Thread implements ShardedEventPublisher
boolean success = this.queue.offer(event);
if (!success) {
LOGGER.warn("Trace Event Publish failed, event : {}, publish queue size : {}", event, currentEventSize());
return true;
}
return true;
}

View File

@ -50,7 +50,6 @@ public class TraceEventPublisherFactory implements EventPublisherFactory {
@Override
public EventPublisher apply(final Class<? extends Event> eventType, final Integer maxQueueSize) {
// Like ClientEvent$ClientChangeEvent cache by ClientEvent
Class<? extends Event> cachedEventType = TraceEvent.class;
for (Class<? extends Event> publisherEvent : publisherEvents) {

View File

@ -63,6 +63,10 @@
<groupId>${project.groupId}</groupId>
<artifactId>nacos-auth</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-trace-plugin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@ -0,0 +1,88 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.core.trace;
import com.alibaba.nacos.common.notify.Event;
import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.common.notify.listener.SmartSubscriber;
import com.alibaba.nacos.common.trace.event.TraceEvent;
import com.alibaba.nacos.common.trace.publisher.TraceEventPublisherFactory;
import com.alibaba.nacos.plugin.trace.NacosTracePluginManager;
import com.alibaba.nacos.plugin.trace.spi.NacosTraceSubscriber;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Combined trace events subscriber.
*
* @author xiweng.yy
*/
public class NacosCombinedTraceSubscriber extends SmartSubscriber {
private final Map<Class<? extends TraceEvent>, Set<NacosTraceSubscriber>> interestedEvents;
public NacosCombinedTraceSubscriber(Class<? extends TraceEvent> combinedEvent) {
this.interestedEvents = new ConcurrentHashMap<>();
TraceEventPublisherFactory.getInstance().addPublisherEvent(combinedEvent);
for (NacosTraceSubscriber each : NacosTracePluginManager.getInstance().getAllTraceSubscribers()) {
filterInterestedEvents(each, combinedEvent);
}
NotifyCenter.registerSubscriber(this, TraceEventPublisherFactory.getInstance());
}
private void filterInterestedEvents(NacosTraceSubscriber plugin, Class<? extends TraceEvent> combinedEvent) {
for (Class<? extends TraceEvent> each : plugin.subscribeTypes()) {
if (combinedEvent.isAssignableFrom(each)) {
interestedEvents.compute(each, (eventClass, nacosTraceSubscribers) -> {
if (null == nacosTraceSubscribers) {
nacosTraceSubscribers = new HashSet<>();
}
nacosTraceSubscribers.add(plugin);
return nacosTraceSubscribers;
});
}
}
}
@Override
public List<Class<? extends Event>> subscribeTypes() {
return new LinkedList<>(interestedEvents.keySet());
}
@Override
public void onEvent(Event event) {
Set<NacosTraceSubscriber> subscribers = interestedEvents.get(event.getClass());
if (null == subscribers) {
return;
}
for (NacosTraceSubscriber each : subscribers) {
try {
each.onEvent((TraceEvent) event);
} catch (Exception ignored) {
}
}
}
public void shutdown() {
NotifyCenter.deregisterSubscriber(this);
}
}

View File

@ -0,0 +1,108 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.core.trace;
import com.alibaba.nacos.common.notify.Event;
import com.alibaba.nacos.common.trace.DeregisterInstanceReason;
import com.alibaba.nacos.common.trace.event.TraceEvent;
import com.alibaba.nacos.common.trace.event.naming.DeregisterInstanceTraceEvent;
import com.alibaba.nacos.common.trace.event.naming.NamingTraceEvent;
import com.alibaba.nacos.common.trace.event.naming.RegisterInstanceTraceEvent;
import com.alibaba.nacos.plugin.trace.NacosTracePluginManager;
import com.alibaba.nacos.plugin.trace.spi.NacosTraceSubscriber;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@SuppressWarnings("all")
@RunWith(MockitoJUnitRunner.class)
public class NacosCombinedTraceSubscriberTest {
@Mock
private NacosTraceSubscriber mockSubscriber;
@Mock
private NacosTraceSubscriber mockSubscriber2;
private NacosCombinedTraceSubscriber combinedTraceSubscriber;
@Before
public void setUp() throws Exception {
Map<String, NacosTraceSubscriber> traceSubscribers = (Map<String, NacosTraceSubscriber>) ReflectionTestUtils
.getField(NacosTracePluginManager.getInstance(), "traceSubscribers");
traceSubscribers.put("nacos-combined", mockSubscriber);
traceSubscribers.put("nacos-combined2", mockSubscriber2);
List<Class<? extends TraceEvent>> testEvents = new LinkedList<>();
testEvents.add(RegisterInstanceTraceEvent.class);
testEvents.add(DeregisterInstanceTraceEvent.class);
testEvents.add(TraceEvent.class);
when(mockSubscriber.subscribeTypes()).thenReturn(testEvents);
when(mockSubscriber2.subscribeTypes()).thenReturn(Collections.singletonList(RegisterInstanceTraceEvent.class));
combinedTraceSubscriber = new NacosCombinedTraceSubscriber(NamingTraceEvent.class);
}
@After
public void tearDown() throws Exception {
Map<String, NacosTraceSubscriber> traceSubscribers = (Map<String, NacosTraceSubscriber>) ReflectionTestUtils
.getField(NacosTracePluginManager.getInstance(), "traceSubscribers");
traceSubscribers.remove("nacos-combined");
traceSubscribers.remove("nacos-combined2");
combinedTraceSubscriber.shutdown();
}
@Test
public void testSubscribeTypes() {
List<Class<? extends Event>> actual = combinedTraceSubscriber.subscribeTypes();
assertEquals(2, actual.size());
assertTrue(actual.contains(RegisterInstanceTraceEvent.class));
assertTrue(actual.contains(DeregisterInstanceTraceEvent.class));
}
@Test
public void testOnEvent() {
RegisterInstanceTraceEvent event = new RegisterInstanceTraceEvent(1L, "", true, "", "", "", "", 1);
doThrow(new RuntimeException("test")).when(mockSubscriber2).onEvent(event);
combinedTraceSubscriber.onEvent(event);
verify(mockSubscriber).onEvent(event);
verify(mockSubscriber2).onEvent(event);
DeregisterInstanceTraceEvent event1 = new DeregisterInstanceTraceEvent(1L, "", true,
DeregisterInstanceReason.REQUEST, "", "", "", "", 1);
combinedTraceSubscriber.onEvent(event1);
verify(mockSubscriber).onEvent(event1);
verify(mockSubscriber2, never()).onEvent(event1);
TraceEvent event2 = new TraceEvent("", 1L, "", "", "");
combinedTraceSubscriber.onEvent(event2);
verify(mockSubscriber, never()).onEvent(event2);
verify(mockSubscriber2, never()).onEvent(event2);
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.naming.misc;
import com.alibaba.nacos.common.trace.event.naming.NamingTraceEvent;
import com.alibaba.nacos.core.trace.NacosCombinedTraceSubscriber;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* Naming Trace event initializer.
*
* @author xiweng.yy
*/
@Component
public class NamingTraceEventInitializer {
@PostConstruct
public void registerSubscriberForNamingEvent() {
new NacosCombinedTraceSubscriber(NamingTraceEvent.class);
}
}

View File

@ -27,8 +27,6 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
@ -45,16 +43,12 @@ public class ClusterControllerTest extends BaseTest {
@Mock
private HttpServletRequest request;
private Map<String, String[]> parameters;
private ClusterController clusterController;
@Before
public void before() {
super.before();
clusterController = new ClusterController(clusterOperatorV2);
parameters = new HashMap<>();
when(request.getParameterMap()).thenReturn(parameters);
}
@Test
@ -73,6 +67,5 @@ public class ClusterControllerTest extends BaseTest {
private void mockRequestParameter(String paramKey, String value) {
when(request.getParameter(paramKey)).thenReturn(value);
parameters.put(paramKey, new String[] {value});
}
}

View File

@ -43,10 +43,8 @@ import org.mockito.junit.MockitoJUnitRunner;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
@ -66,8 +64,6 @@ public class InstanceControllerTest extends BaseTest {
@Mock
private HttpServletRequest request;
private Map<String, String[]> parameters;
private SmartSubscriber subscriber;
private volatile boolean eventReceived = false;
@ -79,8 +75,6 @@ public class InstanceControllerTest extends BaseTest {
public void before() {
super.before();
when(switchDomain.isDefaultInstanceEphemeral()).thenReturn(true);
parameters = new HashMap<>();
when(request.getParameterMap()).thenReturn(parameters);
subscriber = new SmartSubscriber() {
@Override
public List<Class<? extends Event>> subscribeTypes() {
@ -111,7 +105,6 @@ public class InstanceControllerTest extends BaseTest {
private void mockRequestParameter(String key, String value) {
when(request.getParameter(key)).thenReturn(value);
parameters.put(key, new String[] {value});
}
@Test

View File

@ -32,6 +32,7 @@
<modules>
<module>auth</module>
<module>encryption</module>
<module>trace</module>
</modules>
<packaging>pom</packaging>

39
plugin/trace/pom.xml Normal file
View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 1999-2021 Alibaba Group Holding Ltd.
~
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>nacos-plugin</artifactId>
<groupId>com.alibaba.nacos</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>nacos-trace-plugin</artifactId>
<name>nacos-trace-plugin ${project.version}</name>
<url>http://nacos.io</url>
<dependencies>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-common</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,59 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.plugin.trace;
import com.alibaba.nacos.common.spi.NacosServiceLoader;
import com.alibaba.nacos.plugin.trace.spi.NacosTraceSubscriber;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Nacos trace event subscriber manager.
*
* @author xiweng.yy
*/
public class NacosTracePluginManager {
private static final Logger LOGGER = LoggerFactory.getLogger(NacosTracePluginManager.class);
private static final NacosTracePluginManager INSTANCE = new NacosTracePluginManager();
private final Map<String, NacosTraceSubscriber> traceSubscribers;
private NacosTracePluginManager() {
this.traceSubscribers = new ConcurrentHashMap<>();
Collection<NacosTraceSubscriber> plugins = NacosServiceLoader.load(NacosTraceSubscriber.class);
for (NacosTraceSubscriber each : plugins) {
this.traceSubscribers.put(each.getName(), each);
LOGGER.info("[TracePluginManager] Load NacosTraceSubscriber({}) name({}) successfully.", each.getClass(),
each.getName());
}
}
public static NacosTracePluginManager getInstance() {
return INSTANCE;
}
public Collection<NacosTraceSubscriber> getAllTraceSubscribers() {
return new HashSet<>(traceSubscribers.values());
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.plugin.trace.spi;
import com.alibaba.nacos.common.trace.event.TraceEvent;
import java.util.List;
import java.util.concurrent.Executor;
/**
* Nacos trace event subscriber.
*
* @author xiweng.yy
*/
public interface NacosTraceSubscriber {
/**
* Get the plugin name, if the same name has loaded by nacos, the older one will be replaced by new one.
*
* @return plugin name
*/
String getName();
/**
* Event callback.
*
* @param event {@link TraceEvent}
*/
void onEvent(TraceEvent event);
/**
* Returns which trace events are this subscriber interested in.
*
* @return The interested event types.
*/
List<Class<? extends TraceEvent>> subscribeTypes();
/**
* It is up to the listener to determine whether the callback is asynchronous or synchronous.
*
* @return {@link Executor}
*/
default Executor executor() {
return null;
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.plugin.trace;
import com.alibaba.nacos.plugin.trace.spi.NacosTraceSubscriber;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
public class NacosTracePluginManagerTest {
@BeforeClass
public static void setUp() {
NacosTracePluginManager.getInstance();
}
@Test
public void testGetAllTraceSubscribers() {
assertFalse(NacosTracePluginManager.getInstance().getAllTraceSubscribers().isEmpty());
assertContainsTestPlugin();
}
private void assertContainsTestPlugin() {
for (NacosTraceSubscriber each : NacosTracePluginManager.getInstance().getAllTraceSubscribers()) {
if ("trace-plugin-mock".equals(each.getName())) {
return;
}
}
Assert.fail("No found plugin named 'trace-plugin-mock'");
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 1999-2021 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.nacos.plugin.trace.mock;
import com.alibaba.nacos.common.trace.event.TraceEvent;
import com.alibaba.nacos.plugin.trace.spi.NacosTraceSubscriber;
import java.util.List;
public class MockNacosTraceSubscriber implements NacosTraceSubscriber {
@Override
public String getName() {
return "trace-plugin-mock";
}
@Override
public void onEvent(TraceEvent event) {
}
@Override
public List<Class<? extends TraceEvent>> subscribeTypes() {
return null;
}
}

View File

@ -0,0 +1,17 @@
#
# Copyright 1999-2021 Alibaba Group Holding Ltd.
#
# 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
#r
# 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.
#
com.alibaba.nacos.plugin.trace.mock.MockNacosTraceSubscriber

View File

@ -714,6 +714,11 @@
<artifactId>nacos-encryption-plugin</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-trace-plugin</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>nacos-auth</artifactId>

View File

@ -65,8 +65,12 @@ public class RestAPI_ITCase extends NamingBase {
JsonNode json = JacksonUtils.toObj(response.getBody());
Assert.assertNotNull(json.get("serviceCount"));
Assert.assertNotNull(json.get("instanceCount"));
Assert.assertNotNull(json.get("responsibleServiceCount"));
Assert.assertNotNull(json.get("responsibleInstanceCount"));
Assert.assertNotNull(json.get("clientCount"));
Assert.assertNotNull(json.get("connectionBasedClientCount"));
Assert.assertNotNull(json.get("ephemeralIpPortClientCount"));
Assert.assertNotNull(json.get("persistentIpPortClientCount"));
Assert.assertNotNull(json.get("responsibleClientCount"));
}
/**