[Spring Cloud] - Zuul 实践(三) - 过滤器

在设计系统阶段,一定记住Zuul的核心思想:Zuul是做服务的路由,而不是页面的转发。这是zuul与Nginx根本上的不同。

Zuul没有提供良好的跳转或转发功能,这和它的使用场景是有关的。它的核心功能包括验证服务权限,验证成功或失败,成功或失败后放行还是拦截等等。若拦截,只需返回响应码即可,至于成功或失败的页面显示,则应该由前端负责跳转或转发。
请一定遵守标准化的设计,不要让Zuul参与页面跳转(当然,Zuul也可以实现页面跳转,本文后半部分会涉及到,但不推荐由Zuul实现跳转)

Zuul的验证功能基本在它的过滤器中实现。

过滤器类型与请求的生命周期

每一个请求发送到Zuul,都有其对应的不同阶段,可以称其为“生命周期”,比如初始化阶段,处理请求阶段,结束阶段,出error阶段等等。

在谈下一话题前,请一定分清楚“转发”和“路由”的区别。
如果说定义,路由是一种策略,转发是一条路径。
说浅显一点,路由是网状结构下的各种路径解的集合,而转发是点对点的一条通信路径。
再浅显一些,就像学生年代你上课时给女神小红递纸条,路由是你将纸条给你同桌(代理),你同桌会选择一条消息发送的路径,比如将纸条传递给A,再到B,再到C,最后到小红。由同桌发到小红的整个过程就叫路由。你只需要将纸条给同桌(代理),剩下的不需要你参与,你的同桌会选择最优路径,无论是通过ABC,还是BAC,当纸条传递成功,你的代理会反馈给你结果(同桌会告诉你纸条传递成功,小红看都没看就扔进垃圾桶),你不需要知道中间隔了多少人。这就是路由。
而转发,就是你直接把纸条揉成团,扔给小红,小红收到后含羞一笑,你看到后心花怒放。这就是转发。
最浅显的概括就是: 路由是多跳的转发,转发的一跳的路由。这一点从英文routing和forwading的区别就可以明白。
如果还无法理解,请重新读一遍计算机网络。


对于微服务项目来说,Zuul实现的就是路由功能,往往也被会称做路由转发、网关等等,但意思都是一样的。
本文在谈Zuul的拦截器,
每一个request在发送到Zuul后,都有几个不同的阶段(生命周期),Zuul的几个拦截器对应request的不同阶段,所以我们先谈一下request的生命周期:

  1. 请求刚刚被Zuul接收到,Zuul需要解析请求的地址,以及其请求参数等等。
  2. 请求即将被路由。Zuul已经确定此请求该发送至哪一个service,并已准备好将其发送过去。
  3. 请求被路由至目标服务
  4. 请求在以上三个阶段中的某一个出错。

以上四个阶段是request的生命周期,Zuul的四种拦截器分别对应以上的四个request生命周期: PRE, ROUTING, POST, ERROR.

所以,如果想要对request的某个生命周期进行操作(过滤),只需要按需实现这四种拦截器即可。

那么,如何实现这四种拦截器呢?依然超级简单,答案只有一个:继承ZuulFilter类并覆写父类方法即可。
区分拦截器的类型在方法filterType()中。
闲言少叙,上代码。
以下是一个最基本的PRE拦截器。

实现拦截器

定义
新建一个类,继承ZuulFilter,并覆写其方法。

public class PreRequestFilter extends ZuulFilter {
    private static final Logger logger = LoggerFactory.getLogger(RouteRequestFilter.class);

    // 表示此拦截器类型, pre、route、post、error
    @Override
    public String filterType() {
        return "pre";
    }
    
    // 执行顺序,若有多个同种拦截器,比如有多个pre拦截器,他们的执行顺序是什么
    @Override
    public int filterOrder() {
        return 0;
    }

    // 是否应用这个拦截器,true是应用,false表示这个拦截器是失效的
    @Override
    public boolean shouldFilter() {
        return false;
    }

    // 此方法为核心方法,表示拦截器中执行的逻辑
    @Override
    public Object run() throws ZuulException {
        System.out.println("pre");
        return null;
    }
}

注册为Bean
在主类中将此拦截器注册为Bean

@EnableZuulProxy
@SpringBootApplication
public class ZuulApp {

    public static void main(String[] args) {
        SpringApplication.run(ZuulApp.class, args);
    }

    @Bean
    public PreRequestLogFilter preRequestLogFilter(){
        return new PreRequestLogFilter();
    }

此时,一个最基本的拦截器就创建成功了!

拦截请求

在run方法中,添加以下内容:

@Override
    public Object run(){
        //获取上下文
        RequestContext ctx = RequestContext.getCurrentContext();
        //获取Request
        HttpServletRequest request = ctx.getRequest();
        //获取请求参数accessToken
        String accessToken = request.getParameter("accessToken");
        //使用String工具类
        if (StringUtils.isBlank(accessToken)) {
            logger.error("no token");
            logger.warn("accessToken is empty");
            ctx.setSendZuulResponse(false);  //进行拦截
            ctx.setResponseStatusCode(401);
            try {
                ctx.getResponse().getWriter().write("accessToken is empty");
            } catch (IOException e) {
                e.printStackTrace();
            }

            return null;
        }
        return null;
   }

此时,若通过Zuul访问微服务,若没有accessToken参数,会发现请求被拦截,页面上会显示“accessToken is empty”

你可能感兴趣的:(springboot,spring-cloud)