微服务网关Zuul

服务网关的概念:

  API网关是一个服务器,是系统对外的唯一入口。API网关封装了系统内部架构,为每个客户端提供一个定制的API。API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。服务端通过API-GW注册和管理服务。
  作用和应用场景:

    网关具有的职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。当然,最主要的职责还是与“外界联系”。

微服务网关Zuul:

  ZUUL是Netflix开源的微服务网关,它可以和Eureka、Ribbon、Hystrix等组件配合使用,Zuul组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:

    动态路由:动态将请求路由到不同后端集群
    压力测试:逐渐增加指向集群的流量,以了解性能
    负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求
    静态响应处理:边缘位置进行响应,避免转发到内部集群
    身份认证和安全: 识别每一个资源的验证要求,并拒绝那些不符的请求。

    Spring Cloud对Zuul进行了整合和增强。

  Zuul加入后的架构:

    

  搭建Zuul网关服务器:

    1.创建工程导入依赖

      在IDEA中创建ZUUL网关工程 zuul_server ,并引入依赖

<dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        <version>2.1.0.RELEASE</version>
    </dependency>

    2.编写启动类

      @EnableZuulProxy : 通过 @EnableZuulProxy 注解开启Zuul网管功能

@SpringBootApplication
// 开启zuul网关功能
@EnableZuulProxy
public class ZuulServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZuulServerApplication.class, args);
    }
}

    3.编写配置

      创建配置文件 application.yml ,并添加相应配置

server:
  port: 8080
spring:
  application:
    name: zuul-server #服务名称

  Zuul 中的路由转发:

    最直观的理解:“路由”是指根据请求URL,将请求分配到对应的处理程序。在微服务体系中,Zuul负责接收所有的请求。根据不同的URL匹配规则,将不同的请求转发到不同的微服务处理。
    只需要在 application.yml文件中配置路由规则即可:

server:
  port: 8080
spring:
  application:
    name: zuul-server #服务名称
##路由配置
zull:
  routes:
    #以商品微服务为例
    product-server: #路由id,随便写
    path: /service-product/** #映射路径
    url: http://localhost:9011 #映射路径对应的实际url地址
    sensitiveHeaders: #默认zuul会屏蔽cookie,cookie不会传到下游服务,这里设置为空则取
                      #消默认的黑名单,如果设置了具体的头信息则不会传到下游服务

    启动服务,在浏览器中输入 http://localhost:8080/service-product/product/1 ,即可访问到商品微服务。
      

     面向服务的路由:

      微服务一般是由几十、上百个服务组成,对于一个URL请求,最终会确认一个服务实例进行处理。如果对每个服务实例手动指定一个唯一访问地址,然后根据URL去手动实现请求匹配,这样做显然就不合理。

      Zuul支持与Eureka整合开发,根据ServiceID自动的从注册中心中获取服务地址并转发请求,这样做的好处不仅可以通过单个端点来访问应用的所有服务,而且在添加或移除服务实例的时候不用修改Zuul的路由配置。

      1.添加Eureka客户端依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

      2.开启Eureka客户端发现功能(从Spring Cloud Edgware版本开始, @EnableDiscoveryClient 或 @EnableEurekaClient 可省略。)

@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
// 开启zuul网关功能
@EnableZuulProxy
@EnableDiscoveryClient
public class ZuulServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZuulServerApplication.class, args);
    }
}

      3.添加Eureka配置,获取服务信息

##配置eureka
eureka:
  client:
    serviceUrl:
      defaultZone: http://127.0.0.1:9000/eureka/
  instance:
    preferIpAddress: true

      4.修改映射配置,通过服务名称获取

        因为已经有了Eureka客户端,我们可以从Eureka获取服务的地址信息,因此映射时无需指定IP地址,而是通过服务名称来访问,而且Zuul已经集成了Ribbon的负载均衡功能。

##路由配置
zuul:
  routes:
    #以商品微服务为例
    product-server: #路由id,随便写
      path: /service-product/** #映射路径
      #url: http://127.0.0.1:9011 #映射路径对应的实际url地址
      serviceId: service-product #配置转发的微服务名称

      5.重启服务,在浏览器上通过访问 http://localhost:8080/service-product/product/1 查看效果(访问路径中必须包含路由配置中的 path 部分

        

     简化的路由配置:

      在刚才的配置中,我们的规则是这样的

        zuul.routes.<route>.path=/xxx/** : 指定映射路径。 <route> 是自定义的路由名
        zuul.routes.<route>.serviceId=/service - product:指定服务名。

      而大多数情况下,我们的 <route> 路由名称(product-server)往往和服务名(service-product)会写成一样的。因此Zuul就提供了一种简化的配置语法:
        zuul.routes.<serviceId>=<path>

      上面的配置可以简化为一条:

##路由配置
zuul:
  routes:
    service-product: /service-product/**

    默认的路由规则:

      在使用Zuul的过程中,上面讲述的规则已经大大的简化了配置项。但是当服务较多时,配置也是比较繁琐的。
      因此Zuul就指定了默认的路由规则:

        默认情况下,一切服务的映射路径就是服务名本身。
          例如服务名为: service-product ,则默认的映射路径就是: /service-product/**

    添加固定路由前缀:

      zuul.prefix: /api

      访问的路径:http://localhost:8080/api/order-product/order/buy/1

  Zuul 中的过滤器:

    Zuul包含了两个核心功能:对请求的路由和过滤。其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础;而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础。其实,路由功能在真正运行时,它的路由映射和请求转发同样也由几个不同的过滤器完成的。所以,过滤器可以说是Zuul实现API网关功能最为核心的部件,每一个进入Zuul的HTTP请求都会经过一系列的过滤器处理链得到请求响应并返回给客户端。

    ZuulFilter简介:

      Zuul 中的过滤器跟我们之前使用的 javax.servlet.Filter 不一样,javax.servlet.Filter 只有一种类型,可以通过配置 urlPatterns 来拦截对应的请求。而 Zuul 中的过滤器总共有 4 种类型,且每种类型都有对应的使用场景。

        1. PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
        2. ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。
        3. POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTPHeader、收集统计信息和指标、将响应从微服务发送给客户端等。
        4. ERROR:在其他阶段发生错误时执行该过滤器。

    ZuulFilter是过滤器的顶级父类。在这里我们看一下其中定义的4个最重要的方法:

public abstract ZuulFilter implements IZuulFilter{

    abstract public String filterType();

    abstract public int filterOrder();
    
    boolean shouldFilter();// 来自IZuulFilter

    Object run() throws ZuulException;// IZuulFilter
}
View Code

      shouldFilter :返回一个 Boolean 值,判断该过滤器是否需要执行。返回true执行,返回false不执行。
      run :过滤器的具体业务逻辑。
      filterType :返回字符串,代表过滤器的类型。包含以下4种:
        pre :请求在被路由之前执行
        routing :在路由请求时调用
        post :在routing和errror过滤器之后调用
        error :处理请求时发生错误调用
      filterOrder :通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。

    过滤器执行生命周期:

      

      正常流程:

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

      异常流程:

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

      • 如果是error过滤器自己出现异常,最终也会进入POST过滤器,将最终结果返回给请求客户端。

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

    自定义过滤器:

      模拟一个登录的校验。基本逻辑:如果请求中有access-token参数,则认为请求有效,放行。

/**
 * 自定义的zuul过滤器
 *  继承抽象父类
 */
@Component
public class LoginFilter extends ZuulFilter {

    /**
     * 定义过滤器类型
     *  pre
     *  routing
     *  post
     *  error
     */
    public String filterType() {
        return "pre";
    }


    /**
     *  指定过滤器的执行顺序
     *      返回值越小,执行顺序越高
     */
    public int filterOrder() {
        return 1;
    }


    /**
     * 当前过滤器是否生效
     *  true : 使用此过滤器
     *  flase : 不使用此过滤器
     */
    public boolean shouldFilter() {
        return true;
    }


    /**
     * 指定过滤器中的业务逻辑
     *  身份认证:
     *      1.所有的请求需要携带一个参数 : access-token
     *      2.获取request请求
     *      3.通过request获取参数access-token
     *      4.判断token是否为空
     *      4.1 token==null : 身份验证失败
     *      4.2 token!=null : 执行后续操作
     *  在zuul网关中,通过RequestContext的上下问对象,可以获取对象request对象
     */
    public Object run() throws ZuulException {
        //System.out.println("执行了过滤器");
        //1.获取zuul提供的上下文对象RequestContext
        RequestContext ctx = RequestContext.getCurrentContext();
        //2.从RequestContext中获取request
        HttpServletRequest request = ctx.getRequest();
        //3.获取请求参数access-token
        String token = request.getParameter("access-token");
        //4.判断
        if (token == null) {
            //4.1 如果token==null ,拦截请求,返回认证失败
            ctx.setSendZuulResponse(false); // 拦截请求
            // 响应状态码,401-身份未认证
            ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
        }
        //4.2 如果token!=null ,继续后续操作
        return null;
    }
}
View Code

        RequestContext :用于在过滤器之间传递消息。它的数据保存在每个请求的ThreadLocal中。它用于存储请求路由到哪里、错误、HttpServletRequest、HttpServletResponse都存储在RequestContext中。RequestContext扩展了ConcurrentHashMap,所以,任何数据都可以存储在上下文中
      在自定义的过滤器类中需要加上@Component注解,启动测试 ........
        访问成功

         访问出错

 

posted @ 2020-03-16 16:16  糖不甜,盐不咸  阅读(195)  评论(0编辑  收藏  举报