在添加Zuul依赖的时候,它本身就包含了对Hystrix和Ribbon模块的依赖,所以Zuul天生就拥有线程隔离和断路器的自我保护功能,以及对服务调用的客户端负载均衡的功能。但是需要注意的是,当使用path和url的映射关系来配置路由规则的时候,对于路由转发的请求不会采用HystrixCommand来包装,所以这类路由请求没有线程隔离和断路器的保护,并且也不会有负载均衡的能力。因此,我们在使用Zuul的时候尽量使用path和serviceId的组合来进行配置,这样不仅可以保证API网关的健壮和稳定,也能用到Ribbon的客户端负载均衡功能。
Hystrix
之前我们是在Zuul
项目中集成Hystrix
的监控,那么我们启动该服务。然后在Hystrix Dashboard中输入: http://localhost:8280/hystrix.stream,然后进行监控,那么我们将看到如下界面:
这说明,Zuul已经整合了Hystrix。
spring-cloud-starter-zuul
本身已经集成了hystrix和ribbon,所以Zuul天生就拥有线程隔离和断路器的自我保护能力,以及对服务调用的客户端负载均衡功能。但是,我们需要注意,当使用path与url的映射关系来配置路由规则时,对于路由转发的请求则不会采用HystrixCommand
来包装,所以这类路由请求就没有线程隔离和断路器保护功能,并且也不会有负载均衡的能力。因此,我们在使用Zuul的时候尽量使用path和serviceId的组合进行配置,这样不仅可以保证API网关的健壮和稳定,也能用到Ribbon的客户端负载均衡功能。
但Zuul的Hystrix监控的粒度是微服务,而不是某个API,也就是所有经过Zuul的请求都会被Hystrix保护起来。假如,我们现在把Product-Service
服务关闭,再来访问会出现什么结果呢?结果可能不是我们所想那样,如下:
在Zuul中实现熔断功能需要实现FallbackProvider接口。该接口有两个方法,一个是getRoute()方法,用于执定熔断器应用于哪些路由服务;另一个方法fallbackResponse()方法为进入熔断功能是执行的逻辑。FallbackProvider 源码如下:
public interface FallbackProvider {
String getRoute();
ClientHttpResponse fallbackResponse(String route, Throwable cause);
}
实现一个针对eureka-client1服务的熔断器,当eureka-client1的服务组出现故障时,进入熔断逻辑,向浏览器输入一句错误的提示信息,代码如下:
@Component
public class NyZuulFallbackProvider implements FallbackProvider {
protected Logger logger = LoggerFactory.getLogger(NyZuulFallbackProvider.class);
@Override
public String getRoute() {
// 注意: 这里是route的名称,不是服务的名称,
// 如果这里写成大写PRODUCT-SERVICE将无法起到回退作用
return "eureka-client1";
}
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return 200;
}
@Override
public String getStatusText() throws IOException {
return "OK";
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("error! I'm the fallback.".getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
return headers;
}
};
}
}
重新启动工程,关闭eureka-client1的所有实例,早浏览器上访问http://localhost:8777/hello/yang,浏览器显示:
error! I'm the fallback.
如果需要所有的路由服务都加熔断功能,只需要在getRoute()方法上返回“*”的匹配府,代码如下:
@Override
public String getRoute() {
return "*";
}
Ribbon参数配置
提供客户端的负载均衡功能,spring cloud的负载均衡都用到这个库。例如:fegin
它提供了超时重试的功能,配置如下:
ribbon:
ReadTimeout: 2000
ConnectTimeout: 1000
MaxAutoRetries: 1
MaxAutoRetriesNextServer: 1
ribbon.ConnectTimeout:该参数用来设置路由转发请求的时候,创建请求连接的超时时间。若出现路由请求连接超时,会自动进行重试路由请求,如果重试依然失败,Zuul会抛出异常。
ribbon.ReadTimeout:该参数用来设置路由转发请求的超时时间。它的处理与ribbon.ConnectTimeout相似,若出现路由请求连接超时,会自动进行重试路由请求,如果重试依然失败,Zuul会抛出异常。
MaxAutoRetries:最大自动重试次数
MaxAutoRetriesNextServer:最大自动重试下一个服务的次数
注意:
根据上面的介绍我们可以知道,在使用Zuul的服务路由的时候,如果路由转发请求发生了超时(连接超时或者处理超时),只要超时时间小于Hystrix的命令超时时间,那么它就会自动发起重试。但在这种情况下,我们可能需要关闭该重试机制,那么可以通过下面的两个参数来进行设置:
zuul.retryable=false
zuul.routes.<route>.retryable=false
其中,zuul.retryable=false用来关闭全局重试机制,而zuul.routes.<route>.retryable=false则关闭指定路由的重试机制。