问:为什么需要 API 网关服务 SpringCloud Zuul?

答:根据我们之前所学的知识,实现某个系统的功能是完全没问题的。但随着业务不断增加,随之而来以下两个问题。

1、运维人员。从运维角度来看,他们一般是要手工维护 F5 或者 Nginx 这样的设施里的路由规则和服务列表,当有实例增加或者减少或者IP地址变动的情况发生时,他们还需要手工地去同步修改这些信息,以保持实例信息与我们微服务的中间件配置内容一致。设想一下假如一个公司有100个实例需要维护,运维人员就成了“重复造轮子”的人了,而且极易出错。显然,我们需要一套机制来有效减低维护路由规则与服务实例列表的难度

2、开发人员。一个好的系统都会有一些身份、权限校验机制。有时候为了防止客户端在发起请求时被篡改等安全方面的考虑,还会有一些签名校验的机制存在。而我们实施的是微服务架构,就将原本处于一个应用中的多个模块拆分成多个应用,我们就需要在每个应用中都实现一套相同的校验逻辑。这将是一个糟糕的架构设计。所以,我们还需要一套机制能够很好的解决微服务接口访问前置校验的冗余问题

为了解决上面常见的架构问题,API 网关的概念应运而生。它的定义类似于面向对象设计模式中的 Facade 模式,相当于整个微服务架构系统的门面,所有的外部客户端访问都需要经过它来进行调度和过滤。它除了要实现请求路由负载均衡校验过滤等功能,还需要有更多的能力,比如与服务治理的结合、请求转发时的熔断机制、服务的聚合等一系列高级功能。

问:SpringCloud Zuul 如何解决上面的两个普遍问题?

答:Zuul 将自身注册为 Eureka 服务治理下的应用,同时从 EUreka 中获得所有其他微服务的实例信息。对路由规则的维护,Zuul 默认会将以服务名作为 ContextPath 的方式来创建路由映射。其次,对于类似签名的校验、登录校验在微服务中的冗余问题,理论上说,这些校验逻辑在本质上与微服务应用自身的业务并没有太多的关系,所以它们可以独立成一个单独的服务存在。它被剥离出来后,不是给各个微服务调用,而是在 API 网关服务上进行统一调用来对微服务接口做前置过滤,以实现对微服务接口的拦截和校验。Zuul 提供了一套过滤器机制,可以指定哪些规则的请求需要执行校验逻辑,只有通过校验的才会被路由到具体的微服务接口,不然就返回错误信息。这样改造,每个微服务应用就不需要进行非业务性质的校验逻辑了,可以更专注于业务逻辑的开发。所以,在微服务架构中,API 网关的使用几乎成为必然选择。

构建网关

我们先构建一个 SpringBoot 工程,起名:zuul-gateway。并在 pom.xml 中引入 spring-cloud-starter-zuul 依赖。

完整的 pom.xml 配置如下:

<?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>MyProject</artifactId><groupId>com.study</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>zuul-gateway</artifactId><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId><version>${netflix.eureka.client.version}</version></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-zuul</artifactId></dependency></dependencies></project>

等待依赖下载完毕,我们跟进 zuul 依赖,看下它的依赖内容(Ctrl + 鼠标左键):

我们看到 pom 文件是:spring-cloud-starter-netflix-2.0.2.RELEASE.pom

说明:

1、spring-cloud-starter-netflix-ribbon 依赖:该依赖用来实现在网关服务进行路由转发时候的客户端负载均衡以及请求重试。

2、spring-cloud-starter-netflix-hystrix 依赖:该依赖用来在网关服务中实现对微服务转发时候的保护机制,通过线程隔离和断路器,防止微服务的故障引发 API 网关资源无法释放,从而影响其他服务。

3、spring-boot-starter-actuator 依赖:该依赖用来提供常规的微服务管理端点。

4、spring-cloud-starter-netflix-archaius 依赖:该依赖提供对配置信息的快速及线程安全访问,可用于从许多不同来源收集配置属性的框架。

接下来,创建一个启动类:ZuulGatewayApplication,并添加 @EnableZuulProxy 注解开启 Zuul 的 API 网关服务功能。

先创建一个包 com.study,注意启动类的位置。

完整的 ZuulGatewayApplication 代码如下:

package com.study;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;/*** @author biandan* @signature 让天下没有难写的代码* @create 2019-10-27 下午 5:48*/
@EnableZuulProxy
@SpringBootApplication
@EnableDiscoveryClient
@EnableEurekaClient
public class ZuulGatewayApplication {public static void main(String[] args) {SpringApplication.run(ZuulGatewayApplication.class,args);}
}

接下来,创建 application.yml 配置文件,为了测试 zuul 的路由功能,须有路由规则的配置。我们可以使用之前的服务来做测试。具体内容如下:

server:port: 7000eureka:instance:hostname: main.study.comlease-renewal-interval-in-seconds: 30lease-expiration-duration-in-seconds: 90client:serviceUrl:defaultZone: http://${eureka.instance.hostname}:8000/eureka/# 服务的名字
spring:application:name: eureka-zuul-gateway# 路由规则的配置
zuul:routes:api-ribbon: # 针对 ribbon-consumer 服务的配置path: /api-ribbon-url/**serviceId: eureka-ribbon-clientapi-feign: # 针对 feign-consumer 服务的配置path: /api-feign-url/**serviceId: eureka-feign-client

说明:

1、端口号 = 7000(可自定义)

2、配置路由规则的时候,我们分别定义两个名为 api-ribbon 和 api-feign 的路由来映射它们,同时将自己注册成服务,同时可以获取 eureka-ribbon-clienteureka-feign-client 两个服务的实例清单,这样就可以实现 path 映射服务,最后从服务中挑选实例来进行请求转发的完整路由机制。url 中符合 /api-ribbon/** 和 /api-feign/** 规则的,才由 api-ribbon 和 api-feign 负责转发。

注意,我们先把 eureka-client 模块的 EurekaClientApplication 启动类的 sayHello() 方法的线程休眠代码注释掉(不注释的话容易导致请求超时,返回 504):

测试 zuul 的路由转发功能,我们依次启动服务:注册中心(EurekaServerApplication)、注册中心集群(EurekaServerClusterApplication 可不启动)、路由网关ZuulGatewayApplication)、服务提供者(EurekaClientApplication 9000端口、然后修改端口号=9001,再启动,一共2个服务。具体方法见之前的博客)、Ribbon服务消费者(RibbonConsumerApplication)、Feign服务消费者(FeignConsumerApplication)【考验电脑性能的时候到了】。

①我们先进入浏览器地址:http://main.study.com:8000/ 查看注册中心已注册的服务列表

②我们向 路由网关 发起以下请求(就是我们之前测试的在每个服务的 controller 定义的方法):

http://main.study.com:7000/api-ribbon-url/ribbon   和  http://main.study.com:7000/api-feign-url/feign  浏览器显示:

eureka-client-biandan 说:让天下没有难写的代码!from port =9000
eureka-client-biandan 说:让天下没有难写的代码!from port =9001

说明 Zuul 起到了网关路由的作用。

请求过滤

实现路由功能之后,我们的微服务应用接口就可以通过统一的 API 网关入口被客户端访问到了。但是每个客户端的用户请求微服务接口时,它们的权限一般都有限制,系统不想将所有的请求都对它们开放。举一个生活的简单例子:一个学校门口有保安,所有人进出校门保安都可以拦截到(放行和不放两种情况)。如果有人需要问路,保安会给他们指路,因为保安手里或者脑子里已经有整个学校的所有地点列表了。

为了实现对客户端请求的安全校验和权限控制(鉴权),比较好的做法就是将鉴权的微服务剥离出来成一个独立的服务,在这个微服务中实现鉴权的逻辑即可,在客户端的请求到达网关时进行鉴权,微服务应用就可以去除各种复杂的过滤器和拦截器了。这就是我们将要介绍的“请求过滤”。Zuul 允许开发者在 API 网关上通过过滤器来实现对请求的拦截和过滤,做法就是继承 ZuulFilter 抽象类并实现它定义的 4 个抽象函数即可

我们在 zuul-gateway 模块中,创建一个过滤器类,继承 ZuulFilter ,然后重写 4 个抽象函数。

1、创建一个包 filter,在 filter 包里创建 MyFilter

当我们写完代码 extends ZuulFilter 时,发现编译器报错,意思是我们还需要重写 4 个抽象方法。介绍一下快捷键:Alt + Enter

鼠标点到 ZuulFilter 文字,按下快捷键 Alt + Enter(这个快捷键是默认的),然后点中“Implement methods”。

这样,4 个抽象方法就被重写了。只是逻辑我们还需要处理。MyFilter 完整代码如下:

package com.study.filter;import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.servlet.http.HttpServletRequest;
import java.util.Objects;/*** @author biandan* @signature 让天下没有难写的代码* @create 2019-10-27 下午 8:06*/
public class MyFilter extends ZuulFilter {private static Logger LOGGER = LoggerFactory.getLogger(MyFilter.class);/*** 过滤器的类型,它决定过滤器在请求的哪个生命周期中进行* @return*/@Overridepublic String filterType() {return "pre";//这里定义为 pre,代表会在请求被路由之前执行,pre 是固定值。}/*** 过滤器的执行顺序,当请求在一个阶段中存在多个过滤器时,需要根据该方法返回的值依次执行* @return*/@Overridepublic int filterOrder() {return 0;}/*** 判断该过滤器是否需要被执行。* @return*/@Overridepublic boolean shouldFilter() {return true;//返回true,该过滤器对所有请求都会生效}/*** 过滤器的具体逻辑* @return* @throws ZuulException*/@Overridepublic Object run() throws ZuulException {RequestContext context = RequestContext.getCurrentContext();//获取请求上下文HttpServletRequest request = context.getRequest();//获取请求内容LOGGER.info("请求到过滤器了。request url = {},request method = {}",request.getRequestURL().toString(),request.getMethod());Object accessToken = request.getParameter("accessToken");//判断客户端传递的 accessToken 的值是否不为空且等于123if(Objects.nonNull(accessToken) && "123".equals(accessToken)){LOGGER.info("access token is ok,and accessToken ="+accessToken);}else{LOGGER.warn("access token is empty or not equals 123!");context.setSendZuulResponse(false);//设置false,表示不对其进行路由context.setResponseStatusCode(401);//自定义错误码}return null;}
}

说明:

1、filterType:返回的字符串代表过滤器的类型,Zuul 定义了 4 种不同的生命周期类型

①pre:路由之前

②routing:路由发生时

③post:路由之后

④error:发生错误调用时

2、我们在过滤器的具体逻辑里判断客户端传递的 accessToken 是否等于 123,如果不等于则返回错误信息。在实际项目中,这里的逻辑可以写得很复杂,可以是操作数据库、NoSql(非关系型数据库)、缓存等来判断权限。

3、实现了自定义的过滤器之后,它并没有直接生效,我们还需要为其创建具体的 Bean 才能启动该过滤器。我们在启动类里增加如下内容即可:

    @Beanpublic MyFilter myFilter(){return new MyFilter();}

ZuulGatewayApplication 启动类完整的代码如下:

package com.study;import com.study.filter.MyFilter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;/*** @author biandan* @signature 让天下没有难写的代码* @create 2019-10-27 下午 5:48*/
@EnableZuulProxy
@SpringBootApplication
@EnableDiscoveryClient
@EnableEurekaClient
public class ZuulGatewayApplication {public static void main(String[] args) {SpringApplication.run(ZuulGatewayApplication.class,args);}@Beanpublic MyFilter myFilter(){return new MyFilter();}
}

接下来,我们重启 zuul-gateway 服务,浏览器地址输入:

http://main.study.com:7000/api-ribbon-url/ribbon   和 http://main.study.com:7000/api-feign-url/feign

发现都返回了 401,如截图:

我们去 zuul-gateway 服务的控制台看下信息,说明都拦截了不正确的请求且返回错误信息:

接下来,我们在请求的参数里增加 accessToken=123,再次测试:

http://main.study.com:7000/api-ribbon-url/ribbon?accessToken=123

http://main.study.com:7000/api-feign-url/feign?accessToken=123

返回结果:

eureka-client-biandan 说:让天下没有难写的代码!from port =9000
eureka-client-biandan 说:让天下没有难写的代码!from port =9001

我们去 zuul-gateway 服务的控制台看下信息(打印的 url 不会把参数打印出来):

说明 API 网关对客户端的请求进行了过滤。

我们对 Zuul 的 API 网关做个小结:

1、它作为系统的统一入口,屏蔽了系统内部的其它微服务细节。

2、它可以与微服务治理 Eureka 无缝结合,实现自动化的服务实例维护以及负载均衡的路由转发

3、它可以实现接口权限校验与微服务业务的逻辑解耦分离。

4、通过服务网关中的过滤器,在各生命周期中去校验请求的内容,将原本在对外服务层做的校验前移,保证了微服务的无状态性,让服务本身更集中关注业务逻辑的处理(通俗点说,就是让微服务不需要做更多鉴权的逻辑处理)

《自学SpringCloud微服务架构》之第 10 篇 —— API 网关服务 SpringCloud Zuul相关推荐

  1. Spring Cloud Zuul API 网关服务

    API 网关是一个更为智能的应用服务器,它的定义类似于面向对象设计模式中的 Facade 模式,它的存在就像是整个微服务架构系统的门面一样,所有的外部客户端访问都需要经过它来进行调度和过滤.它除了要实 ...

  2. 最新版Spring Cloud Alibaba微服务架构-Ribbon负载均衡篇

    文章目录 前言 一.Ribbon核心概念 二.服务器端负载均衡和Riboon客户端负载均衡 1.服务器端负载均衡: 2.Riboon客户端负载均衡: 三.Ribbon策略 四.Ribbon配置使用 五 ...

  3. .NET Core微服务之基于Ocelot实现API网关服务

    一.啥是API网关? API 网关一般放到微服务的最前端,并且要让API 网关变成由应用所发起的每个请求的入口.这样就可以明显的简化客户端实现和微服务应用程序之间的沟通方式.以前的话,客户端不得不去请 ...

  4. AppsFlyer将API网关服务从Clojure迁移到Golang

    本文要点 AppsFlyer每天处理超过700亿个HTTP请求,并且是使用微服务架构风格构建.系统的入口点是一个被称为API网关的关键任务(非微型)服务,它封装了所有前端服务. 原先的API网关使用了 ...

  5. 美团技术:百亿规模API网关服务Shepherd的设计与实现

    在微服务架构下,服务拆分会让API的规模成倍增长,使用API网关来管理API逐渐成为一种趋势.美团统一API网关服务Shepherd就是在这种背景下应运而生,适用于美团业务且完全自研,用于替换传统的W ...

  6. 百亿规模API网关服务Shepherd的设计与实现

    在微服务架构下,服务拆分会让API的规模成倍增长,使用API网关来管理API逐渐成为一种趋势.美团统一API网关服务Shepherd就是在这种背景下应运而生,适用于美团业务且完全自研,用于替换传统的W ...

  7. 美团:百亿规模API网关服务Shepherd的设计与实现

    一.背景 1.1 API网关是什么? API网关是随着微服务(Microservice)概念兴起的一种架构模式.原本一个庞大的单体应用(All in one)业务系统被拆分成许多微服务(Microse ...

  8. SpringCloudZuul之API网关服务

    Spring Cloud 系列 eureka之服务治理完整过程搭建 eureka之高可用的注册中心 eureka之详解 ribbon之客户端负载均衡 ribbon之配置详解 Hystrix之服务容错保 ...

  9. spring cloud 入门系列六:使用Zuul 实现API网关服务

    通过前面几次的分享,我们了解了微服务架构的几个核心设施,通过这些组件我们可以搭建简单的微服务架构系统.比如通过Spring Cloud Eureka搭建高可用的服务注册中心并实现服务的注册和发现: 通 ...

  10. Spring Cloud Netfilx Zuul : API网关服务

    Spring Cloud Zuul 是Spring Cloud Netflix 子项目的核心组件之一,可以作为微服务架构中的API网关使用,支持动态路由与过滤功能,本文将对其用法进行详细介绍. 1.Z ...

最新文章

  1. 2022-2028年中国医疗+养老产业深度调研及投资前景预测报告
  2. 解决spring-security session超时 Ajax 请求没有重定向的问题
  3. 粒子滤波Matlab示例
  4. 程序包android.support.annotation不存在_我不知道我不了解的Redis知识
  5. Django web框架-----Django连接本地现有mysql数据库
  6. pinfinder开源下载_BayesianNetworktool
  7. Spring Boot+JPA 有查询条件的查询
  8. 《浪潮之巅》吴军:特斯拉自动驾驶堪比中甲水平,全球5G看好华为
  9. mysql semi join_技术分享 | MySQL 子查询优化
  10. arch linux界面优化,ArchLinux美化之界面美化
  11. FFmpeg的编解码(二)
  12. IMX8mp alsa音频调试
  13. LordPe dump进程内存实现
  14. Qt之布局Layout的应用
  15. 中图杯获奖作品计算机组,地理奥赛网-首页
  16. 最小生成树 刘汝佳模板
  17. Python数据可视化,Pyecharts库,国家卫健委疫情风险等级数据可视化
  18. excel冻结窗格线的设置问题
  19. 系统调用的概念及原理
  20. 【网络】TOE、RDMA、smartNIC 是什么和区别|DPU

热门文章

  1. cecore.cls.php 08cms,08cms小说系统 v1.0PHP CMS源码下载-华软网
  2. 前端数据可视化—Echart——笔记整理
  3. Windows 10聚焦怎么用?教你玩转Windows聚焦功能
  4. 台式计算机模拟软件,全国计算机一级Office2010+win7版考试模拟软件
  5. dtcms分页含多个参数,多个参数分页
  6. 在OpenWrt系统的路由器NETGEAR WNDR4300上安装KMS服务器vlmcsd
  7. OpenWrt搭建KMS服务(Vlmcsd)
  8. hdu 5053 the Sum of Cube(水)
  9. Win10自带超级截屏利器
  10. 捷速pdf修改器如何在pdf中添加附件