Break up hystrix dashboard
This commit is contained in:
15
pom.xml
15
pom.xml
@@ -159,6 +159,21 @@
|
||||
<artifactId>jersey-server</artifactId>
|
||||
<version>1.13</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>d3js</artifactId>
|
||||
<version>3.4.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>jquery</artifactId>
|
||||
<version>2.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>bootstrap</artifactId>
|
||||
<version>3.2.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.springframework.cloud.netflix.hystrix;
|
||||
|
||||
import com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -8,7 +9,6 @@ import org.springframework.context.annotation.ImportAware;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.cloud.netflix.hystrix.annotations.EnableHystrix;
|
||||
import org.springframework.cloud.netflix.endpoint.HystrixStreamEndpoint;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
package org.springframework.cloud.netflix.hystrix;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.impl.conn.PoolingClientConnectionManager;
|
||||
import org.apache.http.params.HttpConnectionParams;
|
||||
import org.apache.http.params.HttpParams;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.context.embedded.ServletRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@Configuration
|
||||
public class HystrixDashboardConfiguration {
|
||||
|
||||
@Bean
|
||||
public ServletRegistrationBean proxyStreamServlet() {
|
||||
return new ServletRegistrationBean(new ProxyStreamServlet(), "/proxy.stream");
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy an EventStream request (data.stream via proxy.stream) since EventStream does not yet support CORS (https://bugs.webkit.org/show_bug.cgi?id=61862)
|
||||
* so that a UI can request a stream from a different server.
|
||||
*/
|
||||
public static class ProxyStreamServlet extends HttpServlet {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger logger = LoggerFactory.getLogger(ProxyStreamServlet.class);
|
||||
|
||||
public ProxyStreamServlet() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)
|
||||
*/
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
String origin = request.getParameter("origin");
|
||||
if (origin == null) {
|
||||
response.setStatus(500);
|
||||
response.getWriter().println("Required parameter 'origin' missing. Example: 107.20.175.135:7001");
|
||||
}
|
||||
origin = origin.trim();
|
||||
|
||||
HttpGet httpget = null;
|
||||
InputStream is = null;
|
||||
boolean hasFirstParameter = false;
|
||||
StringBuilder url = new StringBuilder();
|
||||
if (!origin.startsWith("http")) {
|
||||
url.append("http://");
|
||||
}
|
||||
url.append(origin);
|
||||
if (origin.contains("?")) {
|
||||
hasFirstParameter = true;
|
||||
}
|
||||
Map<String, String[]> params = request.getParameterMap();
|
||||
for (String key : params.keySet()) {
|
||||
if (!key.equals("origin")) {
|
||||
String[] values = params.get(key);
|
||||
String value = values[0].trim();
|
||||
if (hasFirstParameter) {
|
||||
url.append("&");
|
||||
} else {
|
||||
url.append("?");
|
||||
hasFirstParameter = true;
|
||||
}
|
||||
url.append(key).append("=").append(value);
|
||||
}
|
||||
}
|
||||
String proxyUrl = url.toString();
|
||||
logger.info("\n\nProxy opening connection to: " + proxyUrl + "\n\n");
|
||||
try {
|
||||
httpget = new HttpGet(proxyUrl);
|
||||
HttpClient client = ProxyConnectionManager.httpClient;
|
||||
HttpResponse httpResponse = client.execute(httpget);
|
||||
int statusCode = httpResponse.getStatusLine().getStatusCode();
|
||||
if (statusCode == HttpStatus.SC_OK) {
|
||||
// writeTo swallows exceptions and never quits even if outputstream is throwing IOExceptions (such as broken pipe) ... since the inputstream is infinite
|
||||
// httpResponse.getEntity().writeTo(new OutputStreamWrapper(response.getOutputStream()));
|
||||
// so I copy it manually ...
|
||||
is = httpResponse.getEntity().getContent();
|
||||
|
||||
// set headers
|
||||
for (Header header : httpResponse.getAllHeaders()) {
|
||||
response.addHeader(header.getName(), header.getValue());
|
||||
}
|
||||
|
||||
// copy data from source to response
|
||||
OutputStream os = response.getOutputStream();
|
||||
int b = -1;
|
||||
while ((b = is.read()) != -1) {
|
||||
try {
|
||||
os.write(b);
|
||||
if (b == 10 /** flush buffer on line feed */) {
|
||||
os.flush();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (e.getClass().getSimpleName().equalsIgnoreCase("ClientAbortException")) {
|
||||
// don't throw an exception as this means the user closed the connection
|
||||
logger.debug("Connection closed by client. Will stop proxying ...");
|
||||
// break out of the while loop
|
||||
break;
|
||||
} else {
|
||||
// received unknown error while writing so throw an exception
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Error proxying request: " + url, e);
|
||||
} finally {
|
||||
if (httpget != null) {
|
||||
try {
|
||||
httpget.abort();
|
||||
} catch (Exception e) {
|
||||
logger.error("failed aborting proxy connection.", e);
|
||||
}
|
||||
}
|
||||
|
||||
// httpget.abort() MUST be called first otherwise is.close() hangs (because data is still streaming?)
|
||||
if (is != null) {
|
||||
// this should already be closed by httpget.abort() above
|
||||
try {
|
||||
is.close();
|
||||
} catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ProxyConnectionManager {
|
||||
private final static PoolingClientConnectionManager threadSafeConnectionManager = new PoolingClientConnectionManager();
|
||||
private final static HttpClient httpClient = new DefaultHttpClient(threadSafeConnectionManager);
|
||||
|
||||
static {
|
||||
logger.debug("Initialize ProxyConnectionManager");
|
||||
/* common settings */
|
||||
HttpParams httpParams = httpClient.getParams();
|
||||
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
|
||||
HttpConnectionParams.setSoTimeout(httpParams, 10000);
|
||||
|
||||
/* number of connections to allow */
|
||||
threadSafeConnectionManager.setDefaultMaxPerRoute(400);
|
||||
threadSafeConnectionManager.setMaxTotal(400);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
package org.springframework.cloud.netflix.endpoint;
|
||||
package org.springframework.cloud.netflix.hystrix;
|
||||
|
||||
import org.springframework.cloud.netflix.endpoint.ServletWrappingEndpoint;
|
||||
|
||||
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springframework.cloud.netflix.hystrix.annotations;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.cloud.netflix.hystrix.HystrixDashboardConfiguration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Created by sgibb on 6/19/14.
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Import(HystrixDashboardConfiguration.class)
|
||||
public @interface EnableHystrixDashboard {
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
package io.spring.cloud.netflix.hystrix;
|
||||
|
||||
import io.spring.cloud.netflix.hystrix.stream.MockStreamServlet;
|
||||
import io.spring.cloud.netflix.hystrix.stream.ProxyStreamServlet;
|
||||
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.context.embedded.ServletRegistrationBean;
|
||||
import org.springframework.boot.context.web.SpringBootServletInitializer;
|
||||
import org.springframework.cloud.netflix.hystrix.annotations.EnableHystrixDashboard;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -15,16 +16,17 @@ import org.springframework.context.annotation.Configuration;
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
@EnableHystrixDashboard
|
||||
@EnableAutoConfiguration
|
||||
public class Application extends SpringBootServletInitializer {
|
||||
public class HystrixDashboardApplication extends SpringBootServletInitializer {
|
||||
|
||||
@Override
|
||||
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
|
||||
return application.sources(Application.class).web(true);
|
||||
return application.sources(HystrixDashboardApplication.class).web(true);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(Application.class).web(true).run(args);
|
||||
new SpringApplicationBuilder(HystrixDashboardApplication.class).web(true).run(args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -32,8 +34,4 @@ public class Application extends SpringBootServletInitializer {
|
||||
return new ServletRegistrationBean(new MockStreamServlet(), "/mock.stream");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ServletRegistrationBean proxyStreamServlet() {
|
||||
return new ServletRegistrationBean(new ProxyStreamServlet(), "/proxy.stream");
|
||||
}
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
package io.spring.cloud.netflix.hystrix.stream;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.impl.conn.PoolingClientConnectionManager;
|
||||
import org.apache.http.params.HttpConnectionParams;
|
||||
import org.apache.http.params.HttpParams;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Proxy an EventStream request (data.stream via proxy.stream) since EventStream does not yet support CORS (https://bugs.webkit.org/show_bug.cgi?id=61862)
|
||||
* so that a UI can request a stream from a different server.
|
||||
*/
|
||||
public class ProxyStreamServlet extends HttpServlet {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger logger = LoggerFactory.getLogger(ProxyStreamServlet.class);
|
||||
|
||||
public ProxyStreamServlet() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)
|
||||
*/
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
String origin = request.getParameter("origin");
|
||||
if (origin == null) {
|
||||
response.setStatus(500);
|
||||
response.getWriter().println("Required parameter 'origin' missing. Example: 107.20.175.135:7001");
|
||||
}
|
||||
origin = origin.trim();
|
||||
|
||||
HttpGet httpget = null;
|
||||
InputStream is = null;
|
||||
boolean hasFirstParameter = false;
|
||||
StringBuilder url = new StringBuilder();
|
||||
if (!origin.startsWith("http")) {
|
||||
url.append("http://");
|
||||
}
|
||||
url.append(origin);
|
||||
if (origin.contains("?")) {
|
||||
hasFirstParameter = true;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, String[]> params = request.getParameterMap();
|
||||
for (String key : params.keySet()) {
|
||||
if (!key.equals("origin")) {
|
||||
String[] values = params.get(key);
|
||||
String value = values[0].trim();
|
||||
if (hasFirstParameter) {
|
||||
url.append("&");
|
||||
} else {
|
||||
url.append("?");
|
||||
hasFirstParameter = true;
|
||||
}
|
||||
url.append(key).append("=").append(value);
|
||||
}
|
||||
}
|
||||
String proxyUrl = url.toString();
|
||||
logger.info("\n\nProxy opening connection to: " + proxyUrl + "\n\n");
|
||||
try {
|
||||
httpget = new HttpGet(proxyUrl);
|
||||
HttpClient client = ProxyConnectionManager.httpClient;
|
||||
HttpResponse httpResponse = client.execute(httpget);
|
||||
int statusCode = httpResponse.getStatusLine().getStatusCode();
|
||||
if (statusCode == HttpStatus.SC_OK) {
|
||||
// writeTo swallows exceptions and never quits even if outputstream is throwing IOExceptions (such as broken pipe) ... since the inputstream is infinite
|
||||
// httpResponse.getEntity().writeTo(new OutputStreamWrapper(response.getOutputStream()));
|
||||
// so I copy it manually ...
|
||||
is = httpResponse.getEntity().getContent();
|
||||
|
||||
// set headers
|
||||
for (Header header : httpResponse.getAllHeaders()) {
|
||||
response.addHeader(header.getName(), header.getValue());
|
||||
}
|
||||
|
||||
// copy data from source to response
|
||||
OutputStream os = response.getOutputStream();
|
||||
int b = -1;
|
||||
while ((b = is.read()) != -1) {
|
||||
try {
|
||||
os.write(b);
|
||||
if (b == 10 /** flush buffer on line feed */) {
|
||||
os.flush();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (e.getClass().getSimpleName().equalsIgnoreCase("ClientAbortException")) {
|
||||
// don't throw an exception as this means the user closed the connection
|
||||
logger.debug("Connection closed by client. Will stop proxying ...");
|
||||
// break out of the while loop
|
||||
break;
|
||||
} else {
|
||||
// received unknown error while writing so throw an exception
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Error proxying request: " + url, e);
|
||||
} finally {
|
||||
if (httpget != null) {
|
||||
try {
|
||||
httpget.abort();
|
||||
} catch (Exception e) {
|
||||
logger.error("failed aborting proxy connection.", e);
|
||||
}
|
||||
}
|
||||
|
||||
// httpget.abort() MUST be called first otherwise is.close() hangs (because data is still streaming?)
|
||||
if (is != null) {
|
||||
// this should already be closed by httpget.abort() above
|
||||
try {
|
||||
is.close();
|
||||
} catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ProxyConnectionManager {
|
||||
private final static PoolingClientConnectionManager threadSafeConnectionManager = new PoolingClientConnectionManager();
|
||||
private final static HttpClient httpClient = new DefaultHttpClient(threadSafeConnectionManager);
|
||||
|
||||
static {
|
||||
logger.debug("Initialize ProxyConnectionManager");
|
||||
/* common settings */
|
||||
HttpParams httpParams = httpClient.getParams();
|
||||
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
|
||||
HttpConnectionParams.setSoTimeout(httpParams, 10000);
|
||||
|
||||
/* number of connections to allow */
|
||||
threadSafeConnectionManager.setDefaultMaxPerRoute(400);
|
||||
threadSafeConnectionManager.setMaxTotal(400);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ endpoints:
|
||||
|
||||
server:
|
||||
port: 7979
|
||||
context-path: /hystrix-dashboard
|
||||
|
||||
logging:
|
||||
level: INFO
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package io.spring.cloud.netflix.zuul;
|
||||
|
||||
import com.netflix.zuul.context.ContextLifecycleFilter;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.context.embedded.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.cloud.netflix.endpoint.HystrixStreamEndpoint;
|
||||
import org.springframework.cloud.netflix.hystrix.HystrixStreamEndpoint;
|
||||
import org.springframework.cloud.netflix.zuul.Routes;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user