一、Zuul
Zuul是通过Servlet来实现的,Zuul通过自定义的ZuulServlet(类似于Spring MVC的DispatcherServlet)来对请求进行控制(一系列过滤器处理Http请求)。
 所有的Request都要经过ZuulServlet的处理,三个核心的方法preRoute(),route(), postRoute(),zuul对request处理逻辑都在这三个方法里,ZuulServlet交给ZuulRunner去执行。
 ZuulRunner直接将执行逻辑交由FilterProcessor处理,ZuulServlet、ZuulRunner、FilterProcessor都是单例。
 FilterProcessor对filter的处理逻辑。
     1.根据Type获取所有输入该Type的filter,List<ZuulFilter> list。
     2.遍历该list,执行每个filter的处理逻辑,processZuulFilter(ZuulFilter filter)。
     3.RequestContext对每个filter的执行状况进行记录,此处的执行状态主要包括其执行时间、以及执行成功或者失败,若失败则对异常封装后抛出。
     4.zuul框架对每个filter的执行结果都没有太多的处理,没把上一filter的执行结果交由下一个将要执行的filter,仅记录执行状态,如果执行失败抛出异常并终止执行。
1、Zuul过滤器的功能

1.身份验证和安全性 - 确定每个资源的身份验证要求并拒绝不满足这些要求的请求。
2.洞察和监控 - 在边缘跟踪有意义的数据和统计数据,以便为我们提供准确的生产视图。
3.动态路由 - 根据需要动态地将请求路由到不同的后端群集。
4.压力测试 - 逐渐增加群集的流量以衡量性能。
5.负载分配 - 为每种类型的请求分配容量并删除超过限制的请求。
6.静态响应处理 - 直接在边缘构建一些响应,而不是将它们转发到内部集群。
2、Zuul 生命周期(四类过滤)

1.PRE: 在请求被路由之前调用。可在集群中选择请求的微服务、认证鉴权,限流等。
2.ROUTING: 将请求路由到微服务。可构建发送给微服务的请求,并使用Apache HttpClient或 Ribbon请求微服务,现在也支持OKHTTP。
3.POST: 在路由到微服务以后执行。可在这种过滤中处理逻辑,如收集统计信息和指标、将响应从微服务发送给客户端等。
4.ERROR: 在其他阶段发生错误时执行该过滤器。可做全局异常处理。
 
  3、Zuul的使用
1.配置
server:
  port: 80
spring:
  application:
    name: demo-zuul
eureka:
  client:
    enabled: true #该客户端是否可用
    service-url:
      defaultZone: http://localhost:8761/eureka #注册中心地址
    register-with-eureka: true #注册该服务,默认为true
    fetch-registry: true #获取服务列表,默认为true 
2.启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.filters.discovery.PatternServiceRouteMapper;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@EnableZuulProxy//开启网关功能
public class DemoZuulApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoZuulApplication.class, args);
    }
    //配置动态路由规则
    @Bean
    public PatternServiceRouteMapper getPatternServiceRouteMapper() {
        return new PatternServiceRouteMapper("(?<name>^.+)", "${name}");
    }
} 
3.写自己的过滤器
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
@Component
public class TokenFilter extends ZuulFilter {
    /**
     * 拦截类型,4种类型 pre route error post
     */
    @Override
    public String filterType() {
        //  FilterConstants.PRE_TYPE;
        //  FilterConstants.ROUTE_TYPE;
        //  FilterConstants.ERROR_TYPE;
        //  FilterConstants.POST_TYPE;
        return FilterConstants.PRE_TYPE;
    }
    /**
     * 该过滤器在所有过滤器的执行顺序值,值越小,越前面执行
     */
    @Override
    public int filterOrder() {
        return 0;
    }
    /**
     * 是否拦截
     */
    @Override
    public boolean shouldFilter() {
        RequestContext ctx = RequestContext.getCurrentContext();
        // RequestContext ctx = RequestContext.getCurrentContext();
        // ctx.getBoolean("isOk");
        HttpServletRequest request = ctx.getRequest();
        String requestURI = request.getRequestURI();
        //排除拦截的url
        if (requestURI.equals("/demo-member/user/loadBalance")) {
            return false;
        }
        return true;
    }
     /**
      * 过滤器具体的业务逻辑
      */
    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String token = request.getParameter("token");
        // ctx.set("isOk",true); // 可以在上下文里面设置一个key,在下一次拦截时,就可以获取到
        if (null == token) {
            ctx.setResponseBody("token is null");
            ctx.setResponseStatusCode(400);
            ctx.setSendZuulResponse(false);
            return null;
        }
        if (!"123456".equals(token)) {
            ctx.setResponseBody("token is error");
            ctx.setResponseStatusCode(400);
            ctx.setSendZuulResponse(false);
            return null;
        }
        ctx.setSendZuulResponse(true);
        return null;
    }
} 















