微服务:SpringCloud zuul网关

一、存在的问题

在之前的文章中,微服务架构已经初具雏形。但还有一些问题:不同的微服务一般会有不同的网 络地址,客户端在访问这些微服务时必须记住几十甚至几百个地址,这对于客户端方来说太复杂也难以维护。如下图:
 
前端应用程序调用我们消费者提供的接口会有以下几个问题:
  • 客户端会请求多个不同的服务,需要维护不同的请求地址,增加开发难度 (各个消费者ip不同)
  • 在某些场景下存在跨域请求的问题
  • 加大身份认证的难度,每个微服务需要独立认证(关于微服务认证,可以看我之前的文章,深入的讲解了分布式的授权与认证)
那我们想,能否用同一个ip来解决呢?这样就出现了我们网关的姿势!
 
通过网关,前端应用程序,只需要调用网关的ip就可以了。在网关就可以找到对应的微服务。
 

二、网关的概念

什么是微服务网关:
 
API网关是一个服务器,是系统对外的唯一入口。API网关封装了系统内部架构,为每个客户端提供一个定制的APIAPI网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。服务端通过API-GW注册和管理服务。
 
作用和应用场景:
 
网关具有的职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。当然,最主
要的职责还是与外界联系
 
常见的API网关实现方式
  • Kong:基于Nginx+Lua开发,性能高,稳定,有多个可用的插件(限流、鉴权等等)可以开箱即用。问题:只支持Http协议;二次开发,自由扩展困难;提供管理API,缺乏更易用的管控、配置方式。
  •  
    Zuul:Netflflix开源,功能丰富,使用JAVA开发,易于二次开发;需要运行在web容器中,如Tomcat。 问题:缺乏管控,无法动态配置;依赖组件较多;处理Http请求依赖的是Web容器,性能不如Nginx;
  •  
    Traefifik:Go语言开发;轻量易用;提供大多数的功能:服务路由,负载均衡等等;提供WebUI。问题:二进制文件部署,二次开发难度大;UI更多的是监控,缺乏配置、管理能力;
  •  
    Spring Cloud Gateway:SpringCloud提供的网关服务
  •  
    Nginx+lua实现:使用Nginx的反向代理和负载均衡可实现对api服务器的负载均衡及高可用问题:自注册的问题和网关本身的扩展性。
     

三、zuul实现网关

1、Zuul简介

ZUULNetflflix开源的微服务网关,它可以和EurekaRibbonHystrix等组件配合使用,Zuul组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:
  • 动态路由:动态将请求路由到不同后端集群
  • 压力测试:逐渐增加指向集群的流量,以了解性能
  • 负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求
  • 静态响应处理:边缘位置进行响应,避免转发到内部集群 
  • 身份认证和安全: 识别每一个资源的验证要求,并拒绝那些不符的请求。Spring CloudZuul进行了整合和增强。

2、搭建一个Zuul网关服务器

(1)一个基础工程

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>SpringCloudDemo</artifactId>
        <groupId>com.springcloud.demo</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>zuul_server</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
    </dependencies>
</project>
server:
  port: 8080 #端口

spring:
  application:
    name: api-zuul-server
package com.springcloud.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {

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

其他服务目录:

person-sevice是9002端口,order-service是9001端口,这两个是对外提供的接口。product-service是9010端口,是微服务。

(2)路由配置

# 路由选择配置
zuul:
  routes:
    service-order:
      path: /order-service/**
      url: http://127.0.0.1:9001
    service-person:
      path: /person-service/**
      url: http://127.0.0.1:9002
只需要在application.yml文件中配置路由规则即可:
  • product-service:配置路由id,可以随意取名
  • url:映射路径对应的实际url地址 
  • path:配置映射路径,这里将所有请求前缀为/product-service/的请求,转发到http://127.0.0.1:9002处理

都是可以的,他们都调用了一个微服务。

(3)面向服务的路由配置(从注册中心拿到服务)

添加注册中心依赖:

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
添加注册中心配置
 
eureka:
  client:
    service-url:
      defaultZone: http://localhost:9000/eureka/
  instance:
    prefer-ip-address: true #使用ip地址注册
# 路由选择配置
zuul:
  routes:
    service-order:
      path: /order-service/**
#      url: http://127.0.0.1:9001
      serviceId: service-order
    service-person:
      path: /person-service/**
#      url: http://127.0.0.1:9002
      serviceId: service-person

 

依然是没有任何问题的!!!
 
简化配置
zuul:
  routes:
     service-order: /service-order/**

3、过滤器

zuul有两个核心功能:请求过滤

ZuulFilter简介
 
Zuul 中的过滤器跟我们之前使用的 javax.servlet.Filter 不一样,javax.servlet.Filter 只有一种类型,可
以通过配置 urlPatterns 来拦截对应的请求。而 Zuul 中的过滤器总共有 4 种类型,且每种类型都有对
应的使用场景。
 
  • PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请
    求的微服务、记录调试信息等。
  •  
    ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用
    Apache HttpClientNetfifilx Ribbon请求微服务。
  •  
    POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP
    Header、收集统计信息和指标、将响应从微服务发送给客户端等。
  •  
    ERROR:在其他阶段发生错误时执行该过滤器。
Zuul提供了自定义过滤器的功能实现起来也十分简单,只需要编写一个类去实现zuul提供的接口
 
public abstract ZuulFilter implements IZuulFilter{ 
    abstract public String filterType(); 

    abstract public int filterOrder(); 

    boolean shouldFilter();// 来自IZuulFilter 
    
    Object run() throws ZuulException;// IZuulFilter 
}
ZuulFilter是过滤器的顶级父类。在这里我们看一下其中定义的4个最重要的方法。
 
  • shouldFilter :返回一个 Boolean 值,判断该过滤器是否需要执行。返回true执行,返回false不执行。
  •  
    run :过滤器的具体业务逻辑。
  •  
    filterType :返回字符串,代表过滤器的类型。包含以下4种:
    •  
      pre :请求在被路由之前执行
    •  
      routing :在路由请求时调用
    • post :在routingerrror过滤器之后调用
    • error :处理请求时发生错误调用
  • filterOrder :通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。

生命周期:

 

4、自定义过滤器

package com.springcloud.demo.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * @ClassName LoginFilter
 * @Description
 * @Author 戴书博
 * @Date 2020/5/28 16:24
 * @Version 1.0
 **/
@Component
public class LoginFilter extends ZuulFilter {

    /**
     * 类型:
     *      pre
     *      routing
     *      post
     *      error
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 指定过滤器的执行顺序
     * 返回值越小,执行顺序越靠前
     */
    @Override
    public int filterOrder() {
        return 0;
    }

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

    /**
     * 执行过滤器的方法
     */
    @Override
    public Object run() throws ZuulException {
        System.out.println("================进入过滤器=================");
        //获取上下文对象
        RequestContext ctx = RequestContext.getCurrentContext();
        //获取request对象
        HttpServletRequest req = ctx.getRequest();
        //从请求中获取token
        String token = req.getParameter("access-token");
        //判断
        if(token == null || token.equals("")){
            // 没有token,登录校验失败,拦截
            ctx.setSendZuulResponse(false);
            // 返回401状态码。也可以考虑重定向到登录页。
            ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
        }
        // 校验通过,可以考虑把用户信息放入上下文,继续向后执行
        return null;
    }
}

在我们之前写的授权与认证中,使用了一个微服务认证。这里简单写一下。

zuul出现的问题:

  • 性能问题 Zuul1x版本本质上就是一个同步Servlet,采用多线程阻塞模型进行请求转发。简单讲,每一个请求,Servlet容器要为该请求分配一个线程专门负责处理这个请求,直到响应返回客户端这个线程才会被释放返回容器线程池。如果后台服务调用比较耗时,那么这个线程就会被阻塞,阻塞期间线程资源被占用,不能干其它事情。我们知道Servlet容器线程池的大小是有限制的,当前端请求量大,而后台慢服务比较多时,很容易耗尽容器线程池内的线程,造成容器无法接受新的请求。
  • 不支持任何长连接,如websocket

源码:[email protected]:Zesystem/springclouddedemowangguan.git

 

 

 

 

 

 

 

 

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章