阅读本文前可先参考
SpringCloud - Spring Cloud根/父项目,开发准备(二)_MinggeQingchun的博客-CSDN博客
一、API网关
引自百度百科
API网关,软件术语,两个相互独立的局域网之间通过路由器进行通信,中间的路由被称之为网关。
任何一个应用系统如果需要被其他系统调用,就需要暴露 API,这些 API 代表着一个一个的功能点。
如果两个系统中间通信,在系统之间加上一个中介者协助 API 的调用,这个中介者就是 API 网关
API 网关是一个搭建在客户端和微服务之间的服务,我们可以在 API 网关中处理一些非业务功能的逻辑,例如权限验证、监控、缓存、请求路由等。
API 网关就像整个微服务系统的门面一样,是系统对外的唯一入口。有了它,客户端会先将请求发送到 API 网关,然后由 API 网关根据请求的标识信息将请求转发到微服务实例。
对于服务数量众多、复杂度较高、规模比较大的系统来说,使用 API 网关具有以下好处:
- 客户端通过 API 网关与微服务交互时,客户端只需要知道 API 网关地址即可,而不需要维护大量的服务地址,简化了客户端的开发。
- 客户端直接与 API 网关通信,能够减少客户端与各个服务的交互次数。
- 客户端与后端的服务耦合度降低。
- 节省流量,提高性能,提升用户体验。
- API 网关还提供了安全、流控、过滤、缓存、计费以及监控等 API 管理功能。
常见的 API 网关实现方案主要有以下 5 种:
- Spring Cloud Gateway
- Spring Cloud Netflix Zuul
- Kong
- Nginx+Lua
- Traefik
二、Gateway
Spring Cloud Gateway 是 Spring Cloud 团队基于 Spring 5.0、Spring Boot 2.0 和 Project Reactor 等技术开发的高性能 API 网关组件。
Spring Cloud Gateway 是基于 WebFlux 框架实现的,而 WebFlux 框架底层则使用了高性能的 Reactor 模式通信框架 Netty
Spring Cloud GateWay 最主要的功能就是路由转发,而在定义转发规则时主要涉及了以下三个核心概念
1、Route(路由)
2、Predicate(断言)
3、Filter(过滤)
核心概念 | 描述 |
---|---|
Route(路由) | 网关最基本的模块。它由一个 ID、一个目标 URI、一组断言(Predicate)和一组过滤器(Filter)组成。 |
Predicate(断言) | 路由转发的判断条件,我们可以通过 Predicate 对 HTTP 请求进行匹配,例如请求方式、请求路径、请求头、参数等,如果请求与断言匹配成功,则将请求转发到相应的服务。 |
Filter(过滤器) | 过滤器,我们可以使用它对请求进行拦截和修改,还可以使用它对上文的响应进行再处理。 |
注:其中 Route 和 Predicate 必须同时声明
Spring Cloud Gateway 工作流程![](https://i-blog.csdnimg.cn/blog_migrate/032ae42204309cfa575e1727bd20932e.png)
1、客户端将请求发送到 Spring Cloud Gateway 上
2、Spring Cloud Gateway 通过 Gateway Handler Mapping 找到与请求相匹配的路由,将其发送给 Gateway Web Handler。
3、Gateway Web Handler 通过指定的过滤器链(Filter Chain),将请求转发到实际的服务节点中,执行业务逻辑返回响应结果。
4、过滤器之间用虚线分开是因为过滤器可能会在转发请求之前(pre)或之后(post)执行业务逻辑。
5、过滤器(Filter)可以在请求被转发到服务端前,对请求进行拦截和修改,例如参数校验、权限校验、流量监控、日志输出以及协议转换等。
6、过滤器可以在响应返回客户端之前,对响应进行拦截和再处理,例如修改响应内容或响应头、日志输出、流量监控等。
7、响应原路返回给客户端
三、Gateway应用
1、新建一个springboot Module(springcloud-7-service-eureka-gateway)
2、添加 spring-cloud-starter-gateway;spring-cloud-starter-netflix-eureka-client等 依赖
注:
在 gateway 网关服务中不能引入 spring-boot-starter-web 的依赖,否则会报错
spring-cloud-starter-gateway 使用的Netty服务器,而 spring-boot-starter-web 使用 的Tomcat服务器;Spring Cloud Gateway使用是基于WebFlux和Netty开发的,和传统的Tomcat服务器方式不同
<dependencies>
<!--spring-cloud-starter-netflix-eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- Spring cloud gateway 网关依赖
特别注意:在 gateway 网关服务中不能引入 spring-boot-starter-web 的依赖,否则会报错
Spring Cloud Gateway使用是基于WebFlux和Netty开发的,和传统的Tomcat服务器方式不同
-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
<!--继承统一的父项目-->
<parent>
<groupId>com.company</groupId>
<artifactId>springcloud-demo</artifactId>
<version>1.0.0</version>
</parent>
<groupId>com.company</groupId>
<artifactId>springcloud-7-service-eureka-gateway</artifactId>
<version>1.0.0</version>
<name>springcloud-7-service-eureka-gateway</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--spring-cloud-starter-netflix-eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- Spring cloud gateway 网关依赖
特别注意:在 gateway 网关服务中不能引入 spring-boot-starter-web 的依赖,否则会报错
Spring Cloud Gateway使用是基于WebFlux和Netty开发的,和传统的Tomcat服务器方式不同
-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
3、application.properties配置文件
server.port=81
#eureka注册中心首页的Application这一栏
spring.application.name=springcloud-7-service-eureka-gateway
#每间隔5s,向Eureka服务注册中心发送一次心跳,证明服务是否依然“存活”
eureka.instance.lease-renewal-interval-in-seconds=2
#告诉服务端,如果10s之内没有发送心跳,就代表故障,将本服务踢出
eureka.instance.lease-expiration-duration-in-seconds=10
#告诉服务端,服务实例以IP作为链接,不是取机器名
eureka.instance.prefer-ip-address=false
#注册服务实例ID,,服务ID必须唯一 springcloud-7-service-eureka-gateway
eureka.instance.instance-id=${spring.application.name}:${server.port}
#注册中心的链接地址 http://eureka8761:8761/eureka,http://eureka8762:8762/eureka,http://eureka8763:8763/eureka
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
#网关路由配置
#开启网关,默认开启
spring.cloud.gateway.enabled=true
#节点 routes 是一个List 对象,其中 routes 集合中中又包含多个对象,每个对象有三个属性(一个 索引[0]代表一个对象)
#路由 id,没有固定规则,但唯一
spring.cloud.gateway.routes[0].id=login-service-route
#匹配后提供服务的路由地址;uri统一资源定位符 url 统一资源标识符
spring.cloud.gateway.routes[0].uri=http://localhost:9001
#以下是断言条件,必选全部符合条件;断言是给某一个路由来设定的一种匹配规则 默认不能作用在动态路由上
#断言,路径匹配,只要Path匹配上了/doLogin 就往 uri 转发 并且将路径带上 注意:Path 中 P 为大写
#也可以全局匹配,如 /service/**
spring.cloud.gateway.routes[0].predicates[0]=Path=/doLogin
#只能是 GET 请求时,才能访问
spring.cloud.gateway.routes[0].predicates[0]=Method=GET
application.yml写法
server:
port: 81
#eureka注册中心首页的Application这一栏
spring:
application:
name: springcloud-7-service-eureka-gateway
#网关路由配置
cloud:
gateway:
#开启网关,默认开启
enabled: true
#节点 routes 是一个List 对象,其中 routes 集合中中又包含多个对象,每个对象有三个属性(一个 索引[0]代表一个对象)
routes:
#路由 id,没有固定规则,但唯一
- id: login-service-route
#匹配后提供服务的路由地址;uri统一资源定位符 url 统一资源标识符
uri: http://localhost:9001
#以下是断言条件,必选全部符合条件;断言是给某一个路由来设定的一种匹配规则 默认不能作用在动态路由上
predicates:
#断言,路径匹配,只要Path匹配上了/doLogin 就往 uri 转发 并且将路径带上 注意:Path 中 P 为大写
#也可以全局匹配,如 /service/**
- Path=/doLogin
#只能是 GET,POST 请求时,才能访问
- Method=GET,POST
eureka:
instance:
#每间隔5s,向Eureka服务注册中心发送一次心跳,证明服务是否依然“存活”
lease-renewal-interval-in-seconds: 2
#告诉服务端,如果10s之内没有发送心跳,就代表故障,将本服务踢出
lease-expiration-duration-in-seconds: 10
#告诉服务端,服务实例以IP作为链接,不是取机器名
prefer-ip-address: false
#注册服务实例ID,,服务ID必须唯一 springcloud-7-service-eureka-gateway
instance-id: ${spring.application.name}:${server.port}
#注册中心的链接地址 http://eureka8761:8761/eureka,http://eureka8762:8762/eureka,http://eureka8763:8763/eureka
client:
service-url:
defaultZone: http://localhost:8761/eureka
注:
(1)Gateway 网关建议 使用 yml 文件格式配置,以properties配置比较繁琐,一般公司项目也建议采取 yml格式
(2)不管 application.properties 还是 application.yml 写法,都要注意 断言 predicates 处的 Path=/doLogin 和 Method=GET,POST写法,不然报如下错误
***************************
APPLICATION FAILED TO START
***************************
Description:
Binding to target [Bindable@4a6facb0 type = java.util.List<org.springframework.cloud.gateway.handler.predicate.PredicateDefinition>, value = 'provided', annotations = array<Annotation>[@javax.validation.constraints.NotEmpty(message={javax.validation.constraints.NotEmpty.message}, groups=[], payload=[]), @javax.validation.Valid()]] failed:
Property: spring.cloud.gateway.routes[0].predicates[0].path
Value: /doLogin
Origin: class path resource [application.yml]:23:21
Reason: The elements [spring.cloud.gateway.routes[0].predicates[0].path,spring.cloud.gateway.routes[0].predicates[1].method] were left unbound.
Property: spring.cloud.gateway.routes[0].predicates[1].method
Value: GET,POST
Origin: class path resource [application.yml]:25:23
Reason: The elements [spring.cloud.gateway.routes[0].predicates[0].path,spring.cloud.gateway.routes[0].predicates[1].method] were left unbound.
4、在 Spring Boot 的启动类中,添加 @EnableEurekaClient 注解开启 Eureka 客户端功能(Gateway网关也是eureka的客户端)
@EnableEurekaClient //Gateway网关也是eureka的客户端
@SpringBootApplication
public class EurekaGateway7Application {
public static void main(String[] args) {
SpringApplication.run(EurekaGateway7Application.class, args);
}
}
5、创建一个服务提供者 springcloud-7-service-eureka-gateway-login
依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--spring-cloud-starter-netflix-eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
application.properties
server.port=9001
#设置应用名称,对应Eureka控制台下 DS Replicas 的 Application(集群部署服务提供者,Application一致,对应多个eureka.instance.instance-id)
spring.application.name=springcloud-7-service-eureka-gateway-login
#每间隔5s,向Eureka服务注册中心发送一次心跳,证明服务是否依然“存活”
eureka.instance.lease-renewal-interval-in-seconds=5
#告诉服务端,如果10s之内没有发送心跳,就代表故障,将本服务踢出
eureka.instance.lease-expiration-duration-in-seconds=10
#告诉服务端,服务实例以IP作为链接,不是取机器名
eureka.instance.prefer-ip-address=false
#注册服务实例ID,,服务ID必须唯一
eureka.instance.instance-id=springcloud-7-service-eureka-gateway-login
#注册中心的链接地址 http://localhost:8761/eureka
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
controller测试类
@RestController
public class LoginController {
@GetMapping("/doLogin")
public String doLogin(String name, String pwd) {
System.out.println(name);
System.out.println(pwd);
// token
String token = UUID.randomUUID().toString();
return token;
}
}
6、分别启动本地 eureka,gateway,login-service ,输入访问 http://localhost:81/doLogin
注:
这里启动 Gateway网关模块,会明显的在控制台下方看到输出:
Netty started on port(s):81
服务者自己的端口 9001 访问 自然无问题