SpringCloudNetflix-Zuul

本文介绍了Zuul的工作流程,包括预处理、路由、后置处理和异常过滤器。详细讲解了自定义前置过滤器用于登录拦截的实现,并阐述了Zuul如何通过ZuulServlet控制流程。此外,还讨论了Zuul的熔断配置,通过实现ZuulFallbackProvider接口进行服务降级,以及Zuul的超时和饥饿加载配置。
摘要由CSDN通过智能技术生成

1.Zull的工作原理

  • Zuul的底层是通过各种Filter来实现的,Zuul中的Filter按照执行顺序分为了以下几种,当各种Filter出现了异常,请求会跳转到ErrorFilter,然后再经过PostFilter,最后返回结果,如果是PostFilter出现异常,那么也会走ErrorFilter,然后直接响应

    • PreFilters:前置Filter,负责鉴权、解包、解码、验签等,customFilters自定义Filter,一般自定义的都是前置Filter,当然也可以自定义其他Filter

    • RoutingFilters:路由Filter,负责分发请求

    • PostFilters:后置Filter,一般负责在数据返回页面之前对数据进行处理,或者是设置返回值内容

    • ErrorFilters:异常Filter,发生异常时进入异常Filter

  • 正常流程:

    • 请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器,而后返回响应

  • 异常流程:

    • 整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,再error处理完毕后,会将请求交给POST过滤器,最后返回给用户

    • 如果是error过滤器自己出现异常,最终也会进入POST过滤器,而后返回

    • 如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的时,请求不会再到达POST过滤器了

2.ZuulServlet&ZuulFilter

2.1.ZuulServlet

  • ZuulServlet:Zull其根本就一个Servlet,我们可以通过ZuulServlet查看到,Zull原理流程就是在Zull此Servlet中进行控制

2.2.ZuulFilter

  • Zuul提供了一个抽象类ZuulFilter,所有其他Filter都是通过继承ZuulFilter实现自己的过滤器功能

    • filterType :是用来指定filter的类型的,例如返回pre那么就是前置filter

    • filterOrder :是filter的执行顺序,越小越先执行

    • shouldFilter :是其父接口IZuulFilter的方法,用来决定run方法是否要被执行

    • run :是其父接口IZuulFilter的方法,该方法是Filter的核心业务方法

3.自定义Filter

  • 定义一个自定义的前置Filter,用于在Zull网关层做登录拦截

    • 如果请求头中包含Token,那么放行

    • 如果请求头中不包含Token,那么拦截,并返回提示信息

  • 第一步:继承ZuulFilter,实现自定义拦截器

package cn.itsource.filter;
​
import com.alibaba.fastjson.JSON;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
​
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
​
/**
 * @BelongsProject: springcloudnetflix-parent
 * @BelongsPackage: cn.itsource.filter
 * @Author: Director
 * @CreateTime: 2022-07-29  11:39
 * @Description: 使用自定义的前置Filter,做登录校验
 * @Version: 1.0
 */
@Component // 必须交给spring管理,否则不能生效
public class LoginCheckFilter extends ZuulFilter {
​
    // filter类型,可以通过父类查看返回值信息示例,例如:pre、post
    @Override
    public String filterType() {
        return "pre";
    }
​
    // filter的执行顺序,数字越小,执行时机越高
    @Override
    public int filterOrder() {
        return 0;
    }
​
    // 是否执行run方法,true:执行/false:不执行
    @Override
    public boolean shouldFilter() {
        // 1.获取请求地址
        HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
        String uri = request.getRequestURI();
        // 2.判断请求路径是否以login/register结尾,包含就不执行登录业务校验,所以返回false
        if (uri.endsWith("login") || uri.endsWith("register")){
            return false;
        }
        // 3.如果不包含那么说明用户访问的资源是受保护的资源,那么需要执行登录校验
        return true;
    }
​
    // 此自定义filter的核心业务方法执行地
    @Override
    public Object run() throws ZuulException {
        // 1.获取到请求头,响应头
        HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
        HttpServletResponse response = RequestContext.getCurrentContext().getResponse();
        response.setContentType("application/json;charset=utf-8");
        // 2.判断是否包含token,如果不包含,那么需要阻止执行后面的filter,并且还要返回错误信息,提示用户登录
        String token = request.getHeader("token");
        // 2.1.判断token为空,那么需要提示用户,并且阻止filter执行
        if (!StringUtils.hasLength(token)){
            try {
                // 1.设置响应内容
                PrintWriter writer = response.getWriter();
                HashMap<String, Object> map = new HashMap<>();
                map.put("success", false);
                map.put("message", "登录校验失败,请重新登录!");
                String data = JSON.toJSONString(map);
                writer.write(data);
                // 2.阻止后续filter执行
                RequestContext.getCurrentContext().setSendZuulResponse(false);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 如果要放行,这里返回null即可,不用管
        return null;
    }
}
  • 第二步:通过访问网关查看登录校验是否成功

  • 第三步:通过postmain传入token,查看登录校验是否成功

2、Zuul的熔断器配置

  • Zuul作为服务网关面向的是客户端/客户端,当服务调用链路出现异常,我们不希望直接把异常信息抛给客户端,而是希望触发降级,返回友好的提示信息,所以我们需要去配置zuul的熔断机制

  • 在Zuul中要实现熔断功能需要实现ZuulFallbackProvider接口,该接口提供了两个方法

    • getRoute():用来指定熔断功能应用于哪些路由的服务

    • fallbackResponse():熔断功能时执行的方法,用来返回托底数据

public interface FallbackProvider {
​
    /**
     * The route this fallback will be used for.
     * @return The route the fallback will be used for.
     */
    public String getRoute();
​
    /**
     * Provides a fallback response based on the cause of the failed execution.
     *
     * @param route The route the fallback is for
     * @param cause cause of the main method failure, may be <code>null</code>
     * @return the fallback response
     */
    ClientHttpResponse fallbackResponse(String route, Throwable cause);
}
  • 第一步:在网关层面对pay-server服务进行降级,服务调用失败触发熔断,返回提示信息

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
​
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
​
​
@Component
public class PayServerFallback implements FallbackProvider {
    
    // 得到日志对象
    private final Logger logger = LoggerFactory.getLogger(FallbackProvider.class);
​
    // 指定要处理的服务。
    @Override
    public String getRoute() {
        return "pay-server";  //"*"代表所有服务都有作用
    }
​
    /**
     * @param route :服务的路由
     * @param cause : 异常
     * @return ClientHttpResponse:熔断后的换回值
     */
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }
​
            @Override
            public int getRawStatusCode() throws IOException {
                return 200;
            }
​
            @Override
            public String getStatusText() throws IOException {
                return "OK";
            }
​
            @Override
            public void close() {
​
            }
​
            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream("抱歉,服务不可用".getBytes());
            }
​
            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

3、Zuul的参数配置

1.超时配置

  • Zuul集成了Hystrix,如果服务的调用链过长,或者Ribbon调用时间过长,可能会触发Hystrix的熔断机制,导致请求拿不到正常的结果,我们通常会对Ribbon和Hystrix的超时时间配置,如下配置对所有消费者微服务都有用

zuul:
  retryable: true #是否开启重试功能
ribbon:
  MaxAutoRetries: 1 #对当前服务的重试次数
  MaxAutoRetriesNextServer: 1 #切换相同Server的次数
  OkToRetryOnAllOperations: false # 对所有的操作请求都进行重试,如post就不能重试,如果没做幂等处理,重试多次post会造成数据的多次添加或修改
  ConnectTimeout: 3000 #请求连接的超时时间
  ReadTimeout: 3000 #请求处理的超时时间
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 9000 #如果配置ribbon的重试,hystrix的超时时间要大于ribbon的超时时间

2.Zuul的饥饿加载

  • 在学习Ribbon的章节我们为了让服务加快第一次调用,我们可以通过设置Ribbon的饥饿加载,Zuul底层通过Ribbon实现负载均衡器,所以也需要指定饥饿加载

  • 需要注意的是,Zuul是通过读取路由配置来实现饥饿加载的,所以如果要让eager-load.enabled: true起作用,我们一般不会使用默认的路由方式,而是单独配置路由规则

zuul:
  ribbon:
    eager-load.enabled: true    # 饥饿加载
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值