Feign的雪崩处理
在声明式远程服务调用Feign中,实现服务灾难性性雪崩效应处理也是通过Hystrix实现的
1. 降级实现
- 引入依赖:
Feign启动器(spring-cloud-starter-openfeign)中是包含Hystrix相关依赖的,如果只是使用服务降级功能,则不需要引入独立依赖;如果需要使用Hystrix其他服务容错能力,则需要依赖spring-cloud-starter-netflix-hystrix依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
- 配置参数:
A、B、C版本默认开启Hystrix,之后版本默认关闭# 开启Hystrix feign.hystrix.enabled=true # 访问错误时是否调用fallback方法逻辑,默认为true hystrix.command.default.fallback.enabled=true # 开启熔断机制,默认开启 hystrix.command.default.circuitBreaker.enabled=true # 多少错误请求数开启断路由 默认20 hystrix.command.default.circuitBreaker.requestVolumeThreshold=20 # 断路由开启后的睡眠时间窗,多少毫秒内不再访问服务 默认5000ms hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=5000 # 时间窗内,错误请求百分比达到多少开启断路由 默认50% hystrix.command.default.circuitBreaker.errorThresholdPercentage=50 # 是否强制开启断路由,默认false hystrix.command.default.circuitBreaker.forceOpen=false # 是否强制关闭断路由,默认false hystrix.command.default.circuitBreaker.forceClosed=false
- 创建接口:定义一个与服务标准api相同的Application Client服务接口(该接口通过@FeignClient注解描述fallback方法所在类是什么)
@FeignClient(name="feign-service-01" fallback=MyServiceConsumerImpl.class) public interface MyServiceConsumer extends MyService{}
- 创建接口实现类:实现的方法都修改为托底方法
@Component public class MyServiceConsumerImpl implements MyServiceConsumer{ //实现的方法返回托底数据即可 }
2. 请求合并
Feign不推荐使用请求合并
Hystrix Dashboard
针对Hystrix进行实时监控的工具,可以直观看到Hystrix Command的请求响应时间、请求成功率等数据
实现
依然在Client端实现
- 引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency>
- 配置相关信息(不配置则监控不到,因为actuator用来采集被监控的数据,actuator考虑了安全问题,默认信息几乎没有,所以需要对其进行配置
# 默认开启health和info,*表示全开(不推荐) management.endpoints.web.exposure.include=* # 添加hytrix信息 management.endpoints.web.exposure.include=hystrix.stream
- 启动类添加EnabledHystrixDashboard注解开启,还需要开启@EnabledCircuitBreaker
@SpringBootApplication @EnableHystrixDashboard @EnableCircuitBreaker public class SpringBootApp{ public static void main(String[] args){ SpringApplication.run(SpringBootApp.class,args); } }
- 访问
http:ip:port/actuator - 显示可访问的监控地址(spring-boot-starter-actuator提供)
http:ip:port/hystrix - 显示Hystrix监控数据图形界面(spring-cloud-starter-netflix-hystrix-dashboard提供)
http:ip:port/actuator/hystrix.stream - 显示Hystrix采集数据(spring-boot-strarter-actuator提供)
Zuul
Zuul是Spring Cloud中的微服务网关,网关是一个网络整体系统中的前置门户入口,请求首先通过网关,进行路径的路由定位到具体的服务节点上
1. 作为路由使用
Zuul首先是一个微服务,也会在Eureka注册中心进行服务的注册和发现
路由表是一个Map<String,Map<String,Object>>
path代表请求路径
url代表Application Client地址(简单直观)
serviceId代表Application Client的名称(集群方便)
- 引入依赖(web、eureka-client、zuul)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-cloud-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
- 创建启动类,开启zuul网关(@EnableZuulProxy)
@SpringBootApplication @EnableZuulProxy public class SpringBootApp{ public static void main(String[] args){ SpringApplication.run(SpringBootApp.class,args); } }
- 配置
# 应用名称 spring.application.name=zuul-01 # 开放端口 server.port=80 # Eureka注册中心 eureka.client.serviceUrl.defaultZone=http://192.168.54.100:8761/eureka,http://192.168.54.100:8762/eureka # 基于路径映射的匹配规则(直观) zuul.routes.test.path=/test/** zuul.routes.test.url=http://localhost:8081/ # 基于服务映射的匹配规则(集群方便,外层key为服务名称就可以省略服务配置) zuul.routes.test.path=/test/** zuul.routes.test.ServiceId=Application-Client-01 # 等同于 zuul.routes.Application-Client-01.path=/test/**
2. 作为网关过滤器使用
- 创建一个过滤器
public class MyFilter extends ZuulFilter{ /**判断当前的过滤器是什么类型 *前置【pre】 - 路由之前执行 *路由后【route】 - 路由之后,未转发请求时执行 *异常【error】 - 发生异常(一定是过滤器异常)时执行 *后置【post】 - 转发请求响应后执行 */ @Override public Stirng filterType(){ //定义当前为前置过滤器 return "pre"; } //判断当前过滤器的执行顺序 //同类过滤器,按顺序执行 //数值越小,越先执行 @Override public int filterOrder(){ return 0; } //是否开启过滤器 @Override public boolean shouldFilter(){ return true; } //定义一个日志 private final Logger logger=LoggerFactory.getLogger(PreFilter.class); //过滤器具体执行内容 @Override public Object run() throws ZuulException(){ //日志打印 logger.info(); return null; } //zuul网关提供的请求上下文 //RequestContext是线程安全的,直接与线程绑定threadLocal.get() RequestContext context=RequestContext.getCurrentContext(); //获取请求 HttpServletRequest request=context.getRequest(); //请求地址 request.getRequestURL(); //请求方法 request.getMethod(); }
Zuul的生命周期
client - zuul - pre filter - routing filter - application - post filter - client
pre filet、routing filter有异常:- error filter - post filter - client
application异常 - sendErrorFilter - error filter - post filter -client
如果post filter异常,则直接由error filter直接响应client
Zuul网关的容错
在Spring Cloud中,Zuul启动器中包含了Hystrix的相关依赖,默认提供了Hystrix Dashboard服务监控数据与Hystrix无缝结合的
1. 服务降级处理
Zuul提供了FallbackProvider接口用于实现fallback处理,只针对timeout异常处理,只要服务有返回(包括返回异常),都不会触发Zuul的fallback容错逻辑,因为Zuul在做请求路由分发的时候,结果由远程服务运算,如果远程服务返回异常,而Zuul不能确定该异常是否是应用想要反馈给客户端的
- 创建FallbackProvider的实现类
@Compoent public class My implements FallbackProvider{ //获取路由信息 @Override public String getRoute(){ //可以使用通配符“*”,代表为全部的服务提供容错处理 return "application-name-*";//为服务名前缀为application-name-所有服务提供容错处理 } //服务容错处理的具体逻辑,route是服务名称,cause是异常类型 public ClientHttpResponse fallbackResponse(String route,Throwable cause){ return new ClientHttpResponse(){ //返回响应状态对象(枚举类型) @Override public HttpStatus getStatusCode() throws IOException{ return HttpStatus.GATEWAY_TIMEOUT;//网关超时 } //返回响应状态码 @Override public int getRawStatusCode() throws IOException{ return this.getStatusCode().value(); } //返回响应状态字符串描述 @Override public String setStatusText() throws IOException{ retuen this.getStatusCode().getReasonPhrease(); } //获取响应输出的内容(以输入流方式返回) @Override public InputStream getBody() throws IOException{ Map<String,Object> result=new HashMap<>(); result.put("message","服务器繁忙,请稍后重试"); ObjectMapper mapper=new ObjectMapper(); Strng message=mapper.writeValueAsString(result); InputStream in=new ByteArrayInputStream(message.getBytes("utf-8")); return in; } //返回响应头 @Override public HttpHeader getHeader(){ HttpHeaders headers=new HttpHeaders(); headers.set("content-type","application/json;charset=utf-8"); return headers; } //回收资源的方法 @Override public void close(){} }; } }
- 配置Zuul的超时设定
注意:HTTP连接的超时设定必须大于ribbon的连接超时设定,这样才能避免在ribbon未超时直接就返回fallback结果# zuul从接收到请求开始计时,到响应返回到浏览器为止(默认1000ms) zuul.host.socket-timeout-millis=4000 # zuul从路由开始计时,到Application Client返回响应为止(默认) zuul.host.connect-timeou-millis=3000 # 请求转发开始计时,到Application Client建立连接为止 ribbon.ConnectTimeout=2000 # 请求转发开始计时,到Application Client返回结果为止 ribbon.ReadTimemout=2100