关于Hystrix
在微服务的应用场景中,通常会有很多层的服务调用。应用程序有数十个依赖关系,每个依赖关系在某些时候不可避免的会出现问题(例如调用响应时间过长或不可用)。
如果一个底层服务出现问题,由于服务与服务之间的依赖性,故障会被向上传播给用户。那么就需要一种机制,当底层服务不可用时,可以阻断故障的传播。
在SpringCloud中提供这个机制的组件就是断路器Hystrix。大致的工作流程如下图:
当对B的调用失败达到一个特定的阀值(5秒之内发生20次失败是Hystrix定义的缺省值), 链路就会被处于open状态,之后所有所有对服务B的调用都不会被执行,取而代之的是由断路器提供的一个表示链路open的Fallback消息。
Hystrix提供了相应机制,可以让开发者定义这个Fallbak消息。Fallback可以是另一个由Hystrix保护的服务调用,也可以是固定的值。Fallback也可以设计成链式调用,先执行某些逻辑,再返回Fallback。
简单来说类似电闸,当电路中出现过载时就会断电保证安全。Hystrix可以检测到服务的可用性,但是不够直观。使用HystrixDashboard会提供一个监控平台,方便查看服务的可用性。
具体实现
Dashboard是一个统一的断路器监控平台,一般会新建一个项目,引入相关依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
然后用@EnableHystrixDashboard注解Spingboot启动类,开启hystrixDashboard:
public class EurekaHystrixMonitorApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaHystrixMonitorApplication.class, args);
}
}
启动项目后访问/hystrix即可访问Hystrix Dashboard:
turbine-hostname:port/turbine.stream开启,实现对默认集群的监控。
turbine-hostname:port/turbine.stream?cluster=[clusterName]开启,实现对clusterName集群的监控。
/hystrix-app:port/hystrix.stream开启,实现对具体某个服务实例的监控。(从springboot2开始,actuator都是要在请求路径加上/actuator,也就是hystrix-app:port/actuator/hystrix.stream)。
利用思路
可以通过相关接口配置监控:
/hystrix/monitor?stream=
/proxy.stream?origin=
可以看到成功在dnslog接收到相关请求:
简单查看下相关的源代码,点击MonitorStream按钮后触发的是如下的js function:
在一系列的非空判断后,使用EventSource对象来实现服务器的推送:
proxy.stream接口其实是一个简单的servlet实现,origin参数就是前面我们输入的dnslog地址,相关的web.xml配置:
<servlet-mapping>
<servlet-name>ProxyStreamServlet</servlet-name>
<url-pattern>/proxy.stream</url-pattern>
</servlet-mapping>
查看相关的Java代码:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String origin = request.getParameter("origin");
String authorization = request.getParameter("authorization");
if (origin == null) {
response.setStatus(500);
response.getWriter().println("Required parameter 'origin' missing. Example: 107.20.175.135:7001");
return;
}
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") && !key.equals("authorization")) {
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("nnProxy opening connection to: {}nn", proxyUrl);
try {
httpget = new HttpGet(proxyUrl);
if (authorization != null) {
httpget.addHeader("Authorization", authorization);
}
HttpClient client = ProxyConnectionManager.httpClient;
HttpResponse httpResponse = client.execute(httpget);
int statusCode = httpResponse.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_OK) {
......
因为整个过程是通过HttpClient发起HTTP请求的,同时也缺少进一步的安全限制。也就是说可以简单的进行ssrf攻击,但没有相应的返回,为blindssrf。例如尝试监控本地不存在的端口1234,返回Unable to connect to Command Metric Stream:
尝试监控存在的端口9999,会返回相关的监控面板:
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论