Browse Source

Implements zuul CounterFactory

CounterFactory is used by ZuulException and was previously initialzed
with a dummy impl.

This adds a Spring Boot CounterService based implementation.

Also adds the ability to implement a zuul TracerFactory.
pull/6/head
Anastasiia Smirnova 8 years ago committed by Spencer Gibb
parent
commit
834980eae7
No known key found for this signature in database
GPG Key ID: 7788A47380690861
  1. 44
      spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulConfiguration.java
  2. 41
      spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulFilterInitializer.java
  3. 40
      spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/metrics/DefaultCounterFactory.java
  4. 30
      spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/metrics/EmptyCounterFactory.java
  5. 45
      spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/metrics/EmptyTracerFactory.java
  6. 102
      spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/ZuulFilterInitializerTests.java
  7. 40
      spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/metrics/DefaultCounterFactoryTests.java
  8. 82
      spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/metrics/ZuulEmptyMetricsApplicationTests.java
  9. 120
      spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/metrics/ZuulMetricsApplicationTests.java

44
spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulConfiguration.java

@ -20,6 +20,8 @@ import java.util.Collection; @@ -20,6 +20,8 @@ import java.util.Collection;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.metrics.CounterService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -44,6 +46,9 @@ import org.springframework.cloud.netflix.zuul.filters.pre.FormBodyWrapperFilter; @@ -44,6 +46,9 @@ import org.springframework.cloud.netflix.zuul.filters.pre.FormBodyWrapperFilter;
import org.springframework.cloud.netflix.zuul.filters.pre.Servlet30WrapperFilter;
import org.springframework.cloud.netflix.zuul.filters.pre.ServletDetectionFilter;
import org.springframework.cloud.netflix.zuul.filters.route.SendForwardFilter;
import org.springframework.cloud.netflix.zuul.metrics.DefaultCounterFactory;
import org.springframework.cloud.netflix.zuul.metrics.EmptyCounterFactory;
import org.springframework.cloud.netflix.zuul.metrics.EmptyTracerFactory;
import org.springframework.cloud.netflix.zuul.web.ZuulController;
import org.springframework.cloud.netflix.zuul.web.ZuulHandlerMapping;
import org.springframework.context.ApplicationEvent;
@ -54,8 +59,12 @@ import org.springframework.context.annotation.Import; @@ -54,8 +59,12 @@ import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.context.event.ContextRefreshedEvent;
import com.netflix.zuul.FilterLoader;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.filters.FilterRegistry;
import com.netflix.zuul.http.ZuulServlet;
import com.netflix.zuul.monitoring.CounterFactory;
import com.netflix.zuul.monitoring.TracerFactory;
/**
* @author Spencer Gibb
@ -179,8 +188,39 @@ public class ZuulConfiguration { @@ -179,8 +188,39 @@ public class ZuulConfiguration {
private Map<String, ZuulFilter> filters;
@Bean
public ZuulFilterInitializer zuulFilterInitializer() {
return new ZuulFilterInitializer(this.filters);
public ZuulFilterInitializer zuulFilterInitializer(
CounterFactory counterFactory, TracerFactory tracerFactory) {
FilterLoader filterLoader = FilterLoader.getInstance();
FilterRegistry filterRegistry = FilterRegistry.instance();
return new ZuulFilterInitializer(this.filters, counterFactory, tracerFactory, filterLoader, filterRegistry);
}
}
@Configuration
@ConditionalOnClass(CounterService.class)
protected static class ZuulCounterFactoryConfiguration {
@Bean
@ConditionalOnBean(CounterService.class)
public CounterFactory counterFactory(CounterService counterService) {
return new DefaultCounterFactory(counterService);
}
}
@Configuration
protected static class ZuulMetricsConfiguration {
@Bean
@ConditionalOnMissingBean(CounterFactory.class)
public CounterFactory counterFactory() {
return new EmptyCounterFactory();
}
@ConditionalOnMissingBean(TracerFactory.class)
@Bean
public TracerFactory tracerFactory() {
return new EmptyTracerFactory();
}
}

41
spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulFilterInitializer.java

@ -22,13 +22,15 @@ import java.util.Map; @@ -22,13 +22,15 @@ import java.util.Map;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import lombok.extern.apachecommons.CommonsLog;
import org.springframework.util.ReflectionUtils;
import com.netflix.zuul.FilterLoader;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.filters.FilterRegistry;
import com.netflix.zuul.monitoring.MonitoringHelper;
import com.netflix.zuul.monitoring.CounterFactory;
import com.netflix.zuul.monitoring.TracerFactory;
import lombok.extern.apachecommons.CommonsLog;
/**
* @author Spencer Gibb
@ -38,10 +40,22 @@ import com.netflix.zuul.monitoring.MonitoringHelper; @@ -38,10 +40,22 @@ import com.netflix.zuul.monitoring.MonitoringHelper;
@CommonsLog
public class ZuulFilterInitializer implements ServletContextListener {
private Map<String, ZuulFilter> filters;
public ZuulFilterInitializer(Map<String, ZuulFilter> filters) {
private final Map<String, ZuulFilter> filters;
private final CounterFactory counterFactory;
private final TracerFactory tracerFactory;
private final FilterLoader filterLoader;
private final FilterRegistry filterRegistry;
public ZuulFilterInitializer(Map<String, ZuulFilter> filters,
CounterFactory counterFactory,
TracerFactory tracerFactory,
FilterLoader filterLoader,
FilterRegistry filterRegistry) {
this.filters = filters;
this.counterFactory = counterFactory;
this.tracerFactory = tracerFactory;
this.filterLoader = filterLoader;
this.filterRegistry = filterRegistry;
}
@Override
@ -49,32 +63,31 @@ public class ZuulFilterInitializer implements ServletContextListener { @@ -49,32 +63,31 @@ public class ZuulFilterInitializer implements ServletContextListener {
log.info("Starting filter initializer context listener");
// FIXME: mocks monitoring infrastructure as we don't need it for this simple app
MonitoringHelper.initMocks();
FilterRegistry registry = FilterRegistry.instance();
TracerFactory.initialize(tracerFactory);
CounterFactory.initialize(counterFactory);
for (Map.Entry<String, ZuulFilter> entry : this.filters.entrySet()) {
registry.put(entry.getKey(), entry.getValue());
filterRegistry.put(entry.getKey(), entry.getValue());
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
log.info("Stopping filter initializer context listener");
FilterRegistry registry = FilterRegistry.instance();
for (Map.Entry<String, ZuulFilter> entry : this.filters.entrySet()) {
registry.remove(entry.getKey());
filterRegistry.remove(entry.getKey());
}
clearLoaderCache();
TracerFactory.initialize(null);
CounterFactory.initialize(null);
}
private void clearLoaderCache() {
FilterLoader instance = FilterLoader.getInstance();
Field field = ReflectionUtils.findField(FilterLoader.class, "hashFiltersByType");
ReflectionUtils.makeAccessible(field);
@SuppressWarnings("rawtypes")
Map cache = (Map) ReflectionUtils.getField(field, instance);
Map cache = (Map) ReflectionUtils.getField(field, filterLoader);
cache.clear();
}

40
spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/metrics/DefaultCounterFactory.java

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
/*
* Copyright 2013-2015 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
*
* 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 org.springframework.cloud.netflix.zuul.metrics;
import org.springframework.boot.actuate.metrics.CounterService;
import com.netflix.zuul.monitoring.CounterFactory;
/**
* A counter based monitoring factory that uses {@link CounterService} to increment counters.
*
* @author Anastasiia Smirnova
*/
public class DefaultCounterFactory extends CounterFactory {
private final CounterService counterService;
public DefaultCounterFactory(CounterService counterService) {
this.counterService = counterService;
}
@Override
public void increment(String name) {
counterService.increment(name);
}
}

30
spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/metrics/EmptyCounterFactory.java

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
/*
* Copyright 2013-2015 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
*
* 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 org.springframework.cloud.netflix.zuul.metrics;
import com.netflix.zuul.monitoring.CounterFactory;
/**
* A counter based monitoring factory that does nothing.
*
* @author Anastasiia Smirnova
*/
public class EmptyCounterFactory extends CounterFactory {
@Override
public void increment(String name) {
}
}

45
spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/metrics/EmptyTracerFactory.java

@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
/*
* Copyright 2013-2015 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
*
* 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 org.springframework.cloud.netflix.zuul.metrics;
import com.netflix.zuul.monitoring.Tracer;
import com.netflix.zuul.monitoring.TracerFactory;
/**
* A time based monitoring factory that does nothing.
*
* @author Anastasiia Smirnova
*/
public class EmptyTracerFactory extends TracerFactory {
private final EmptyTracer emptyTracer = new EmptyTracer();
@Override
public Tracer startMicroTracer(String name) {
return emptyTracer;
}
private static final class EmptyTracer implements Tracer {
@Override
public void setName(String name) {
}
@Override
public void stopAndLog() {
}
}
}

102
spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/ZuulFilterInitializerTests.java

@ -0,0 +1,102 @@ @@ -0,0 +1,102 @@
/*
* Copyright 2013-2017 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
*
* 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 org.springframework.cloud.netflix.zuul;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContextEvent;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.ReflectionUtils;
import com.netflix.zuul.FilterLoader;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.filters.FilterRegistry;
import com.netflix.zuul.monitoring.CounterFactory;
import com.netflix.zuul.monitoring.TracerFactory;
import org.junit.Test;
import java.lang.reflect.Constructor;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
public class ZuulFilterInitializerTests {
private static final ServletContextEvent DUMMY_SERVLET_CONTEXT_EVENT = mock(
ServletContextEvent.class);
private Map<String, ZuulFilter> filters = getFilters();
private CounterFactory counterFactory = mock(CounterFactory.class);
private TracerFactory tracerFactory = mock(TracerFactory.class);
private FilterLoader filterLoader = new FilterLoader();
private FilterRegistry filterRegistry = getFilterRegistry();
private final ZuulFilterInitializer initializer = new ZuulFilterInitializer(filters,
counterFactory, tracerFactory, filterLoader, filterRegistry);
@Test
public void shouldSetupOnContextInitializedEvent() throws Exception {
initializer.contextInitialized(DUMMY_SERVLET_CONTEXT_EVENT);
assertEquals(tracerFactory, TracerFactory.instance());
assertEquals(counterFactory, CounterFactory.instance());
assertThat(filterRegistry.getAllFilters())
.containsAll(filters.values());
}
@Test
public void shouldCleanupOnContextDestroyed() throws Exception {
initializer.contextDestroyed(DUMMY_SERVLET_CONTEXT_EVENT);
assertEquals(null, ReflectionTestUtils.getField(TracerFactory.class, "INSTANCE"));
assertEquals(null,
ReflectionTestUtils.getField(CounterFactory.class, "INSTANCE"));
assertTrue(FilterRegistry.instance().getAllFilters().isEmpty());
assertTrue(getHashFiltersByType().isEmpty());
}
private Map getHashFiltersByType() {
Field field = ReflectionUtils.findField(FilterLoader.class, "hashFiltersByType");
ReflectionUtils.makeAccessible(field);
return (Map) ReflectionUtils.getField(field, FilterLoader.getInstance());
}
private Map<String, ZuulFilter> getFilters() {
Map<String, ZuulFilter> filters = new HashMap<>();
filters.put("key1", mock(ZuulFilter.class));
filters.put("key2", mock(ZuulFilter.class));
return filters;
}
private FilterRegistry getFilterRegistry() {
try {
Constructor<FilterRegistry> constructor = FilterRegistry.class
.getDeclaredConstructor(new Class[0]);
constructor.setAccessible(true);
return constructor.newInstance(new Object[0]);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
}

40
spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/metrics/DefaultCounterFactoryTests.java

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
/*
* Copyright 2013-2015 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
*
* 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 org.springframework.cloud.netflix.zuul.metrics;
import org.springframework.boot.actuate.metrics.CounterService;
import com.netflix.zuul.monitoring.CounterFactory;
import org.junit.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
public class DefaultCounterFactoryTests {
private static final String NAME = "my-super-metric-name";
private final CounterService counterService = mock(CounterService.class);
private final CounterFactory factory = new DefaultCounterFactory(counterService);
@Test
public void shouldIncrement() throws Exception {
factory.increment(NAME);
verify(counterService).increment(NAME);
}
}

82
spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/metrics/ZuulEmptyMetricsApplicationTests.java

@ -0,0 +1,82 @@ @@ -0,0 +1,82 @@
/*
* Copyright 2013-2015 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
*
* 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 org.springframework.cloud.netflix.zuul.metrics;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.cloud.ClassPathExclusions;
import org.springframework.cloud.netflix.zuul.ZuulConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.netflix.zuul.monitoring.CounterFactory;
import com.netflix.zuul.monitoring.TracerFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
@ClassPathExclusions({ "spring-boot-starter-actuator-*.jar",
"spring-boot-actuator-*.jar" })
public class ZuulEmptyMetricsApplicationTests {
private AnnotationConfigApplicationContext context;
@Before
public void setUp() throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(ZuulEmptyMetricsApplicationTestsConfiguration.class,
ZuulConfiguration.class);
context.refresh();
this.context = context;
}
@After
public void tearDown() throws Exception {
if (this.context != null) {
this.context.close();
}
}
@Test
public void shouldSetupDefaultCounterFactoryIfCounterServiceIsPresent()
throws Exception {
CounterFactory factory = this.context.getBean(CounterFactory.class);
assertEquals(EmptyCounterFactory.class, factory.getClass());
}
@Test
public void shouldSetupEmptyTracerFactory() throws Exception {
TracerFactory factory = this.context.getBean(TracerFactory.class);
assertEquals(EmptyTracerFactory.class, factory.getClass());
}
@Configuration
static class ZuulEmptyMetricsApplicationTestsConfiguration {
@Bean
ServerProperties serverProperties() {
return new ServerProperties();
}
}
}

120
spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/metrics/ZuulMetricsApplicationTests.java

@ -0,0 +1,120 @@ @@ -0,0 +1,120 @@
/*
* Copyright 2013-2015 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
*
* 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 org.springframework.cloud.netflix.zuul.metrics;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.metrics.CounterService;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.zuul.EnableZuulServer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import com.netflix.zuul.exception.ZuulException;
import com.netflix.zuul.monitoring.CounterFactory;
import com.netflix.zuul.monitoring.TracerFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {
ZuulMetricsApplicationTests.ZuulMetricsApplicationTestsConfiguration.class,
ZuulMetricsApplicationTests.ZuulConfig.class }, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@DirtiesContext
public class ZuulMetricsApplicationTests {
private static final Map<String, Long> counters = new HashMap<>();
@Autowired
CounterFactory counterFactory;
@Autowired
TracerFactory tracerFactory;
@Test
public void shouldSetupDefaultCounterFactoryIfCounterServiceIsPresent()
throws Exception {
assertEquals(DefaultCounterFactory.class, counterFactory.getClass());
}
@Test
public void shouldSetupEmptyTracerFactory() throws Exception {
assertEquals(EmptyTracerFactory.class, tracerFactory.getClass());
}
@Test
public void shouldIncrementCounters() throws Exception {
new ZuulException("any", 500, "cause");
new ZuulException("any", 500, "cause");
assertEquals((long) counters.get("ZUUL::EXCEPTION:cause:500"), 2L);
new ZuulException("any", 404, "cause2");
new ZuulException("any", 404, "cause2");
new ZuulException("any", 404, "cause2");
assertEquals((long) counters.get("ZUUL::EXCEPTION:cause2:404"), 3L);
}
// Don't use @SpringBootApplication because we don't want to component scan
@Configuration
@EnableAutoConfiguration
@EnableZuulServer
static class ZuulConfig {
}
@Configuration
static class ZuulMetricsApplicationTestsConfiguration {
@Bean
public ServerProperties serverProperties() {
return new ServerProperties();
}
@Bean
public CounterService counterService() {
return new CounterService() {
// not thread safe, but we are ok with it in tests
@Override
public void increment(String metricName) {
Long counter = counters.get(metricName);
if (counter == null) {
counter = 0L;
}
counters.put(metricName, ++counter);
}
@Override
public void decrement(String metricName) {
}
@Override
public void reset(String metricName) {
}
};
}
}
}
Loading…
Cancel
Save