这里写目录标题
1服务熔断 Hystrix是什么
就是一个组件;
1.1为什么使用这个组件
各个服务之间是相互调用的,如果A服务调用B服务,但是B服务响应慢,或者网络中断了,导致A服务一直等待,A服务不能使用,万一其他C服务调用A服务,那么这样,就会一连串的导致整个微服务不能使用,这样就出现问题了;
总结:
一个服务不能使用,导致整个微服务不能使用,这样是不可取的;
为了解决此问题,微服务架构中引入了一种叫熔断器的服务保护机制。
1.2作用
微服务架构中的熔断器,就是当被调用方没有响应,调用方直接返回一个错误响
应即可,而不是长时间的等待,这样避免调用时因为等待而线程一直得不到释放,
避免故障在分布式系统间蔓延;
2入门案例
因为是消费端调用服务端,服务端可能出错,导致消费端一直等待;
所以我们的服务熔断组件需要加到消费端;这样,消费端得不到回应的话,就自己返回一个错误,不需要一直调用等待了;
所以是客户端使用这个组件
1 在消费端加入组件的依赖
<!--Spring Cloud 熔断器起步依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.4.RELEASE</version>
</dependency>
2、在入口类中使用@EnableCircuitBreaker 注解开启断路器功能,也可以使用一个名为@SpringCloudApplication 的注解代替主类上的三个注解;
3、在调用远程服务的方法上添加注解:
@HystrixCommand(fallbackMethod=“error”)
举例
我们在消费端重新写一个方法调用提供者的方法,并且加了这个注解
@HystrixCommand(fallbackMethod=“error”)
这个注解的意思是:当这个方法出错,调用error这个方法,所以我们需要重新写一个方法error
@RequestMapping("/web/hystrix")
@HystrixCommand(fallbackMethod="error")
public String hystrix () {
int a = 10 / 0 ; //除数是不能为0的,会抛出运行时异常
return restTemplate.getForEntity("http://01-SERVICE-PROVIDER/hello", String.class).getBody();
}
error 方法
/** * 熔断的回调方法,也就是降级方法 * * @return */
public String error(Throwable throwable) {
System.out.println("异常:" + throwable.getMessage());
//访问远程服务失败,该如何处理?这些处理逻辑就可以写在该方法中
return "error";
}
以上启动关联的项目,那么浏览器访问,我们就可以看到
2.1修改 hystrix 的默认超时时间
默认是调用的服务端如果1秒没有返回,就触发这个熔断,所以,我们需要修改,只需要在注解里面加参数就可以了
{@HystrixProperty(name=“execution.isolation.thread.timeoutInMilliseconds”, value=“3500”)}
/** * hystrix超时时间是3.5秒 * * @return */
@RequestMapping("/web/hystrix")
@HystrixCommand(fallbackMethod="error",commandProperties={
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="3500")})
public String hystrix () {
int a = 10 / 0 ; //除数是不能为0的,会抛出运行时异常
return restTemplate.getForEntity("http://01-SERVICE-PROVIDER/hello", String.class).getBody();
}
2.2 Hystrix 的服务降级
就是远程服务不能用了,才有这个,这个就是执行fallbackMethod里面的方法
2.3 Hystrix 的异常处理
就是如何获取异常信息
我们在调用服务提供者时,我们自己也有可能会抛异常,默认情况下方法抛了异
常会自动进行服务降级,交给服务降级中的方法去处理; 当我们自己发生异常后,只需要在服务降级方法中添加一个 Throwable 类型的
参数就能够获取到抛出的异常的类型,如下
此时我们可以在控制台看到异常的类型;
如果远程服务有一个异常抛出后我们不希望进入到服务降级方法中去处理,而是
直接将异常抛给用户,那么我们可以在@HystrixCommand 注解中添加忽略异
常,如下
2.4自定义 Hystrix 请求的服务异常熔断处理
我们也可以自定义类继承自 HystrixCommand 来实现自定义的 Hystrix 请求, 在 getFallback 方法中调用 getExecutionException 方法来获取服务抛出的异
常;
/** * 自定义的Hystrix请求 * */
public class MyHystrixCommand extends HystrixCommand<String> {
private RestTemplate restTemplate;
public MyHystrixCommand (Setter setter, RestTemplate restTemplate) {
super(setter);
this.restTemplate = restTemplate;
}
@Override
protected String run() throws Exception {
int a = 10 / 0;
//调用远程的服务
return restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/hello", String.class).getBody();
}
/** * 当远程服务超时、异常、不可用等情况时,会触发该熔断方法 * * @return */
@Override
public String getFallback() {
Throwable throwable = super.getExecutionException();
System.out.println(throwable.getMessage());
System.out.println(throwable.getStackTrace());
//实现服务熔断/降级逻辑
return "error";
}
}
controller 里面进行调用上面的
@RequestMapping("/web/hystrix2")
public String hystrix2 () throws ExecutionException, InterruptedException {
MyHystrixCommand myHystrixCommand = new MyHystrixCommand(com.netflix.hystrix.HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("")), restTemplate);
//同步调用(该方法执行后,会等待远程的返回结果,拿到了远程的返回结果,该方法才返回,然后代码继续往下执行)
//String str = myHystrixCommand.execute();
//异步调用(该方法执行后,不会马上有远程的返回结果,将来会有结果)
Future<String> future = myHystrixCommand.queue();
//写一些业务的逻辑
//阻塞的方法,直到拿到结果
String str = future.get();
//写一些业务逻辑
return str;
}
2.5 Hystrix 仪表盘监控
Hystrix 仪表盘(Hystrix Dashboard),就像汽车的仪表盘实时显示汽车的各
项数据一样,Hystrix 仪表盘主要用来监控 Hystrix 的实时运行状态,通过它我们可以看到 Hystrix 的各项指标信息,从而快速发现系统中存在的问题进而解决它。
我们重新创建一个项目,专门做这个功能
2.5.1 搭建这个项目
1 创建springboot项目,导入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.jing</groupId>
<artifactId>04-service-hystrixboard</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>04-service-hystrixboard</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--hystrix-dashboard功能的起步依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
2 添加好依赖之后,在入口类上添加@EnableHystrixDashboard 注解开启仪表盘;
4 属性配置
最后,我们可以根据个人习惯配置一下 application.properties 文件,如下:
server.port=3721
至此,我们的 Hystrix 监控环境就搭建好了;
导入依赖,加配置,启动就可以了
2.5.2 解读页面
Hystrix 仪表盘工程已经创建好了,现在我们需要有一个服务,让这个服务提供
一个路径为/actuator/hystrix.stream 接口,然后就可以使用 Hystrix 仪表盘来
对该服务进行监控了;
我们改造消费者服务,让其能提供/actuator/hystrix.stream 接口,步骤如下:
1需要有一个 spring boot 的服务监控依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2 配置文件需要配置 spring boot 监控端点的访问权限:
management.endpoints.web.exposure.include=*
这个是用来暴露 endpoints 的,由于 endpoints 中会包含很多敏感信息,除
了 health 和 info 两个支持直接访问外,其他的默认不能直接访问,所以我们
让它都能访问,或者指定:
management.endpoints.web.exposure.include=hystrix.stream
3 访问入口 http://localhost:8088/actuator/hystrix.stream
ip是消费者的ip和端口
注意:这里有一个细节需要注意,要访问/hystrix.stream 接口,首先得访问
consumer 工程中的任意一个其他接口,否则直接访问/hystrix.stream 接口时 会输出出一连串的 ping: ping: …,先访问 consumer 中的任意一个其他接口,
然后再访问/hystrix.stream 接口即可;
以上看到的页面不好看,所以我们要对这个进行仪表盘的监控,把这个项目的这个路径放到仪表盘项目里面
3 声明式服务消费 Feign
这个也是一个组件
,因此 Spring Cloud 基于 Netflix Feign 整合了 Ribbon 和 Hystrix 两个组件,让我们的开发工作变得更加简单;
同时还提供了一种声明式的 Web 服务客户端定义方式
3.1 创建项目
当前项目就是消费者项目,只是这个里面使用了feign
1 创建springboot项目
2 配置依赖
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--SpringBoot的父级依赖-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.jing</groupId>
<artifactId>05-service-feign</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>05-service-feign</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--SpringBoot开发web项目的起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--eureka客户端进行服务发现的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--spring cloud feign的起步依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
<!--Spring Cloud熔断器起步依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.46</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--spring boot的编译打包插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
3 在项目入口类上添加@EnableFeignClients 注解表示开启 Spring Cloud Feign
的支持功能;
4 定义一个 HelloService 接口,通过@FeignClient 注解来指定服务名称,进而绑定服务,然后再通过 SpringMVC 中提供的注解来绑定服务提供者提供的接口,
/** * 使用feign的客户端注解绑定远程服务的名称 * 远程服务的名称可以大写,也可以小写 * */
@FeignClient(name="01-SERVICE-PROVIDER"/*fallback = MyFallback.class*/)
public interface HelloService {
/** * 声明一个方法,这个方法就是远程的服务提供者提供的那个方法 * * @return */
@RequestMapping("/hello")
public String hello();
}
5 使用 Controller 中调用服务
@RestController
public class FeignController {
@Autowired
private HelloService helloService;
@RequestMapping("/web/hello")
public String hello () {
//调用声明式的接口方法,实现对远程服务的调用
return helloService.hello();
}
}
6 第六步:属性配置
在 application.properties 中指定服务注册中心、端口号等信息,如下:
server.port=8082
#配置服务的名称
spring.application.name=05-service-feign
#配置eureka注册中心地址
eureka.client.service-url.defaultZone=http://eureka8762:8762/eureka
#开启hystrix功能
feign.hystrix.enabled=true
7 第七步:测试
依次启动注册中心、服务提供者和 feign 实现服务消费者,
3.2 负载均衡:
我们知道,Spring Cloud 提供了 Ribbon 来实现负载均衡,使用 Ribbo 直接注
入一个 RestTemplate 对象即可,RestTemplate 已经做好了负载均衡的配置;
在 Spring Cloud 下,使用 Feign 也是直接可以实现负载均衡的,定义一个注解
有@FeignClient 注解的接口,然后使用@RequestMapping 注解到方法上映
射远程的 REST 服务,此方法也是做好负责均衡配置的。
3.2 服务熔断:
1、在 application.properties 文件开启 hystrix 功能
feign.hystrix.enabled=true
2 我们重新写一个回调的类,这个类要继承service接口,重写里面的方法,方法里面的东西就是远程出现错误的时候,走当前里面的东西;
3 在feign的service方法上面写这个
4 当提供者接口出现错误的时候,就会走回调函数里面的东西
3.3 服务熔断获取异常信息
为@FeignClient 修饰的接口加上 fallback 方法可以实现远程服务发生异常后进
行服务的熔断,但是不能获取到远程服务的异常信息,如果要获取远程服务的异
常信息,怎么办?此时可以使用 fallbackFactory:
1 创建写一个类,
@Component
public class MyFallbackFactory implements FallbackFactory<HelloService> {
@Override
public HelloService create(Throwable throwable) {
return new HelloService() {
@Override
public String hello() {
return throwable.getMessage();
}
};
}
}
2 servcie 层调用
这样就可以了,只要提供者方法出错,那么在回调函数里面就会打印出来