Zuul服务网关的简单使用与相关知识点分析

Zuul网关是系统的唯一对外的入口,介于客户端和服务器端之间的中间层,处理非业务功能 提供路由请求、鉴权、监控、缓存、限流等功能。

  • 主流的网关:
    Zuul、kong(基于Nginx的API gateway)、nginx+lua(是一个高性能的HTTP和反向代理服务器,lua是脚本语言,让Nginx执行Lua脚本,并且高并发、非阻塞的处理各种请求)

利用Zuul搭建服务网关:

  1. 搭建网关服务,引入依赖“spring-cloud-starter-netflix-eureka-client”和“spring-cloud-starter-netflix-zuul”
    (如果是使用Idea的Spring Initializr来搭建,则选中eureka client和zuul即可)
  2. 在application.yml中配置服务端口,如8070,以及注册中心
server:
  port: 8070

eureka:
  client:
    serviceUrl:
       defaultZone: http://localhost:8761/eureka/
  1. 在启动类中贴上@EnableZuulProxy注解,并启动

默认的启动访问网关的url访问网关的端口号,并且拼接上对应的资源路径,且在资源路径前加上应用的服务名称
如:

http://localhost:8070/product-server/api/v1/product/get?id=2
http://localhost:8070/order-server/api/v1/order/save?userId=1&productId=2

网关的配置

路由规则

默认的路由规则是使用服务名去访问,如“order-server”,“product-server”等。需要修改,那么就需要加上zuul.routes

zuul:
  routes:
    order-server-route: -- 这个可以随便写
      path: /order/**
      serviceId: order-server

这时候默认的“order-server”还是可以进行访问,如果不想要默认的访问,可以配置 zuul.routes.ignoredPatterns:=/*-server/**

zuul:
  routes:
    ignoredPatterns: /*-server/**
    order-server-route:
      path: /order/**
      serviceId: order-server
    product-server-route:
      path: /product/**
      serviceId: product-server

过滤请求头的问题

默认"Cookie", “Set-Cookie”, "Authorization"等值会被网关拦截,从而传到服务里面的是null值,在ZuulProperties中是默认配置的

private Set<String> sensitiveHeaders = new LinkedHashSet<>(Arrays.asList("Cookie", "Set-Cookie", "Authorization"));

如果需要取消,可以在配置文件中配置

zuul:
    sensitive-headers:  -- 配置为空,就相当于不拦截这些属性

Zuul的工作原理

可以参考 https://github.com/Netflix/zuul/wiki/How-it-Works

  • ZuulServlet
    Zuul服务其实就是以Servlet启动,也就是说,其底层是通过Servlet,在其service方法中可以看到执行流程逻辑,前置过滤器,路由过滤器,后置过滤器依次执行。最终是通过FilterProcessor执行类来进行过滤器的控制,调用,以及结果的操作运算。

上文说到Zuul提供路由请求、鉴权、监控、缓存、限流等功能(这些功能的启用与配置可以在网上直接搜索),而实现这些功能原理是过滤器
Zuul由过滤器组成

  • At the center of Zuul is a series of Filters that are capable of performing a range of actions during the routing of HTTP requests and responses.
    Zuul的核心实质是一系列的过滤器,能够在HTTP请求和响应的路由过程中执行一系列操作

ZuulFilter:

通过观察runFilter()这个方法,可以发现fiter的执行操作。是先调用shouldFilter()方法来判断是否需要过滤,后才执行run()方法调用filter。

模拟实现鉴权登录

假设有两个服务
order-server (需要登录)
product-server (不需要登录)

@Component //一定要贴这个注解,将其交给Spirng管理。 ZuulFilters的注册是通过从Spring容器里拿到所有继承了ZuulFilter的实例
public class AuthZuulFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

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

    @Override
    public boolean shouldFilter() {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        if(request.getRequestURI().indexOf("order")>0){
            System.out.println(request.getRequestURI());
            return true;
        }
        return false;
    }

    @Override
    public Object rn() throws ZuulException {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        String token = request.getHeader("token");
        if(StringUtils.isEmpty(token)){
            token = request.getParameter("token");
        }
        if(StringUtils.isEmpty(token)){
            currentContext.setSendZuulResponse(false);  // 执行结果 response为false
            currentContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()); //未授权
        }
        return null;
    }
}

总结:

  • ZuulFilter的加载过程:
    首先,进行过滤器的加载,这个过程是ZuulServerAutoConfiguration的bean对象ZuulFilterInitializer来完成的。
    而其中,程序首先从Spring中拿到继承了ZuulFilter抽象类的实例
@Configuration
	protected static class ZuulFilterConfiguration {

		@Autowired
		private Map<String, ZuulFilter> filters;

		@Bean
		public ZuulFilterInitializer zuulFilterInitializer(CounterFactory counterFactory,
				TracerFactory tracerFactory) {
			FilterLoader filterLoader = FilterLoader.getInstance();
			FilterRegistry filterRegistry = FilterRegistry.instance();
			return new ZuulFilterInitializer(this.filters, counterFactory, tracerFactory,
					filterLoader, filterRegistry);
		}

	}

通过Spirng拿到所有的过滤器,然后以type区分,也就是前置过滤,路由过滤还是后置过滤等。
其次,将过滤器放到FilterRegistry工厂里,这是一个单例对象,利用一个ConcurrentHashMap来储存所有的过滤器。并在第一次请求时,拿到所有的过滤器,放到FilterLoader中缓存hashFiltersByType中。

  • ZuulFilter的执行过程:
  1. 依次执行preFilter、routeFilter、postFilter
  2. 每种Filter的执行,都是由FilterProcessor发起的,首先拿到全部该种类的过滤器,再循环一个个过滤器执行。
  3. 每次执行Filter,都会先判断是否需要过滤,判断的逻辑写在shouldFilter()中,如果需要,最后执行run方法进行过滤逻辑。
  4. 真正的调用发起是在RibbonRoutingFilter中,其type为 “route” , 其run方法执行了forward来发起远程调用,用的其实也就是Ribbon方式。注意,RibbonRoutingFilter的shouldFilter()方法会根据RequestContext的Response的sendZuulResponse是否为true,是才执行该过滤器,即发起调用,否则就执行跳过。所以上面设置了该属性为false,就不会发起调用。
  5. 调用结果保存到RquestContext中的response中。
发布了25 篇原创文章 · 获赞 9 · 访问量 6631

猜你喜欢

转载自blog.csdn.net/qq_40233503/article/details/98798833