技术需求点:
1.介绍Zuul网关路由和过滤器两大功能的工作原理;
2.模拟Zuul路由网关功能;
3.使用Zuul网关过滤器实现简单的请求鉴权;
一.Zuul介绍
来自客户端的请求,一切对服务的请求都会经过Zuul网关,然后由网关实现鉴权、动态路由等操作,Zuul就是我们的统一入口。
Zuul是Netflix开源的微服务网关,可以和SpringCloud系列的组件(Eureka、Ribbon、Hystrix、Feign等)配合使用,它的核心是一系列的过滤器,主要可以完成以下功能:
- 安全认证:拒绝不符合要求的请求;
- 性能监控:在网关的边缘位置追踪并统计数据,以视图方式展现;
- 压力测试:逐渐增加指向集群的流量,测试性能;
- 负载均衡:为每种负载类型分配对应容量,丢弃过量的请求;
- 静态响应处理:在服务边界位置直接返回结果;
- 动态路由:动态的将请求理由到不同的后端集群;
- 多区域弹性:跨越AWS Region进行请求路由,只在实现ELB使用的多样化,让系统边缘更贴近系统使用者;
二.路由功能
使用SpringBoot和SpringCloud搭建项目的详细方法请参考我的SpringCloud+SpringBoot搭建服务注册与调用平台这篇文章,本文是在这篇文章搭建的服务基础上添加Zuul网关的。
1.项目结构
2.pom依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
3.配置文件
Zuul网关是一个单独的服务,也可以被注册到eureka注册中心,所以我们在zuul服务里配置service-id就可以找到被调用的服务,zuul的路由功能表现在path的配置里,请求地址【类似http://127.0.0.1:9093/user-service/user】中只有带着/user-service才可以被正确的转发。
spring.application.name=gateway
server.port=9093
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://192.168.33.100:8761/eureka/,http://192.168.33.101:8761/eureka/
#1.user-service-rouuts:路由id,相当于个路由器,负责转发请求
#2.zuul.routes.user-service-routes.path:请求地址中必须带着/user-service才会被网关认为是有效请求
#3.zuul.routes.user-service-routes.service-id:调用哪个服务处理请求,根据服务id寻找服务
zuul.routes.user-service-routes.path=/user-service/ ** # "**"前面没有空格,简书格式有点问题,不加空格下面的代码就是灰色的
zuul.routes.user-service-routes.service-id=eureka-provider
- 配置优化
实际上,path和service-id可以不配,zuul会用service-id作为默认的路由id,比如我们的服务id是eureka-provider,使用【http://127.0.0.1:9093/eureka-provider/user】就可以通过网关访问到eureka-provider的user接口。
但是有时实际业务中有一些需要特殊配置的路由,例如在链接前面加个前缀,不希望某些服务经过网关(静态网页等资源)等,这时候就必须配置path和service-id了,再加上这样的配置:
zuul.prefix=api
zuul.ignored-services=eureka-consumer
4.启动类
@EnableZuulProxy
@EnableFeignClients
@SpringCloudApplication
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class,args);
}
}
5.测试结果
输入http://127.0.0.1:9093/user-service/user发现可以请求成功
输入http://127.0.0.1:9093/user发现请求失败
三.过滤器
Zuul作为网关的一个重要功能,就是实现请求的鉴权,比如服务端需要判定客户端的请求是合法的才处理,鉴权极大的提高了系统的安全性,避免被暴力攻击。鉴权往往是通过Zuul的过滤器实现的,即在请求进入业务代码之前,对请求的合法性进行判定。(过滤器的使用场景还有异常处理,服务调用时长统计等)
1.ZuulFilter的生命周期
请求首先会经过pre类型的过滤器,然后到达routing类型过滤器,进行路由,请求此时到达真正的服务提供者执行请求,返回结果后,会到达post过滤器,然后返回响应。整个过程中,如果出现异常,会交给error过滤器处理,但是不同阶段进入error过滤器的时机是不同的,感兴趣的同学可查阅官方文档,在此不再赘述。
2.ZuulFilter类
@Component
public class MyZuulFilter 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 {
System.out.println("pre过滤器=============");
RequestContext requestContext = new RequestContext();
HttpServletRequest request = requestContext.getRequest();
String access = request.getParameter("access-token");
if(!StringUtils.isBlank(access)){
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.SC_METHOD_NOT_ALLOWED);
}
return null;
}
}
ZuulFilter里面有四个抽象方法必须被重写,
- filterType:过滤类型,有pre(前置过滤)、post(后置过滤)、routing(业务代码前过滤)、error(异常过滤器),一个过滤器只属于一种类型;
- filterOrder:过滤器的执行优先级,数值最小的先被执行;
- shouldFilter:是否开启过滤器,返回值为boolean类型;
- run:过滤器中需要执行的业务代码;
只要把该类@Component一下就生效了,很简单。
Zuul由动态读取、编译和执行Filter框架的能力,每个Filter之间没有直接联系,但都可以通过RequestContext共享一些状态数据。
3.测试结果
输入http://127.0.0.1:9093/user-service/user?access-token=1发现可以请求成功
输入http://127.0.0.1:9093/user-service/user请求返回405
四.Zuul的回退机制和高可用
1.通过实现FallbackProvider 接口可以实现Zuul的容错与回退功能,详细参考https://blog.csdn.net/qq_27384769/article/details/82991261这篇文章;
2.作为网关这么重要的角色,实现高可用是非常必要的,一般在Zuul网关之前我们会加一个nginx,利用nginx实现负载均衡和高可用,或者客户端的请求先向Eureka Server获取网关地址的方式实现。
参考文章:
参考1:https://www.jianshu.com/p/174cbb706c39
参考2:https://www.cnblogs.com/duanxz/p/7527765.html
参考3:https://blog.csdn.net/qq_27384769/article/details/82991261
参考4:https://www.cnblogs.com/tripleDemo/p/11649966.html
参考5,源码:https://segmentfault.com/a/1190000015915402