Spring Cloud_25_网关Zuul/功能进阶

网关Zuul/功能进阶

  • 之前章节已经学过Zuul路由配置的知识,Zuul的请求会经过多个阶段,每个阶段都会有多个过滤器
  • 其中负责路由的阶段就叫做“routing”过滤器

1、过滤器优先级

  • 实际过程中,是routing过滤器中将请求转发至源服务
  • 数字越小,优先级越高
  • routing过滤器中,最终只会选择一个进行执行,那么为什么也会有优先级呢?会通过外部的一个值,来决定执行哪个过滤器,例如,RibbonRoutingFilter这个过滤器执行完成之后,就将外部值设置一下,告诉SimpleHostRoutingFilter/SendForwardFilter过滤器,这个请求已经进行转发了,每个过滤器中都有一个是否执行的判断方法,通过这个方法来判断是否执行下个过滤器

2、自定义过滤器

  • shouldFilter方法
  • 优先级
  • 仍然使用上章节的项目进行测试

2.1、编写过滤器

package com.atm.cloud.filter;

import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;

import com.netflix.zuul.ZuulFilter;

public class MyFilter extends ZuulFilter {

    /**
     * 过滤器配置完成之后,还需要告知Spring容器这个过滤器的存在
     */

    /**
     * 返回false/true,判断是否执行MyFilter过滤器
     * <p>
     * 决定该过滤器是否要执行
     */
    public boolean shouldFilter() {
        /*
         * 如果A过滤器执行过一次,就将外部值设置为“我已经执行过一次转发”,
         * 下一个过滤器B则会根据这个值进行校验,是否能继续执行B过滤器(“自己要不要执行?”),
         */
        // return false;
        return true;// 为了测试,我们将其设置为总是执行
    }

    public Object run() {
        System.out.println("Come into MyFilter...");
        return null;
    }

    /**
     * 过滤器中有多个阶段,通过该方法,设置自定义过滤器所处阶段
     */
    @Override
    public String filterType() {
        // 设置为"routing"阶段
        return FilterConstants.ROUTE_TYPE;
    }

    /**
     * 设置优先级
     */
    @Override
    public int filterOrder() {
        // 从过滤器优先级的图可知,RibbonRoutingFilter为10,SimpleHostRoutingFilter为100,SendForwardFilter为500
        // 将优先级设置为最高,<10即可
        return 1;
    }

}

2.2、配置过滤器

package com.atm.cloud.filter;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 将过滤器配置进Spring容器,Spring将会注册MyFilter过滤器
 *
 */
@Configuration
public class MyFilterConfig {

    @Bean
    public MyFilter myFilter() {
        return new MyFilter();
    }

}

  • 如果我需要过滤器能动态加载,该怎么做呢?

3、动态加载过滤器

  • 相对于服务提供者、服务调用者而言,网关更需要稳定地提供服务
  • 如果需要增加过滤器,重启网关的代价过大(重启期间,整个集群不可用)
  • Zuul提供了动态加载顾虑起的功能,可以使用groovy来编写过滤器,然后添加到加载目录中,让Zuul去动态加载

3.1、网关项目引入依赖

<!-- groovy -->
<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy-all</artifactId>
    <version>2.4.12</version>
</dependency>
  • 在网关启动时,Zuul使用groovy编译器,需要随时读取过滤器(定时扫描是否新增过滤器)

3.2、网关项目启动类

package com.atm.cloud;

import java.io.File;

import javax.annotation.PostConstruct;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

import com.netflix.zuul.FilterFileManager;
import com.netflix.zuul.FilterLoader;
import com.netflix.zuul.groovy.GroovyCompiler;
import com.netflix.zuul.groovy.GroovyFileFilter;

@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ZuulRouterApp {

    // 动态加载过滤器 start,这是从Zuul官方源代码中Copy出来的
    @PostConstruct
    public void zuulInit() {
        FilterLoader.getInstance().setCompiler(new GroovyCompiler());
        // 会到groovy/filters默认目录下读取配置zuul.filter.root,获取脚本根目录
        String scriptRoot = System.getProperty("zuul.filter.root",
                "groovy/filters");
        // 获取刷新间隔,5s刷新一次
        String refreshInterval = System.getProperty(
                "zuul.filter.refreshInterval", "5");
        if (scriptRoot.length() > 0)
            scriptRoot = scriptRoot + File.separator;
        try {
            // 通过FilterFileManager设置GroovyFileFilter过滤器
            FilterFileManager.setFilenameFilter(new GroovyFileFilter());
            // 通过FilterFileManager的init进行Groovy的文件管理,
            // 参数说明:刷新时间、文件目录(因为我们有三个阶段,所以我们提供三个目录)
            FilterFileManager.init(Integer.parseInt(refreshInterval),
                    scriptRoot + "pre", scriptRoot + "route", scriptRoot
                            + "post");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 动态加载过滤器 end

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

3.3、网关项目创建目录

3.4、DynamicFilter.groovy

package groovy.filters;

import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;

import com.netflix.zuul.ZuulFilter;

class DynamicFilter extends ZuulFilter {

    public boolean shouldFilter() {
        return true;
    }

    public Object run() {
        System.out.println("Come into DynamicFilter...");
        return null;
    }

    public String filterType() {
        return FilterConstants.ROUTE_TYPE;
    }

    public int filterOrder() {
        return 3;
    }
}
  • 将文件直接上传至指定目录下(如:此处上传至groovy/filters/route下,当访问时,程序会自动加载该过滤器)【注意,编写xx.groovy文件时,包名与编码格式】

4、请求上下文

  • 编写过滤器可以参考此例子
package com.atm.cloud.filter;

import javax.servlet.http.HttpServletRequest;

import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.web.client.RestTemplate;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;

/**
 * 使用RestTemplate来调用集群服务
 *
 */
public class RestTemplateFilter extends ZuulFilter {

    private RestTemplate restTemplate;

    public RestTemplateFilter(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    public boolean shouldFilter() {
        /*
         * HTTP请求的全部信息,都封装在一个RequestContext的对象中, 该对象继承ConcurrentHashMap,
         * 可以将RequestContext看作一个Map
         * RequestContext维护着当前线程的全部请求变量,例如请求的URI、serviceid、主机等信息
         * 以RequestContext为基础,编写一个自定义的过滤器,使用RestTemplate来调用集群服务
         */
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        // 获取请求URL
        String uri = request.getRequestURI();

        // 为了不影响其他路由,uri中含有rest-tpl-sale才执行本路由
        if (uri.indexOf("rest-tpl-sale") != -1) {
            return true;
        } else {
            return false;
        }

    }

    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        // 获取需要调用的服务id
        String serviceId = (String) ctx.get("serviceId");

        // 获取请求的URI
        String uri = (String) ctx.get("requestURI");

        // 组合成URL给RestTemplated调用
        String url = "http://" + serviceId + uri;
        System.out.println("Come into RestTemplateFilter...,URL:" + url);

        // 调用并获取结果
        String result = this.restTemplate.getForObject(url, String.class);

        // 设置路由状态,表示已经进行路由
        // 将结果设置进请求上下文中
        ctx.setResponseBody(result);

        // 设置响应标识
        ctx.sendZuulResponse();

        return null;
    }

    @Override
    public String filterType() {
        return FilterConstants.ROUTE_TYPE;
    }

    @Override
    public int filterOrder() {
        return 2;
    }

}

5、@EnableZuulServer注解

  • 与@EnableZuulProxy的区别
  • @EnableZuulProxy的过滤器范围

  • @EnableZuulServer的过滤器范围(不具备调用集群服务能力,也不具备简单路由功能)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

'嗯哼。

生生不息,“折腾”不止,Thx

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值