Hystrix
简介
Hystix,即熔断器。
主页:https://github.com/Netflix/Hystrix/
Hystrix是Netflix开源的一个延迟和容错库,用于隔离访问远程服务、第三方库,防止出现级联失败。
使用熔断器Hystrix为了优化项目。
熔断器的工作机制
正常工作的情况下,客户端请求调用服务API接口:
当有服务出现异常时,直接进行失败回滚,00000000处理:
当服务繁忙时,如果服务出现异常,不是粗暴的直接报错,而是返回一个友好的提示,虽然拒绝了用户的访问,但是会返回一个结果。
这就好比去买鱼,平常超市买鱼会额外赠送杀鱼的服务。等到逢年过节,超时繁忙时,可能就不提供杀鱼服务了,这就是服务的降级。
系统特别繁忙时,一些次要服务暂时中断,优先保证主要服务的畅通,一切资源优先让给主要服务来使用,在双十一、618时,京东天猫都会采用这样的策略。
动手实践
引入依赖
首先在student-service中引入Hystrix依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
开启熔断
在启动类上添加注解 “@EnableHystrix”
package com.czxy; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.hystrix.EnableHystrix; @SpringBootApplication @EnableEurekaClient @EnableHystrix //开启熔断器 public class StudentApplication { public static void main(String[] args) { SpringApplication.run(StudentApplication.class,args); } }
改造调用方
我们改造student-service,修改ClassesDao的findAll方法,并且声明一个失败时的回滚处理函数dataFallback:
package com.czxy.dao; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Repository; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.client.RestTemplate; import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; @Repository public class ClassesDao { @Resource private RestTemplate restTemplate; @GetMapping @HystrixCommand(fallbackMethod = "findAllFallback") public List<String> findAll(){ ResponseEntity<List> entity = restTemplate.getForEntity("http://classes-service/classes", List.class); List<String> list = entity.getBody(); return list; } /** * 失败时的回调函数 * @return */ public List<String> findAllFallback(){ List<String> list = new ArrayList<>(); list.add("模拟数据1"); list.add("模拟数据2"); list.add("模拟数据3"); return list; } }
- @HystrixCommand(fallbackMethod="dataFallback"):声明一个失败回滚处理函数dataFallback,当findAll执行超时(默认是1000毫秒),就会执行fallback函数,返回替代内容。
- 为了方便查看熔断的触发时机,我们记录请求访问时间。
- 多学一招:通过@HystrixCommand的commandProperties 可以设置默认时间
/
// HystrixCommandProperties Hystrix命令参数配置类 @HystrixCommand(fallbackMethod = "dataFallback",commandProperties = @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1000"))
改造服务提供者
改造服务提供者classes_service,随机休眠一段时间,以触发熔断:
package com.czxy.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.List; import java.util.Random; @RestController @RequestMapping("/classes") public class ClassesController { @Resource private HttpServletRequest request; @GetMapping public List<String> findAll() throws InterruptedException { // 模拟网络延迟 Thread.sleep(new Random().nextInt(2000)); List<String> list = new ArrayList<>(); list.add("Java12班"); list.add("Java34班"); list.add("服务端端口:" + request.getServerPort()); return list; } }
改造StudentController,记录执行时间
package com.czxy.controller; import com.czxy.service.StudentService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.List; /** * @author 桐叔 * @email liangtong@itcast.cn */ @RestController @RequestMapping("/student") public class StudentController { @Resource private StudentService studentService; @GetMapping public List<String> findAll(){ long start = System.currentTimeMillis(); List<String> list = studentService.findAll(); long end = System.currentTimeMillis(); list.add("耗时:" + (end-start)); return list; } }
启动测试
启动服务
如果超过1秒将使用“模拟数据”
如果超过1秒将使用“模拟数据”
面试题
如果熔断和重试机制,都配置,是都生效?还是某个生效?
经测试发现是熔断生效,为什么?
1. Ribbon重试机制的超时时间设置的是1000ms:
2.Hystix的超时时间默认也是1000ms
3.实际执行后发现,没有触发重试机制,而是先触发了熔断。
所以,Ribbon的超时时间一定要小于Hystix的超时时间。
我们可以通过hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds来设置
# Hystrix超时时间。 hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 3000 # 设置hystrix的超时时间为3000ms
Feign
在前面的学习中,我们使用了Ribbon的负载均衡功能,大大简化了远程调用时的代码:
restTemplate.getForEntity("http://classes-service/classes", List.class);
如果就学到这里,你可能以后需要编写类似的大量重复代码,格式基本相同,无非参数不一样。有没有更优雅的方式,来对这些代码再次优化呢?
这就是我们接下来要学的Feign的功能了。
简介
Feign是一种声明式、模板化的HTTP客户端。
在SpringCloud中使用Feign,我们可以做到使用HTTP请求远程服务时能与调用本地方法一样的编码体验,开发者完全感知不到这是远程方法,更感知不到这是个HTTP请求。
快速入门
导入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
开启Feign功能
我们在启动类上,添加注解 @EnableFeignClients,开启Feign功能
package com.czxy; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.hystrix.EnableHystrix; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableEurekaClient @EnableHystrix //开启熔断器 @EnableFeignClients //开启feign public class StudentApplication { public static void main(String[] args) { SpringApplication.run(StudentApplication.class,args); } }
Feign的客户端
- 通过@FeignClient 声明Feign客户端。
- value : 用于设置服务名称
- path:用于设置路径前缀(也就是controller配置的路径)
- Feign类似于MyBatis。
- @FeignClient类似 @Mapper注解。
- Feign中的方法需要与服务的方法声明完全一致。注意:路径
- Feign会根据注解帮我们生成URL。
步骤一:编写 ClassesFeign
package com.czxy.feign; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import java.util.List; @FeignClient(value="classes-service",path="/classes") public interface ClassesFeign { @GetMapping public List<String> findAll(); }
修改StudentService,不再调用ClassesDao,直接使用
package com.czxy.service; import com.czxy.dao.ClassesDao; import com.czxy.feign.ClassesFeign; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; @Service public class StudentService { // @Resource // private ClassesDao classesDao; @Resource private ClassesFeign classesFeign; public List<String> findAll() { // return classesDao.findAll(); return classesFeign.findAll(); } }
启动测试
正常访问
如果超时,抛异常
入门总结
第一步:导入Feign依赖
第二步:在StudentApplication启动类添加 @EnableFeginClients
第三步:编写Feign客户端 使用@FeignClient声明客户端 参数:value设置服务名称 path设置路径前缀(也就是controller配置的路径)
第四步:在controller中使用Feign客户端发起远程请求调用
负载均衡
之前使用RestTemplate进行远程调用时,需要添加额外注解来完成负载均衡处理。
Feign中本身已经集成了Ribbon依赖,不需要额外引入依赖,就可以完成负载均衡处理。采用之前的配置就可以。
classes-service: ribbon: ConnectTimeout: 250 # Ribbon的连接超时时间 ReadTimeout: 1000 # Ribbon的数据读取超时时间 OkToRetryOnAllOperations: true # 是否对所有操作都进行重试 MaxAutoRetriesNextServer: 1 # 切换实例的重试次数 MaxAutoRetries: 1 # 对当前实例的重试次数
另外,还可以使用ribbon.xx来进行ribbon的全局配置。
Hystrix支持
Feign默认也有对Hystix的集成,只不过,默认情况下是关闭的。
我们需要通过下面的参数来开启:
feign: hystrix: enabled: true # 开启Feign的熔断功能1.
但是,Feign中的Fallback配置不像Ribbon中那样简单了。
首先,我们要定义一个类,实现DataFeign接口,作为fallback的处理类
package com.czxy.feign; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; email liangtong@itcast.cn @Component public class ClassesFeignFallback implements ClassesFeign { @Override public List<String> findAll() { List<String> list = new ArrayList<>(); list.add("模拟数据111"); list.add("模拟数据222"); list.add("模拟数据333"); return list; } }
然后在DataFeign中,指定刚才编写的实现类
package com.czxy.feign; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import java.util.List; @FeignClient(value="classes-service",path="/classes" ,fallback = ClassesFeignFallback.class ) public interface ClassesFeign { @GetMapping public List<String> findAll(); }
重启测试:
请求压缩(了解)
Spring Cloud Feign 支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗。通过下面的参数即可开启请求与响应的压缩功能:
feign: compression: request: enabled: true # 开启请求压缩 response: enabled: true # 开启响应压缩
同时,我们也可以对请求的数据类型,以及触发压缩的大小下限进行设置:
feign: compression: request: enabled: true # 开启请求压缩 mime-types: text/html,application/xml,application/json # 设置压缩的数据类型 min-request-size: 2048 # 设置触发压缩的大小下限
注:上面的数据类型、压缩大小下限均为默认值。
日志级别(了解)
前面讲过,通过logging.level.xx=debug来设置日志级别。然而这个对Fegin客户端而言不会产生效果。因为@FeignClient注解修改的客户端在被代理时,都会创建一个新的Fegin.Logger实例。我们需要额外指定这个日志的级别才可以。
设置com.czxy包下的日志级别都为debug
logging: level: com: czxy: feign: debug
编写配置类,定义日志级别
package com.czxy.config; import feign.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FeignConfig { @Bean public Logger.Level feignLoggerLevel(){ return Logger.Level.FULL; } }
这里指定的Level级别是FULL,Feign支持4种级别:
- NONE:不记录任何日志信息,这是默认值。
- BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
- HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
- FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
在FeignClient中指定配置类:
/** * Created by liangtong. */ @FeignClient(value="service",path="/test",fallback = DataFeignFallback.class,configuration = FeignConfig.class) public interface DataFeign { @GetMapping public ResponseEntity<String> test(); }
重启项目,即可看到每次访问的日志: