SpringCloud实战---第十五篇:微服务网关GateWay
系列文章目录
SpringCloud快速入门到精通各组件原理
专栏传送门
@TOC
前言
我们学习的知识框架图
说起来容易做起来难,一步一步都干完!!!
学习一定要自己动手搞一搞,不能只眼会。
学习笔记是跟着尚硅谷的视频学的:https://www.bilibili.com/video/BV18E411x7eT?p=1
一、关于Zuul和GateWay
zuul是最早的网关组件,但是由于核心人员的离开及更新不及时,导致zuul2胎死,SpringCloud自己研发了一个GateWay网关组件来替换zuul,因此我们不再学习zuul,学习最新的GateWay。
二、GateWay简介
- GateWay的目标是提供统一的路由方式,且基于Filter链的方式提供了网关的基本功能,例如:安全、监控/指标、限流。
- GateWay是SpringCloud自研的用于替代Zuul1.0的网关。
- GateWay基于WebFlux框架实现,而WebFlux框架的底层使用的是高性能的Reactor模式的通信框架Netty。
优点 - 基于SpringBoot2.0构建,天然与SpringCloud整合。
- 动态路由:能够匹配任何请求属性。
- 可以对路由指定Predicate(路由)和Filter(过滤器)。
- 集成Hystrix断路器功能。
- 集成SpringCloud服务发现功能。
- 请求限流功能。
- 支持路径重写。
- 易于编写的Predicate和Filter。
- GateWay官网https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/。
三、GateWay三大概念
1. Route(路由)
路由是构建网关的基本模块,它由目标ID,目标URL,一系列的断言和过滤器组成,如果断言为True则匹配该路由。
2. Predicate(断言)
开发人员可以匹配HTTP请求中的所有内容,如果请求与断言相匹配则进行路由。
3. Filter(过滤)
指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
4. 总体
Predicate可以理解为是我们的匹配条件,而Filter可以无所不能的拦截器。有了这两个元素,再加上目标URI,就可以实现一个具体的路由了。GateWay的核心逻辑就是路由转发和执行过滤链。
构建基础工程
先构建好基础工程(一篇一篇看过来的不用重新构建)
构建基础父工程:https://blog.csdn.net/weixin_43464964/article/details/121979995
Rest风格微服务:https://blog.csdn.net/weixin_43464964/article/details/121980366
传统分布式方法:https://blog.csdn.net/weixin_43464964/article/details/121999966
改造工程,抽取公共模块:https://blog.csdn.net/weixin_43464964/article/details/122000586
使用Eureka:https://blog.csdn.net/weixin_43464964/article/details/122045319
Eureka集群: https://blog.csdn.net/weixin_43464964/article/details/122046056
想偷懒的请下载;gitee上我上传的代码: https://gitee.com/xiaoZ1712/cloud2021
基础工程构建完成的目录结构:
启动所有模块,访问
localhost:7001
显示如下,代表基础工程没问题
话不多说,立马开干
四、GateWay9527模块搭建
1. 创建cloud-gateway-gateway9527模块
模块名
cloud-gateway-gateway9527
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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud2021</artifactId>
<groupId>com.atguigu.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-gateway-gateway9527</artifactId>
<dependencies>
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!--一般基础配置类-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
application.yml
server:
port: 9527
spring:
application:
name: cloud-gateway
eureka:
instance:
hostname: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
基础包
com.atguigu.springcloud
主启动类GateWayMain9527
package com.atguigu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* @Author: Daisen.Z
* @Date: 2021/12/28 16:26
* @Version: 1.0
* @Description:
*/
@SpringBootApplication
@EnableEurekaClient
public class GateWayMain9527 {
public static void main(String[] args) {
SpringApplication.run(GateWayMain9527.class,args);
}
}
2. 配置路由
修改Gateway9527模块配置文件,配置路由
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
# uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
# uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
#- After=2020-02-21T15:51:37.485+08:00[Asia/Shanghai]
#- Cookie=username,zzyy
#- Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式
eureka:
instance:
hostname: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
3. 启动测试
为了方便测试,把服务注册中心7001改成单节单的
defaultZone: http://eureka7001.com:7001/eureka/
启动7001、8001和9527模块
访问注册中心,查看已注册的服务
http://localhost:7001/
8001和GateWay都注册上来了
接下来,我们访问8001的get接口
http://localhost:8001/payment/get/1
能够成功访问
接着,我们通过路由网关进行访问
http://localhost:9527/payment/get/1
哎!也能够进行访问,这说明路由已经生效了。
网关的作用也体现出来了,我们渐渐的不需要暴露服务的真实端口,而是通过统一的网关进行访问。
五、GateWay的两种配置方式
GateWay有两种配置方式,其1是上述的yml配置文件配置
第二种是通过代码中注入RouteLocator的Bean来实现,接下来我们对第二种方式做下讲解
我们自己用第二种方式写一个路由,然后实现转发到baidu
1. 新建配置类
给GateWay9527新建配置类
package com.atguigu.springcloud.config;
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 org.springframework.stereotype.Component;
/**
* @Author: Daisen.Z
* @Date: 2021/12/28 17:07
* @Version: 1.0
* @Description:
*/
@Configuration
public class GateWayConfig {
/**
* 配置了一个id为route-name的路由规则,
* 当访问地址 http://localhost:9527/guonei时会自动转发到地址:http://news.baidu.com/guonei
*/
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
// path后跟的是访问9527的地址,uri后跟的是转发的地址
routes.route("path_route_atguigu",
r -> r.path("/guonei")
.uri("http://news.baidu.com/guonei")).build();
return routes.build();
}
}
2. 启动测试
重启GateWay9527工程
访问
http://localhost:9527/guonei
会发现会被转发到百度新闻,证明我们的配置生效。
六、GateWay动态路由
为了能动态的支持扩容及负载均衡,我们不能将GateWay配置的路由URI写死,而是应该以服务名去调用。
默认情况下Gateway会根据注册中心注册的服务列表,
以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能。
1. 修改9527配置文件
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
#- After=2020-02-21T15:51:37.485+08:00[Asia/Shanghai]
#- Cookie=username,zzyy
#- Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式
eureka:
instance:
hostname: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
2. 启动测试
将8002工程也改成只注册到单节单7001注册中心,后边的删掉
重新启动7001、9527、8001和8002工程
访问9527的路由接口
http://localhost:9527/payment/get/1
多次刷新,会发现访问的端口在8001和8002间切换,动态路由配置成功。
七、GateWay的Predicate断言
我们之前加的已经有一种断言
他代表的意思是对路径的断言,匹配到,就进行转发,GateWay还支持了其他多种方式的断言,来提高我们的匹配准确度(可以理解为and条件)。
我们查看官网第4条关于断言的配置,看都支持哪些
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/#the-before-route-predicate-factory
常用的断言:
- Path Route Predicate: 路径匹配
- After Route Predicate: 在某个时间之后的请求才开始匹配,之前的就算匹配上了也不会转发
与之相同的还有Before、Between
- After=2020-02-05T15:10:03.685+08:00[Asia/Shanghai]
时间格式如果获得(主要是获取末尾中括号里面的时区)?
public static void main(String[] args) {
ZonedDateTime now = ZonedDateTime.now();
System.out.println(now);
}
修改完之后可以自行测试,看是否某个时间之后才能访问
3. Cookie Route Predicate: 带某个Cookie才能访问,前边是Cookie的name,后边是正则匹配值
- Cookie=username,zzyy
测试:
给我们的8001和8002的controller加上lb接口
@GetMapping(value = "/payment/lb")
public String getPaymentLB()
{
return serverPort;
}
启动7001,8001,8002,9527工程,使用curl进行测试(curl类似于postman的命令行模式,使用postman也可)
curl下载地址
链接:https://pan.baidu.com/s/1ExwK5IyvKqAzknnsQY5Qgg
提取码:7jjj
在curl的解压目录下调出cmd窗口,访问接口,注意这时没有带cookie
curl http://localhost:9527/payment/lb
访问不成功
接下来我们携带上cookie,再访问一次
curl http://localhost:9527/payment/lb --cookie "username=zzyy"
哎,访问就通过了,66的
4. Header Route Predicate: 带有某个header才能访问,前边是属性名称,后边是一个正则表达式
- Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式
重启测试
curl http://localhost:9527/payment/lb -H "X-Request-Id:123"
能够成功访问
改成负数试一下
curl http://localhost:9527/payment/lb -H "X-Request-Id:-123"
会发现访问不通过,接口已经猩猩的兄弟废废了
- Host Route Predicate:带某个Host才能访问
- Method Route Predicate:使用某种请求方法才能访问。如Get,Post
- Query Route Predicate: 要有某个参数,并且符合正则才能访问
具体的配置信息请查看官网:
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/#the-before-route-predicate-factory
说白了,Predicate就是为了实现一组匹配规则,
让请求过来找到对应的Route进行处理。
八、GateWay的Filter过滤器
1. 是个啥
路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用。Spring Cloud Gateway 内置了多种路由过滤器,他们都由GatewayFilter的工厂类来产生。
2. 周期
分为前置和后置,就是请求前过滤和请求后过滤
3. 种类-局部的
官网上给出的有几十种,感兴趣可以自己查看,使用起来很简单,和断言类似,可以自行上官网上查看。
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/#the-addrequestparameter-gatewayfilter-factory
4. 种类-全局的
全局的也有八九种,使用的方式和断言类似,可以自行上官网上查看。
5. 自定义过滤器(重点)
- 自定义过滤器(重点)
主要是自定义一个过滤器,实现两个接口implements GlobalFilter,Ordered
类,有注释,自己看一下就能懂
package com.atguigu.springcloud.filter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Date;
/**
* @Author: Daisen.Z
* @Date: 2021/12/31 17:50
* @Version: 1.0
* @Description:
*/
@Component
@Slf4j
public class MyLogGatewayFilter implements GlobalFilter, Ordered {
// 拦截查询条件不带uname的请求
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("**************** come in MyLogGatewayFilter :"+new Date());
String uname = exchange.getRequest().getQueryParams().getFirst("uname");
if (uname == null){
// 非法用户
log.info("**************** 用户名为null,非法用户 ");
// 不通过就给一个有好的提示,这里演示给个状态码
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
// 没有被拦截就放行
return chain.filter(exchange);
}
// 加载过滤器的顺序,顺序越小优先级越高
@Override
public int getOrder() {
return 0;
}
}
注意:之前的配置文件要改一下
启动8001,8002,7001,9527工程进行测试
访问
http://localhost:9527/payment/lb?uname=zs
正常
访问不带uname的:
http://localhost:9527/payment/lb
总结
- GateWay官网https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/。
- GateWay的目标是提供统一的路由方式,且基于Filter链的方式提供了网关的基本功能,例如:安全、监控/指标、限流。
- 说白了,Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理,而Filter就是过滤器,可以在请求接口执行前或请求接口返回后做一些操作如记录入局日志等。
- 网关作为统一入口来匹配不同的请求进行转发,方便接口的统一控制。
- 点赞
- 收藏
- 关注作者
评论(0)