前言
Spring Cloud Zuul 主要的功能是提供负载均衡、反向代理、权限认证、动态路由、监控、弹性、安全等的边缘服务。其主要作用是为微服务架构提供了前门保护的作用,同时将权限控制这些较重的非业务逻辑内容迁移到服务路由层面,使得服务集群主体能够具备更高的可复用性和可测试性
通俗一点来说,就是对服务提供一层保护,对外界的请求进行过滤转发到后端服务中。
这里我们可以通过几张简单的示例图来进行了解.。
不使用路由网关的示例图:
使用路由网关的示例图:
使用路由网关并且加上注册中心的示例图:
Zuul是spring cloud中的微服务网关。网关:是一个网络整体系统中的前置门户入口。请求首先通过网关,进行路径的路由,定位到具体的服务节点上。
Zuul是一个微服务网关,首先是一个微服务。也是会在Eureka注册中心中进行服务的注册和发现。也是一个网关,请求应该通过Zuul来进行路由。
Zuul网关的作用
统一入口:未全部为服务提供一个唯一的入口,网关起到外部和内部隔离的作用,保障了后台服务的安全性。
鉴权校验:识别每个请求的权限,拒绝不符合要求的请求。
动态路由:动态的将请求路由到不同的后端集群中。
减少客户端与服务端的耦合:服务可以独立发展,通过网关层来做映射。
网关zuul在项目中的使用
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
在网关项目springboot启动类生开启网关
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
网关配置文件中配置
eureka:
instance:
prefer-ip-address: true
instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}
appname: ${spring.application.name}
lease-renewal-interval-in-seconds: 2
lease-expiration-duration-in-seconds: 6
client:
registry-fetch-interval-seconds: 5
serviceUrl:
defaultZone: http://localhost:8761/eureka
# 以上是eureka的配置
# 以下是zuul配置
zuul:
set-content-length: true
retryable: true
prefix: /user/api # 配置前缀 请求前缀
routes: # 路由
user-service: /login/** # 配置路由规则 这一种是简化配置 直接key是服务名,value就是路由规则
account-service: #路由名称 # 这一种是成规配置
path: /account/** #路由路径
serviceId: account-service #服务Id
ribbon-isolation-strategy: thread
ribbon:
MaxAutoRetries: 0
MaxAutoRetriesNextServer: 1
OkToRetryOnAllOperations: true
ReadTimeout: 1500
ConnectTimeout: 500
MaxConnectionsPerHost: 200
MaxTotalConnections: 1000
然后就是正常的请求网关,网关会根据你的规则路有到哪个服务
Zuul网关配置总结
网关配置方式有多种,默认、URL、服务名称、排除|忽略、前缀。
网关配置没有优劣好坏,应该在不同的情况下选择合适的配置方案。
zuul网关其底层使用ribbon来实现请求的路由,并内置Hystrix,可选择性提供网关fallback逻辑。使用zuul的时候,并不推荐使用Feign作为application client端的开发实现。毕竟Feign技术是对ribbon的再封装,使用Feign本身会提高通讯消耗,降低通讯效率,只在服务相互调用的时候使用Feign来简化代码开发就够了
Zuul网关过滤器
Zuul中提供了过滤器定义,可以用来过滤代理请求,提供额外功能逻辑。如:权限验证,日志记录等。
Zuul提供的过滤器是一个父类。父类是ZuulFilter。通过父类中定义的抽象方法filterType,来决定当前的Filter种类是什么。有前置过滤、路由后过滤、后置过滤、异常过滤。
1、前置过滤:是请求进入Zuul之后,立刻执行的过滤逻辑。
2、路由后过滤:是请求进入Zuul之后,并Zuul实现了请求路由后执行的过滤逻辑,路由后过滤,是在远程服务调用之前过滤的逻辑。
3、后置过滤:远程服务调用结束后执行的过滤逻辑。
4、异常过滤:是任意一个过滤器发生异常或远程服务调用无结果反馈的时候执行的过滤逻辑。无结果反馈,就是远程服务调用超时。
过滤器实现方式
继承父类ZuulFilter。在父类中提供了4个抽象方法,分别是:filterType, filterOrder, shouldFilter, run。其功能分别是:
1、filterType:方法返回字符串数据,代表当前过滤器的类型。可选值有-pre, route, post, error。
pre - 前置过滤器,在请求被路由前执行,通常用于处理身份认证,日志记录等;
route - 在路由执行后,服务调用前被调用;
error - 任意一个filter发生异常的时候执行或远程服务调用没有反馈的时候执行(超时),通常用于处理异常;
post - 在route或error执行后被调用,一般用于收集服务信息,统计服务性能指标等,也可以对response结果做特殊处理。
2、filterOrder:返回int数据,用于为同filterType的多个过滤器定制执行顺序,返回值越小,执行顺序越优先。
3、shouldFilter:返回boolean数据,代表当前filter是否生效。
4、run:具体的过滤执行逻辑。如pre类型的过滤器,可以通过对请求的验证来决定是否将请求路由到服务上;如post类型的过滤器,可以对服务响应结果做加工处理(如为每个响应增加footer数据)。
过滤器的生命周期
前置过滤
@Component
@Slf4j
public class TraceIdFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
String traceId =requestContext.getRequest().getHeader(Constants.TRACE_ID);
MDC.put(Constants.TRACE_ID,traceId);
return null;
}
}
过滤器禁用
有的场景下,我们需要禁用过滤器,此时可以采取下面的两种方式来实现:
利用 shouldFilter 方法中的 return false 让过滤器不再执行
通过配置方式来禁用过滤器,格式为“zuul. 过滤器的类名.过滤器类型 .disable=true”。如果我们需要禁用“使用过滤器”部分中的 IpFilter,可以用下面的配置:
zuul.IpFilter.pre.disable=true
Zuul网关的容错(与Hystrix的无缝结合)
在spring cloud中,Zuul启动器中包含了Hystrix相关依赖,在Zuul网关工程中,默认是提供了Hystrix Dashboard服务监控数据的(hystrix.stream),但是不会提供监控面板的界面展示。可以说,在spring cloud中,zuul和Hystrix是无缝结合的。
Zuul中的服务降级处理
在Edgware版本之前,Zuul提供了接口ZuulFallbackProvider用于实现fallback处理。从Edgware版本开始,Zuul提供了ZuulFallbackProvider的子接口FallbackProvider来提供fallback处理。
Zuul的fallback容错处理逻辑,只针对timeout异常处理,当请求被Zuul路由后,只要服务有返回(包括异常),都不会触发Zuul的fallback容错逻辑。
因为对于Zuul网关来说,做请求路由分发的时候,结果由远程服务运算的。那么远程服务反馈了异常信息,Zuul网关不会处理异常,因为无法确定这个错误是否是应用真实想要反馈给客户端的。
fallBack代码示列
@Component
public class ZuulFallbackProvider implements FallbackProvider {
// 指定fallback的服务 * 代表所有
@Override
public String getRoute() {
return "*";
}
@Override
public ClientHttpResponse fallbackResponse(String route, final Throwable cause) {
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 {
Result result = Result.fail(EnumCode.FAIL.getCode(), cause.getMessage());
return new ByteArrayInputStream(JSON.toJSONString(result).getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
Zuul网关的限流保护
Zuul网关组件也提供了限流保护。当请求并发达到阀值,自动触发限流保护,返回错误结果。只要提供error错误处理机制即可。
Zuul的限流保护需要额外依赖spring-cloud-zuul-ratelimit组件。
<dependency>
<groupId>com.marcosbarbero.cloud</groupId>
<artifactId>spring-cloud-zuul-ratelimit</artifactId>
<version>2.4.1.RELEASE</version>
</dependency>
全局限流配置 局部限流配置
使用全局限流配置,zuul会对代理的所有服务提供限流保护。
zuul:
ratelimit:
key-prefix: your-prefix # 缓存key的名称定义前缀。
enabled: true
repository: REDIS
behind-proxy: true
add-response-headers: true
default-policy-list: # 可选,默认的全局配置,如果根据规则查询不到配置则应用这个定义的配置
- limit: 10 #可选,时间窗口内最多访问次数
quota: 1000 # 可选时间窗口单位是秒
refresh-interval: 60 # 默认的刷新时间窗口的间隔。
type: # 可选 规则类型
- user
- origin # IP
- url
- http_method # HTTP方法 可以组合
#局部限流
policy-list: # 具体的规则定义
myServiceId: # SpringCloud的服务编号
- limit: 10 # optional - request number limit per refresh interval window
quota: 1000 # optional - request time limit per refresh interval window (in seconds)
refresh-interval: 60 #default value (in seconds)
# 以下是参考的规则案例 URL是采用左侧前缀方式
type: #optional
- user
- origin
- url
- type: #optional value for each type
- user=anonymous
- origin=somemachine.com
- url=/api #url prefix
- role=user
- http_method=get #case insensitive
- type:
- url_pattern=/api/*/payment
这里需要说明的是:
limit : 1
quota : 10
refresh-interval : 60
三个组合在一起的意思就是 : 在10秒内如果访问次数超过1次,则会被限流,一旦限流需要在60秒之后才会被解封
Zuul网关性能调优:网关的两层超时调优
使用Zuul的spring cloud微服务结构图:
从上图中可以看出。整体请求逻辑还是比较复杂的,在没有zuul网关的情况下,app client请求app service的时候,也有请求超时的可能。那么当增加了zuul网关的时候,请求超时的可能就更明显了。
当请求通过zuul网关路由到服务,并等待服务返回响应,这个过程中zuul也有超时控制。zuul的底层使用的是Hystrix+ribbon来实现请求路由。结构如下:
zuul中的Hystrix内部使用线程池隔离机制提供请求路由实现,其默认的超时时长为1000毫秒。ribbon底层默认超时时长为5000毫秒。如果Hystrix超时,直接返回超时异常。如果ribbon超时,同时Hystrix未超时,ribbon会自动进行服务集群轮询重试,直到Hystrix超时为止。如果Hystrix超时时长小于ribbon超时时长,ribbon不会进行服务集群轮询重试。
# 开启zuul网关重试
zuul.retryable=true
# hystrix超时时间设置
# 线程池隔离,默认超时时间1000ms
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=8000#
ribbon超时时间设置:建议设置比Hystrix小
# 请求连接的超时时间: 默认5000ms
ribbon.ConnectTimeout=5000
# 请求处理的超时时间: 默认5000ms
ribbon.ReadTimeout=5000
# 重试次数:MaxAutoRetries表示访问服务集群下原节点(同路径访问);MaxAutoRetriesNextServer表示访问服务集群下其余节点(换台服务器)ribbon.MaxAutoRetries=1
ribbon.MaxAutoRetriesNextServer=1
# 开启重试
ribbon.OkToRetryOnAllOperations=true
Spring-cloud中的zuul网关重试机制是使用spring-retry实现的。工程必须依赖下述资源:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>