文章大纲

一、Zuul是什么
二、Zuul的基本实现
三、路由配置细节
四、异常处理细节
五、项目源码与参考资料下载
六、参考文章

 

一、Zuul是什么

  到目前为止,我们Spring Cloud中的内容已经介绍了很多了,Ribbon、Hystrix、Feign这些知识点大家都耳熟能详了,我们在前文也提到过微服务就是把一个大的项目拆分成很多小的独立模块,然后通过服务治理让这些独立的模块配合工作等。那么大家来想这样两个问题:1.如果我的微服务中有很多个独立服务都要对外提供服务,那么对于开发人员或者运维人员来说,他要如何去管理这些接口?特别是当项目非常大非常庞杂的情况下要如何管理?2.权限管理也是一个老生常谈的问题,在微服务中,一个独立的系统被拆分成很多个独立的模块,为了确保安全,我难道需要在每一个模块上都添加上相同的鉴权代码来确保系统不被非法访问?如果是这样的话,那么工作量就太大了,而且维护也非常不方便。
  为了解决上面提到的问题,我们引入了API网关的概念,API网关是一个更为智能的应用服务器,它有点类似于我们微服务架构系统的门面,所有的外部访问都要先经过API网关,然后API网关来实现请求路由、负载均衡、权限验证等功能。Spring Cloud中提供的Spring Cloud Zuul实现了API网关的功能,本文我们就先来看看Spring Cloud Zuul的一个基本使用。

二、Zuul的基本实现

1. 创建Spring Boot项目

项目名称为:api-gateway

 
 
 
 

创建后项目结构如下:

 

2. pom.xml文件添加依赖

这里我们主要添加两个依赖spring-cloud-starter-zuul和spring-cloud-starter-eureka,spring-cloud-starter-zuul依赖中则包含了ribbon、hystrix、actuator等

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <groupId>com.wxc</groupId>
  7. <artifactId>api-gateway</artifactId>
  8. <version>1.0-SNAPSHOT</version>
  9. <parent>
  10. <groupId>org.springframework.boot</groupId>
  11. <artifactId>spring-boot-starter-parent</artifactId>
  12. <version>1.5.7.RELEASE</version>
  13. <relativePath/> <!-- lookup parent from repository -->
  14. </parent>
  15. <properties>
  16. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  17. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  18. <java.version>1.8</java.version>
  19. <spring-cloud.version>Dalston.SR3</spring-cloud.version>
  20. </properties>
  21. <dependencies>
  22. <dependency>
  23. <groupId>org.springframework.boot</groupId>
  24. <artifactId>spring-boot-starter</artifactId>
  25. </dependency>
  26. <dependency>
  27. <groupId>org.springframework.cloud</groupId>
  28. <artifactId>spring-cloud-starter-zuul</artifactId>
  29. </dependency>
  30. <dependency>
  31. <groupId>org.springframework.cloud</groupId>
  32. <artifactId>spring-cloud-starter-eureka</artifactId>
  33. </dependency>
  34. </dependencies>
  35. <dependencyManagement>
  36. <dependencies>
  37. <dependency>
  38. <groupId>org.springframework.cloud</groupId>
  39. <artifactId>spring-cloud-dependencies</artifactId>
  40. <version>${spring-cloud.version}</version>
  41. <type>pom</type>
  42. <scope>import</scope>
  43. </dependency>
  44. </dependencies>
  45. </dependencyManagement>
  46. </project>

3. 静态资源添加配置

application.properties添加以下配置,application.properties文件中的配置可以分为两部分,一部分是Zuul应用的基础信息,还有一部分则是路由规则,如下:

  1. # 基础信息配置
  2. spring.application.name=api-gateway
  3. server.port=2006
  4. # 路由规则配置
  5. zuul.routes.api-a.path=/api-a/**
  6. zuul.routes.api-a.serviceId=feign-consumer
  7. # API网关也将作为一个服务注册到eureka-server上
  8. eureka.client.service-url.defaultZone=http://peer1:1111/eureka

我们在这里配置了路由规则所有符合/api-a/**的请求都将被转发到feign-consumer服务上,至于feign-consumer服务的地址到底是什么则由eureka-server去分析,我们这里只需要写上服务名即可。以上面的配置为例,如果我请求http://localhost:2006/api-a/hello1接口则相当于请求http://localhost:2005/hello1(我这里feign-consumer的地址为http://localhost:2005),我们在路由规则中配置的api-a是路由的名字,可以任意定义,但是一组path和serviceId映射关系的路由名要相同。

4. 创建项目启动入口类

com.wxc.test包下创建ApiGatewayApplication.java,入口类上添加@EnableZuulProxy注解表示开启Zuul的API网关服务功能

  1. package com.wxc.test;
  2. import com.wxc.test.filter.PermisFilter;
  3. import org.springframework.boot.SpringApplication;
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;
  5. import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
  6. import org.springframework.context.annotation.Bean;
  7. @SpringBootApplication
  8. @EnableZuulProxy
  9. public class ApiGatewayApplication {
  10. public static void main(String[] args) {
  11. //测试访问地址:http://localhost:2006/api-a/hello1
  12. SpringApplication.run(ApiGatewayApplication.class, args);
  13. }
  14. //配置过滤器
  15. @Bean
  16. PermisFilter permisFilter() {
  17. return new PermisFilter();
  18. }
  19. }

5. 添加请求过滤

构建好了网关,接下来我们就来看看如何利用网关来实现一个简单的权限验证。这里就涉及到了Spring Cloud Zuul中的另外一个核心功能:请求过滤。请求过滤有点类似于Java中Filter过滤器,先将所有的请求拦截下来,然后根据现场情况做出不同的处理,这里我们就来看看Zuul中的过滤器要如何使用。很简单,两个步骤:
5.1 定义过滤器
com.wxc.test.filter包下创建PermisFilter.java

  1. package com.wxc.test.filter;
  2. import com.netflix.zuul.ZuulFilter;
  3. import com.netflix.zuul.context.RequestContext;
  4. import javax.servlet.http.HttpServletRequest;
  5. /**
  6. * 1.filterType方法的返回值为过滤器的类型,过滤器的类型决定了过滤器在哪个生命周期执行,pre表示在路由之前执行过滤器,
  7. * 其他可选值还有post、error、route和static,当然也可以自定义。
  8. * 2.filterOrder方法表示过滤器的执行顺序,当过滤器很多时,这个方法会有意义。
  9. * 3.shouldFilter方法用来判断过滤器是否执行,true表示执行,false表示不执行,在实际开发中,
  10. * 我们可以根据当前请求地址来决定要不要对该地址进行过滤,这里我直接返回true。
  11. * 4.run方法则表示过滤的具体逻辑,假设请求地址中携带了login参数的话,则认为是合法请求,否
  12. * 则就是非法请求,如果是非法请求的话,首先设置ctx.setSendZuulResponse(false);表示不对该请求进行路由,然后设置响应码和响应值。
  13. * 这个run方法的返回值在当前版本(Dalston.SR3)中暂时没有任何意义,可以返回任意值。
  14. */
  15. public class PermisFilter extends ZuulFilter {
  16. @Override
  17. public String filterType() {
  18. return "pre";
  19. }
  20. @Override
  21. public int filterOrder() {
  22. return 0;
  23. }
  24. @Override
  25. public boolean shouldFilter() {
  26. return true;
  27. }
  28. @Override
  29. public Object run() {
  30. RequestContext ctx = RequestContext.getCurrentContext();
  31. HttpServletRequest request = ctx.getRequest();
  32. String login = request.getParameter("login");
  33. if (login == null) {
  34. ctx.setSendZuulResponse(false);
  35. ctx.setResponseStatusCode(401);
  36. ctx.addZuulResponseHeader("content-type","text/html;charset=utf-8");
  37. ctx.setResponseBody("非法访问");
  38. }
  39. return null;
  40. }
  41. }

温馨提示:
(1)filterType方法的返回值为过滤器的类型,过滤器的类型决定了过滤器在哪个生命周期执行,pre表示在路由之前执行过滤器,其他可选值还有post、error、route和static,当然也可以自定义。
(2)filterOrder方法表示过滤器的执行顺序,当过滤器很多时,这个方法会有意义。
(3)shouldFilter方法用来判断过滤器是否执行,true表示执行,false表示不执行,在实际开发中,我们可以根据当前请求地址来决定要不要对该地址进行过滤,这里我直接返回true。
(4)run方法则表示过滤的具体逻辑,假设请求地址中携带了login参数的话,则认为是合法请求,否则就是非法请求,如果是非法请求的话,首先设置ctx.setSendZuulResponse(false);表示不对该请求进行路由,然后设置响应码和响应值。这个run方法的返回值在当前版本(Dalston.SR3)中暂时没有任何意义,可以返回任意值。

5.2 配置过滤器Bean
在项目入口类ApiGatewayApplication.java中配置

  1. @Bean
  2. PermisFilter permisFilter() {
  3. return new PermisFilter();
  4. }

6. 启动项目并访问

启动项目之前,确保服务注册中心、服务提供者、服务消费者都已开启,之后运行Zuul项目

 

浏览器输入http://localhost:2006/api-a/hello1进行访问,出现以下结果,代表网关的拦截已生效

 

三、路由配置细节

1. 简介

上面内容我们介绍了API网关的基本构建方式以及请求过滤,小伙伴们对Zuul的作用应该已经有了一个基本的认识,但是对于路由的配置我们只是做了一个简单的介绍,本文我们就来看看路由配置的其他一些细节。

2. 配置详解

首先我们来回忆一下上篇文章我们配置路由规则的那两行代码:

  1. zuul.routes.api-a.path=/api-a/**
  2. zuul.routes.api-a.serviceId=feign-consumer

我们说当我的访问地址符合/api-a/**规则的时候,会被自动定位到feign-consumer服务上去,不过两行代码有点麻烦,我们可以用下面一行代码来代替,如下:

  1. zuul.routes.feign-consumer=/api-a/**

zuul.routes后面跟着的是服务名,服务名后面跟着的是路径规则,这种配置方式显然更简单。
如果映射规则我们什么都不写,系统也给我们提供了一套默认的配置规则,默认的配置规则如下:

  1. zuul.routes.feign-consumer.path=/feign-consumer/**
  2. zuul.routes.feign-consumer.serviceId=feign-consumer

默认情况下,Eureka上所有注册的服务都会被Zuul创建映射关系来进行路由,但是对于我这里的例子来说,我希望提供服务的是feign-consumer,hello-service作为服务提供者只对服务消费者提供服务,不对外提供服务,如果使用默认的路由规则,则Zuul也会自动为hello-service创建映射规则,这个时候我们可以采用如下方式来让Zuul跳过hello-service服务,不为其创建路由规则:

  1. zuul.ignored-services=hello-service

有的小伙伴可能为有疑问,我们定义路由规则/api-a/*的时候,为什么最后面是两个,一个可不可以呢?当然可以,不过意义可就不一样了,Zuul中的路由匹配规则使用了Ant风格定义,一共有三种不同的通配符:

 

有的时候我们还会遇到这样一个问题,比如我有两个服务,一个叫做feign-consumer,还有一个叫做feign-consumer-hello,此时我的路由配置规则可能这样来写:

  1. zuul.routes.feign-consumer.path=/feign-consumer/**
  2. zuul.routes.feign-consumer.serviceId=feign-consumer
  3. zuul.routes.feign-consumer-hello.path=/feign-consumer/hello/**
  4. zuul.routes.feign-consumer-hello.serviceId=feign-consumer-hello

此时我访问feign-consumer-hello的路径会同时被这两条规则所匹配,Zuul中的路径匹配方式是一种线性匹配方式,即按照路由匹配规则的存储顺序依次匹配,因此我们只需要确保feign-consumer-hello的匹配规则被先定义feign-consumer的匹配规则被后定义即可,但是在properties文件中我们不能保证这个先后顺序,此时我们需要用YAML来配置,这个时候我们可以删掉resources文件夹下的application.properties,然后新建一个application.yml,内容如下:

  1. spring:
  2. application:
  3. name: api-gateway
  4. server:
  5. port: 2006
  6. zuul:
  7. routes:
  8. feign-consumer-hello:
  9. path: /feign-consumer/hello/**
  10. serviceId: feign-consumer-hello
  11. feign-consumer:
  12. path: /feign-consumer/**
  13. serviceId: feign-consumer
  14. eureka:
  15. client:
  16. service-url:
  17. defaultZone: http://localhost:1111/eureka/

这个时候我们就可以确保先加载feign-consumer-hello的匹配规则,后加载feign-consumer的匹配规则。

上文我们说了一个zuul.ignored-services=hello-service属性可以忽略掉一个服务,不给某个服务设置映射规则,这个配置我们可以进一步细化,比如说我不想给/hello接口路由,那我们可以按如下方式配置(后面我都用yaml配置):

  1. zuul:
  2. ignored-patterns: /**/hello/**

此时访问/hello接口就会报404错误,同时我们也可以看到后台打印如下日志:

 

此外,我们也可以统一的为路由规则增加前缀,设置方式如下:

  1. zuul:
  2. prefix: /myapi

此时我们的访问路径就变成了http://localhost:2006/myapi/feign-consumer/hello1

一般情况下API网关只是作为系统的统一入口,但是有的时候我们可能也需要在API网关上做一点业务逻辑操作,比如我现在在api-gateway项目中新建如下Controller:

  1. @RestController
  2. public class HelloController {
  3. @RequestMapping("/local")
  4. public String hello() {
  5. return "hello api gateway";
  6. }
  7. }

我希望用户在访问/local时能够自动跳转到这个方法上来处理,那么此时我们需要用到Zuul的本地跳转,配置方式如下:

  1. zuul:
  2. prefix: /myapi
  3. ignored-patterns: /**/hello/**
  4. routes:
  5. local:
  6. path: /local/**
  7. url: forward:/local

此时访问http://localhost:2006/myapi/local结果如下:

 

我们在使用Nginx的时候,会涉及到一个请求头信息的配置,防止页面重定向后跳转到上游服务器上去,这个问题在Zuul中一样存在,假设我的feign-consumer中提供了一个接口/hello4,当访问/hello4接口的时候,页面重定向到/hello,默认情况下,重定向的地址是具体的服务实例的地址,而不是API网关的跳转地址,这种做法会暴露真实的服务地址,所以需要在Zuul中配置,配置方式很简单,如下:

  1. zuul:
  2. add-host-header: true

表示API网关在进行请求路由转发前为请求设置Host头信息。
默认情况下,敏感的头信息无法经过API网关进行传递,我们可以通过如下配置使之可以传递:

  1. zuul:
  2. routes:
  3. feign-consumer:
  4. sensitiveHeaders:

在Zuul中,Ribbon和Hystrix的配置还是和之前的配置方式一致,这里我就不赘述了,如果我们想关闭Hystrix重试机制,可以通过如下方式:
关闭全局重试机制:

  1. zuul:
  2. retryable: false

关闭某一个服务的重试机制:

  1. zuul:
  2. routes:
  3. feign-consumer:
  4. retryable: false

四、异常处理细节

https://mp.weixin.qq.com/s/Y73q8XrVoEuVqCNpexiVHg

五、项目源码与参考资料下载

链接:https://pan.baidu.com/s/1EGzN_WvAgLZz4yFJ0t-fVQ
提取码:ln1k

六、参考文章

https://www.cnblogs.com/lenve/p/7985943.html

API网关服务Zuul-Spring Cloud学习第五天(非原创)的更多相关文章

  1. API网关服务:Spring Cloud Zuul

    最近在学习Spring Cloud的知识,现将API网关服务:Spring Cloud Zuul 的相关知识笔记整理如下.[采用 oneNote格式排版]

  2. 第七章 API网关服务:Spring Cloud Zuul

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

  3. 断路器Hystrix与Turbine集群监控-Spring Cloud学习第三天(非原创)

    文章大纲 一.Hystrix基础介绍二.断路器Hystrix简单使用三.自定义Hystrix请求命令四.Hystrix的服务降级与异常处理五.Hystrix的请求缓存与请求合并六.Hystrix仪表盘 ...

  4. Feign详细使用-Spring Cloud学习第四天(非原创)

    文章大纲 一.Feign是什么二.Feign的基本实现三.Feign的继承特性四.Feign配置详解五.项目源码与参考资料下载六.参考文章   一.Feign是什么 前面几篇文章我们详细的介绍了Rib ...

  5. Spring Cloud Bus介绍--Spring Cloud学习第七天(非原创)

    一.什么是Spring Cloud Bus二.Spring Cloud Bus之RabbitMQ介绍三.Spring Cloud Bus整合RabbitMQ四.Spring Cloud Bus整合Ka ...

  6. springCloud学习05之api网关服务zuul过滤器filter

    前面学习了zuul的反向代理.负载均衡.fallback回退.这张学习写过滤器filter,做java web开发的对filter都不陌生,那就是客户端(如浏览器)发起请求的时候,都先经过过滤器fil ...

  7. SpringCloud开发学习总结(八)—— API网关服务Zuul(一)

    大多数情况下,为了保证对外服务的安全性,我们在服务端实现的为服务接口时往往都会有一定的权限校验机制,比如对用户登录状态的校验等:同时为了防止客户端在发起请求时被篡改等安全方面的考虑,还会有一些签名校验 ...

  8. springcloud中的API网关服务Zuul

    到目前为止,我们Spring Cloud中的内容已经介绍了很多了,Ribbon.Hystrix.Feign这些知识点大家都耳熟能详了,我们在前文也提到过微服务就是把一个大的项目拆分成很多小的独立模块, ...

  9. spring cloud学习笔记五 网关服务zuul

    网关服务是指,客户端发送的请求不用直接访问特定的微服务接口,而且是经过网关服务的接口进行交互,网关服务再去到特定的微服务中进行调用.   网关服务的路由功能和Nginx的反向代理一样,所有的服务都先会 ...

随机推荐

  1. JVM——Java类加载机制总结

    )解析:解析阶段是把虚拟机中常量池的符号引用替换为直接引用的过程. 2.3 初始化 类初始化时类加载的最后一步,前面除了加载阶段用户可以通过自定义类加载器参与以外,其余都是虚拟机主导和控制.到了初始化 ...

  2. Java多线程并发技术

    Java多线程并发技术 参考文献: http://blog.csdn.net/aboy123/article/details/38307539 http://blog.csdn.net/ghsau/a ...

  3. DirectShow简单入门程序

    1.首先确认已安装过相关工具及配置环境,然后打开vs2010,新建一对话框应用程序 取名为Player_test1,然后打开菜单->项目->属性-> 添加strmmiids.lib库 ...

  4. 零基础学习 Python 之字符串

    初识字符串 维基百科对于字符串的定义式:字符串是由零个或者多个字符组成的有限串行.你之前学会敲的第一行 print 代码里的 "Hello World",就是一个字符串.字符串的本 ...

  5. Python之threading多线程

    1.threading模块是Python里面常用的线程模块,多线程处理任务对于提升效率非常重要,先说一下线程和进程的各种区别,如图 概括起来就是 IO密集型(不用CPU) 多线程计算密集型(用CPU) ...

  6. java作业8

    interface Pet{ public String getName(); public String getColor(); public int getAge(); } class Cat i ...

  7. iis特殊后缀文件下载404

    项目中有特殊类型的文件上传到服务器,下载的时候报404,下载不下来.如后缀名为qwwq这种类型. 因为遇到过这种情况,以前都是配置mime类型好的,但是这次没有找到,到底应该配置什么mime类型,一时 ...

  8. 【bzoj4836】[Lydsy2017年4月月赛]二元运算 分治+FFT

    题目描述 定义二元运算 opt 满足   现在给定一个长为 n 的数列 a 和一个长为 m 的数列 b ,接下来有 q 次询问.每次询问给定一个数字 c  你需要求出有多少对 (i, j) 使得 a_ ...

  9. php处理ajax

    首先安装wamp,若安装过mysql则终止进程防止冲突,可以访问localhost说明成功.在www目录下新建项目,使用localhost访问. php: <?php //3.获取ajax传过来 ...

  10. Do not use built-in or reserved HTML elements as component id: header

    刚刚在搭建项目时发现控制台报错 查找发现是因为组件名称所致,也就是当我们起名一个header.vue的组件时,我们安装的vue插件会自动把name设置为default 这就造成了错误 把header修 ...