zoukankan      html  css  js  c++  java
  • Spring Cloud 入门 之 Zuul 篇(五)

    原文地址:Spring Cloud 入门 之 Zuul 篇(五)
    博客地址:http://www.extlight.com

    一、前言

    随着业务的扩展,微服务会不对增加,相应的其对外开放的 API 接口也势必增多,这不利于前端的调用以及不同场景下数据的返回,因此,我们通常都需要设计一个 API 网关作为一个统一的 API 入口,来组合一个或多个内部 API。

    二、简单介绍

    2.1 API 网关使用场景

    黑白名单: 实现通过 IP 地址控制请求的访问
    
    日志:实现访问日志的记录,进而实现日志分析,处理性能指标等
    
    协议适配:实现通信协议的校验、适配转换的功能
    
    身份认证:对请求进行身份认证
    
    计流限流:可以设计限流规则,记录访问流量
    
    路由:将请求进行内部(服务)转发
    

    2.2 API 网关的实现

    业界常用的 API 网关有很多方式,如:Spring Cloud Zuul、 Nginx、Tyk、Kong。本篇介绍的对象正是 Spring Cloud Zuul

    Zuul 是 Netflix 公司开源的一个 API 网关组件,提供了认证、鉴权、限流、动态路由、监控、弹性、安全、负载均衡、协助单点压测等边缘服务的框架。

    Spring Cloud Zuul 是基于 Netflix Zuul 的微服务路由和过滤器的解决方案,也用于实现 API 网关。其中,路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入门的基础。而过滤功能是负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础。

    Spring Cloud Zuul 和 Eureka 进行整合时,Zuul 将自身注册到 Eureka 服务中,同时从 Eureka 中获取其他微服务信息,以便请求可以准确的通过 Zuul 转发到具体微服务上。

    三、实战演练

    本次测试案例基于之前发表的文章中介绍的案例进行演示,不清楚的读者请先转移至 《Spring Cloud 入门 之 Hystrix 篇(四)》 进行浏览。

    当前的项目列表如下:

    服务实例 端口 描述
    common-api - 公用的 api,如:实体类
    eureka-server 9000 注册中心(Eureka 服务端)
    goods-server 8081 商品服务(Eureka 客户端)
    goods-server-02 8082 商品服务(Eureka 客户端)
    goods-server-03 8083 商品服务(Eureka 客户端)
    order-server 8100 订单服务(Eureka 客户端)

    创建一个为名 gateway-server 的 Spring Boot 项目。

    3.1 添加依赖

    <!-- eureka 客户端 -->
    <dependency>
    	<groupId>org.springframework.cloud</groupId>
    	<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    
    <!-- zuul 网关 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    </dependency>
    

    3.2 配置文件

    server:
        port: 9600
     
    spring:
        application:
            name: gateway
    
    eureka:
        instance:
            instance-id: gateway-9600
            prefer-ip-address: true 
        client:
            service-url:
                defaultZone: http://localhost:9000/eureka/  # 注册中心访问地址
    

    3.3 启动 Zuul

    在启动类上添加 @EnableZuulProxy 注解:

    @EnableZuulProxy
    @SpringBootApplication
    public class GatewayApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(GatewayApplication.class, args);
    	}
    }
    

    启动上边的所有项目,打开 Postman 请求订单下单接口,如下图:

    图中,我们首先不经过网关直接访问 order-server 项目请求地址:http://localhost:8100/order/place

    之后再修改成访问 gateway-server 项目的请求地址:http://localhost:9600/order/order/place

    最终,响应结果都一样。

    提示:http://localhost:9600/order/order/place 中第一个 order 表示的是注册在 Eureka 上的订单服务名称。

    3.4 zuul 常用配置

    修改路由:

    zuul:
      sensitive-headers: # 全局忽略敏感头,即允许接收 cookie 等请求头信息   
      routes:
        extlight: # 任意名字,保证唯一即可
          path: /extlight/** # 自定义,真正用到的请求地址
          service-id: ORDER  # 路由到的目标服务名称
    

    将订单服务的路由名称改成 extlight。

    使用 Postman 请求下单接口,运行结果:

    请求成功。

    禁用路由:

    zuul:
      ignored-patterns:
      - /order/order/**   
    

    http://localhost:9600/order/order/place 无法被正常路由到订单服务,响应返回 404。

    路由加前缀:

    zuul:
      prefix: /api
    

    所有请求中的 path 需要添加 api 前缀。如: http://localhost:9600/extlight/order/place 需要改成 http://localhost:9600/api/extlight/order/place

    设置敏感头:

    zuul:
      sensitive-headers: # 设置全局敏感头,如果为空,表示接收所有敏感头信息
    

    zuul:
      routes:
        extlight: # 任意名字,保证唯一即可
          path: /extlight/** # 自定义,真正用到的请求地址
          service-id: ORDER  # 路由到的目标服务名称
          sensitive-headers: # 针对 /extlight/ 的请求设置敏感头信息
    

    四、Zuul 自定义过滤器

    Zuul 的核心技术就是过滤器,该框架提供了 ZuulFilter 接口让开发者可以自定义过滤规则。

    我们以身份检验为例,自定义 ZuulFilter 过滤器实现该功能。

    4.1 创建用户服务

    新建名为 user-server 的项目。

    添加依赖:

    <!-- common api -->
    <dependency>
      <groupId>com.extlight.springcloud</groupId>
      <artifactId>common-api</artifactId>
      <version>${parent-version}</version>
    </dependency>
      	
    <!-- springmvc -->
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- eureka 客户端 -->
    <dependency>
    	<groupId>org.springframework.cloud</groupId>
    	<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    

    application.yml:

    server:
        port: 8200
    
    spring:
      application:
        name: USER
        
    eureka:
        instance:
            instance-id: user-api-8200
            prefer-ip-address: true # 访问路径可以显示 IP
        client:
            service-url:
                defaultZone: http://localhost:9000/eureka/  # 注册中心访问地址
    

    登录接口:

    @RestController
    @RequestMapping("/user")
    public class LoginController {
    
    	@PostMapping("/login")
    	public Result login(String username, String password, HttpServletResponse response) {
    		
    		
    		if ("admin".equals(username) && "admin".equals(password)) {
    			// 模拟生成 token,实际开发中 token 应存放在数据库或缓存中
    			String token = "123456";
    			Cookie cookie = new Cookie("token", token);
    			cookie.setPath("/");
    			cookie.setMaxAge(60 * 10);
    			response.addCookie(cookie);
    			
    			return Result.success();
    		}
    		
    		return Result.fail(401, "账号或密码错误");
    	}
    }
    

    user-server 启动类:

    @EnableEurekaClient
    @SpringBootApplication
    public class UserServerApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(UserServerApplication.class, args);
    	}
    }
    

    4.2 创建 ZuulFilter 过滤器

    在 gateway-server 项目中,新建一个过滤器,需要继承 ZuulFilter 类

    @Component
    public class AuthenticationFilter extends ZuulFilter {
    
    	/**
    	 * 是否开启过滤
    	 */
    	@Override
    	public boolean shouldFilter() {
    		RequestContext context = RequestContext.getCurrentContext();
            HttpServletRequest request = context.getRequest();
            
    		boolean flag = request.getRequestURI().contains("/login");
    		// 如果是登录请求不进行过滤
    		if (flag) {
    			System.out.println("========不执行 zuul 过滤方法=======");
    		} else {
    			System.out.println("========执行 zuul 过滤方法=======");
    		}
    		return !flag;
    	}
    
    	/**
    	 * 过滤器执行内容
    	 */
    	@Override
    	public Object run() throws ZuulException {
    		
    		RequestContext context = RequestContext.getCurrentContext();
            HttpServletRequest request = context.getRequest();
            String token = request.getParameter("token");
            // 此处模拟获取数据库或缓存中的 token
            String dbToken = "123456";
            // 此处简单检验 token
            if (token == null || "".equals(token) || !dbToken.equals(token)) {
            	context.setSendZuulResponse(false);
            	context.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
            }
            
    		return null;
    	}
    
    	/**
    	 * 过滤器类型
    	 */
    	@Override
    	public String filterType() {
    		return "pre";
    	}
    
    	/**
    	 * 过滤器执行顺序
    	 */
    	@Override
    	public int filterOrder() {
    		return 0;
    	}
    
    }
    

    其中,filterType 有 4 种类型:

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

    其过滤顺序如下图:

    4.3 测试过滤器

    运行所有项目,测试操作步骤如下:

    请求用户服务的登录接口(http://localhost:9600/user/user/login),请求不执行 zuul 过滤方法,并且请求响应返回的 cookie 包含 token
    
    请求订单服务的下单接口(http://localhost:9600/extlight/order/place),但不携带 token,请求需要执行 zuul 过滤方法,请求响应 401 权限不足
    
    请求订单服务的下单接口(http://localhost:9600/extlight/order/place),携带之前登录接口返回的 token,请求需要执行 zuul 过滤方法,校验通过后路由到订单服务执行之后的操作
    

    测试效果图如下:

    五、案例源码

    Zuul demo 源码

    六、参考资料

    Announcing Zuul: Edge Service in the Cloud

  • 相关阅读:
    深入浅出java IO模型
    MySQL 最基本的SQL语法/语句
    mysql sql常用语句大全
    Mysql数据库常用操作语句大全
    python3.6.1 安装PyQt5,以及配置QTDesigner,PyUIC
    Python之文件操作:os模块
    Python之OS模块函数
    Python OS模块
    Shell编程基础
    mysqldump恢复
  • 原文地址:https://www.cnblogs.com/moonlightL/p/10598469.html
Copyright © 2011-2022 走看看