Spring Cloud系列(二十五) Zuul配置Hystrix和Ribbon(Finchley.RC2版本)

Hystrix和Ribbon配置

由于Spring Cloud Zuul包含了对Hystrix和Ribbon的依赖,所以Zuul拥有线程隔离和断路器的自我保护功能,以及对客户端的负载均衡能力。但是,需要注意在使用path和url的映射关系来配置路由规则时,对于路由转发的请求不会采用HystrixCommand来包装,所以这类路由请求没有线程隔离和断路器的保护以及负载均衡的能力。所以使用Zuul的时候尽量使用path和serviceId的方式配置。

在使用Zuul时可以配置Hystrix和Ribbon的参数来调整路由各种超时时间等配置。

hystrix.command.default.execution.isolation.thread.timeoutMilliseconds:该参数用来设置API网关中路由转发请求的HystrixCommand超时时间,单位为毫秒。当路由转发请求的命令执行时间超过该配置值后,Hystrix会将该执行命令标记为TIMEOUT并抛出异常。

ribbon.ConnectTimeout:该参数用来设置路由转发请求的时候,创建请求连接的超时时间。当ribbon.ConnectTimeout的配置值小于hystrix.command.default.execution.isolation.thread.timeoutMilliseconds配置值的时候,若出现路由请求连接超时,会自动进行重试路由请求,如果重试依然失败,Zuul会抛出异常。如果ribbon.ConnectTimeout的配置值大于hystrix.command.default.execution.isolation.thread.timeoutMilliseconds配置值的时候,不会进行请求重试,直接抛出异常。

ribbon.ReadTimeout:该参数用来设置路由转发请求的超时时间。它的处理与ribbon.ConnectTimeout相似,当ribbon.ReadTimeout的配置值小于hystrix.command.default.execution.isolation.thread.timeoutMilliseconds配置值的时候,若出现路由请求连接超时,会自动进行重试路由请求,如果重试依然失败,Zuul会抛出异常。如果ribbon.ReadTimeout的配置值大于hystrix.command.default.execution.isolation.thread.timeoutMilliseconds配置值的时候,不会进行请求重试,直接抛出异常。

所以,在使用Zuul的服务路由时,如果路由转发请求发生超时(连接超时或处理超时),只要设置超时时间小于Hystrix的命令超时时间,才可以自动发起重试。另外我们也可以关闭重试机制:

# 全局关闭重试机制
zuul.retryable=false
# 指定路由关闭重试机制
zuul.routes.<route>.retryable=false

实现重试机制方式:

第一步,引入spring-retry依赖。

<dependency>
	<groupId>org.springframework.retry</groupId>
	<artifactId>spring-retry</artifactId>
</dependency>

第二步,修改配置文件,添加如下内容。

hello-service:
  ribbon:
    #对当前实例的重试次数
    MaxAutoRetries: 3
    #切换实例的重试次数
    MaxAutoRetriesNextServer: 0
zuul:
  retryable: true

第三步,为了模拟出Zuul重试的功能,需要对后端应用服务进行改造,改造后的内容如下

@RequestMapping(value = "/hello")
public String index() {
   System.out.println("request is coming...");
   try {
      Thread.sleep(100000);
   } catch (InterruptedException e) {
      System.out.println("线程被打断... " + e.getMessage());
   }
   return "hello spring ...";
}

通过使用Thread.sleep(100000)达到Zuul转发超时情况(Zuul默认连接超时未2s、read超时时间为5s),从而触发Zuul的重试功能。

Zuul超时
如果要为通过Zuul代理的请求配置套接字超时和读取超时,则有两种选择,具体取决于你的配置:

  • 如果Zuul使用服务发现,则需要使用ribbon.ReadTimeoutribbon.SocketTimeout功能区属性配置这些超时。
  • 如果通过指定URL配置了Zuul路由,则需要使用zuul.host.connect-timeout-milliszuul.host.socket-timeout-millis

服务降级

当我们的后端服务出现异常的时候,我们不希望将异常抛出给最外层,期望服务可以自动进行一降级。Zuul给我们提供了这样的支持。当某个服务出现异常时,直接返回我们预设的信息。我们只需要实现FallbackProvider接口并重写提供的两个方法。

public interface FallbackProvider {


	public String getRoute();

	
	ClientHttpResponse fallbackResponse(String route, Throwable cause);
}
  • getRoute():指明拦截你需要降级的服务名,如果需要所有调用都支持回退,则return "*"或return null。
  • fallbackResponse():定制返回内容。

示例:

import com.alibaba.fastjson.JSONObject;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

@Component
public class MyProvinder implements FallbackProvider {
	/**
	 * //返回服务id,如果需要所有调用都支持回退,则return "*"或return null
	 */
	public String getRoute() {
		return "hello-service";
	}

	/**
	 * 如果请求用户服务失败,返回什么信息给消费者客户端
	 */
	public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
		return new ClientHttpResponse() {

			public InputStream getBody() throws IOException {
				JSONObject r = new JSONObject();
				r.put("state", "9999");
				r.put("msg", "系统错误,请求失败");
				return new ByteArrayInputStream(r.toJSONString().getBytes("UTF-8"));
			}

			public HttpHeaders getHeaders() {
				HttpHeaders headers = new HttpHeaders();
				// 和body中的内容编码一致,否则容易乱码
				headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
				return headers;
			}

			/**
			 * 网关向api服务请求是失败了,但是消费者客户端向网关发起的请求是OK的, 不应该把api的404,500等问题抛给客户端
			 * 网关和api服务集群对于客户端来说是黑盒子
			 */
			public HttpStatus getStatusCode() throws IOException {
				return HttpStatus.OK;
			}

			public int getRawStatusCode() throws IOException {
				return HttpStatus.OK.value();
			}

			public String getStatusText() throws IOException {
				return HttpStatus.OK.getReasonPhrase();
			}

			public void close() {

			}

		};
	}

}

再次调用失败的请求会返回:

Hystrix监控

由于Zuul自带Hystrix,所以可以直接整合Hystrix仪表盘实现监控。具体实现可以查看我这篇文章:Spring Cloud系列(十九) Hystrix仪表盘(Finchley.RC2版本)

注意:@EnableZuulProxy注解包含了@EnableCircuitBreaker,所以不需要显示添加了。

另外,由于Zuul默认使用信号量来实现隔离,ThreadPool信息会一直处于Loading状态,需要通过Hystrix配置把隔离机制改为线程池的方式才能得以展示。此时execution.isolation.strategy=THREAD这个配置不会生效,需要使用zuul.ribbonIsolationStrategy=THREAD的方式。当 zuul.ribbonIsolationStrategy=THREAD 时,Hystrix的线程隔离策略将会作用于所有路由。此时,HystrixThreadPoolKey 默认为“RibbonCommand”。这意味着,所有路由的HystrixCommand都会在相同的Hystrix线程池中执行。可使用以下配置,让每个路由使用独立的线程池:

zuul:
  threadPool:
    useSeparateThreadPools: true

使用如上配置后,默认的HystrixThreadPoolkey 将与每个路由的服务标识相同。如果你想HystrixThreadPoolKey 添加前缀,可使用类似如下的配置:

zuul:
  threadPool:
    useSeparateThreadPools: true
    threadPoolKeyPrefix: zuulgw

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值