微服务架构➖SpringCloud➖Gateway
关于作者
- 作者介绍
🍓 博客主页:作者主页
🍓 简介:JAVA领域优质创作者🥇、一名在校大三学生🎓、在校期间参加各种省赛、国赛,斩获一系列荣誉 🏆、阿里云专家博主 、51CTO专家博主
🍓 关注我:关注我学习资料、文档下载统统都有,每日定时更新文章,励志做一名JAVA资深程序猿👨💻
1. 什么是网关
网关是微服务最边缘的服务,直接暴露给用户,用来做用户和微服务的桥梁。
- 没有网关:客户端直接访问我们的微服务,会需要在客户端配置很多的 ip:port,如果 user-service 并发比较大,则无法完成负载均衡
- 有网关:客户端访问网关,网关来访问微服务,(网关可以和注册中心整合,通过服务名称找到目标的 ip:prot)这样只需要使用服务名称即可访问微服务,可以实现负载均衡,可 以实现 token 拦截,权限验证,限流等操
2. 简介
你们项目里面 用的什么网关? gateway zuul,它是 Spring Cloud 官方提供的用来取代 zuul(netflix)的新一代网关。(zuul:1.0 , 2.0 ,zuul 的本质,一组过滤器,根据自定义的过滤器顺序来执行,本质就是 web 组件 web 三大组件(监听器 过滤器
Zuul1.0 使用的是 BIO(Blocking IO) tomcat7.0 以前都是 BIO 性能一般
Zuul2.0 性能好 NIO AIO 异步非阻塞 io a+nio = aio = async + no blocking io,它基于 spring5.x,springboot2.x 和 ProjectReactor 等技术。 它的目地是让路由更加简单,灵活,还提供了一些强大的过滤器功能,例如:熔断、限流、重 试,自义定过滤器等 token 校验 ip 黑名单等
SpringCloud Gateway作为Spring Cloud生态的网关,目标是替代Zuul,在SpringCloud2.0 以上的版本中,没有对新版本的 zuul2.0 以上的最新高性能版本进行集成,仍然还是使用的 zuul1.x[可以看项目依赖找到]非 Reactor 模式的老版本。而为了提升网关的性能,SpringCloud Gateway 是基于 webFlux 框架实现的,而 webFlux 框架底层则使用了高性能 的 Reactor 模式通信框架的 Netty NIO(非阻塞式 io) BIO 你只需要了解网关能做什么? 网关里面写什么代码 就可以了
3. Spring Cloud Gateway工作流程
- 客户端向 springcloud Gateway 发出请求
- 在 Gateway Handler Mapping 中找到与 请求相匹配的路由,将其发送到 Gateway Web Handler
- Handler 再通过指定的过滤器来将请求发送到我们实际的服务的业务逻辑,然后返回。
- 过滤器之间用虚线分开是因为过滤器可能会在发送请求之前【pre】或之后【post】执行业务 逻辑,对其进行加强或处理。
- Filter 在 【pre】 类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转 换等
- Filter 在【post】 类型的过滤器中可以做响应内容、响应头的修改、日志的输出,流量监控等有着 非常重要的作用。
总结:Gateway 的核心逻辑也就是 路由转发 + 执行过滤器
4. Spring Cloud Gateway 三大核心概念
4.1 Route(路由)(重点 和 eureka 结合做动态路由)
路由信息的组成: 由一个 ID、一个目的 URL、一组断言工厂、一组 Filter 组成。 如果路由断言为真,说明请求 URL 和配置路由匹配。
4.2 Predicate(断言)(就是一个返回 bool 的表达式)
Java 8 中的断言函数。 lambda 四大接口:供给形,消费性,函数型,断言型
Spring Cloud Gateway 中 的 断 言 函 数 输 入 类 型 是 Spring 5.0 框 架 中 的 ServerWebExchange。Spring Cloud Gateway 的断言函数允许开发者去定义匹配来自于 Http Request 中的任何信息比如请求头和参数。
4.3 Filter(过滤) (重点)
一个标准的 Spring WebFilter。 Web 三大组件(servlet listener filter) mvc、interceptor
Spring Cloud Gateway 中的 Filter 分为两种类型的 Filter,分别是 Gateway Filter 和 Global Filter。过滤器 Filter 将会对请求和响应进行修改处理。 一个是针对某一个路由(路径)的 filter 对某一个接口做限流 一个是针对全局的 filter token ip 黑名单
5. Nginx 和 Gateway 的区别
Nginx 在做路由,负载均衡,限流之前,都有修改 nginx.conf 的配置文件,把需要负载均衡, 路由,限流的规则加在里面。Eg:使用 nginx
但是 gateway 不同,gateway 自动的负载均衡和路由,gateway 和 eureka 高度集成,实现 自动的路由,和 Ribbon 结合,实现了负载均衡(lb),gateway 也能轻易的实现限流和权限验证。
Nginx(c)比 gateway(java)的性能高一点。 本质的区别呢?
- Nginx (更大 服务器级别的)
- Gateway (项目级别的)
6. Gateway 快速入门
6.1 新建项目,导入依赖
01-gateway-server、02-login-service、03-teacher-service
6.2 模块02-login-service
pom.xml
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>
<groupId>com.zmz</groupId>
<artifactId>login-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>login-service</name>
<description>login-service Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
<spring-cloud.version>Hoxton.SR9</spring-cloud.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-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.12.RELEASE</version>
<configuration>
<mainClass>com.zmz.LoginServiceApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
实体类User.java
java
package com.zmz.pojo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ProjectName: 04-hystrix
* @Package: com.zmz.pojo
* @ClassName: User
* @Author: 张晟睿
* @Date: 2022/10/9 14:57
* @Version: 1.0
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class User {
private Integer id;
private String name;
private String pwd;
private Integer age;
}
Usercontroller.java
java
package com.zmz.controller;
import com.zmz.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.Duration;
import java.util.Random;
import java.util.UUID;
/**
* @ProjectName: 04-hystrix
* @Package: com.zmz.controller
* @ClassName: LoginController
* @Author: 张晟睿
* @Date: 2022/10/9 14:59
* @Version: 1.0
*/
@RestController
public class LoginController {
@Autowired
public StringRedisTemplate redisTemplate;
@GetMapping("doLogin")
public String doLogin(String name, String pwd) {
System.out.println(name + " " + pwd);
User user = new User(1, name, pwd, 23);
String token = UUID.randomUUID().toString();
redisTemplate.opsForValue().set(token, user.toString(), Duration.ofSeconds(7200));
return token;
}
}
配置application.yml
yml
server:
port: 8088
spring:
application:
name: login-service
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
instance:
hostname: localhost
instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
6.3 网关配置01-gateway-server
yml
server:
port: 80
spring:
application:
name: gateway-server
cloud:
gateway:
enabled: true #只要加了依赖,默认开启
routes:
- id: login-service-route #路由id,保持唯一
uri: http://localhost:8088 # 或者直接使用uri: lb://login-service 来进行动态路由 #uri 统一资源定位符 url统一资源标识符
predicates: #断言是给某个路由来做的 断言不能给动态路由来配置,只能在写好的predicates断言内才能生效
- Path=/doLogin #断言匹配规则,只要匹配上/doLogin 就往uri转发 并且将路径带上
- After=2022-10-09T17:18:39.831+08:00[Asia/Shanghai]
- Method=GET,POST
# - Query=name,admin. #正则表达式的值
# - Path=/mySerivice/** #多个路径进行匹配
filters:
- name: RequestRateLimiter #这个是过滤器的名称
args: #过滤器的参数
key-resolver: '#{@ipKeyResolver}' #通过spel表达式取IOC容器中的值
redis-rate-limiter.replenishRate: 1 #生成令牌的速度
redis-rate-limiter.burstCapacity: 3 #桶容量
discovery:
locator:
enabled: true #开启动态路由 开启通过业务名称找到对应服务的功能
lower-case-service-id: true #服务名称小写开启
# globalcors:
# corsConfigurations:
# '[/**]':
# allowCredentials: true # 可以携带cookie
# allowedHeaders: '*'
# allowedMethods: '*'
# allowedOrigins: '*'
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
registry-fetch-interval-seconds: 3 #网关拉去服务列表的时间缩短
instance:
hostname: localhost
instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
6.4 测试
6.5 配置路由
java
package com.zmz.config;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.ZonedDateTime;
/**
* @ProjectName: 04-hystrix
* @Package: com.zmz.config
* @ClassName: RouteConfig
* @Author: 张晟睿
* @Date: 2022/10/9 15:24
* @Version: 1.0
*/
@Configuration
public class RouteConfig {
/**
*
* 代码路由 和 yml不冲突都可以使用
* @param builder
* @return
*/
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("test-id", r -> r.path("/nav/web").uri("https://www.csdn.net")) // 地址栏中的地址会变为https://www.csdn.net/nav/web
.route("openfeign-id", r -> r.path("/nav/ai").uri("https://blog.csdn.net/nav/ai")) //地址栏中的地址会变为http://localhost/nav/ai
.route("kubernetes-id", r -> r.path("/spring-cloud-kubernetes").uri("https://www.csdn.net")).build();//总结 如果请求的uri中匹配地址包含了访问地址 就不会进行拼接
}
public static void main(String[] args) {
ZonedDateTime now = ZonedDateTime.now();
System.out.println(now);//2022-10-09T16:18:39.831+08:00[Asia/Shanghai]
}
}