网关

网关,对内,是内部服务拜访外部网站的出口;对外,是外部网站拜访内部资源的入口。

网关就像一个管家,作为内外服务的一道屏障,能够很好的保护内部服务资源的安全,流量监控、一致认证、防止直接暴露内部服务信息等。

网关作业原理

能够把网关看作一道屏障,外部恳求先通过网关,然后由网关转发至后端服务,后端服务处理后,将呼应成果经网关回来给调用方:

zull 网关实践

流量一致收口到网关,咱们能够利用网关做很多通用的工作,比方:

  1. 权限认证、解析
  2. 目标搜集

日益成熟的 web 商场,现成的网关组件有很多,比方常见的 Spring Cloud Gateway、Netflix 的 zull 网关等。

网关的根本设计思路:

  1. 路由:能够将恳求转发至对应的服务,这是网关的中心能力,转发规矩能够多种多样
  2. 过滤器:网关需要易于扩展的 filter、可插拔式的,这儿就会用到常见的职责链形式。

本文首要讲解 web 服务恳求过程中经常运用的网关 zull 相关原理和实践。

zull 网关

根本装备

一份常见的 zull 网关装备:

server:
  port: 8080
spring:
  application:
    name: zuul-gateway
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
zuul:
  prefix: /api
  ignored-services: '*'
  routes:
    service1:
      path: /service1/**
      serviceId: service1
      stripPrefix: false
    service2:
      path: /service2/**
      serviceId: service2
      stripPrefix: false

设置了一切路由的前缀为/api,然后咱们设置了 ignored-services为'*',这意味着 Zuul 将不会自动创立 Eureka 服务的路由。

  1. 然后界说了两个路由规矩,一切以 /service1/ 最初的恳求都会被路由到 service1 服务,一切以 /service2/ 最初的恳求都会被路由到 service2 服务。
  2. stripPrefix 设置为 false 意味着在路由恳求时,不会去掉途径前缀。
  3. retryable 设置为 true 意味着当恳求失利时,Zuul 会尝试重新发送恳求。

默认规矩

假如你没有在 Zuul 的装备中清晰指定要转发的服务,那么 Zuul 默认会将一切从 Eureka 服务注册中心注册的服务都创立路由规矩。

例如,假如你有一个名为 service1 的服务在 Eureka 注册中心注册,那么 Zuul 默认会创立一个如下的路由规矩:

zuul:
  routes:
    service1:
      path: /service1/**
      serviceId: service1

这意味着一切以 /service1/ 最初的恳求都会被路由到 service1 服务。

假如你不期望 Zuul 自动创立路由规矩,你能够在装备中增加 ignored-services: '*',这会告诉 Zuul 疏忽一切服务:

zuul:
  ignored-services: '*'

然后,你能够手动增加你期望Zuul转发的服务。例如:

zuul:
  routes:
    service1:
      path: /my-service1/**
      serviceId: service1

在这个比如中,一切以 /my-service1/ 最初的恳求都会被路由到 service1 服务。

途径形式

在 Zuul 的装备中,能够为每个服务界说一个或多个途径形式,Zuul 会将匹配这些形式的恳求转发到相应的服务。

例如,以下装备将一切以 /service1/ 最初的恳求转发到 service1 服务,将一切以 /service2/ 最初的恳求转发到 service2 服务:

zuul:
  routes:
    service1:
      path: /service1/**
      serviceId: service1
    service2:
      path: /service2/**
      serviceId: service2

你也能够为同一个服务界说多个途径形式,例如:

zuul:
  routes:
    service1:
      path: /service1/**,/api/service1/**
      serviceId: service1

在这个比如中,一切以 /service1/或/api/service1/ 最初的恳求都会被转发到 service1 服务。

按 URL 转发

在一些特殊的场景,你可能想要隐藏后端接口,需要在网关按接口维度做映射,比方:前端接口:/front/getUser -> 后端接口:/user-service/baseInfo

Zull 没有做到这样细粒度,不过它的 Filter 机制很方便扩展。

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import javax.servlet.http.HttpServletRequest;
public class DynamicRouteFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return "pre"; // 在恳求被路由之前调用
    }
    @Override
    public int filterOrder() {
        return 0; // filter履行顺序,通过数字指定
    }
    @Override
    public boolean shouldFilter() {
        // 这儿能够写逻辑判别,是否要过滤,本文true,永远过滤。
        return true;
    }
    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        // ...
        Map<String, Object> routeMap;
        String uri = requestContext.getRequest().getRequestURI();
        // ...
        if(routeMap.contains(uri)) {
            ctx.setSendZuulResponse(true); // 对恳求进行路由
            ctx.setResponseStatusCode(200);
            ctx.set("isSuccess", true);
        } else {
            ctx.setSendZuulResponse(false); // 不对其进行路由
            ctx.setResponseStatusCode(401); // 回来过错码
            ctx.setResponseBody("IP address is not allowed"); // 回来过错内容
            ctx.set("isSuccess", false);
        }
        return null;
    }
}

在以上 routeMap 中,你能够自界说前后端接口映射装备,然后在此处判别当时恳求是否装备了映射联系,进而决议是转发仍是呼应过错码。

ZuulFilter 接口界说了 Zuul 过滤器的根本结构,包括两个办法:

  1. shouldFilter():这个办法决议了是否需要履行 run() 办法。假如回来 true,则履行run() 办法;假如回来 false,则不履行。
  2. run():这是过滤器的中心办法,当 shouldFilter() 回来 true 时,该办法会被调用。这个办法能够包括过滤器的详细逻辑,例如安全验证、限流操控等。假如在履行过程中产生过错,会抛出 ZuulException 反常。

生命周期

以一条恳求为例,先看看一条网关的全生命周期:

  1. 预过滤(Pre Filter) :这是恳求在进入 Zuul 路由之前履行的过滤,一般用于实现身份验证、记载调试信息、决议是否需要对恳求进行路由等。
  2. 路由(Routing Filter) :在这个阶段,Zuul 会依据设定的规矩将恳求路由到对应的服务实例。
  3. 后过滤(Post Filter) :这是在路由到微服务后履行的过滤器,这个过滤器将会对回来的数据进行处理,比方增加 HTTP Header、搜集计算和目标信息、将呼应输出到客户端等。
  4. 过错处理(Error Filter) :在整个生命周期中任何阶段产生过错时都会进入该过滤器,该过滤器用于一致处理恳求过程中出现的反常。
// Pre Filter
public class SimplePreFilter extends ZuulFilter {
  @Override
  public String filterType() {
    return "pre";
  }
  @Override
  public int filterOrder() {
    return 1;
  }
  @Override
  public boolean shouldFilter() {
    return true;
  }
  @Override
  public Object run() {
    RequestContext ctx = RequestContext.getCurrentContext();
    HttpServletRequest request = ctx.getRequest();
    log.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));
    return null;
  }
}
// Post Filter
public class SimplePostFilter extends ZuulFilter {
  @Override
  public String filterType() {
    return "post";
  }
  @Override
  public int filterOrder() {
    return 1;
  }
  @Override
  public boolean shouldFilter() {
    return true;
  }
  @Override
  public Object run() {
    RequestContext ctx = RequestContext.getCurrentContext();
    HttpServletRequest request = ctx.getRequest();
    log.info("Response Status : " + ctx.getResponseStatusCode());
    return null;
  }
}
// Error Filter
public class SimpleErrorFilter extends ZuulFilter {
  @Override
  public String filterType() {
    return "error";
  }
  @Override
  public int filterOrder() {
    return 1;
  }
  @Override
  public boolean shouldFilter() {
    return true;
  }
  @Override
  public Object run() {
    RequestContext ctx = RequestContext.getCurrentContext();
    HttpServletRequest request = ctx.getRequest();
    log.info("Error occurred, request to " + request.getRequestURL().toString());
    return null;
  }
}

以上比如中,界说了三个过滤器:预过滤器、后过滤器和过错过滤器。

  1. 预过滤器记载了恳求的HTTP办法和URL
  2. 后过滤器记载了呼应的状态码
  3. 过错过滤器记载了产生过错的恳求URL。

以上就是 zull 网关的中心原理以及实践,欢迎交流讨论!