From 2287e0caf223270824c962c6a0a3e2e46589a678 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=AB=8B?= Date: Thu, 30 Nov 2017 09:48:35 -0600 Subject: [PATCH 1/3] fix zuul filters package url in Edgware (#2489) fix zuul filters package url in Edgware --- docs/src/main/asciidoc/spring-cloud-netflix.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/spring-cloud-netflix.adoc b/docs/src/main/asciidoc/spring-cloud-netflix.adoc index 1aae2fa8..2d6fe30f 100644 --- a/docs/src/main/asciidoc/spring-cloud-netflix.adoc +++ b/docs/src/main/asciidoc/spring-cloud-netflix.adoc @@ -1930,7 +1930,7 @@ maps all paths in "/api/{all}" to the Zuul filter chain. === Disable Zuul Filters Zuul for Spring Cloud comes with a number of `ZuulFilter` beans enabled by default -in both proxy and server mode. See https://github.com/spring-cloud/spring-cloud-netflix/tree/master/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters[the zuul filters package] for the +in both proxy and server mode. See https://github.com/spring-cloud/spring-cloud-netflix/tree/master/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/filters[the zuul filters package] for the possible filters that are enabled. If you want to disable one, simply set `zuul...disable=true`. By convention, the package after `filters` is the Zuul filter type. For example to disable From ebdcca4fa8250bd6a74f71a31b292300e8208992 Mon Sep 17 00:00:00 2001 From: Daniel Lavoie Date: Fri, 1 Dec 2017 16:38:15 -0500 Subject: [PATCH 2/3] Hystrix Dashboard Cannot display anything for Turbine Stream (#2484) Closes #2475 --- .../contract/HystrixContractUtils.java | 10 +++- .../src/main/resources/stubs/simpleBody.json | 1 + .../hystrix/stream/HystrixStreamTask.java | 1 + .../hystrix/stream/HystrixStreamTests.java | 4 +- .../hystrix/stream/StreamSourceTestBase.java | 4 ++ .../shouldProduceValidMetricsData.groovy | 1 + .../stream/TurbineStreamConfiguration.java | 4 ++ .../turbine/stream/TurbineStreamTests.java | 52 +++++++++---------- 8 files changed, 46 insertions(+), 31 deletions(-) diff --git a/spring-cloud-netflix-hystrix-contract/src/main/java/org/springframework/cloud/netflix/hystrix/contract/HystrixContractUtils.java b/spring-cloud-netflix-hystrix-contract/src/main/java/org/springframework/cloud/netflix/hystrix/contract/HystrixContractUtils.java index 28b00bff..d8e5364e 100644 --- a/spring-cloud-netflix-hystrix-contract/src/main/java/org/springframework/cloud/netflix/hystrix/contract/HystrixContractUtils.java +++ b/spring-cloud-netflix-hystrix-contract/src/main/java/org/springframework/cloud/netflix/hystrix/contract/HystrixContractUtils.java @@ -15,6 +15,8 @@ */ package org.springframework.cloud.netflix.hystrix.contract; +import static org.assertj.core.api.Assertions.assertThat; + import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Map; @@ -22,10 +24,9 @@ import java.util.Map; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.util.StreamUtils; -import static org.assertj.core.api.Assertions.assertThat; - /** * @author Dave Syer + * @author Daniel Lavoie * */ public class HystrixContractUtils { @@ -41,6 +42,11 @@ public class HystrixContractUtils { } } + public static void checkEvent(String event) { + assertThat(event).isNotNull(); + assertThat(event).isEqualTo("message"); + } + public static void checkOrigin(Map origin) { assertThat(origin.get("host")).isNotNull(); assertThat(origin.get("port")).isNotNull(); diff --git a/spring-cloud-netflix-hystrix-contract/src/main/resources/stubs/simpleBody.json b/spring-cloud-netflix-hystrix-contract/src/main/resources/stubs/simpleBody.json index 51e2359b..7e1bcfef 100644 --- a/spring-cloud-netflix-hystrix-contract/src/main/resources/stubs/simpleBody.json +++ b/spring-cloud-netflix-hystrix-contract/src/main/resources/stubs/simpleBody.json @@ -5,6 +5,7 @@ "serviceId":"application", "id":"application:0" }, + "event" : "message", "data":{ "type":"HystrixCommand", "name":"application.hello", diff --git a/spring-cloud-netflix-hystrix-stream/src/main/java/org/springframework/cloud/netflix/hystrix/stream/HystrixStreamTask.java b/spring-cloud-netflix-hystrix-stream/src/main/java/org/springframework/cloud/netflix/hystrix/stream/HystrixStreamTask.java index 7a3a9252..28f14aff 100644 --- a/spring-cloud-netflix-hystrix-stream/src/main/java/org/springframework/cloud/netflix/hystrix/stream/HystrixStreamTask.java +++ b/spring-cloud-netflix-hystrix-stream/src/main/java/org/springframework/cloud/netflix/hystrix/stream/HystrixStreamTask.java @@ -138,6 +138,7 @@ public class HystrixStreamTask implements ApplicationContextAware { json.writeStartObject(); addServiceData(json, registration); + json.writeStringField("event", "message"); json.writeObjectFieldStart("data"); json.writeStringField("type", "HystrixCommand"); String name = key.name(); diff --git a/spring-cloud-netflix-hystrix-stream/src/test/java/org/springframework/cloud/netflix/hystrix/stream/HystrixStreamTests.java b/spring-cloud-netflix-hystrix-stream/src/test/java/org/springframework/cloud/netflix/hystrix/stream/HystrixStreamTests.java index 543669f4..a9602a62 100644 --- a/spring-cloud-netflix-hystrix-stream/src/test/java/org/springframework/cloud/netflix/hystrix/stream/HystrixStreamTests.java +++ b/spring-cloud-netflix-hystrix-stream/src/test/java/org/springframework/cloud/netflix/hystrix/stream/HystrixStreamTests.java @@ -27,7 +27,6 @@ import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.cloud.stream.test.binder.MessageCollector; -import org.springframework.context.annotation.Configuration; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.test.annotation.DirtiesContext; @@ -43,6 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat; /** * @author Spencer Gibb + * @author Daniel Lavoie */ @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { @@ -95,6 +95,8 @@ public class HystrixStreamTests { JsonNode tree = mapper.readTree((String) message.getPayload()); assertThat(tree.hasNonNull("origin")); assertThat(tree.hasNonNull("data")); + assertThat(tree.hasNonNull("event")); + assertThat(tree.findValue("event").asText().equals("message")); } } diff --git a/spring-cloud-netflix-hystrix-stream/src/test/java/org/springframework/cloud/netflix/hystrix/stream/StreamSourceTestBase.java b/spring-cloud-netflix-hystrix-stream/src/test/java/org/springframework/cloud/netflix/hystrix/stream/StreamSourceTestBase.java index 8f5324aa..92784d2f 100644 --- a/spring-cloud-netflix-hystrix-stream/src/test/java/org/springframework/cloud/netflix/hystrix/stream/StreamSourceTestBase.java +++ b/spring-cloud-netflix-hystrix-stream/src/test/java/org/springframework/cloud/netflix/hystrix/stream/StreamSourceTestBase.java @@ -68,6 +68,10 @@ public abstract class StreamSourceTestBase { "application.hello"); } + public void assertEvent(Object input) { + HystrixContractUtils.checkEvent((String) input); + } + @EnableAutoConfiguration @EnableCircuitBreaker @RestController diff --git a/spring-cloud-netflix-hystrix-stream/src/test/resources/contracts/shouldProduceValidMetricsData.groovy b/spring-cloud-netflix-hystrix-stream/src/test/resources/contracts/shouldProduceValidMetricsData.groovy index 4c7f7cdc..452bbd44 100644 --- a/spring-cloud-netflix-hystrix-stream/src/test/resources/contracts/shouldProduceValidMetricsData.groovy +++ b/spring-cloud-netflix-hystrix-stream/src/test/resources/contracts/shouldProduceValidMetricsData.groovy @@ -22,6 +22,7 @@ org.springframework.cloud.contract.spec.Contract.make { body(HystrixContractUtils.simpleBody()) testMatchers { jsonPath('$.origin', byCommand('assertOrigin($it)')) + jsonPath('$.event', byCommand('assertEvent($it)')) jsonPath('$.data', byCommand('assertData($it)')) } } diff --git a/spring-cloud-netflix-turbine-stream/src/main/java/org/springframework/cloud/netflix/turbine/stream/TurbineStreamConfiguration.java b/spring-cloud-netflix-turbine-stream/src/main/java/org/springframework/cloud/netflix/turbine/stream/TurbineStreamConfiguration.java index 5ef71914..517daf1d 100644 --- a/spring-cloud-netflix-turbine-stream/src/main/java/org/springframework/cloud/netflix/turbine/stream/TurbineStreamConfiguration.java +++ b/spring-cloud-netflix-turbine-stream/src/main/java/org/springframework/cloud/netflix/turbine/stream/TurbineStreamConfiguration.java @@ -50,6 +50,7 @@ import rx.subjects.PublishSubject; /** * @author Spencer Gibb + * @author Daniel Lavoie */ @Configuration @EnableConfigurationProperties(TurbineStreamProperties.class) @@ -103,6 +104,9 @@ public class TurbineStreamConfiguration implements SmartLifecycle { return output.doOnUnsubscribe( () -> log.info("Unsubscribing RxNetty server connection")) .flatMap(data -> response.writeAndFlush(new ServerSentEvent( + null, + Unpooled.copiedBuffer("message", + StandardCharsets.UTF_8), Unpooled.copiedBuffer(JsonUtility.mapToJson(data), StandardCharsets.UTF_8)))); }, serveSseConfigurator()); diff --git a/spring-cloud-netflix-turbine-stream/src/test/java/org/springframework/cloud/netflix/turbine/stream/TurbineStreamTests.java b/spring-cloud-netflix-turbine-stream/src/test/java/org/springframework/cloud/netflix/turbine/stream/TurbineStreamTests.java index 48f8d80c..7a976867 100644 --- a/spring-cloud-netflix-turbine-stream/src/test/java/org/springframework/cloud/netflix/turbine/stream/TurbineStreamTests.java +++ b/spring-cloud-netflix-turbine-stream/src/test/java/org/springframework/cloud/netflix/turbine/stream/TurbineStreamTests.java @@ -16,16 +16,15 @@ package org.springframework.cloud.netflix.turbine.stream; +import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.net.URI; import java.util.Map; -import java.util.concurrent.CountDownLatch; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,13 +48,13 @@ import org.springframework.http.client.ClientHttpResponse; import org.springframework.integration.support.management.MessageChannelMetrics; import org.springframework.messaging.SubscribableChannel; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.util.StringUtils; import org.springframework.web.client.RestTemplate; import static org.assertj.core.api.Assertions.assertThat; /** * @author Spencer Gibb + * @author Daniel Lavoie */ @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = TurbineStreamTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = { @@ -67,9 +66,6 @@ import static org.assertj.core.api.Assertions.assertThat; "stubrunner.ids=org.springframework.cloud:spring-cloud-netflix-hystrix-stream:${projectVersion:1.4.0.BUILD-SNAPSHOT}:stubs" }) @AutoConfigureStubRunner public class TurbineStreamTests { - - private static Log log = LogFactory.getLog(TurbineStreamTests.class); - @Autowired StubTrigger stubTrigger; @@ -85,8 +81,6 @@ public class TurbineStreamTests { @Autowired TurbineStreamConfiguration turbine; - private CountDownLatch latch = new CountDownLatch(1); - @EnableAutoConfiguration @EnableTurbineStream public static class Application { @@ -109,16 +103,16 @@ public class TurbineStreamTests { assertThat(metrics).containsEntry("type", "HystrixCommand"); assertThat(((MessageChannelMetrics) input).getSendCount()).isEqualTo(count + 1); } + + private boolean containsMetrics(String line) { + return line.startsWith("data:") && !line.contains("Ping"); + } @SuppressWarnings("unchecked") private Map extractMetrics(String body) throws Exception { - String[] split = body.split("data:"); - for (String value : split) { - if (value.contains("Ping") || value.length() == 0) { - continue; - } - else { - return mapper.readValue(value, Map.class); + for (String value : body.split("\n")) { + if (containsMetrics(value)) { + return mapper.readValue(value.split("data:")[1], Map.class); } } return null; @@ -129,21 +123,23 @@ public class TurbineStreamTests { // The message has to be sent after the endpoint is activated, so this is a // convenient place to put it stubTrigger.trigger("metrics"); - byte[] bytes = new byte[1024]; - StringBuilder builder = new StringBuilder(); - int read = 0; - while (read >= 0 - && StringUtils.countOccurrencesOf(builder.toString(), "\n") < 2) { - read = response.getBody().read(bytes, 0, bytes.length); - if (read > 0) { - latch.countDown(); - builder.append(new String(bytes, 0, read)); + + String responseBody = ""; + boolean metricFound = false; + try (BufferedReader buffer = new BufferedReader( + new InputStreamReader(response.getBody()))) { + do { + String line = buffer.readLine(); + responseBody += line + "\n"; + if (containsMetrics(line)) { + metricFound = true; + } } - log.debug("Building: " + builder); + while (!metricFound); } - log.debug("Done: " + builder); + return ResponseEntity.status(response.getStatusCode()) - .headers(response.getHeaders()).body(builder.toString()); + .headers(response.getHeaders()).body(responseBody); } /** From efb91874f846e8decef8f4178bcb261d3468ce91 Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Sun, 3 Dec 2017 15:06:48 -0800 Subject: [PATCH 3/3] Correcting image links. Fixes #2487 --- docs/src/main/asciidoc/spring-cloud-netflix.adoc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/src/main/asciidoc/spring-cloud-netflix.adoc b/docs/src/main/asciidoc/spring-cloud-netflix.adoc index 2d6fe30f..00e6ebc9 100644 --- a/docs/src/main/asciidoc/spring-cloud-netflix.adoc +++ b/docs/src/main/asciidoc/spring-cloud-netflix.adoc @@ -4,7 +4,7 @@ :github-code: http://github.com/{github-repo}/tree/{github-tag} :all: {asterisk}{asterisk} :nofooter: -:imagesdir: ./images +:branch: 1.4.x = Spring Cloud Netflix *{spring-cloud-version}* @@ -530,12 +530,12 @@ example `eureka.instance.hostname=${HOST_NAME}`. Netflix has created a library called https://github.com/Netflix/Hystrix[Hystrix] that implements the http://martinfowler.com/bliki/CircuitBreaker.html[circuit breaker pattern]. In a microservice architecture it is common to have multiple layers of service calls. .Microservice Graph -image::HystrixGraph.png[] +image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-netflix/{branch}/docs/src/main/asciidoc/images/Hystrix.png[] A service failure in the lower level of services can cause cascading failure all the way up to the user. When calls to a particular service is greater than `circuitBreaker.requestVolumeThreshold` (default: 20 requests) and failue percentage is greater than `circuitBreaker.errorThresholdPercentage` (default: >50%) in a rolling window defined by `metrics.rollingStats.timeInMilliseconds` (default: 10 seconds), the circuit opens and the call is not made. In cases of error and an open circuit a fallback can be provided by the developer. .Hystrix fallback prevents cascading failures -image::HystrixFallback.png[] +image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-netflix/{branch}/docs/src/main/asciidoc/images/HystrixFallback.png[] Having an open circuit stops cascading failures and allows overwhelmed or failing services time to heal. The fallback can be another Hystrix protected call, static data or a sane empty value. Fallbacks may be chained so the first fallback makes some other business call which in turn falls back to static data. @@ -641,7 +641,7 @@ To enable the Hystrix metrics stream include a dependency on `spring-boot-starte One of the main benefits of Hystrix is the set of metrics it gathers about each HystrixCommand. The Hystrix Dashboard displays the health of each circuit breaker in an efficient manner. .Hystrix Dashboard -image::Hystrix.png[] +image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-netflix/{branch}/docs/src/main/asciidoc/images/Hystrix.png[] == Hystrix Timeouts And Ribbon Clients @@ -2631,7 +2631,7 @@ The counter records a single time-normalized statistic. A timer is used to measure how long some event is taking. Spring Cloud automatically records timers for Spring MVC requests and conditionally `RestTemplate` requests, which can later be used to create dashboards for request related metrics like latency: .Request Latency -image::RequestLatency.png[] +image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-netflix/{branch}/docs/src/main/asciidoc/images/RequestLatency.png[] [source,java] ----