1.Gateway
1.1. 为什么使用网关
- 使用Spring Cloud实现微服务的架构基本成型
我们使用Spring Cloud Netflix中的Eureka实现了服务注册中心以及服务注册与发现;而服 务间通过Ribbon或Feign实现服务的消费以及均衡负载。为了使得服务集群更为健壮,使用 Hystrix的融断机制来避免在微服务架构中个别服务出现异常时引起的故障蔓延。
在该架构中,我们的服务集群包含:内部服务Service A和Service B,他们都会注册与订阅 服务至Eureka Server,而Open Service是一个对外的服务,通过均衡负载公开至服务调用 方。我们把焦点聚集在对外服务这块,直接暴露我们的服务地址,这样的实现是否合理,或 者是否有更好的实现方式呢?
- 客户端多次请求不同的微服务,增加客户端的复杂性
在微服务架构中,不同的微服务可以有不同的网络地址,各个微服务之间通过互相调用完成 用户请求,客户端可能通过调用N个微服务的接口完成一个用户请求。
-
无法直接复用既有请求接口。
当我们对一个即有的请求接口,实现外部访问时,如果要增加权限校验等操作,我们就不得 不在原有接口上增加校验逻辑,或增加一个代理来实现权限控制,这样就无法直接复用既有 的请求接口。
-
http请求不同服务次数增加,性能不高
一个项目是会有多个不同的服务来提供对应的服务功能的,但是不同的服务其访问地址是不 一样的,所以在项目中就需要针对不同的服务请求设置不一样的地址,导致请求不同服务次 数增加,性能不高。
-
可以使用服务网关,解决以上问题
网关旨在为微服务架构提供一种简单而有效的统一的API路由管理方式,网关就是系统的入 口,封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共 逻辑可以在这里实现,诸如认证、鉴权、监控、缓存、负载均衡、流量管控、路由转发等。
在目前的网关解决方案里,有Nginx+ Lua、 Netflix Zuul 、 Spring Cloud Gateway等等
1.2. Gateway-概述
SpringCloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一 的 API 路由管理方式。
SpringCloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Zuul,在Spring Cloud 2.0以上版本中,没有对新版本的Zuul 2.0以上最新高性能版本进行集成,仍然还是使用的 Zuul 2.0之前的非Reactor模式的老版本。而为了提升网关的性能,SpringCloud Gateway是基于 WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。
Spring Cloud Gateway 的目标,不仅提供统一的路由方式,并且基于 Filter链的方式提供了网关 基本的功能,例如:安全,监控/指标,和限流。
提前声明:Spring Cloud Gateway 底层使用了高性能的通信框架Netty。
1.3. Gateway-快速入门
准备工作
1.创建一个springboot工程,删除src目录,修改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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.12</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>springcloud-day03-getweay-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springcloud-day03-getweay-parent</name>
<description>springcloud-day03-getweay-parent</description>
<packaging>pom</packaging>
<modules>
<module>springcloud-day03-getweay-provider</module>
<module>springcloud-day03-getweay-consumer</module>
<module>springcloud-day03-getweay</module>
</modules>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR7</spring-cloud.version>
</properties>
<dependencies>
<!--nacos起步依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>0.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.创建provider工程
provider的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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<version>0.0.1-SNAPSHOT</version>
<artifactId>springcloud-day03-getweay-parent</artifactId>
</parent>
<groupId>com.example</groupId>
<artifactId>springcloud-day03-getweay-provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springcloud-day03-getweay-provider</name>
<description>springcloud-day03-getweay-provider</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
provider的application.yml文件
server:
port: 8081
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
application:
name: gateway-provider
provider的Controller
@RestController
public class ProviderController {
@RequestMapping("/provider")
public String provider() {
return "Hello Nacos GateWay Provider";
}
}
3.创建consumer工程
consumer的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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>springcloud-day03-gateway-parent</artifactId>
<groupId>com.example</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.example</groupId>
<artifactId>springcloud-day04-gateway-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springcloud-day04-gateway-consumer</name>
<description>springcloud-day04-gateway-consumer</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
</project>
consumer的application.yml文件
server:
port: 8082
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
application:
name: gatewey-consumer
consumer的Fegin客户端
@FeignClient("gateway-provider")
public interface ProviderFeginClient {
@RequestMapping("/provider")
public String provider();
}
consumer的Controller
@RestController
@RequestMapping("/api")
public class ConsumerController {
@Autowired
private ProviderFeginClient providerFeginClient;
@RequestMapping("/consumer")
public String consumer(String username) {
String str = providerFeginClient.provider();
return str;
}
}
consumer的启动引导类
@SpringBootApplication
@EnableFeignClients
public class SpringcloudDay03GetweayConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudDay03GetweayConsumerApplication.class, args);
}
}
4.运行测试
运行provider和consumer项目,查看nacos注册中心中的内容
浏览器输入地址访问测试。
在整个微服务结构项目中,consumer就是open service,那针对open service直接暴露服务的 地址,是不合理的,最好是能通过网关路由到open service。
构建网关微服务
构建步骤
构建
- 搭建网关模块
- 引入依赖: spring-cloud-starter-gateway
- 编写启动类
- 编写配置文件
- 启动测试
示例
1.创建网关微服务,引入坐标,配置application.yml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>springcloud-day03-getweay-parent</artifactId>
<groupId>com.example</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.example</groupId>
<artifactId>springcloud-day03-getweay</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springcloud-day03-getweay</name>
<description>springcloud-day03-getweay</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--引入gateway 网关-->
<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>
</project>
server:
port: 9001
spring:
application:
name: gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
# 网关配置
gateway:
# 路由配置:转发规则
routes:
# id: 唯一标识。默认是一个UUID
# uri: 转发路径
# predicates: 条件,用于请求网关路径的匹配规则
- id: nacos-consumer
uri: http://localhost:8082/
predicates:
- Path=/api/**
2.在父工程的pom.xml文件的modules配置聚合操作
<modules>
<module>springcloud-day03-getweay-provider</module>
<module>springcloud-day03-getweay-consumer</module>
<module>springcloud-day03-getweay</module>
</modules>
3.启动所有项目,在nacos注册中心中查看服务
浏览器输入访问网关服务的地址,进行测试。
1.4. Gateway-静态路由
server:
port: 9001
spring:
application:
name: gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
# 网关配置
gateway:
# 路由配置:转发规则
routes:
# id: 唯一标识。默认是一个UUID
# uri: 转发路径
# predicates: 条件,用于请求网关路径的匹配规则
- id: nacos-consumer
uri: http://localhost:8082/
predicates:
- Path=/api/**
1.5. Gateway-动态路由
步骤
- 修改uri属性: uri: lb://注册中心中的对应服务的名称
示例
在网关微服务的application.yml文件中配置
server:
port: 9001
spring:
application:
name: gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
# 网关配置
gateway:
# 路由配置:转发规则
routes:
# id: 唯一标识。默认是一个UUID
# uri: 转发路径
# predicates: 条件,用于请求网关路径的匹配规则
- id: nacos-consumer
uri: lb://localhost:8082/
predicates:
- Path=/api/**
1.6. Gateway-前缀设置
添加前缀设置
server:
port: 9001
spring:
application:
name: gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
# 网关配置
gateway:
# 路由配置:转发规则
routes:
# id: 唯一标识。默认是一个UUID
# uri: 转发路径
# predicates: 条件,用于请求网关路径的匹配规则
- id: nacos-consumer
uri: lb://localhost:8082/
predicates:
- Path=/**
#自动给请求路径添加前缀, http://localhost:9001/consumer ->
http://localhost:9001/api/consumer
- PrefixPath=/api
去除前缀设置
server:
port: 9001
spring:
application:
name: gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
# 网关配置
gateway:
# 路由配置:转发规则
routes:
- id: nacos-consumer
uri: lb://localhost:8082/
predicates:
- Path=/aaa/api/**
filters:
#去除前缀,1表示过滤1个路径,2表示两个路径,以此类推
#http://localhost:9001/aaa/api/consumer ->http://localhost:9001/api/consumer
- StripPrefix=1
1.7. Gateway-过滤器-概述
- Gateway 支持过滤器功能,对请求或响应进行拦截,完成一些通用操作。
- Gateway 提供两种过滤器方式:“pre” 和“post”。
- pre 过滤器(请求),在转发之前执行,可以做参数校验、权限校验、流量监控、日志输 出、协议转换等。
- post 过滤器(响应),在响应之前执行,可以做响应内容、响应头的修改,日志的输出, 流量监控等。
- Gateway 还提供了两种类型过滤器
- GatewayFilter:局部过滤器,针对单个路由
- GlobalFilter :全局过滤器,针对所有路由
1.8. Gateway-过滤器-局部过滤器
概念
- GatewayFilter 局部过滤器,是针对单个路由的过滤器。
- 在Spring Cloud Gateway 组件中提供了大量内置的局部过滤器,对请求和响应做过滤操作。
- 遵循约定大于配置的思想,只需要在配置文件配置局部过滤器名称,并为其指定对应的值, 就可以让其生效
示例
1.在网关服务的application.yml文件中配置
server:
port: 9001
spring:
application:
name: nacos-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
routes:
- id: nacos-consumer
uri: lb://nacos-consumer
predicates:
- Path=/aaa/api/**
filters:
#去除前缀,1表示过滤1个路径,2表示两个路径,以此类推
#http://localhost:9001/aaa/api/consumer ->http://localhost:9001/api/consumer
- StripPrefix=1
#给请求路径增加请求参数和值
- AddRequestParameter=username,zhangsan
2.修改消费者的Controller的方法的参数
@RestController
@RequestMapping("/api")
public class ConsumerController {
@Autowired
private ProviderFeginClient providerFeginClient;
//接收网关局部过滤器强制添加的请求参数
@RequestMapping("/consumer")
public String consumer(String username){
String msg = providerFeginClient.provider();
return msg+" "+username;
}
}
3.启动测试,浏览器输入地址:http://localhost:9001/aaa/api/consumer,会在Consumer的打 印台看到网关自动增加的请求参数
1.9. Gateway-过滤器-全局过滤器
概念
- GlobalFilter 全局过滤器,不需要在配置文件中配置,系统初始化时加载,并作用在每个路 由上。
- Spring Cloud Gateway 核心的功能也是通过内置的全局过滤器来完成。
自定义全局过滤器步骤
- 定义类实现 GlobalFilter 和 Ordered接口。
- 复写方法。
- 完成逻辑处理 。
示例
在api-gateway-server中创建全局过滤器
@Component
public class MyFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("自定义全局过滤器执行了~~~");
return chain.filter(exchange);//放行
}
/**
* 过滤器排序
* @return 数值越小 越先执行
*/
@Override
public int getOrder() {
return 0;
}
}
启动项目,访问测试,在网关微服务的打印台会看到
2.Nacos配置中心
2.1.配置中心简介
随着微服务架构的发展,企业级项目由无数的服务组成,这时候急需用到集中管理、治理的配置 的组件,来统一管理各个服务的开关、配置参数、数据库地址、服务器等等,然而这还不够,还 要对这个管理配置的组件有着修改后实时发布、多环境、灰度发布、权限控制、审核等等机制, 由此配置中心出现了。
2.2.配置中心好处
- 集中管理配置文件
- 不同环境不同配置,动态化的配置更新
- 配置信息改变时,不需要重启即可更新配置信息到服务
2.3.配置中心产品
- SpringCloud Config
- Apollo
- Nacos
2.4.Nacos配置中心介绍
Nacos支持动态的配置管理,将服务的配置信息分环境、分类别进行管理,并且支持热更新。 Nacos同springcloud-config一样,在项目初始化时,要保证先从配置中心配置拉取,拉取配置 后,才能保证项目的正常启动。不过与Config不同Nacos的配置信息存储于数据库中,支持配置 信息的监听和版本回滚。
springboot中配置文件的加载是存在优先顺序的,bootstrap.yml优先级高于application.yml。
bootstrap.yml(bootstrap.properties)(springboot固定名称的配置文件) 用来程序引导时执 行,应用于更加早期配置信息读取。
2.5.Nacos配置中心实现
1.启动Nacos,在Nacos的管理界面中选择配置管理,选择配置列表,在打开的列表界面中选择 创建配置按钮,会弹出创建配置的页面。
其中:
- Data ID:配置文件的名称
- 定义规则为:${prefix}-${spring.profile.active}.${file-extension}
- prefix 默认为 spring.application.name 的值,也可以通过配置项 spring.cloud.nacos.config.prefix 来配置。
- spring.profile.active 即为当前环境对应的 profile,可以通过配置项 spring.profile.active 来配置。
- file-exetension 为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension 来配置。支持 properties 和 yaml 类型。
- 注意:当 spring.profile.active 为空时,对应的连接符 - 也将不存在, dataId 的拼接格式变成 ${prefix}.${file-extension}
- 定义规则为:${prefix}-${spring.profile.active}.${file-extension}
- Group:配置文件分组
- 描述:配置文件的描述信息。
- 配置格式:配置文件的格式。
- 配置内容:配置文的内容
在创建配置页面中创建Data Id 为nacos-provider.yml的配置文件,其中Group为默认的 DEFAULT_GROUP,配置文件的格式也相应的选择yaml,其内添加配置,如图所示:
之后,点击发布按钮,发布远程配置文件。之后点击配置列表,可以查看到发布的远程配置文件
2.修改provider工程的pom.xml文件,引入nacos-config依赖
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
</dependency>
<!-- nacos配置中心起步依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>0.2.2.RELEASE</version>
</dependency>
3.修改provider项目,将application.yml文件删除,然后在resources目录下创建bootstrap.yml
server:
port: 8081
spring:
application:
name: nacos-provider
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
#配置中心连接地址
server-addr: 127.0.0.1:8848
#远程配置文件所需的组
group: DEFAULT_GROUP
#远程配置文件的类型
file-extension: yml
#远程配置文件的data id
shared-dataids: gateway-provider.yml
#需要远程刷新的配置文件的data id
refreshable-dataids: gateway-provider.yml
4.在provider项目中创建ProviderProperties类,获取配置文件中的内容
@Component
@ConfigurationProperties(prefix = "spring.datasource")
@RefreshScope //动态更新远程配置文件中的配置
public class ProviderProperties {
private String driverClassName;
private String url;
private String username;
private String password;
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
5.修改provider项目的Controller
@RestController
public class ProviderController {
@Autowired
private ProviderProperties properties;
@RequestMapping("/provider")
public String provider(){
return properties.getDriverClassName()+" "+properties.getUrl()+""+properties.getUsername()+" "+properties.getPassword();
}
}
6.启动provider、consumer、gateway网关微服务,进行访问测试
7.到nacos中修改远程配置的内容
点击发布,不用重启项目,浏览器再次访问测试