Gateway--网关

基础概念

Spring Cloud Gateway 是微服务架构中的 API 网关,把原本散落在前端的众多微服务地址收敛为"一个统一入口",由它按请求路径判断该转给哪个服务,并在转发时融入负载均衡;

如果对于"网关"基础作用概念由遗漏的,可以参考我这篇博客:微服务--分布式基础-CSDN博客

这么一说有点像我们学习过的Nginx,他们都是网关,还是有点小区别:

Nginx 更偏向高性能的流量接入层与反向代理,Gateway 则更贴近业务、可编程能力更强

下面的学习中,我们会通过实现这两个简单需求来学习如何使用gateway网关:

实践

创建网关

由于网关它并不属于我们任何的业务,它只是我们整个项目架构的一部分,因此我们直接在项目根目录新建一个模块,专门用于网关实现

引入依赖

依次引入网关的依赖以及Nacos的注册中心(网关要通过注册中心才知道转发的微服务地址在哪里)

同时也要引入负载均衡

随后我们写简单的yml配置文件

启动类不要忘记加上"服务发现"的注解

至此网关创建完成,我们可以在nacos服务列表中看到网关

路由转发

规则配置

新建一份application-route.yml配置文件,在此配置网关的路由转发

然后依次来解释一下各个属性的作用:

id:代表网关给微服务转发请求的名字(填啥都没问题,就是用于区分其他转发的名字而已)

**uri:**要给哪一个微服务进行转发(前面的lb代表loadBalence负载均衡)

predicates:断言规则 ,只有符合该规则的请求才会转发,其中这里设定的断言规则是"Path":代表只有你的路径是xxx,该请求才会被转发到相关微服务。除了Path,还有很多其他规则可以设置,我们之后再说

uri详解

其中,你可能会疑问:uri填写的是微服务名称,我想要访问某一个微服务的某一个特定业务,比如说order微服务的"创建订单",那它是怎么访问到创建订单这个业务的呢?

其实这里是被断言规则的Path给弄混淆了,Path是判断需要转发到哪一个微服务的条件,因此我们在使用网关转发时肯定会带上路径,当Path通过了,我们的请求就被转发到相应的微服务,那我们的请求又是什么呢?那不就是我们刚刚自己写到网址中的路径吗!

比如我们在网址栏输入:http://localhost:80/order/create 访问网关,网关的Path就会判断是否符合我们的路径条件(比如我们上面设置的order),符合了,**就把"http://localhost:80/order/create"请求发送给service-order微服务**,service-order看到路径是/order/create就知道访问的是"创建订单"业务了,因此返回相关业务的数据

配置完后不要忘记在原本生效的yml文件中引入我们新写的配置

我们测试一下:

访问网关的80端口,然后对其发送创建订单的请求

可以看到请求转发成功

断言拓展

长写法

刚刚上文展示了简单的断言写法,只需要在predicates下直接写规则即可,这种简单的写法被称作"短写法",适合规则不多的时候写,对于断言还有一种"长写法",可以更加细致的定义断言,我们一起来看看:

还是以我们刚刚实现过的路径规则来举例吧:

predicates的长写法,由name(工厂名/键)和args(参数)

比较反直觉的一点:这个name并不是可以随便写命名的 ,断言所需要用到的规则被封装到不同的类中(RoutePredicateFactory断言工厂),其中不同的工厂用于封装不同的规则,比如我们的路径规则就被封装到了Path**RoutePredicateFactory中,** 因此如果我们需要使用到Path路径规则,在name这里就需要写Path(工厂前面的那个名词),这样使用的时候才会读取工厂的相关配置以及参数,才能正常供我们使用

同理,既然我们的name确定了,args就会随着name而确定,在PathRoutePredicateFactory工厂中,有patterns和matchTrailingSlash两个参数,其中的patterns就是我们前文提及的需要检测的路径,只有路径对了才会请求转发,后面的"matchTrailingSlash"简单来说就是用于管你的路径是否可以多一个"/"比如设置为true时,"/order/"和"/order"就被视作同一个路径

自定义断言

我们上文说了,断言的name不能随便写,每一个name都有他们自己独有的规则,但是毕竟是别人封装的代码,有时候我们自己的一些功能可能没办法通过别人提前预设好的断言规则实现,因此我们就可以自定义断言规则来满足我们自己的需求

比如我们希望当用户是Vip时才给访问,那么我们可以检测参数比如username,当username=vipgod时就可转发请求

自定义断言我们自己写比较繁琐,涉及到工厂的继承之类的,因此我们完全可以让AI工具帮我们实现,我们明确自己的需求即可:

复制代码
需求:帮我实现一个自定义断言。
- 断言名:<名字,如 Vip>
- 需要哪些配置参数:<参数名> = <这个参数代表什么(业务语言,如"要检查的请求参数名")>
- 什么条件算通过(要转发):<用自然语言写清楚>
- 什么条件算不通过(不转发):<...>

验收标准(我会按这个自测):
- 请求 <URL A> → 期望 <结果>
- 请求 <URL B> → 期望 <结果>

产出要求:
1. 代码写在 <哪个包> 下,带中文注释,注释讲清"为什么这么写",我要看懂原理。
2. 值不要写死在代码里,都要从配置读。
3. 最后给我三样东西:
   a) yml 配置示例(长写法 + 快捷写法各一份);
   b) 一张表说明每个配置参数的含义,并明确指出这个参数指的是"请求里的什么"
      (请求参数名?请求头?路径?方法?)------ 我怕把"配置参数"和"请求参数"搞混;
   c) 我怎么验证(几条 curl/浏览器请求 + 期望结果)。
4. 我用的是较新版本的 Spring Cloud Gateway,注意 API 有没有变动,别照抄网上旧教程的写法。
5. 需求有歧义、或你觉得会踩坑,先问我一句再动手。

可以看到实现的还是不错的

由此我们来配置好这个自定义的断言

只有符合我们的断言,才能跳转到bing页面

测试结果如下:

过滤器filter

使用场景和我们之前讲OpenFeign的Interceptor拦截器(OpenFeign-CSDN博客)差不多,作用的效果也差不多,要么在请求发出前对其修改又或者是请求发出之后修改

比如很多个远程调用的接口,此时需要统一修改请求内容,我们总不可能一个个去修改每一个接口,使用filter对请求统一修改即可。这里不过多赘述

基本使用

举一个简单的例子来介绍过滤器的基本使用吧

我们想让创建订单的响应头多一行:X-Request-Id值为Giao,那我们就可以在配置文件新增filter过滤器的配置:

测试结果如下

默认filter

其实就是一个对网关中所有路由都生效的filter,还是拿刚刚新增响应头举例吧,刚刚我们只配置在了order微服务,现在我希望让他对于所有的网关路由都实现新增响应头,那么我们就可以使用默认filter去实现:

可以看到访问product微服务也有我们新增的响应头了

全局filter

简单来说他就是一个"可以自定义"并且同时对全局网关路由生效的filter,它不用配置在我们的yml文件中,直接注入到Spring容器中就直接生效

简单写了一个全局filter,让他输出请求响应时间

可以看到,无论什么请求,都有被记录响应时间,证明全局生效了

自定义filter工厂

刚刚的全局filter必须是全局生效,如果我们只想让某一个网关路由生效怎么办,我们只需要再自定义一个自己的filter工厂即可,和刚刚的全局filter不同,放在容器后还必须得再yml文件中配置才可以生效

简单写了一个往响应头中添加jwt令牌的filter

java 复制代码
@Component
@Slf4j
public class OnceTokenGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {

    @Override
    public GatewayFilter apply(NameValueConfig config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

                // 放行 -> 等这一趟请求/响应走完 -> 再往响应头里塞一个一次性令牌
                return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                    ServerHttpResponse response = exchange.getResponse();
                    HttpHeaders headers = response.getHeaders();

                    String value = config.getValue();
                    if ("uuid".equalsIgnoreCase(value)) {
                        // 配置写 uuid -> 每次请求现生成一个 UUID(所以叫"一次性")
                        value = UUID.randomUUID().toString();
                    } else if ("jwt".equalsIgnoreCase(value)) {
                        // 配置写 jwt -> 用一段示例 JWT(真实项目里应由签发方生成)
                        value = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
                    }

                    headers.add(config.getName(), value);
                    log.info("[OnceToken] 响应头写入 {}={}", config.getName(), value);
                }));
            }
        };
    }
}

让其只对order微服务生效

测试:

order微服务相关路由多了token响应头

而其余路由没有多

跨域配置

前后端相互访问时,需要跨域,因此我们需要给网关做一个跨域配置,直接如下图所示配置即可

相关推荐
海马11 小时前
Spring Cloud 开发知识整理
spring·spring cloud·wpf
骇客野人11 小时前
微服务多级缓存架构设计
缓存·微服务·架构
4SAPI12 小时前
AI API Gateway平台哪个好?从架构视角看企业多模型网关选型与实践
java·大数据·人工智能·gateway·php
我不是阵雨12 小时前
微服务幂等性深度解剖
微服务·云原生·架构
JJJennie77713 小时前
大模型网关怎么选?MAI Gateway vs 开源方案全面对比
网关·开源·gateway·ai网关
栗子~~13 小时前
SpringCloud Gateway 基于 Nacos 实现动态路由
java·spring cloud·gateway
code_slave(码畜)15 小时前
微服务架构落地:消息队列架构设计(上篇)——异步解耦、削峰填谷,看懂业务事件流转本质
spring boot·spring cloud·微服务·云原生·架构
code_slave(码畜)19 小时前
微服务架构落地:消息队列架构设计(下篇)——消息堆积、死信治理与集群监控告警实战
spring boot·spring cloud·微服务·云原生·中间件·架构
知守观1 天前
feign-core 就在依赖树里,运行时却找不到类:一次 provided scope 引发的启动失败排查
java·spring cloud·maven