Spring Cloud系列教程(九):服务网关Zuul(Finchley版本)

一、前言

Zuulnetflix开源的一个API网关服务器, 其本质上是一个web servlet应用。Zuul 是在云平台上提供动态路由监控弹性安全等边缘服务的框架。Zuul相当于是PCAPPH5等客户端和 Netflix 流应用的 Web网站后端所有请求的前门,ZuulSpring Cloud提供的api网关和过滤组件,网关将所有服务的API接口统一聚合,统一对外暴露。外界调用API接口时,不需要知道微服务系统中各服务相互调用的复杂性和具体的API接口调用地址,保护了内部微服务单元的API接口;网关可以做用户身份认证和权限认证,防止非法请求操作API接口;网关可以实现监控功能,实时日志输出,对请求进行记录;网关可以实现流量监控,在高流量的情况下,对服务降级;API接口从内部服务分离出来,方便做测试。
总而言之Zuul具有以下功能:

  • 认证
  • 过滤
  • 压力测试
  • Canary测试
  • 动态路由
  • 服务迁移
  • 负载均衡
  • 安全
  • 静态请求处理
  • 动态流量管理

二、Zuul网关的作用

网关通常可以做的事情有以下几个点:

  • API服务的统一入口: 为全部为服务提供一个唯一的入口,网关起到外部和内部隔离的作用,保障了后台服务的安全性。
  • 鉴权校验、验证登录: 识别每个请求的权限,拒绝不符合要求的请求。
  • 动态路由: 动态的将请求路由到不同的后端集群中。
  • 减少客户端与服务端的耦合: 服务可以独立发展,通过网关层来做映射。
  • 日志拦截: 进行核心日志统一拦截跟踪记录。
  • 解决跨域: 前后端分离常见跨域问题。
  • 反向代理: 不暴露具体API服务的接口调用真实IP地址。
  • 黑名单、白名单过滤: 实现黑名单和白名单过滤、管理。
  • 负载均衡: 实现对服务负载均衡。
  • 限流熔断: 服务限流、熔断降级隔离处理
    在这里插入图片描述
    网图借鉴度娘上的文章:SpringCloud之Zuul网关原理及其配置

三、Nginx与Zuul的区别

  • Nginx是采用服务器负载均衡进行转发
  • Zuul是依赖Ribboneureka实现本地负载均衡转发
  • 二者相对来说Nginx功能比Zuul功能更加强大,能够整合其他语言比如lua脚本实现强大的功能
  • Nginx可以更好的抗高并发,Zuul网关适用于请求过滤和拦截等。

SpringCloud中想要使用Zuul网关服务组件的话,只需引入一个依赖组件即可:

1 <dependency> 2 <groupId>org.springframework.cloud</groupId> 3 <artifactId>spring-cloud-starter-netflix-zuul</artifactId> 4 </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工程依赖

1 <!--SpringBoot依赖版本--> 2 <parent> 3 <groupId>org.springframework.boot</groupId> 4 <artifactId>spring-boot-starter-parent</artifactId> 5 <version>2.0.3.RELEASE</version> 6 <relativePath/> 7 </parent> 8 9 <groupId>com.thinkingcao</groupId> 10 <artifactId>springcloud-zuul_gateway</artifactId> 11 <version>0.0.1-SNAPSHOT</version> 12 <name>springcloud-zuul_gateway</name> 13 <description>SpringCloud整合Zuul实现APi网关拦截请求</description> 14 15 <!--项目编码、jdk版本、SpringCloud版本定义--> 16 <properties> 17 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 18 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> 19 <java.version>1.8</java.version> 20 <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> 21 </properties> 22 23 <!--声明管理SpringCloud版本依赖信息--> 24 <dependencyManagement> 25 <dependencies> 26 <dependency> 27 <groupId>org.springframework.cloud</groupId> 28 <artifactId>spring-cloud-dependencies</artifactId> 29 <version>${ 30 31 32 spring-cloud.version}</version> 33 <type>pom</type> 34 <scope>import</scope> 35 </dependency> 36 </dependencies> 37 </dependencyManagement> 38 39 <dependencies> 40 <!-- SpringBootWeb组件 --> 41 <dependency> 42 <groupId>org.springframework.boot</groupId> 43 <artifactId>spring-boot-starter-web</artifactId> 44 </dependency> 45 46 <!-- springcloud整合eureka客户端组件 --> 47 <dependency> 48 <groupId>org.springframework.cloud</groupId> 49 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 50 </dependency> 51 52 <!-- SpringCloud整合Zuul APi网关--> 53 <dependency> 54 <groupId>org.springframework.cloud</groupId> 55 <artifactId>spring-cloud-starter-netflix-zuul</artifactId> 56 </dependency> 57 </dependencies> 58 59 <!--maven插件--> 60 <build> 61 <plugins> 62 <plugin> 63 <groupId>org.springframework.boot</groupId> 64 <artifactId>spring-boot-maven-plugin</artifactId> 65 </plugin> 66 </plugins> 67 </build>

2.配置application.yml文件

关于zuul配置文件信息那一块,application.yml里写的很详细了,这里就不在做过多解释,如果不清楚的可以在文章末尾评论,我每天都会看,回复给你们;

1##服务端口配置 2server: 3 port: 9999 4 5#定义网关服务名称(服务注册到eureka名称) 6spring: 7 application: 8 name: zuul-gateway-service 9 #解决响应给客户端浏览器中文乱码问题 10 http: 11 encoding: 12 charset: UTF-8 13 enabled: true 14 force: true 15 16 17#在此指定服务注册中心地址,将当前订单服务注册到eureka注册中心上 18eureka: 19 client: 20 service-url: 21 defaultZone: http://127.0.0.1:9000/eureka 22 #启动注册操作,该值默认为true。若设置为fasle将不会启动注册操作。是否需要去检索寻找服务,默认是true 23 register-with-eureka: true 24 #是否需要从eureka上获取注册信息 25 fetch-registry: true 26 #表示eureka client间隔多久去拉取服务器注册信息,默认为3027 registry-fetch-interval-seconds: 10 28 29 ##心跳检测与续约时间(测试环境和本地开发环境将值设置小一点,保证服务关闭后,注册中心能够及时踢出) 30 instance: 31 #客户端向Eureka注册中心发送心跳的时间间隔,单位为秒(默认为30s),(客户端会按照此规则向Eureka服务端发送心跳检测包) 32 lease-renewal-interval-in-seconds: 2 33 #Eureka注册中心在收到客户端最后一次心跳之后等待的时间上限,单位为秒(默认为90s),超过时间则剔除(客户端会按照此规则向Eureka服务端发送心跳检测包) 34 lease-expiration-duration-in-seconds: 2 35 36##配置Zuul网关反向代理 37zuul: 38 routes: 39 #定义Zuul网关转发的服务规则api-a和api-b等(这个规则名可以随便取) 40 api-a: 41 #以/api-member/ 的访问请求, URL: 127.0.0.1:9999/zuul-api-member/ ,会转发到会员服务(服务提供者) 42 path: /zuul-api-member/** 43 # 指定服务别名,表示需要转发到哪个服务 44 serviceId: app-thinkingcao-member 45 #定义Zuul网关转发的服务规则api-a 46 api-b: 47 # 以/api-order/ 的访问请求, URL: 127.0.0.1:9999/zuul-api-order/, 会转发到订单服务(服务消费者) 48 path: /zuul-api-order/** 49 # 指定服务别名,表示需要转发到哪个服务 50 serviceId: app-thinkingcao-feign

3.修改ZuulGatewayApp启动类

  • @EnableZuulProxy: 表示开启Zuul网关代理功能

  • @EnableEurekaClient: 表示开启Eureka客户端像Eureka服务端注册与发现服务功能

  • @EnableZuulServer: ,另外还有个@EnableZuulServer注解,这里解释下:@EnableZuulProxy简单理解为@EnableZuulServer的增强版,当ZuulEurekaRibbon等组件配合使用时,我们使用@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 {

    1public static void main(String[] args) { 2 3 4 5 SpringApplication.run(ZuulGatewayApp.class, args); 6}

    }

4.编写AuthorizationFilter鉴权请求获取Token

1package com.thinkingcao.api.filter; 2 3import com.netflix.zuul.ZuulFilter; 4import com.netflix.zuul.context.RequestContext; 5import com.netflix.zuul.exception.ZuulException; 6import org.apache.commons.lang.StringUtils; 7import org.springframework.stereotype.Component; 8 9import javax.servlet.http.HttpServletRequest; 10 11/** 12 * @desc: 网关Filter过滤器 13 * @author: cao_wencao 14 * @date: 2020-03-02 11:47 15 */ 16@Component 17public class AuthorizationFilter extends ZuulFilter { 18 19 20 21 22 //指定过滤器的类型 23 // pre :可以在请求被路由之前调用 24 // route :在路由请求时候被调用 25 // post :在route和error过滤器之后被调用 26 // error :处理请求时发生错误时被调用 27 @Override 28 public String filterType() { 29 30 31 32 return "pre"; 33 } 34 35 //过滤器的执行顺序,数值越小,优先级越高。当一个请求在同一阶段的时候存在多个过滤器的时候,通过数值表示过滤器的执行顺序 36 @Override 37 public int filterOrder() { 38 39 40 41 return 0; 42 } 43 44 //是否执行该过滤器,true 为执行,false 为不执行,这个也可以利用配置中心来实现,达到动态的开启和关闭过滤器。 45 @Override 46 public boolean shouldFilter() { 47 48 49 50 return true; 51 } 52 53 //执行自己的业务逻辑,编写业务逻辑代码 54 @Override 55 public Object run() throws ZuulException { 56 57 58 59 // 案例:拦截所有的服务接口,判断所有的APi请求,请求头中是否有携带Token参数,用Authorization字段传递 60 // 1.获取上下文 61 RequestContext currentContext = RequestContext.getCurrentContext(); 62 // 2.获取 Request 63 HttpServletRequest request = currentContext.getRequest(); 64 // 3.获取token 的时候 从请求头中获取 65 String token = currentContext .getRequest().getHeader("Authorization"); 66 if (StringUtils.isEmpty(token)) { 67 68 69 70 // 不会继续执行... 不会去调用服务接口,网关服务直接响应给客户端 71 currentContext.setSendZuulResponse(false); 72 currentContext.setResponseBody("Token参数为空,请先登录后再执行操作"); 73 currentContext.setResponseStatusCode(401); 74 //解决响应给客户端浏览器中文乱码问题 75 currentContext.getResponse().setContentType("text/html;chatset=utf-8"); 76 return null; 77 // 返回一个错误提示 78 } 79 // 正常则执行调用其他服务接口... 80 return null; 81 } 82}

六、开始测试

1. 依次启动服务

AppEurekaServer(注册中心)——>AppMemberProvider(服务提供者)——>AppFeignClient(服务消费者)——>ZuulGatewayApp(网关服务)
在这里插入图片描述

2. 访问Eureka

在这里插入图片描述

3. 通过网关入口调用会员服务

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源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )