您的位置:首页 > 编程语言 > Java开发

05.Spring Cloud学习笔记之服务容错保护组件Hystrix

2017-09-03 19:55 836 查看

前言

在微服务架构中存在着很多的服务单元,若一个单元出现故障会导致调用方的对外服务出现延迟,若此时调用方的请求不断的增加,最后就会因为等待出现故障的依赖方响应形成任务积压,最终导致自身服务瘫痪,最终导致整个系统瘫痪(雪崩效应),这样的系统架构很不稳定,为了解决这一类问题产生了断路器等一系列的服务保护机制

Ribbon使用Hystrix

在服务消费的pom.xml文件中添加hystrix依赖

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>


使用@HystrixCommand注解为请求指定fallback方法

@HystrixCommand(fallbackMethod = "indexFallback")
@RequestMapping(value = "/index", method = RequestMethod.GET)
public String index() {
return restTemplate.getForEntity("http://user-service/hello", String.class).getBody();
}

public String indexFallback(){
return "Index Fallback";
}


以上配置 当/index请求失败后会调用indexFallback方法。注:indexFallback方法的声明必须与index方法一致

在启动类添加@EnableCircuitBreaker注解

package com.roberto.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@EnableEurekaClient
@EnableCircuitBreaker
@SpringBootApplication
public class SpringCloudMovieService {
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}

public static void main(String args[]) {
SpringApplication.run(SpringCloudMovieService.class, args);
}
}


停止user-service服务后 调用/index接口进行测试



发现在前面几次调用浏览器刷新后返回Index Fallback,随着错误请求次数的增加,Hystrix的断路器状态会自动打开,后来的请求直接返回Index Fallback,而不会再去请求user-service服务

Feign使用Hystrix

在服务消费的pom.xml文件中添加hystrix依赖和Feign依赖

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>


Feign失败回调类

package com.roberto.springcloud.feign;

import org.springframework.stereotype.Component;

@Component
public class HystrixClientFallback implements UserFeignClient {
@Override
public String index() {
return "Hello Fallback By Hystrix With Feign";
}
}


UserFeignClient接口

package com.roberto.springcloud.feign;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@FeignClient(name = "user-service", fallback = HystrixClientFallback.class)
public interface UserFeignClient {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
String index();
}


Controller调用UserFeignClient

package com.roberto.springcloud.controller;

import com.roberto.springcloud.feign.UserFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MovieController {
@Autowired
private UserFeignClient userFeignClient;

@RequestMapping(value = "/index", method = RequestMethod.GET)
public String index() {
return userFeignClient.index();
}
}


在启动类添加@EnableFeignClients,@EnableCircuitBreaker注解

package com.roberto.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;

@EnableEurekaClient
@EnableFeignClients
@EnableCircuitBreaker
@SpringBootApplication
public class SpringCloudMovieService {
public static void main(String args[]) {
SpringApplication.run(SpringCloudMovieService.class, args);
}
}


在配置文件application.yml中添加配置,启用feign的hystrix机制

feign:
hystrix:
enabled: true


停止user-service服务后 调用/index接口进行测试



禁用单个FegionClient的Hystrix的支持

新建Feign配置类,该类需不能被@SpringBootApplication注解扫描到,否则会覆盖全局Feign配置

package com.roberto.springcloud.feign;

import feign.Feign;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
public class FeignConfiguration {
@Bean
@Scope("prototype")
public Feign.Builder feignBuilder() {
return Feign.builder();
}
}


为指定@FeignClient注解添加configuration属性指向上述Feign配置类

@FeignClient(name = "USER-SERVICE", configuration = FeignConfiguration.class)


Feign使用fallbackFactory属性打印fallback异常

新建HystrixClientFactory类

package com.roberto.springcloud.feign;

import feign.hystrix.FallbackFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class HystrixClientFactory implements FallbackFactory<UserFeignClient> {
private static final Logger LOGGER = LoggerFactory.getLogger(HystrixClientFactory.class);

@Override
public UserFeignClient create(Throwable cause) {
HystrixClientFactory.LOGGER.info("fallback; reason was: {}", cause.getMessage());
return new UserFeignClient() {
@Override
public String index() {
return "Hello HystrixClientFactory fallback";
}
};
}
}


在@FeignClient注解中添加fallbackFactory属性指向HystrixClientFactory类

package com.roberto.springcloud.feign;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@FeignClient(name = "USER-SERVICE", fallbackFactory = HystrixClientFactory.class)
public interface UserFeignClient {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
String index();
}


配置Hystrix隔离策略

@HystrixCommand(fallbackMethod = "indexFallback", commandProperties = @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"))
@RequestMapping(value = "/index", method = RequestMethod.GET)
public String index() {
return restTemplate.getForEntity("http://user-service/hello", String.class).getBody();
}

public String indexFallback(){
return "Index Fallback";
}

在@HystrixCommand添加commandProperties = @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE")配置说明/index请求和indexFallback方法是在同一个线程中,execution.isolation.strategy的默认值为THREAD,表示当/index请求失败时会新开一个线程调用indexFallback方法即处于不同线程下

全局配置,在application.yml配置文件中添加如下内容
hystrix.command.default.execution.isolation.strategy:SEMAPHORE


配置Hystrix超时时间

@HystrixCommand(fallbackMethod = "indexFallback", commandProperties = @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000"))
@RequestMapping(value = "/index", method = RequestMethod.GET)
public String index() {
return restTemplate.getForEntity("http://user-service/hello", String.class).getBody();
}

public String indexFallback(){
return "Index Fallback";
}

在@HystrixCommand添加commandProperties = @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")配置说明当请求服务如果超过了Hystrix配置的超时时间(5秒),会直接进入fallbackMethod方法

全局配置,在application.yml配置文件中添加如下内容
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000


health端点查看系统状态

在pom.xml添加以下依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>


访问http://hostname:port/health查看系统状态



常见问题:

有时候访问/health端点只出现如下内容



出现该问题的原因是由于没有权限查看,解决该问题的方式有两种。

第一种是在application.yml配置文件下添加如下内容,表示取消认证(不推荐因为不安全)

management:
security:
enabled: false


第二是配置导入spring-boot-starter-security依赖后,配置认证登录后即可查看详细信息,这个在之前的博客中有提及

注:/health端点并不是为hystrix专门设计的,只是因为这里用到了所以在此博客中讲述

Hystrix Dashboard监控

单体应用监控

新建Dashboard实例工程

在pom.xml文件中添加如下依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>spring-cloud-parent</artifactId>
<groupId>com.roberto.springcloud</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>

<artifactId>spring-cloud-dashboard</artifactId>
<packaging>jar</packaging>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
</dependencies>
</project>


在启动类添加@EnableHystrixDashboard注解

package com.roberto.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

@EnableHystrixDashboard
@SpringBootApplication
public class SpringCloudDashboardApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudDashboardApplication.class, args);
}
}


修改application.yml文件如下

server:
port: 9091
spring:
application:
name: hystrix-dashboard


访问Hystrix Dashboard监控界面,http://localhost:9091/hystrix



Hystrix Dashboard监控单点实例节点需要通过访问实例/hystrix.stream接口来实现,我们需要为服务实例添加这个断点(注:服务实例是指要监控的实例并非dashboard),要开放/hystrix.stream接口只需如下两步

1.在pom.xml文件中添加以下依赖

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>


2.确保在服务启动类上添加了@EnableCircuitBreaker注解,开启断路器功能

在Hystrix Dashboard控制面板输入监控实例信息 开始监控



监控单体应用最终效果图如下所示



使用Turbine进行集群应用监控

新建turbine实例,pom.xml文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>spring-cloud-parent</artifactId>
<groupId>com.roberto.springcloud</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>

<artifactId>spring-cloud-turbine</artifactId>
<packaging>jar</packaging>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-turbine</artifactId>
</dependency>
</dependencies>
</project>


启动类添加@EnableTurbine注解

package com.roberto.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.turbine.EnableTurbine;

@EnableTurbine
@SpringBootApplication
public class SpringCloudTurbineApplication {
public static void main(String args[]) {
SpringApplication.run(SpringCloudTurbineApplication.class, args);
}
}


application.yml文件配置如下

server:
port: 9091
spring:
application:
name: turbine-service
eureka:
client:
serviceUrl:
defaultZone: http://roberto:roberto123@localhost:8761/eureka instance:
prefer-ip-address: true
turbine:
aggregator:
clusterConfig: default # 指定聚合哪些集群,多个使用","分割,默认为default。可使用http://.../turbine.stream?cluster={clusterConfig之一}访问
appConfig: MOVIE-SERVICE # 配置Eureka中的serviceId列表,表明监控哪些服务
clusterNameExpression: "'default'"
# 1. clusterNameExpression指定集群名称,默认表达式appName;此时:turbine.aggregator.clusterConfig需要配置想要监控的应用名称
# 2. 当clusterNameExpression: default时,turbine.aggregator.clusterConfig可以不写,因为默认就是default
# 3. 当clusterNameExpression: metadata['cluster']时,假设想要监控的应用配置了eureka.instance.metadata-map.cluster:ABC,则需要配置,同时turbine.aggregator.clusterConfig: ABC


其中MOVIE-SERVICE为服务名称,Turbine只能用来监控实现了Hystrix的工程项目,所以此处的MOVIE-SERVICE必须实现Hystrix

访问/turbine.stream接口进行数据的监测,将接口地址复制到Dashboard中即可监控集群



监控效果图



常见问题:假设在要监控的某个实例中配置了management.context-path=xxx或server.context-path=xxx那么需要在turbine配置文件中添加如下内容

turbine.instanceUrlSuffix.<cluster-name> = xxx

# 如果是全局配置则改为
# turbine.instanceUrlSuffix = xxx


假设在要监控的某个实例中配置了management.port=xxx那么需要在该实例下添加如下配置

eureka.instance.metadata-map.management.port=xxx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐