Spring Cloud Zuul是提供负载均衡、反向代理、权限认证的一个API gateway。
简单路由
- 添加依赖
引入 spring-cloud-starter-zuul 包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
- 配置文件
spring.application.name=gateway-service-zuul
server.port=9000
#这里的配置表示,访问/yyy/** 直接重定向到http://www.xxx.com/**
zuul.routes.api-name.path=/yyy/**
zuul.routes.api-name.url=http://www.xxx.com/
api-name为路由的名字,可自定,但是一组映射关系的path和url要相同
3. 启动类
启动类添加 @EnableZuulProxy ,支持网关路由。
启动后访问http://localhost:9000/yyy/zzz会跳转到http://www.xxx.com/zzz
服务路由
通过url映射转发有局限性,服务变化配置就得变。将Zuul服务化可以实现对serviceId的映射。
- 添加eureka依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
- 配置文件
配置修改为:
spring.application.name=gateway-service-zuul
server.port=9000
zuul.routes.api-name.path=/producer/**
zuul.routes.api-name.serviceId=eureka-producer
eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/
启动后访问http://localhost:9000/producer/xx会跳转到eureka上serviceId=eureka-producer的服务/xx
默认路由规则
不用每个服务都配置,默认情况下,Zuul会代理所有注册到Eureka Server的微服务,并且Zuul的路由规则如下: http://ZUUL_HOST:ZUUL_PORT/serviceId/**
会被转发到serviceId对应的微服务。
服务过滤
在Zuul中定义过滤器只需要继承ZuulFilter抽象类,实现其定义的四个抽象函数,就可对请求进行拦截与过滤,实现对外服务的安全控制。
自定义Filter
实现自定义Filter,需要继承ZuulFilter的类,并覆盖其中的4个方法:
- filterType:返回一个字符串代表过滤器的类型。在zuul中定义了四种不同生命周期的过滤器类:
- pre:可以在请求被路由之前调用
- routing:在路由请求时候被调用
- post:在routing和error过滤器之后被调用
- error:处理请求时发生错误时被调用
- filterOrder:通过int值来定义过滤器的执行顺序
- shouldFilter:返回一个boolean类型来判断该过滤器是否要执行,实现过滤器的开关。
- run:过滤器的具体逻辑。
public class AccessFilter extends ZuulFilter {
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
Object accessToken = request.getParameter("accessToken");
if(accessToken == null) {
//令zuul过滤该请求,不对其进行路由
ctx.setSendZuulResponse(false);
//设置了其返回的错误码
ctx.setResponseStatusCode(401);
//对返回body内容进行编辑
ctx.setResponseBody("");
return null;
}
log.info("access token ok");
return null;
}
}
实现了自定义过滤器之后,还需要在主类实例化该过滤器才能生效:
@Bean
public AccessFilter accessFilter() {
return new AccessFilter();
}
filterType生命周期
核心过滤器
Spring Cloud Zuul默认地实现了一批核心过滤器,它们会在API网关服务启动的时候被自动地加载和启用。
|类型 | 顺序 | 过滤器 |功能| |— | — | —| —| —| |pre |-3 | ServletDetectionFilter | 标记处理Servlet的类型 pre | -2 |Servlet30WrapperFilter | 包装HttpServletRequest请求 pre | -1 | FormBodyWrapperFilter | 包装请求体 route | 1 | DebugFilter |标记调试标志 route | 5 | PreDecorationFilter | 处理请求上下文供后续使用 route | 10 |RibbonRoutingFilter | serviceId请求转发 route | 100 | SimpleHostRoutingFilter | url请求转发 route | 500 | SendForwardFilter | forward请求转发 post | 0 | SendErrorFilter | 处理有错误的请求响应 post | 1000 | SendResponseFilter | 处理正常的请求响应
禁用指定的Filter
可以在application.yml中配置需要禁用的filter,格式:
zuul:
FormBodyWrapperFilter:
pre:
disable: true
全局异常处理
添加一个类型为”error”的filter,将错误信息写入RequestContext,这样SendErrorFilter就可以获取错误信息了。
class ErrorFilter extends ZuulFilter {
@Override
String filterType() {
return FilterConstants.ERROR_TYPE;
}
@Override
int filterOrder() {
return 10;
}
@Override
boolean shouldFilter() {
return true;
}
@Override
Object run() {
RequestContext context = getRequestContext();
Throwable throwable = context.getThrowable();
ctx.set("error.status_code", HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
ctx.set("error.exception", throwable.getCause());
return null;
}
}
路由熔断
Zuul提供熔断支持。当某个服务出现异常时,直接返回预设的信息。 目前只支持服务级别的熔断,不支持具体到某个URL进行熔断。
自定义fallback
通过继承FallbackProvider接口,自定义fallback,并指定给某个route,实现该route的熔断处理。
public interface FallbackProvider {
//指明拦截哪个服务
public String getRoute();
//定制返回内容
public ClientHttpResponse fallbackResponse();
//异常信息
public ClientHttpResponse fallbackResponse(Throwable cause);
}
路由重试
Zuul结合Spring Retry实现了路由重试。用了retry,断路器就只有在该服务的所有实例都无法运作的情况下才能起作用。不用retry,仅使用负载均衡和熔断,就必须考虑到是否能够接受单个服务实例关闭和eureka刷新服务列表之间带来的短时间的熔断。如果可以接受,就无需使用retry。
- 添加Spring Retry依赖
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
- 配置文件中开启Zuul Retry
#是否开启重试功能
zuul.retryable=true
#对当前服务的重试次数
ribbon.MaxAutoRetries=2
#切换相同Server的次数
ribbon.MaxAutoRetriesNextServer=0
Cookie和头信息
Cookie
配置文件中设置sensitiveHeaders,添加需要忽略的信息到请求上下文中,供后续ROUTE阶段的过滤器使用 默认情况下,Zuul在请求路由时,会过滤HTTP请求头信息中的一些敏感信息,默认的敏感头信息通过zuul.sensitiveHeaders定义,包括Cookie、Set-Cookie、Authorization。
- 设置全局参数覆盖默认值:
zuul.sensitiveHeaders= # 使用空来覆盖默认值
- 指定路由的参数配置:
zuul.routes.<routeName>.sensitive-headers= # 对指定路由的敏感头设置为空
zuul.routes.<routeName>.custom-sensitive-headers=true # 对指定路由开启自定义敏感头
头信息
通过配置属性zuul.add-host-header=true正确处理头信息,比如重定向
性能优化参考
- 在application.yml文件中配置线程数、缓冲大小
server:
tomcat:
max-threads: 128 # 最大worker线程
min-spare-threads: 64 # 最小worker线程
undertow:
io-threads: 8 # IO线程数,默认为CPU核心数,最小为2
worker-threads: 40 # 阻塞任务线程池,值设置取决于系统的负载,默认为io-threads * 8
buffer-size: 512 # 每块buffer的空间大小
buffers-per-region: 10 # 每个区分配的buffer数量
direct-buffers: 512 # 是否分配的直接内存
- 在application.yml文件中配置zuul和ribbon
zuul:
host:
max-total-connections: 500 # 每个服务的http客户端连接池最大连接,默认值是200
max-per-route-connections: 50 # 每个route可用的最大连接数,默认值是20
ribbon-isolation-strategy: THREAD # 可选:SEMAPHORE THREAD
- 在application.yml文件中配置hystrix
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds 设置thread的默认超时时间,默认值是10000。
hystrix.command.[CommandKey].execution.isolation.thread.timeoutInMilliseconds 设置不同微服务的超时时间。