Spring Cloud学习笔记【十一】微服务网关Zuul的过滤和限流

                        Spring Cloud学习笔记【十一】微服务网关Zuul的过滤和限流

zuul的工作原理

zuul的核心是一系列的filters, 其作用可以类比Servlet框架的Filter,或者AOP。这些过滤器帮助我们执行以下功能:

  • 身份验证和安全性——识别每个资源的身份验证需求并拒绝不满足这些需求的请求。
  • 洞察和监控——在边缘跟踪有意义的数据和统计数据,以便为我们提供准确的生产视图。
  • 动态路由——根据需要动态地将请求路由到不同的后端集群。
  • 压力测试——逐步增加集群的流量,以评估性能。
  • 减少负载——为每种类型的请求分配容量,并删除超过限制的请求。
  • 静态响应处理——直接在边缘构建一些响应,而不是将它们转发到内部集群。
  • 多区域弹性——跨AWS区域路由请求,以使我们的ELB使用多样化,并使我们的优势更接近我们的成员。

a.依赖引入

implementation 'org.springframework.cloud:spring-cloud-starter-netflix-zuul'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'

b.@EnableZuulProxy注解添加

@EnableEurekaClient
@EnableZuulProxy
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

d.配置文件配置    

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
spring:
  application:
    name: zull
server:
  port: 9000

c.编写测试demo

@RestController
@RequestMapping("/order")
public class OrderInfoController {
    @Autowired
    private OrderCodeConfig orderCodeConfig;
    @Autowired
    private OrderInfoService orderInfoService;
    /**
     * @todo 查询订单列表
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/list",method = RequestMethod.GET)
    public String list() throws Exception {
        List<OrderInfo> list = orderInfoService.list();
        return JSON.toJSONString(list);
    }
    /**
     * @todo 查询订单配置
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/config",method = RequestMethod.GET)
    public String config() throws Exception {
        return JSON.toJSONString(orderCodeConfig);
    }
}

e.自定义过滤器

@Component
public class TokenFilter extends ZuulFilter {

    private Log LOGGER = LogFactory.getLog(TokenFilter.class);

    /**
     * 过滤器类型:有四种类型(分别为:ERROR_TYPE、POST_TYPE、PRE_TYPE、ROUTE_TYPE)
     *
     * @return
     */
    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    /**
     * 过滤的顺序:越小越靠前,FilterConstants.PRE_DECORATION_FILTER_ORDER已经定义了的大小顺序,可以直接使用
     *
     * @return
     */
    @Override
    public int filterOrder() {
        return PRE_DECORATION_FILTER_ORDER - 1;
    }

    /**
     * 过滤是否生效:代表这个过滤器是否生效(true 、 false)
     *
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 处理业务代码的逻辑
     *
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        LOGGER.info("进入token的过滤器");
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String token = request.getParameter("token");//获取请求的参数
        if (StringUtils.isNotBlank(token)) {
            ctx.setSendZuulResponse(true);  //对该请求进行路由
            ctx.setResponseStatusCode(HttpStatus.OK.value());
            ctx.set("isSuccess", true); // 设值,让下一个Filter看到上一个Filter的状态
        } else {
            ctx.setSendZuulResponse(false);  //过滤该请求,不对其进行路由
            ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());// 返回错误码
            ctx.setResponseBody("this is not auth"); //返回错误信息
            ctx.set("isSuccess", false);
        }
        ctx.getResponse().setContentType("text/html;charset=UTF-8");
        return null;
    }

}

启动测试:http://localhost:9000/myOrder/order/list,不进行token携带

http://localhost:9000/myOrder/order/list?token=abc,进行token携带

直接查看这个类,定义了过滤器的一些常用的信息:

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

1、filterType过滤器类型(一共定义了四种)

PRE:该类型的filters在Request routing到源web-service之前执行。用来实现Authentication、选择源服务地址等

ROUTING:该类型的filters用于把Request routing到源web-service,源web-service是实现业务逻辑的服务。这里使用HttpClient请求web-service。

POST:该类型的filters在ROUTING返回Response后执行。用来实现对Response结果进行修改,收集统计数据以及把Response传输会客户端。

ERROR:上面三个过程中任何一个出现错误都交由ERROR类型的filters进行处理。

主要关注 preposterror。分别代表前置过滤,后置过滤和异常过滤。

请求先进入pre的filter类,你可以进行一些权限认证,日志记录,或者额外给Request增加一些属性供后续的filter使用。pre会优先按照order从小到大执行,然后再去执行请求转发到业务服务。

post,那么就会执行完被路由的业务服务后,再进入post的filter,在post的filter里,一般做一些日志记录,或者额外增加response属性什么的。

error,如果在上面的任何一个地方出现了异常,就会进入到type为error的filter中,进行异常的统一处理。

2、filterOrder过滤器顺序(数字越大,优先级越低)

3、shouldFilter过滤器是否生效(true/false)

4、Run方法(处理逻辑的地方)

  下图是filter的执行顺序

过滤器间的协调
    过滤器没有直接的方式来访问对方。 它们可以使用RequestContext共享状态,这是一个类似Map的结构,具有一些显式访问器方法用于被认为是Zuul的原语,内部是使用ThreadLocal实现的。

禁用Zuul过滤器

    设置zuul.<SimpleClassName>.<filterType>.disable=true ,即可禁用SimpleClassName所对应的过滤器。比如:自定义的过滤器com.example.demo.filter.TokenFilter进行禁用,可以这样设置:

zuul:
  TokenFilter:
    pre:
      disable: true

限流

Zuul 上实现限流是个不错的选择,只需要编写一个过滤器就可以了,关键在于如何实现限流的算法。限流算法有漏桶算法以及令牌桶算法,可参考 https://www.cnblogs.com/LBSer/p/4083131.htmlGoogle Guava 为我们提供了限流工具类RateLimiter。

@Component
public class RateLimitZuulFilter extends ZuulFilter {

    private final RateLimiter rateLimiter = RateLimiter.create(1000.0);

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

    @Override
    public int filterOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }

    @Override
    public boolean shouldFilter() {
        // 这里可以考虑弄个限流开启的开关,开启限流返回true,关闭限流返回false,你懂的。
        return true;
    }

    @Override
    public Object run() {
        try {
            RequestContext currentContext = RequestContext.getCurrentContext();
            HttpServletResponse response = currentContext.getResponse();
            if (!rateLimiter.tryAcquire()) {
                HttpStatus httpStatus = HttpStatus.TOO_MANY_REQUESTS;

                response.setContentType(MediaType.TEXT_PLAIN_VALUE);
                response.setStatus(httpStatus.value());
                response.getWriter().append(httpStatus.getReasonPhrase());

                currentContext.setSendZuulResponse(false);

                throw new ZuulException(
                        httpStatus.getReasonPhrase(),
                        httpStatus.value(),
                        httpStatus.getReasonPhrase()
                );
            }
        } catch (Exception e) {
            ReflectionUtils.rethrowRuntimeException(e);
        }
        return null;
    }
}

这个是单点的限流措施,对于分布式和微服务可以参考:http://www.itmuch.com/spring-cloud-sum/spring-cloud-ratelimit/

跨域

@Configuration
public class CorsConfig {
    @Bean
    public FilterRegistrationBean corsFilter() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return bean;
    }
}
发布了71 篇原创文章 · 获赞 31 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/BThinker/article/details/104007894