Zuul的作用
Zuul是spring cloud中推荐使用的api网关,一般来说,服务是集群部署的,并且不会直接暴露到外网中直接调用,外部客户端想要调用服务无法直接调用服务的实际地址,实际情况也不允许这样做,这时候就需要通过一个称为网关的组件来处理客户端的请求,通过网关来进行分配,zuul的主要功能就是对所有的服务请求进行统一拦截管理,通过负载均衡分发服务请求,与Nginx类似,同时,zuul还可以对服务请求进行监控、日志管理、验签等操作,zuul就像一个守门神,对所有要进城的人进行筛查、登记,然后分配到具体要去的地方。
总的来说,zuul可以实现反向代理、负载均衡、限流、监控、日志、验签等操作。
Zuul与Nginx的区别
相同点:可以实现反向代理、负载均衡,都是网关
区别:
Zuul:java语言编写,主要适用于微服务框架下的,是在客户端做负载均衡
Nginx:C语言编写,在服务器端做负载均衡,更适用于对外部的请求进行分发,从功能上来说,Nginx比Zuul更强大,还可以通过一些脚本来扩展功能,Zuul的功能,Nginx也可以实现,但是很麻烦,需要用其他的语言实现。
搭建Zuul网关
1.引入zuul和eureka客户端依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.启动类开启zuul,@EnableZuulProxy
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args){
SpringApplication.run(ZuulApplication.class,args);
}
}
3.配置路由
#zuul路由配置
zuul:
routes:
#路由名称,命名无强制要求
eureka-client:
#匹配的请求路径
path: /eureka-client-zuul/**
#实际转发的服务别名
serviceId: eureka-client
eureka-feign-client:
path: /eureka-feign-client-zuul/**
serviceId: eureka-feign-client
zuul的原理
zuul实际上也是一个服务,其核心组件其实是过滤器,zuul的路由转发实际上是通过一层一层不同类型的过滤器实现的,按照顺序主要分为几种:
pre
pre过滤器可以理解为预处理,是最先经过的过滤器,在请求还没有被转发之前被调用,常用来用来做验签、黑白名单处理,主要做了几个工作:
1.判断request的涞源
2.将HttpServletRequest包装成特定对象
3.将content-type为特定类型的request包装成特定对象
4.处理debug信息
5.进行路由规则匹配等预处理操作
route
route过滤器主要是对请求进行转发,route只会对指定了serviceId的请求进行处理,在route过滤器中将会通过Ribbon以及Hystrix实现负载均衡和限流,是zuul的核心。
1.匹配path中符合规则的请求路径
2.根据serviceid从eureka中获取服务地址
3.通过Ribbon做负载均衡获取具体调用地址
4.通过httpclient方式进行服务调用
post
post过滤器是在服务完成调用之后,请求已经处理完,结果返回到客户端之前调用的,无论是在调用过程中成功与否,最终都会通过post过滤器,在这里可以对响应进行处理
error
error过滤器就是在上面三个过滤器发生异常的时候会触发,但最终还是需要通过post过滤器将响应返回给客户端
关于zuul过滤器的具体实现原理参考:
https://www.jianshu.com/p/ff863d532767
https://www.cnblogs.com/trust-freedom/p/9982680.html
自定义Zuul过滤器实现
1.继承ZuulFilter
2.指定过滤器的类型,实现filterType方法(pre、route、post、error)
3.指定过滤器的顺序,filterOrder方法,默认是0,所有的过滤器都有自己的顺序,包括zuul自己的过滤器,具体顺序可以参考上面贴出的链接,里面有详细解释
4.设置是否执行过滤器,shouldFilter方法,返回true/false,设置为false的话,过滤器的逻辑便不会被执行
5.实现过滤器的业务逻辑,run方法,下面是一个例子验证是否请求中是否带有userToken参数,如果没有则不做转发,返回提示。
6.通过注解@Component将自定义过滤器加入到spring容器当中
@Component
public class TokenFilter extends ZuulFilter {
@Autowired
HttpServletRequest request;
//过滤器的类型
//pre:可以在请求被路由之前调用
//routing: 路由请求时被调用
//post:在routing和error过滤器之后被调用
//error:处理请求时发生错误时被调用
@Override
public String filterType() {
return "pre";
}
//过滤器的顺序
@Override
public int filterOrder() {
return 0;
}
//过滤器是否要执行
@Override
public boolean shouldFilter() {
return true;
}
//过滤器业务逻辑,举例验证是否有userToken参数
@Override
public Object run() throws ZuulException {
String token = request.getParameter("userToken");
if (StringUtils.isEmpty(token)){
//获取当前上下文
RequestContext currentContext = RequestContext.getCurrentContext();
//设置不转发zuul请求
currentContext.setSendZuulResponse(false);
//设置拦截返回的提示
currentContext.setResponseBody("token is empty");
//设置状态码
currentContext.setResponseStatusCode(400);
return null;
}
return null;
}
}