六、Zuul
1、简要概述
不使用Zuul的开发架构是:
2、Zuul作用
加入Zuul之后的架构是:
不管是来自于客户端(PC或移动端)的请求,还是服务自己内部调用。一切对服务的请求都会经过Zuul这个网关,然后再由网关来实现鉴权、动态路由等等操作。Zuul就是我们服务的统一入口。
3、Zuul进行地址转换和负载均衡
在父工程下创建一个miscroservicecloudzuulgateway9527的maven工程
在pom文件中添加如下依赖:
<dependencies>
<!--zuul路由网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<!--actuator监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--hystrix容错-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!--日常标配-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>SpringCloud</groupId>
<artifactId>miscroserviceapi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
<version>1.2.6.RELEASE</version>
</dependency>
</dependencies>
编写主启动类:
package springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableZuulProxy
public class Zuul_9527_StartSpringCloudApp {
public static void main(String[] args) {
SpringApplication.run(Zuul_9527_StartSpringCloudApp.class, args);
}
}
以kv形式配置路由规则,k就是路由id,path就是所匹配的路径,最终让此端口的这一路径会先通过网关然后才能访问微服务,
server:
port: 9527
zuul:
prefix: /example
# ignored-services: miscroservicecloud-dept
ignored-services: "*"
routes:
mydept.serviceId: miscroservicecloud-dept
mydept.path: /mydept/**
我们可以发现,现在已经不用再访问本来的端口访问到数据了,而是经过了网关去转发请求地址,来到了9527。
但是有一个问题,如果以后的微服务地址发生了变化,或者启动了多台集群需要我们配置负载均衡,那么我们就要在zuul的依赖中引入Eureka客户端的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
然后进行相关的配置
server:
port: 9527
spring:
application:
name: miscroservice-cloud-zuul-gateway
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
instance:
instance-id: gateway-9527.com
prefer-ip-address: true
info:
app.name: miscrocloud
company.name: www.cloud.com
build.artifactId: $project.artifactId$
build.version: $project.version$
zuul:
prefix: /example
# ignored-services: miscroservicecloud-dept
ignored-services: "*"
routes:
mydept.serviceId: miscroservicecloud-dept
mydept.path: /mydept/**
当一个request请求过来之后,先进行端口和serviceID的匹配,匹配到之后将原服务转发到zuul网关的服务 ,但服务本身没有地址,因此要拉取Eureka的INSTANCE列表,获取具体的路径地址,然后通过负载均衡算法,这就是面向服务的路由,内部同时实现了负载均衡和地址转发。
4、过滤器
Zuul作为网关中的一个重要功能,母目的就是要实现请求的鉴权。但是这个动作我们往往是通过Zuul提供的过滤器来实现的。
<1>、ZuulFilter
ZuulFilter是过滤器的最高级的父类,其中有四个最重要的方法
public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> {
abstract public String filterType();//过滤器类型
abstract public int filterOrder();//过滤器顺序
boolean shouldFilter();// 来自IZuulFilter,要不要过滤
Object run() throws ZuulException;// IZuulFilter,过滤逻辑
}
shouldFilter
:返返回的是一个boolean
值,判断该过滤器是否要执行,返回true
则执行,反之则不执行。run
:过滤器的具体业务逻辑filterType
:返回的是字符串,代表是过滤器的类型,其中包括四种类型:pre
:请求在被路由之前执行route
:路由请求时调用post
:在routing和errror过滤器之后调用error
:处理请求时发生错误调用
filterOrder
:通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。
<2>、生命周期
正常流程:
请求到达首先会经过pre类型过滤器,然后到达routing类型,进行路由请求就到达真正的服务提供者执行请求,返回结果后,会先经过post过滤器。之后返回响应。
异常流程:
整个过程中,pre类型或者routing类型过滤器出现异常,都会直接进入error过滤器,待error型过滤器处理完毕后,将请求交给POST过滤器,最终返回给用户。
如果是error过滤器自己出现异常,最终也会进入POST过滤器,之后返回。
如果是post过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的时,请求不会再到达post类型的过滤器。
<3>、自定义过滤器
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class LoginFilter extends ZuulFilter {
@Override
public String filterType() {//过滤器种类
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {//过滤器优先级
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter() {//判断是否过滤
return true;
}
@Override
public Object run() throws ZuulException {//过滤逻辑,登录拦截
//获取请求上下文
RequestContext ctx = RequestContext.getCurrentContext();
//获取request
HttpServletRequest request = ctx.getRequest();
//获取请求参数
String take = request.getParameter("access-token");
//判断是否存在
if (StringUtils.isBlank(take)) {
//不存在,未登录,就拦截
ctx.setSendZuulResponse(false);
//返回403
ctx.setResponseStatusCode(HttpStatus.FORBIDDEN.value());
}
return null;
}
}
运行进行测试,输入原来的地址,出现403禁止访问的错误,是因为没有按照之前的自定义过滤器所编写的规则,同时提交一个请求参数,由于我们没有具体的业务实现,所以填写什么路由规则都可以
<4>、Zuul负载均衡和熔断
Zuul中默认已经集成了Ribbon负载均衡和Hystix熔断机制。但所有的超时策略都是默认值,比如熔断超时时间只有1s,很容易就触发了。因此建议我们手动进行配置:
ribbon:
ReadTimeout: 60000
ConnectTimeout: 60000
hystrix:
command:
default:
execution:
isolution:
thread:
timeoutInMilliseconds: 2000