Remove Legacy Metrics

- Simplify MBeans - instead of wrapping to expose lifecycle methods,
  implement `ManageableLifecycle`. Register an additional MBean for
  polled endpoints to control the lifecycle.

* Polishing

- Move `QueueChannel` `@ManagedAttribute`s to `QueueChannelOperations`
- Make all `AbstractEndpoints` `IntegrationManagedResource`s and remove `ManagedEndpoint`
  to allow exposure of any `@Managed*` methods (including those on `Pausable`)
- Revert to `Lifecycle` for classes that are not related to endpoints
- Remove legacy metrics from docs
This commit is contained in:
Gary Russell
2020-08-07 12:56:57 -04:00
committed by GitHub
parent da5d002d64
commit 1beb854fb4
148 changed files with 533 additions and 7064 deletions

View File

@@ -29,8 +29,8 @@
<bean class="org.springframework.integration.jmx.NamedFieldsMBeanAttributeFilter">
<constructor-arg>
<array>
<value type="java.lang.String">SendCount</value>
<value type="java.lang.String">SendErrorCount</value>
<value type="java.lang.String">QueueSize</value>
<value type="java.lang.String">RemainingCapacity</value>
</array>
</constructor-arg>
</bean>
@@ -47,7 +47,7 @@
</int:service-activator>
<int:channel id="out">
<int:queue/>
<int:queue capacity="10" />
</int:channel>
<int-jmx:tree-polling-channel-adapter id="adapterNot"

View File

@@ -76,11 +76,11 @@ public class MBeanAttributeFilterTests {
@SuppressWarnings("unchecked")
Map<String, Object> bean = (Map<String, Object>) payload
.get(domain + ":name=in,type=MessageChannel");
.get(this.domain + ":name=out,type=MessageChannel");
assertThat(bean.size()).isEqualTo(2);
assertThat(bean.containsKey("SendCount")).isTrue();
assertThat(bean.containsKey("SendErrorCount")).isTrue();
assertThat(bean.containsKey("QueueSize")).isTrue();
assertThat(bean.containsKey("RemainingCapacity")).isTrue();
adapter.stop();
}
@@ -106,10 +106,7 @@ public class MBeanAttributeFilterTests {
List<String> keys = new ArrayList<>(bean.keySet());
Collections.sort(keys);
assertThat(keys)
.containsExactly("LoggingEnabled", "MaxSendDuration", "MeanErrorRate", "MeanErrorRatio",
"MeanSendDuration", "MeanSendRate", "MinSendDuration", "StandardDeviationSendDuration",
"SubscriberCount", "TimeSinceLastSend");
assertThat(keys).containsExactly("LoggingEnabled", "SubscriberCount");
adapterNot.stop();
}

View File

@@ -16,7 +16,7 @@
<si:channel id="controlChannel"/>
<si:control-bus input-channel="controlChannel"/>
<si:control-bus id = "cb" input-channel="controlChannel"/>
<jmx:mbean-export id="integrationMbeanExporter" server="mbs" default-domain="tests.ControlBusParser"/>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,9 +46,8 @@ public class ControlBusParserTests {
public void testControlMessageToChannelMetrics() {
MessageChannel control = this.context.getBean("controlChannel", MessageChannel.class);
MessagingTemplate messagingTemplate = new MessagingTemplate();
Object value = messagingTemplate.convertSendAndReceive(control,
"@integrationMbeanExporter.getChannelSendRate('testChannel').count", Object.class);
assertThat(value).isEqualTo(0);
Object value = messagingTemplate.convertSendAndReceive(control, "@cb.isRunning()", Object.class);
assertThat(value).isEqualTo(Boolean.TRUE);
}
}

View File

@@ -20,12 +20,7 @@
object-naming-strategy="keyNamer"
managed-components="\!excluded, f*, b*, q*, t*" />
<si:management
default-logging-enabled="false"
default-counts-enabled="false"
default-stats-enabled="false"
counts-enabled-patterns="foo, !baz, ba*"
stats-enabled-patterns="fiz, buz" />
<si:management default-logging-enabled="false"/>
<util:properties id="appProperties">
<prop key="foo">foo</prop>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2019 the original author or authors.
* Copyright 2014-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -69,10 +69,6 @@ public class EnableMBeanExportTests {
@Autowired
private IntegrationManagementConfigurer configurer;
@SuppressWarnings("deprecation")
@Autowired
private org.springframework.integration.support.management.MetricsFactory myMetricsFactory;
@SuppressWarnings("unchecked")
@Test
public void testEnableMBeanExport() throws MalformedObjectNameException, ClassNotFoundException {
@@ -80,14 +76,7 @@ public class EnableMBeanExportTests {
assertThat(this.exporter.getServer()).isSameAs(this.mBeanServer);
String[] componentNamePatterns = TestUtils.getPropertyValue(this.exporter, "componentNamePatterns", String[].class);
assertThat(componentNamePatterns).containsExactly("input", "inputX", "in*");
String[] enabledCounts = TestUtils.getPropertyValue(this.configurer, "enabledCountsPatterns", String[].class);
assertThat(enabledCounts).containsExactly("foo", "bar", "baz");
String[] enabledStats = TestUtils.getPropertyValue(this.configurer, "enabledStatsPatterns", String[].class);
assertThat(enabledStats).containsExactly("qux", "!*");
assertThat(TestUtils.getPropertyValue(this.configurer, "defaultLoggingEnabled", Boolean.class)).isFalse();
assertThat(TestUtils.getPropertyValue(this.configurer, "defaultCountsEnabled", Boolean.class)).isTrue();
assertThat(TestUtils.getPropertyValue(this.configurer, "defaultStatsEnabled", Boolean.class)).isTrue();
assertThat(TestUtils.getPropertyValue(this.configurer, "metricsFactory")).isSameAs(this.myMetricsFactory);
Set<ObjectName> names = this.mBeanServer.queryNames(ObjectName.getInstance("FOO:type=MessageChannel,*"), null);
// Only one registered (out of >2 available)
@@ -118,12 +107,7 @@ public class EnableMBeanExportTests {
defaultDomain = "${managed.domain}",
managedComponents = {"input", "${managed.component}"})
@EnableIntegrationManagement(
defaultLoggingEnabled = "false",
defaultCountsEnabled = "true",
defaultStatsEnabled = "true",
countsEnabled = { "foo", "${count.patterns}" },
statsEnabled = { "qux", "!*" },
metricsFactory = "myMetricsFactory")
defaultLoggingEnabled = "false")
public static class ContextConfiguration {
@Bean
@@ -141,12 +125,6 @@ public class EnableMBeanExportTests {
return new QueueChannel();
}
@SuppressWarnings("deprecation")
@Bean
public org.springframework.integration.support.management.MetricsFactory myMetricsFactory() {
return new org.springframework.integration.support.management.DefaultMetricsFactory();
}
}
public static class EnvironmentApplicationContextInitializer

View File

@@ -1,102 +0,0 @@
/*
* Copyright 2009-2020 the original author or authors.
*
* 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
*
* https://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 org.springframework.integration.monitor;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.support.context.NamedComponent;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageDeliveryException;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Dave Syer
* @author Gary Russell
* @author Artem Bilan
*
* @since 2.0
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext
public class ChannelIntegrationTests {
@Autowired
private MessageChannel requests;
@Autowired
private PollableChannel intermediate;
@Autowired
private PollableChannel sourceChannel;
@Autowired
private IntegrationMBeanExporter messageChannelsMonitor;
@Test
public void testMessageChannelStatistics() {
this.requests.send(new GenericMessage<>("foo"));
String intermediateChannelName = ((NamedComponent) this.intermediate).getBeanName();
assertThat(messageChannelsMonitor.getChannelSendCount(intermediateChannelName)).isEqualTo(1);
double rate =
messageChannelsMonitor.getChannelSendRate(((NamedComponent) this.requests).getBeanName()).getMean();
assertThat(rate).as("No statistics for requests channel").isGreaterThanOrEqualTo(0);
rate = messageChannelsMonitor.getChannelSendRate(intermediateChannelName).getMean();
assertThat(rate).as("No statistics for intermediate channel").isGreaterThanOrEqualTo(0);
assertThat(intermediate.receive(100L)).isNotNull();
assertThat(messageChannelsMonitor.getChannelReceiveCount(intermediateChannelName)).isEqualTo(1);
requests.send(new GenericMessage<>("foo"));
try {
requests.send(new GenericMessage<>("foo"));
}
catch (@SuppressWarnings("unused") MessageDeliveryException e) {
}
assertThat(messageChannelsMonitor.getChannelSendCount(intermediateChannelName)).isEqualTo(3);
assertThat(messageChannelsMonitor.getChannelSendErrorCount(intermediateChannelName)).isEqualTo(1);
assertThat(messageChannelsMonitor.getChannelMetrics(intermediateChannelName)).isSameAs(intermediate);
@SuppressWarnings("deprecation")
org.springframework.integration.support.management.BaseHandlerMetrics handlerMetrics = messageChannelsMonitor
.getHandlerMetrics("bridge");
assertThat(handlerMetrics.handleCount()).isEqualTo(3);
assertThat(handlerMetrics.errorCount()).isEqualTo(1);
assertThat(this.sourceChannel.receive(10000)).isNotNull();
assertThat(messageChannelsMonitor.getSourceMessageCount("source")).isGreaterThan(0);
assertThat(messageChannelsMonitor.getSourceMetrics("source").getMessageCount()).isGreaterThan(0);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2009-2019 the original author or authors.
* Copyright 2009-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -88,10 +88,6 @@ public class HandlerMonitoringIntegrationTests {
int before = service.getCounter();
channel.send(new GenericMessage<>("bar"));
assertThat(service.getCounter()).isEqualTo(before + 1);
int count = messageHandlersMonitor.getHandlerDuration(monitor).getCount();
assertThat(count > 0).as("No statistics for input channel").isTrue();
}
finally {
context.close();
@@ -118,11 +114,13 @@ public class HandlerMonitoringIntegrationTests {
private int counter;
@Override
public void execute(String input) throws Exception {
Thread.sleep(10L); // make the duration non-zero
this.counter++;
}
@Override
public int getCounter() {
return counter;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2009-2019 the original author or authors.
* Copyright 2009-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -122,8 +122,8 @@ public class MBeanExporterIntegrationTests {
Set<ObjectName> names =
server.queryNames(
ObjectName.getInstance("org.springframework.integration:type=ManagedEndpoint,*"), null);
assertThat(names.size()).isEqualTo(2);
names = server.queryNames(ObjectName.getInstance("org.springframework.integration:name=explicit,*"), null);
assertThat(names.size()).isEqualTo(3);
names = server.queryNames(ObjectName.getInstance("org.springframework.integration:name=explicit.adapter,*"), null);
assertThat(names.size()).isEqualTo(1);
MBeanOperationInfo[] operations = server.getMBeanInfo(names.iterator().next()).getOperations();
String startName = null;

View File

@@ -1,228 +0,0 @@
/*
* Copyright 2009-2019 the original author or authors.
*
* 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
*
* https://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 org.springframework.integration.monitor;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.junit.Test;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.channel.AbstractMessageChannel;
import org.springframework.integration.channel.interceptor.WireTap;
import org.springframework.integration.support.context.NamedComponent;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandlingException;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.messaging.support.InterceptableChannel;
/**
* @author Dave Syer
* @author Gary Russell
* @author Artem Bilan
*
*/
public class MessageChannelsMonitorIntegrationTests {
private static Log logger = LogFactory.getLog(MessageChannelsMonitorIntegrationTests.class);
private AbstractMessageChannel channel;
private Service service;
private IntegrationMBeanExporter messageChannelsMonitor;
public void setMessageHandlersMonitor(IntegrationMBeanExporter messageChannelsMonitor) {
this.messageChannelsMonitor = messageChannelsMonitor;
}
public void setService(Service service) {
this.service = service;
}
@Test
public void testSendWithAnonymousHandler() throws Exception {
doTest("anonymous-channel.xml", "anonymous");
}
@Test
public void testSendWithProxiedChannel() throws Exception {
doTest("proxy-channel.xml", "anonymous");
}
@Test
public void testRates() throws Exception {
try (ClassPathXmlApplicationContext context = createContext("anonymous-channel.xml")) {
this.channel = context.getBean("anonymous", AbstractMessageChannel.class);
int before = service.getCounter();
CountDownLatch latch = new CountDownLatch(50);
service.setLatch(latch);
for (int i = 0; i < 50; i++) {
channel.send(new GenericMessage<>("bar"));
Thread.sleep(20L);
}
assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue();
assertThat(service.getCounter()).isEqualTo(before + 50);
// The handler monitor is registered under the endpoint id (since it is explicit)
int sends = messageChannelsMonitor.getChannelSendRate(this.channel.getBeanName()).getCount();
assertThat(sends).as("No send statistics for input channel").isEqualTo(50);
long sendsLong = messageChannelsMonitor.getChannelSendRate(this.channel.getBeanName()).getCountLong();
assertThat(sends).as("No send statistics for input channel").isEqualTo(sendsLong);
}
}
@Test
public void testErrors() throws Exception {
try (ClassPathXmlApplicationContext context = createContext("anonymous-channel.xml")) {
this.channel = context.getBean("anonymous", AbstractMessageChannel.class);
int before = service.getCounter();
CountDownLatch latch = new CountDownLatch(10);
service.setLatch(latch);
for (int i = 0; i < 5; i++) {
channel.send(new GenericMessage<>("bar"));
Thread.sleep(20L);
}
try {
channel.send(new GenericMessage<>("fail"));
}
catch (MessageHandlingException e) {
// ignore
}
for (int i = 0; i < 5; i++) {
channel.send(new GenericMessage<>("bar"));
Thread.sleep(20L);
}
assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue();
assertThat(service.getCounter()).isEqualTo(before + 10);
// The handler monitor is registered under the endpoint id (since it is explicit)
int sends = messageChannelsMonitor.getChannelSendRate(this.channel.getBeanName()).getCount();
assertThat(sends).as("No send statistics for input channel").isEqualTo(11);
int errors = messageChannelsMonitor.getChannelErrorRate(this.channel.getBeanName()).getCount();
assertThat(errors).as("No error statistics for input channel").isEqualTo(1);
}
}
@Test
public void testQueues() throws Exception {
try (ClassPathXmlApplicationContext context = createContext("queue-channel.xml")) {
this.channel = context.getBean("queue", AbstractMessageChannel.class);
int before = service.getCounter();
CountDownLatch latch = new CountDownLatch(10);
service.setLatch(latch);
for (int i = 0; i < 5; i++) {
channel.send(new GenericMessage<>("bar"));
Thread.sleep(20L);
}
try {
channel.send(new GenericMessage<>("fail"));
}
catch (MessageHandlingException e) {
// ignore
}
for (int i = 0; i < 5; i++) {
channel.send(new GenericMessage<>("bar"));
Thread.sleep(20L);
}
assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue();
assertThat(service.getCounter()).isEqualTo(before + 10);
// The handler monitor is registered under the endpoint id (since it is explicit)
int sends = messageChannelsMonitor.getChannelSendRate(this.channel.getBeanName()).getCount();
assertThat(sends).as("No send statistics for input channel").isEqualTo(11);
int receives = messageChannelsMonitor.getChannelReceiveCount(this.channel.getBeanName());
assertThat(receives).as("No send statistics for input channel").isEqualTo(11);
int errors = messageChannelsMonitor.getChannelErrorRate(this.channel.getBeanName()).getCount();
assertThat(errors).as("Expect no errors for input channel (handler fails)").isEqualTo(0);
}
}
private void doTest(String config, String channelName) throws Exception {
try (ClassPathXmlApplicationContext context = createContext(config)) {
MessageChannel channel = context.getBean(channelName, MessageChannel.class);
int before = service.getCounter();
CountDownLatch latch = new CountDownLatch(1);
service.setLatch(latch);
channel.send(new GenericMessage<>("bar"));
assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue();
assertThat(service.getCounter()).isEqualTo(before + 1);
// The handler monitor is registered under the endpoint id (since it is explicit)
int sends = messageChannelsMonitor.getChannelSendRate(((NamedComponent) channel).getBeanName()).getCount();
assertThat(sends).as("No statistics for input channel").isEqualTo(1);
assertThat(channel).isInstanceOf(InterceptableChannel.class);
List<ChannelInterceptor> channelInterceptors = ((InterceptableChannel) channel).getInterceptors();
assertThat(channelInterceptors.size()).isEqualTo(1);
assertThat(channelInterceptors.get(0)).isInstanceOf(WireTap.class);
}
}
private ClassPathXmlApplicationContext createContext(String config) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config, getClass());
context.getAutowireCapableBeanFactory()
.autowireBeanProperties(this, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
return context;
}
public static class Service {
private int counter;
private volatile CountDownLatch latch;
public void setLatch(CountDownLatch latch) {
this.latch = latch;
}
public void execute(String input) {
if ("fail".equals(input)) {
throw new RuntimeException("Planned");
}
counter++;
latch.countDown();
}
public int getCounter() {
return counter;
}
}
@Aspect
public static class TestChannelInterceptor {
@Before("execution(* *..MessageChannel+.send(*)) && args(input)")
public void around(Message<?> input) {
logger.debug("Handling: " + input);
}
}
}

View File

@@ -1,112 +0,0 @@
/*
* Copyright 2009-2019 the original author or authors.
*
* 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
*
* https://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 org.springframework.integration.monitor;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.messaging.PollableChannel;
/**
* @author Dave Syer
* @author Gary Russell
* @author Artem Bilan
*/
public class MessageSourceMonitoringIntegrationTests {
private PollableChannel channel;
private Service service;
private IntegrationMBeanExporter exporter;
public void setMessageHandlersMonitor(IntegrationMBeanExporter exporter) {
this.exporter = exporter;
}
public void setService(Service service) {
this.service = service;
}
@Test
public void testSendAndHandleWithEndpointName() throws Exception {
// The message source monitor is registered under the endpoint id (since it is explicit)
doTest("explicit-source.xml", "input", "explicit");
}
@Test
public void testSendAndHandleWithAnonymous() throws Exception {
// The message source monitor is registered under the channel name
doTest("anonymous-source.xml", "anonymous", "anonymous");
}
private void doTest(String config, String channelName, String monitor) throws Exception {
ClassPathXmlApplicationContext context = createContext(config, channelName);
try {
int before = service.getCounter();
channel.receive(1000L);
channel.receive(1000L);
assertThat(before < service.getCounter()).isTrue();
int count = exporter.getSourceMessageCount(monitor);
assertThat(count > 0).as("No statistics for input channel").isTrue();
}
finally {
context.close();
}
}
private ClassPathXmlApplicationContext createContext(String config, String channelName) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config, getClass());
context.getAutowireCapableBeanFactory()
.autowireBeanProperties(this, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
channel = context.getBean(channelName, PollableChannel.class);
return context;
}
public interface Service {
String execute() throws Exception;
int getCounter();
}
public static class SimpleService implements Service {
private int counter;
public String execute() throws Exception {
Thread.sleep(10L); // make the duration non-zero
counter++;
return "count=" + counter;
}
public int getCounter() {
return counter;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,12 +47,12 @@ public class Int2307Tests {
int count = 0;
for (ObjectInstance mbean : mbeans) {
if (mbean.toString()
.startsWith("org.springframework.integration.support.management.LifecycleTrackableMessageHandlerMetrics[test.domain:type=MessageHandler,name=rlr,bean=endpoint,random=")) {
.startsWith("org.springframework.integration.router.RecipientListRouter[test.domain:type=MessageHandler,name=rlr,bean=endpoint,random=")) {
bits |= 2;
count++;
}
else if (mbean.toString()
.startsWith("org.springframework.integration.support.management.TrackableRouterMetrics[test.domain:type=MessageHandler,name=hvr,bean=endpoint,random=")) {
.startsWith("org.springframework.integration.router.HeaderValueRouter[test.domain:type=MessageHandler,name=hvr,bean=endpoint,random=")) {
bits |= 8;
count++;
}