系列文章目录
SpringCloud快速入门到精通各组件原理
专栏传送门
文章目录
系列文章目录前言一、关于Zuul和GateWay二、GateWay简介三、GateWay三大概念1. Route(路由)2. Predicate(断言)3. Filter(过滤)4. 总体构建基础工程话不多说,立马开干四、GateWay9527模块搭建1. 创建cloud-gateway-gateway9527模块2. 配置路由3. 启动测试五、GateWay的两种配置方式1. 新建配置类2. 启动测试六、GateWay动态路由1. 修改9527配置文件2. 启动测试七、GateWay的Predicate断言八、GateWay的Filter过滤器1. 是个啥2. 周期3. 种类-局部的4. 种类-全局的5. 自定义过滤器(重点)总结前言
我们学习的知识框架图
说起来容易做起来难,一步一步都干完!!!
学习一定要自己动手搞一搞,不能只眼会。
学习笔记是跟着尚硅谷的视频学的:/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的核心逻辑就是路由转发和执行过滤链。
构建基础工程
先构建好基础工程(一篇一篇看过来的不用重新构建)
构建基础父工程:/weixin_43464964/article/details/121979995
Rest风格微服务:/weixin_43464964/article/details/121980366
传统分布式方法:/weixin_43464964/article/details/121999966
改造工程,抽取公共模块:/weixin_43464964/article/details/122000586
使用Eureka:/weixin_43464964/article/details/122045319
Eureka集群: /weixin_43464964/article/details/122046056
想偷懒的请下载;gitee上我上传的代码: /xiaoZ1712/cloud
基础工程构建完成的目录结构:
启动所有模块,访问
localhost:7001
显示如下,代表基础工程没问题
话不多说,立马开干
四、GateWay9527模块搭建
1. 创建cloud-gateway-gateway9527模块
模块名
cloud-gateway-gateway9527
pom.xml
<?xml version="1.0" encoding="UTF-8"?><project xmlns="/POM/4.0.0"xmlns:xsi="/2001/XMLSchema-instance"xsi:schemaLocation="/POM/4.0.0 /xsd/maven-4.0.0.xsd"><parent><artifactId>cloud</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: 9527spring:application:name: cloud-gatewayeureka:instance:hostname: cloud-gateway-serviceclient: #服务提供者provider注册进eureka服务列表内service-url:register-with-eureka: truefetch-registry: truedefaultZone: :7001/eureka
基础包
com.atguigu.springcloud
主启动类GateWayMain9527
package com.atguigu.springcloud;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.flix.eureka.EnableEurekaClient;/*** @Author: Daisen.Z* @Date: /12/28 16:26* @Version: 1.0* @Description:*/@SpringBootApplication@EnableEurekaClientpublic class GateWayMain9527 {public static void main(String[] args) {SpringApplication.run(GateWayMain9527.class,args);}}
2. 配置路由
修改Gateway9527模块配置文件,配置路由
server:port: 9527spring:application:name: cloud-gatewaycloud: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=-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-serviceclient: #服务提供者provider注册进eureka服务列表内service-url:register-with-eureka: truefetch-registry: truedefaultZone: :7001/eureka
3. 启动测试
为了方便测试,把服务注册中心7001改成单节单的
defaultZone: :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.ponent;/*** @Author: Daisen.Z* @Date: /12/28 17:07* @Version: 1.0* @Description:*/@Configurationpublic class GateWayConfig {/*** 配置了一个id为route-name的路由规则,* 当访问地址 http://localhost:9527/guonei时会自动转发到地址:/guonei*/@Beanpublic RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();// path后跟的是访问9527的地址,uri后跟的是转发的地址routes.route("path_route_atguigu",r -> r.path("/guonei").uri("/guonei")).build();return routes.build();}}
2. 启动测试
重启GateWay9527工程
访问
http://localhost:9527/guonei
会发现会被转发到百度新闻,证明我们的配置生效。
六、GateWay动态路由
为了能动态的支持扩容及负载均衡,我们不能将GateWay配置的路由URI写死,而是应该以服务名去调用。
默认情况下Gateway会根据注册中心注册的服务列表,
以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能。
1. 修改9527配置文件
server:port: 9527spring:application:name: cloud-gatewaycloud: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=-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-serviceclient: #服务提供者provider注册进eureka服务列表内service-url:register-with-eureka: truefetch-registry: truedefaultZone: :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=-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下载地址
链接:/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,PostQuery 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.ponent;import org.springframework.web.server.ServerWebExchange;import reactor.core.publisher.Mono;import java.util.Date;/*** @Author: Daisen.Z* @Date: /12/31 17:50* @Version: 1.0* @Description:*/@Component@Slf4jpublic class MyLogGatewayFilter implements GlobalFilter, Ordered {// 拦截查询条件不带uname的请求@Overridepublic 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);}// 加载过滤器的顺序,顺序越小优先级越高@Overridepublic int getOrder() {return 0;}}
注意:之前的配置文件要改一下
启动8001,8002,7001,9527工程进行测试
访问
http://localhost:9527/payment/lb?uname=zs
正常
访问不带uname的:
http://localhost:9527/payment/lb