一、前言
Zuul
是netflix
开源的一个API
网关服务器, 其本质上是一个web servlet
应用。Zuul
是在云平台上提供动态路由
,监控
,弹性
,安全
等边缘服务的框架。Zuul
相当于是PC
、APP
、H5
等客户端和 Netflix
流应用的 Web
网站后端所有请求的前门,Zuul
是Spring Cloud
提供的api
网关和过滤组件,网关将所有服务的API
接口统一聚合,统一对外暴露。外界调用API接口时,不需要知道微服务系统中各服务相互调用的复杂性和具体的API
接口调用地址,保护了内部微服务单元的API
接口;网关可以做用户身份认证和权限认证,防止非法请求操作API
接口;网关可以实现监控功能,实时日志输出,对请求进行记录;网关可以实现流量监控,在高流量的情况下,对服务降级;API
接口从内部服务分离出来,方便做测试。
总而言之Zuul
具有以下功能:
- 认证
- 过滤
- 压力测试
- Canary测试
- 动态路由
- 服务迁移
- 负载均衡
- 安全
- 静态请求处理
- 动态流量管理
二、Zuul网关的作用
网关通常可以做的事情有以下几个点:
- API服务的统一入口: 为全部为服务提供一个唯一的入口,网关起到外部和内部隔离的作用,保障了后台服务的安全性。
- 鉴权校验、验证登录: 识别每个请求的权限,拒绝不符合要求的请求。
- 动态路由: 动态的将请求路由到不同的后端集群中。
- 减少客户端与服务端的耦合: 服务可以独立发展,通过网关层来做映射。
- 日志拦截: 进行核心日志统一拦截跟踪记录。
- 解决跨域: 前后端分离常见跨域问题。
- 反向代理: 不暴露具体API服务的接口调用真实IP地址。
- 黑名单、白名单过滤: 实现黑名单和白名单过滤、管理。
- 负载均衡: 实现对服务负载均衡。
- 限流熔断: 服务限流、熔断降级隔离处理
网图借鉴度娘上的文章:SpringCloud之Zuul网关原理及其配置
三、Nginx与Zuul的区别
Nginx
是采用服务器负载均衡进行转发Zuul
是依赖Ribbon
和eureka
实现本地负载均衡转发- 二者相对来说
Nginx
功能比Zuul
功能更加强大,能够整合其他语言比如lua脚本
实现强大的功能 Nginx
可以更好的抗高并发,Zuul
网关适用于请求过滤和拦截等。
在SpringCloud
中想要使用Zuul
网关服务组件的话,只需引入一个依赖组件即可:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
Zuul
本质上是通过Servlet
来实现,通过自定义的ZuulServlet
来对请求进行控制。核心是一系列过滤器,可以在Http
请求的发起和响应返回期间执行一系列过滤器。Zuul
采取了动态读取、编译和运行这些过滤器。过滤器之间不能直接通信,而是通过RequestContext
对象来共享数据,每个请求都会创建一个RequestContext
对象。
四、搭建Zuul网关拦截请求头Token鉴权
基于前面第四节搭建的环境,我们继续使用前面搭建好的工程,作为本章学习:
- eureka注册中心工程:
springcloud-eureka-server
- 服务提供者工程:
springcloud-app-member
- 服务消费者工程:
springcloud-feign-client
然后我们再搭建一个基于Zuul
的网关路由服务工程,Zuul
网关单独作为一个微服务工程模块,因此我们再新建一个项目springcloud-zuul_gateway
,然后在Zuul
网关服务里实现Zuul
网关拦截请求头的Token
参数,实现鉴权,没有传递token
参数在请求头的话,则返回401
,无权操作的结果,如果有token
参数,则直接放行去执行其他业务逻辑代码,这个过程由Feign
客户端调用其他微服务接口。
五、搭建环境
1. 引入springcloud-feign-client
工程依赖
<!--SpringBoot依赖版本-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.thinkingcao</groupId>
<artifactId>springcloud-zuul_gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springcloud-zuul_gateway</name>
<description>SpringCloud整合Zuul实现APi网关拦截请求</description>
<!--项目编码、jdk版本、SpringCloud版本定义-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>
<!--声明管理SpringCloud版本依赖信息-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${
spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- SpringBootWeb组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- springcloud整合eureka客户端组件 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- SpringCloud整合Zuul APi网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
</dependencies>
<!--maven插件-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2.配置application.yml
文件
关于zuul
配置文件信息那一块,application.yml
里写的很详细了,这里就不在做过多解释,如果不清楚的可以在文章末尾评论,我每天都会看,回复给你们;
##服务端口配置
server:
port: 9999
#定义网关服务名称(服务注册到eureka名称)
spring:
application:
name: zuul-gateway-service
#解决响应给客户端浏览器中文乱码问题
http:
encoding:
charset: UTF-8
enabled: true
force: true
#在此指定服务注册中心地址,将当前订单服务注册到eureka注册中心上
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:9000/eureka
#启动注册操作,该值默认为true。若设置为fasle将不会启动注册操作。是否需要去检索寻找服务,默认是true
register-with-eureka: true
#是否需要从eureka上获取注册信息
fetch-registry: true
#表示eureka client间隔多久去拉取服务器注册信息,默认为30秒
registry-fetch-interval-seconds: 10
##心跳检测与续约时间(测试环境和本地开发环境将值设置小一点,保证服务关闭后,注册中心能够及时踢出)
instance:
#客户端向Eureka注册中心发送心跳的时间间隔,单位为秒(默认为30s),(客户端会按照此规则向Eureka服务端发送心跳检测包)
lease-renewal-interval-in-seconds: 2
#Eureka注册中心在收到客户端最后一次心跳之后等待的时间上限,单位为秒(默认为90s),超过时间则剔除(客户端会按照此规则向Eureka服务端发送心跳检测包)
lease-expiration-duration-in-seconds: 2
##配置Zuul网关反向代理
zuul:
routes:
#定义Zuul网关转发的服务规则api-a和api-b等(这个规则名可以随便取)
api-a:
#以/api-member/ 的访问请求, URL: 127.0.0.1:9999/zuul-api-member/ ,会转发到会员服务(服务提供者)
path: /zuul-api-member/**
# 指定服务别名,表示需要转发到哪个服务
serviceId: app-thinkingcao-member
#定义Zuul网关转发的服务规则api-a
api-b:
# 以/api-order/ 的访问请求, URL: 127.0.0.1:9999/zuul-api-order/, 会转发到订单服务(服务消费者)
path: /zuul-api-order/**
# 指定服务别名,表示需要转发到哪个服务
serviceId: app-thinkingcao-feign
3.修改ZuulGatewayApp
启动类
@EnableZuulProxy: 表示开启
Zuul
网关代理功能@EnableEurekaClient: 表示开启
Eureka
客户端像Eureka
服务端注册与发现服务功能@EnableZuulServer: ,另外还有个
@EnableZuulServer
注解,这里解释下:@EnableZuulProxy
简单理解为@EnableZuulServer
的增强版,当Zuul
与Eureka
、Ribbon
等组件配合使用时,我们使用@EnableZuulProxy
。package com.thinkingcao.api;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication @EnableEurekaClient @EnableZuulProxy public class ZuulGatewayApp {
public static void main(String[] args) { SpringApplication.run(ZuulGatewayApp.class, args); }
}
4.编写AuthorizationFilter
鉴权请求获取Token
package com.thinkingcao.api.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
* @desc: 网关Filter过滤器
* @author: cao_wencao
* @date: 2020-03-02 11:47
*/
@Component
public class AuthorizationFilter extends ZuulFilter {
//指定过滤器的类型
// pre :可以在请求被路由之前调用
// route :在路由请求时候被调用
// post :在route和error过滤器之后被调用
// 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 {
// 案例:拦截所有的服务接口,判断所有的APi请求,请求头中是否有携带Token参数,用Authorization字段传递
// 1.获取上下文
RequestContext currentContext = RequestContext.getCurrentContext();
// 2.获取 Request
HttpServletRequest request = currentContext.getRequest();
// 3.获取token 的时候 从请求头中获取
String token = currentContext .getRequest().getHeader("Authorization");
if (StringUtils.isEmpty(token)) {
// 不会继续执行... 不会去调用服务接口,网关服务直接响应给客户端
currentContext.setSendZuulResponse(false);
currentContext.setResponseBody("Token参数为空,请先登录后再执行操作");
currentContext.setResponseStatusCode(401);
//解决响应给客户端浏览器中文乱码问题
currentContext.getResponse().setContentType("text/html;chatset=utf-8");
return null;
// 返回一个错误提示
}
// 正常则执行调用其他服务接口...
return null;
}
}
六、开始测试
1. 依次启动服务
AppEurekaServer
(注册中心)——>AppMemberProvider
(服务提供者)——>AppFeignClient
(服务消费者)——>ZuulGatewayApp
(网关服务)
2. 访问Eureka
3. 通过网关入口调用会员服务
a. 不传Token参数
URL: http://127.0.0.1:9999/zuul-api-member/getMember
a. 传Token参数- URL: http://127.0.0.1:9999/zuul-api-member/getMember
- 在
Authorization
里面放入token
的值
4. 通过Feign客户端消费服务
a. 不传Token参数
a. 传Token参数
5. 基于Feign消费服务实现本地负载均衡效果
为了实现负载均衡的效果,上面已经开启了4个微服务工程,这个时候我们将服务提供者AppMemberProvider
设置为允许启动多个实例,然后再启动一个端口为8767
的实例,这就实现了服务提供者的集群效果,如下:
a. 传Token参数
Postman工具交替显示响应结果如下:
我是会员服务,订单服务调用会员服务成功啦, 端口号为: 8765
我是会员服务,订单服务调用会员服务成功啦, 端口号为: 8767
七、源码
1. 源码: https://github.com/Thinkingcao/SpringCloudLearning/tree/master/springcloud-zuul
八、SpringCloud系列教程
1. 下一节: Spring Cloud系列教程(十):分布式配置中心Config(Finchley版本)
SpringCloud教程汇总: Spring Cloud系列教程(汇总篇):专栏汇总篇(持续更新中)
本文同步分享在 博客“Thinkingcao”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。