0


SpringGateway网关

SpringGateway

SpringGateway网关

奈非框架简介

早期(2020年前)奈非提供的微服务组件和框架受到了很多开发者的欢迎

这些框架和SpringCloud Alibaba的对应关系我们要了解

现在还有很多旧项目维护是使用奈非框架完成的微服务架构

Nacos对应Eureka都是注册中心

Dubbo对应Ribbon+feign都是实现微服务远程RPC调用的组件

Sentinel对应Hystrix都是做项目限流熔断降级的组件

SpringGateway对应Zuul都是网关组件

Gateway框架不是阿里写的,是Spring提供的

什么是网关

"网"指网络,"关"指关口或关卡

网关:就是指网络中的关口\关卡

网关就是当前微服务项目的"统一入口"

程序中的网关就是当前微服务项目对外界开放的统一入口

所有外界的请求都需要先经过网关才能访问到我们的程序

提供了统一入口之后,方便对所有请求进行统一的检查和管理

在这里插入图片描述

网关的主要功能有

  • 将所有请求统一经过网关
  • 网关可以对这些请求进行检查
  • 网关方便记录所有请求的日志
  • 网关可以统一将所有请求路由到正确的模块\服务上

“路由"的近义词就是"分配”

Spring Gateway简介

我们使用Spring Gateway作为当前项目的网关框架

Spring Gateway是Spring自己编写的,也是SpringCloud中的组件

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Spring Gateway官网

https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/

网关实例项目git地址

https://gitee.com/jtzhanghl/gateway-demo.git

为什么选择 Gateway

在这里插入图片描述

Gateway 的特性

在这里插入图片描述

Gateway 与 Zuul 的区别

在这里插入图片描述
在这里插入图片描述

Gateway 的三大核心概念

Route (路由)

在这里插入图片描述

Predicate (断言)

在这里插入图片描述

Filter (过滤)

在这里插入图片描述

总结

在这里插入图片描述

Gateway 的工作流程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

简单网关演示

SpringGateway网关是一个依赖,不是一个软件

所以我们要使用它的话,必须先创建一个SpringBoot项目

这个项目也要注册到Nacos注册中心,因为网关项目也是微服务项目的一个组成部分

beijing和shanghai是编写好的两个项目

gateway项目就是网关项目,需要添加相关配置

<dependencies><!--   SpringGateway的依赖   --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><!--   Nacos依赖   --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--   网关负载均衡依赖    --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency></dependencies>

我们从yml文件配置开始添加

server:port:9000spring:application:name: gateway
  cloud:nacos:discovery:server-addr: localhost:8848gateway:# route就是路由的意思,下面就是配置路由信息# 一个网关项目大多会配置很多路由# 所以这个网关配置是一个List集合类型routes:# List类型元素赋值时,每个元素都要以"-"开头,在这个"-"之后,# 编写的所有内容,都是同一个对象的属性值# id设置当前路由的名称,也是唯一标识,和其它配置没有对应关系,注意不能和之后的id名称重复即可-id: gateway-beijing
          # uri属性配置的是路由目标服务器的名称,"beijing"指注册到Nacos名称为"beijing"的模块# lb就是负载均衡LoadBalance的缩写,标识路由支持负载均衡uri: lb://beijing
          # predicate是断言的意思,断言指某些条件满足时,执行某些操作# predicates配置也是一个List类型的属性,所以它赋值也要以"-"开头predicates:# 下面是断言的内容,Path表示判断路径,"/bj/**"表示判断路径是否以"/bj/"开头# 当断言条件满足时,就会按上面uri的配置,路由到该服务器模块# ↓   P是大写的!!!!!- Path=/bj/**

先启动nacos

再启动beijing

最后启动gateway

网关多路由配置

上面只配置了一个beijing的路由设置

下面我们修改yml文件也实现shanghai的路由设置

gateway:# route就是路由的意思,下面就是配置路由信息# 一个网关项目大多会配置很多路由# 所以这个网关配置是一个List集合类型routes:-id: gateway-shanghai
      uri: lb://shanghai
      predicates:- Path=/sh/**# beijing配置略

在保证nacos启动的情况下

beijing服务器如果启动无需重启

启动shanghai项目

最后重启网关

测试网关路由到两个模块的效果

http://localhost:9000/bj/show可以访问beijing服务器的资源

http://localhost:9000/sh/show可以访问shanghai服务器的资源

以此类推,再有很多服务器时,我们都可以仅使用9000端口号来将请求路由到正确的服务器

就实现了gateway成为项目的统一入口的效果

Gateway配置路由的两种方式

  • 在配置文件yml中配置
  • 代码中注入RouteLocator的Bean

以第二种为例, 业务需求 - 通过端口为9527网关访问到百度网址
百度国内新闻网址,需要外网 - http://baidu.com

编码: 创建 config.GateWayConfig 类
在这里插入图片描述

packagecom.atguigu.springcloud.config;importorg.springframework.cloud.gateway.route.RouteLocator;importorg.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;@ConfigurationpublicclassGateWayConfig{@BeanpublicRouteLocatorcustomRouteLocator(RouteLocatorBuilder routeLocatorBuilder){RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();

        routes.route("path_route_atguigu",
                r -> r.path("/guonei").uri("http://baidu.com")).build();return routes.build();}}

浏览器输入http://localhost:9527/guonei,返回http://baidu.com相同的页面。

动态路由

网关项目的配置会随着微服务模块数量增多而变得复杂,维护的工作量也会越来越大

所以我们希望gateway能够设计一套默认情况下自动路由到每个模块的路由规则

这样的话,不管当前项目有多少个路由目标,都不需要维护yml文件了

这就是我们SpringGateway的动态路由功能

在这里插入图片描述

配置文件中开启即可

gateway:discovery:locator:# 开启动态路由功能,默认值是关闭的# 动态路由规则:在网关端口号后,先编写要路由目标服务器注册到Naocs的名称# 在编写访问这个服务器的具体路径# 例如要访问 localhost:9001/bj/show  -> localhost:9000/beijing/bj/showenabled:true

按上面修改完配置之后

我们可以重启gateway来测试动态路由路径是否生效

动态路由生成规则为:在网关端口号后先写要路由到的目标服务器在nacos注册的名称,再编写具体路径

内置断言

我们上面章节在网关配置中使用了predicates(断言)的配置

断言的意思就是判断某个条件是否满足

我们之前使用了Path断言,判断请求的路径是不是满足条件,例如是不是/sh/** /bj/**

如果路径满足这个条件,就路由到指定的服务器

但是Path实际上只是SpringGateway提供的多种内置断言中的一种
在这里插入图片描述

还有很多其它断言

  • After
  • Before
  • Between
  • Cookie
  • Header
  • Host
  • Method
  • Path
  • Query
  • Remoteaddr

时间相关

After,Before,Between

判断当前时间在指定时间之前,之后或之间的操作

如果条件满足可以执行路由操作,否则拒绝访问

表示时间的格式比较特殊,先使用下面代码获得时间

ZonedDateTime.now()

运行程序输出,可获得当前时间,这个时间的格式可能是

2023-04-21T11:13:54.967+08:00[Asia/Shanghai]

下面在yml配置中添加新的断言配置

使用After设置必须在指定时间之后访问

routes:-id: gateway-shanghai
    uri: lb://shanghai
    predicates:- Path=/sh/**# After是时间断言,判断当前请求访问时的时间是否晚于配置的时间# 如果成立正常访问,如果判断不成立,返回404错误,多个断言之间是"与"的关系- After=2023-04-21T11:21:30.967+08:00[Asia/Shanghai]

使用Before设置必须在指定时间之前才能访问服务

否则发生404错误拒绝访问

predicates:- Path=/sh/**- Before=2023-04-21T11:23:20.967+08:00[Asia/Shanghai]

使用Between设置必须在指定时间之间访问

predicates:- Path=/sh/**- Between=2023-04-21T11:25:25.967+08:00[Asia/Shanghai],2023-04-21T11:25:45.967+08:00[Asia/Shanghai]

要求指定参数的请求

Query断言,判断是否包含指定的参数名称,包含参数名称才能通过路由
在这里插入图片描述

predicates:- Path=/sh/**# Query断言判断请求中是否包含指定参数名称,这里设置为username,如果没有发生404错误- Query=username

重启gateway测试

必须是包含username参数的请求才能访问到指定的页面

例如:http://localhost:9000/sh/show?username=tom

带 Cookie 参数的断言

在这里插入图片描述

spring:cloud:gateway:routes:-id: cookie_route
        uri: https://example.org
        predicates:- Cookie=username, yyx

不带 Cookie 访问
在这里插入图片描述
携带 Cookie 访问
在这里插入图片描述

带 Header 参数的断言

在这里插入图片描述

spring:cloud:gateway:routes:-id: header_route
        uri: https://example.org
        predicates:- Header=X-Request-Id, \d+  # 请求头要有 X-Request-Id 属性并且值为整数的正则表达式

在这里插入图片描述

Host 断言

在这里插入图片描述

Method 断言

在这里插入图片描述

内置过滤器

Gateway还提供的内置过滤器

不要和我们学习的filter混淆

内置过滤器允许我们在路由请求到目标资源的同时,对这个请求进行一些加工或处理

在这里插入图片描述

常见过滤器也有一些

AddRequestParameter过滤器

我们给大家演示一下AddRequestParameter过滤器

它的作用是在请求中添加参数和它对应的值

routes:-id: gateway-shanghai
    uri: lb://shanghai
    filters:# 如果路由顺利成功,这个内置过滤器会自动在请求中添加下面的参数# 属性名称为age,默认值18,如果请求中包含age的值,这个值就不会生效了- AddRequestParameter=age,18predicates:- Path=/sh/**# Query断言判断请求中是否包含指定参数名称,这里设置为username,如果没有发生404错误- Query=username

在shanghai的控制器方法中添加代码接收username,age的值

@GetMapping("/show")publicStringshow(String username,Integer age){// 2023-04-21T11:13:54.967+08:00[Asia/Shanghai]System.out.println(ZonedDateTime.now());return"这里是上海!username:"+username+",age:"+age;}

重启shanghai和gateway进行测试

http://localhost:9000/sh/show?username=jerry

因为过滤器的存在,控制器可以获取网关过滤器添加的参数值

在这里插入图片描述

自定义过滤器

创建类 filter.MyLogGateWayFilter

packagecom.atguigu.springcloud.filter;importlombok.extern.slf4j.Slf4j;importorg.springframework.cloud.gateway.filter.GatewayFilterChain;importorg.springframework.cloud.gateway.filter.GlobalFilter;importorg.springframework.core.Ordered;importorg.springframework.http.HttpStatus;importorg.springframework.stereotype.Component;importorg.springframework.web.server.ServerWebExchange;importreactor.core.publisher.Mono;importjava.util.Date;@Component@Slf4jpublicclassMyLogGateWayFilterimplementsGlobalFilter,Ordered{@OverridepublicMono<Void>filter(ServerWebExchange exchange,GatewayFilterChain chain){
        log.info("*********** come in MyLogGateWayFilter:  "+newDate());String uname = exchange.getRequest().getQueryParams().getFirst("uname");if(uname ==null){
            log.info("******* 用户名为null,非法用户,o(╥﹏╥)o");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);return exchange.getResponse().setComplete();}return chain.filter(exchange);}@OverridepublicintgetOrder(){return0;// 数字越小,过滤器优先级越高}}

启动项目,正常访问携带参数,不携带报错
正常访问如:http://localhost:9527/payment/lb?uname=123
错误访问如:http://localhost:9527/payment/lb

路由配置的设计规则

路由规则解释

路由规则一定是在开发之前就设计好的

一般可以使用约定好的路径开头来实现的

例如

gateway项目

如果路径以 /bj开头,就是要访问beijing项目

如果路径以 /sh开头.就是养访问shanghai项目

csmall项目

如果路径是 /base/business开头的, 就去找nacos-business服务器

如果路径是 /base/cart开头的, 就去找nacos-cart服务器

如果路径是 /base/order开头的, 就去找nacos-order服务器

如果路径是 /base/stock开头的, 就去找nacos-stock服务器

在这里插入图片描述

csmall项目网关

创建网关项目,然后父子相认

修改子项目pom文件和依赖

<?xml version="1.0" encoding="UTF-8"?><projectxmlns="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>cn.tedu</groupId><artifactId>csmall</artifactId><version>0.0.1-SNAPSHOT</version><relativePath/><!-- lookup parent from repository --></parent><groupId>cn.tedu</groupId><artifactId>gateway</artifactId><version>0.0.1-SNAPSHOT</version><name>gateway</name><description>Demo project for Spring Boot</description><dependencies><!-- web实例 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--  Nacos注册依赖 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId></dependency></dependencies></project>

也删除test测试文件夹

application.properties换为yml

配置如下

server:port:19000spring:application:name: gateway-server
  cloud:nacos:discovery:server-addr: localhost:8848gateway:discovery:locator:# 开启动态路由enabled:truemain:# 防止SpringMVC和SpringGateway依赖冲突的配置web-application-type: reactive

网关项目的knife4j配置

我们希望配置网关之后,在使用knife4j测试时

就不来回切换端口号了

我们需要在网关项目中配置Knife4j才能实现

而这个配置是固定的,

只要是网关项目配置各个子模块的knife4j功能,就直接复制这几个类即可

csmall-finish中直接复制config\controller\filter

cn.tedu.gateway.config

SwaggerProvider

@ComponentpublicclassSwaggerProviderimplementsSwaggerResourcesProvider{/**
     * 接口地址
     */publicstaticfinalStringAPI_URI="/v2/api-docs";/**
     * 路由加载器
     */@AutowiredprivateRouteLocator routeLocator;/**
     * 网关应用名称
     */@Value("${spring.application.name}")privateString applicationName;@OverridepublicList<SwaggerResource>get(){//接口资源列表List<SwaggerResource> resources =newArrayList<>();//服务名称列表List<String> routeHosts =newArrayList<>();// 获取所有可用的应用名称
        routeLocator.getRoutes().filter(route -> route.getUri().getHost()!=null).filter(route ->!applicationName.equals(route.getUri().getHost())).subscribe(route -> routeHosts.add(route.getUri().getHost()));// 去重,多负载服务只添加一次Set<String> existsServer =newHashSet<>();
        routeHosts.forEach(host ->{// 拼接urlString url ="/"+ host +API_URI;//不存在则添加if(!existsServer.contains(url)){
                existsServer.add(url);SwaggerResource swaggerResource =newSwaggerResource();
                swaggerResource.setUrl(url);
                swaggerResource.setName(host);
                resources.add(swaggerResource);}});return resources;}}

cn.tedu.gateway.controller

SwaggerController类

@RestController@RequestMapping("/swagger-resources")publicclassSwaggerController{@Autowired(required =false)privateSecurityConfiguration securityConfiguration;@Autowired(required =false)privateUiConfiguration uiConfiguration;privatefinalSwaggerResourcesProvider swaggerResources;@AutowiredpublicSwaggerController(SwaggerResourcesProvider swaggerResources){this.swaggerResources = swaggerResources;}@GetMapping("/configuration/security")publicMono<ResponseEntity<SecurityConfiguration>>securityConfiguration(){returnMono.just(newResponseEntity<>(Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()),HttpStatus.OK));}@GetMapping("/configuration/ui")publicMono<ResponseEntity<UiConfiguration>>uiConfiguration(){returnMono.just(newResponseEntity<>(Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()),HttpStatus.OK));}@GetMapping("")publicMono<ResponseEntity>swaggerResources(){returnMono.just((newResponseEntity<>(swaggerResources.get(),HttpStatus.OK)));}}

cn.tedu.gateway.filter

SwaggerHeaderFilter类

@ComponentpublicclassSwaggerHeaderFilterextendsAbstractGatewayFilterFactory{privatestaticfinalStringHEADER_NAME="X-Forwarded-Prefix";privatestaticfinalStringURI="/v2/api-docs";@OverridepublicGatewayFilterapply(Object config){return(exchange, chain)->{ServerHttpRequest request = exchange.getRequest();String path = request.getURI().getPath();if(!StringUtils.endsWithIgnoreCase(path,URI)){return chain.filter(exchange);}String basePath = path.substring(0, path.lastIndexOf(URI));ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();return chain.filter(newExchange);};}}

gateway项目删除test文件夹

测试网关路由效果,和knife4j效果

启动Nacos\Seata\Sentinel

启动cart\stock\order\business

最后启动gateway

可以通过19000端口测试各个业务模块的功能

http://localhost:19000/nacos-stock/doc.html

http://localhost:19000/nacos-cart/doc.html

http://localhost:19000/nacos-order/doc.html

http://localhost:19000/nacos-business/doc.html

如果不使用网关一切正常,但是启动网关访问失败的话,就是gateway项目配置问题

Gateway和SpringMvc依赖冲突问题和解决

之前网关的演示项目我们添加的网关依赖

<!-- Spring Gateway 网关依赖 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency>

当前csmall项目需要配置knife4j的路由配置,需要编写一个控制器

所以我们添加了SpringMVC的依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>

这两个依赖在同一个项目中时,默认情况下启动会报错

SpringMVC框架依赖中自带一个Tomcat服务器

而SpringGateway框架中自带一个Netty的服务器

在启动项目时,两个框架中包含的服务器都想占用相同端口,因为争夺端口号的主动权而发生冲突

导致启动服务时报错

要想能够正常启动必须在yml文件配置

spring:main:web-application-type: reactive

reactive:反应的

添加这个配置之后,会Tomcat服务器会变成非阻塞的运行


本文转载自: https://blog.csdn.net/weixin_44807758/article/details/130273943
版权归原作者 櫻花 所有, 如有侵权,请联系我们删除。

“SpringGateway网关”的评论:

还没有评论