Gateway案例

news2025/1/10 11:18:32

官网:Spring Cloud Gateway

中文文档:Spring Cloud Gateway 2.1.0 中文官网文档 - 腾讯云开发者社区-腾讯云

一、网关介绍:

网关就是当前微服务的统一入口
通常在微服务项目中,只有网关项目是暴露在网络里的,其他服务一般都是在内网里,
用户访问网关,网关根据访问的路径,来进行路由
Gateway 网关也是微服务的一部分,需要将项目注册到Nacos
因为某一个服务可能存在多台服务器,所以也需要负载均衡依赖

二、案例:

2.1.依赖项
 

<!--Gateway网关依赖-->
<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>
<!--Nacos依赖-->
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

 2.2.yml文件配置的方式实现路由

纯yml文件配置的方式实现路由(包含Nacos注册与Gateway配置)

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        # 设置nacos服务器的地址
        server-addr: localhost:8848
        # 默认为true是临时实例, 改为 false 就是永久实例
        ephemeral: true
    gateway:
      #下面编写Gateway路由配置
      routes:
          #当前路由名称
        - id: gateway-beijing
          #当匹配路由的路径时,设置访问的服务器(Nacos里注册的服务器,在那个项目的spring.application.name设置里)
          #lb是loadbalancer负载均衡的缩写
          uri: lb://beijing
          #断言 既满足条件时做某些事情
          predicates:
              #当请求路径以/bj/开头时,就会路由到上面设置的  lb://beijing  服务器
            - Path=/bj/**

        - id: gateway-shanghai
          uri: lb://shanghai
          predicates:
            - Path=/sh/**

编写配置类的方式实现配置(此种方式,配置文件里只配置Nacos注册中心即可)

2.3.用配置类进行配置

用配置类进行配置,则配置文件只需要写下边部分就够了

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        # 设置nacos服务器的地址
        server-addr: localhost:8848

配置类如下:

package cn.tedu.gateway.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;


@Configuration
public class GatewayConfiguration {
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        routes
                //名称为gateway-beijing的路由,匹配地址 /bj/** 使用  Nacos  里的 beijing 去处理请求 lb为负载均衡
                .route("gateway-beijing", r -> r.path("/bj/**").uri("lb://beijing"))
                .route("gateway-shanghai", r -> r.path("/sh/**").uri("lb://shanghai"))
            
                .route("gateway-after", r ->
                    //匹配路径为 /show
                    r.path("/show")
                    //多个断言之间,使用and方法连接
                    .and()
                    //断言时间,只能在此时间后访问
                    .after(ZonedDateTime.parse("2022-08-25T10:00:00+08:00[Asia/Shanghai]"))
                    .and()
                    //断言查询参数,必须包含age,如  /show?age=1
                    .query("age")
                    //设置过滤器,在过滤器内添加请求参数,那么实际控制器收到的请求为: /show?age=1&name=tom
                    .filters(f -> f.addRequestParameter("name", "tom"))
                    //使用shanghai去处理请求
                    .uri("lb://shanghai")
                 )
                //将路径为 /personal 的请求,转到石墨文档,石墨文档收到请求后,请求地址为: https://shimo.im/personal
                .route("gateway-shimo", r -> r.path("/personal").uri("https://shimo.im"))
                .build();
        return routes.build();
    }
    
}

2.4.配置文件中添加动态路由:

动态路由(默认关闭)

开启方式:在配置文件中设置spring.cloud.gateway.discovery.locator.enabled=true

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        # 设置nacos服务器的地址
        server-addr: localhost:8848
        # 默认为true是临时实例, 改为 false 就是永久实例
        ephemeral: true
    gateway:
      discovery:
        locator:
          #开启动态路由
          enabled: true

开启后,无需编写配置类或yaml内的配置
访问路由时,需要带上Nacos里注册的名称

如,假设
Gateway网关的端口为9000
北京服务器端口为9001(Nacos注册名为beijing)
上海服务器端口为9002(Nacos注册名为shanghai)
2个服务器同时都暴露了一个 /xx/show 的接口

原访问地址:


http://localhost:9001/bj/show
http://localhost:9002/sh/show
 

通过网关的访问地址:

http://localhost:9000/beijing/bj/show   >>等价于>>   http://localhost:9001/bj/show
http://localhost:9000/shanghai/sh/show    >>等价于>>   http://localhost:9002/sh/show
 

其中,地址中的 **shanghai/beijing **即为Nacos里,注册服务器的名称,请求会被路由到相应的服务器

注:动态路由与手动设置配置文件或编写配置类可以同时存在,并且同时生效

2.5.网关模块用knife4j测试:

在网关中使用swagger/knife4j来测试其他服务的接口
在pom文件中添加swagger/knife4j依赖
添加依赖

<dependency>
  <groupId>com.github.xiaoymin</groupId>
  <artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>

添加以下3个类…添加后,
访问 http://网关地址:网关端口/服务名称/doc.html 来访问
如下,其中 nacos-stock 是注册到Nacos的一个服务名称

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

控制器类:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import springfox.documentation.swagger.web.*;

import java.util.Optional;

@RestController
@RequestMapping("/swagger-resources")
public class SwaggerController {
    @Autowired(required = false)
    private SecurityConfiguration securityConfiguration;
    @Autowired(required = false)
    private UiConfiguration uiConfiguration;
    private final SwaggerResourcesProvider swaggerResources;
    @Autowired
    public SwaggerController(SwaggerResourcesProvider swaggerResources) {
        this.swaggerResources = swaggerResources;
    }
    @GetMapping("/configuration/security")
    public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
        return Mono.just(new ResponseEntity<>(
            Optional.ofNullable(securityConfiguration).orElse(
            SecurityConfigurationBuilder.builder().build()), 
           HttpStatus.OK));
    }
    @GetMapping("/configuration/ui")
    public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
        return Mono.just(new ResponseEntity<>(
            Optional.ofNullable(uiConfiguration).orElse(
            UiConfigurationBuilder.builder().build()),
             HttpStatus.OK));
    }
    @GetMapping("")
    public Mono<ResponseEntity> swaggerResources() {
        return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
    }
}

配置类

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Component
public class SwaggerProvider implements SwaggerResourcesProvider {
    /**
     * 接口地址
     */
    public static final String API_URI = "/v2/api-docs";
    /**
     * 路由加载器
     */
    @Autowired
    private RouteLocator routeLocator;
    /**
     * 网关应用名称
     */
    @Value("${spring.application.name}")
    private String applicationName;

    @Override
    public List<SwaggerResource> get() {
        //接口资源列表
        List<SwaggerResource> resources = new ArrayList<>();
        //服务名称列表
        List<String> routeHosts = new ArrayList<>();
        // 获取所有可用的应用名称
        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 = new HashSet<>();
        routeHosts.forEach(host -> {
            // 拼接url
            String url = "/" + host + API_URI;
            //不存在则添加
            if (!existsServer.contains(url)) {
                existsServer.add(url);
                SwaggerResource swaggerResource = new SwaggerResource();
                swaggerResource.setUrl(url);
                swaggerResource.setName(host);
                resources.add(swaggerResource);
            }
        });
        return resources;
    }
}

过滤器类

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;

@Component
public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory {
    private static final String HEADER_NAME = "X-Forwarded-Prefix";

    private static final String URI = "/v2/api-docs";

    @Override
    public GatewayFilter apply(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);
        };
    }
}

注意: 由于Spring Boot,Spring Cloud Gateway都带有spring-boot-starter-web依赖,但是前者使用Tomcat,后者使用Netty,会导致冲突,项目无法启动
解决办法有3个

1. 在配置文件中,指定spring.main.web-application-type=reactive

spring:
  main:
    web-application-type: reactive

2. 添加spring-boot-starter-web依赖时,排除Tomcat

<!-- web实例 -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
  <exclusions>
    <exclusion>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
    </exclusion>
  </exclusions>
</dependency>

3.不添加spring-boot-starter-web依赖,只添加Spring Cloud Gateway依赖

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/490355.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Tomcat安装步骤及详细配置教程(2022最新版)

网上的tomcat安装及配置教程一大堆&#xff0c;但是好多都过时了&#xff0c;根本不适用现在的版本&#xff0c;今天凯歌整理一篇Tomcat安装步骤及详细配置教程&#xff0c;2022年最新版~ Tomcat安装及配置教程主要分为四步&#xff1a; 步骤一&#xff1a;首先确认自己是否已…

ChatGPT登录操作扫盲级教程,附ChatGPT登录常见报错及处理技巧

文 / 韩彬&#xff08;微信公众号&#xff1a;量子论&#xff09; 有了帐号&#xff0c;我们自然可以使用ChatGPT尽情玩耍了。 知识扩展&#xff1a;ChatGPT是啥&#xff0c;以及注册的问题&#xff0c;可以看《ChatGPT常见问题手册&#xff0c;通俗易懂版&#xff0c;3分钟了解…

Windows微信聊天图片文件的解码和图片、视频占满电脑磁盘空间的清理

1 问题现象 我的Windows版微信最近老是提示“磁盘空间不足200MB&#xff0c;需及时清理磁盘”。 使用文件资源管理器查看我的电脑磁盘使用情况&#xff0c;发现C盘只剩下174MB空间可用。系统盘C盘空间耗尽已经严重影响电脑的使用。 2 问题分析 2.1 磁盘空间占用情况分析 由于…

【学习笔记】pandas提取excel数据形成三元组,采用neo4j数据库构建小型知识图谱

前言 代码来自github项目 neo4j-python-pandas-py2neo-v3&#xff0c;项目作者为Skyelbin。我记录一下运行该项目的一些过程文字以及遇到的问题和解决办法。 一、提取excel中的数据转换为DataFrame三元组格式 from dataToNeo4jClass.DataToNeo4jClass import DataToNeo4j imp…

实操带你使用Mybatis_plus(2)

文章目录 一、通用ServiceService CRUD 接口a> IServiceb>创建Service接口和实现类测试 二、常用注解1、TableName2、TableId雪花算法3、TableField4、TableLogic 一、通用Service Service CRUD 接口 通用 Service CRUD 封装IService 接口&#xff0c;进一步封装 CRUD …

选择无服务器:Babbel 的迁移故事

Babbel 是什么&#xff1f; Babbel 是一个完整的语言学习产品生态系统&#xff0c;囊括了世界上最畅销的语言学习应用程序。我们已售出超过 1000 万份订阅和超过 60,000 门涵盖 14 种语言的课程&#xff0c;创造了全球第一语言学习目的地。自 2007 年推出产品的第一天起&#…

vivid源码分析

vivid源码分析 文章目录 vivid源码分析如何编写V4L2驱动分析vivid.c的open,read,write,ioctl过程openreadioctlv4l2_ctrl_handler使用过程 如何编写V4L2驱动 分配/设置/注册v4l2_device.v4l2_device_register,v4l2_device(辅助作用&#xff0c;提供自旋锁&#xff0c;引用计数…

LeetCode单链表OJ题目做题思路分享

目录 移除链表元素链表的中间节点链表中倒数第K个节点合并两个有序链表 移除链表元素 链接: link 题目描述&#xff1a; 思路分享&#xff1a; 我们上个博客分享了第一种方法&#xff0c;下面我们分析第二种方法&#xff1a;思路就是将每一个不等于我们要删除的值的节点依次尾…

【硬件】嵌入式电子设计基础之产品实践

电子技术是一门实践性非常强的学科&#xff0c;学习电子元器件基础知识和设计技能&#xff0c;最终为的是把具备一定功能的电路板制作出来&#xff0c;解决科研、生产、生活中的实际问题。 本篇文章从实际的电子产品出发&#xff0c;让您能够初步体验电子产品的硬件设计过程&am…

【Unity编辑器】拓展Project视图

目录 1、拓展右键菜单 2、创建一个菜单 3、拓展布局 4、监听事件 首先创建一个Editor文件夹&#xff0c;此文件夹可以作为多个目录的子文件夹存在&#xff0c;这样开发者就可以按照功能来划分&#xff0c;将不同功能的编辑代码放在不同的Editor目录下。 如果属于编辑模式下…

多维时序 | MATLAB实现BP、SVM、LSSVM多变量时间序列预测(考虑历史特征的影响,多指标、多图输出)

多维时序 | MATLAB实现BP、SVM、LSSVM多变量时间序列负荷预测(考虑历史特征的影响&#xff0c;多指标、多图输出) 目录 多维时序 | MATLAB实现BP、SVM、LSSVM多变量时间序列负荷预测(考虑历史特征的影响&#xff0c;多指标、多图输出)预测效果基本介绍程序设计学习总结参考资料…

大规模并行处理架构Doris概述篇

目录 1 Doris概述篇1.1 前言1.2 Doris简介1.3 核心特性1.4 Doris特点1.5 Doris发展历程1.6 对比其他的数据分析框架1.7 开源OLAP引擎对比1.8 使用场景1.9 使用用户 2 Doris原理篇2.1 名称解释2.2 整体架构2.3 元数据结构2.4 数据分发 1 Doris概述篇 1.1 前言 Doris由百度大数据…

BI 商业智能和报表,傻傻分不清楚?一文给你讲透

我们经常所听到的大数据、商业智能BI、数据分析、数据挖掘等我们都统称为数据信息化。数据信息化可以帮助企业全面的了解企业的经营管理&#xff0c;从经验驱动到数据驱动&#xff0c;降低情绪、心理等主观影响&#xff0c;形成以数据为基础的业务决策支撑&#xff0c;提高决策…

C++入门(内容补充)

目录 前言 1.auto关键字 1.1 auto的使用细则 1.2 auto不能推导的场景 2. 基于范围的for循环(C11) 2.1 范围for的使用条件 3.指针空值nullptr(C11) 3.1 C98中的指针空值 前言 之前给大家更新了一系列关于C的基础语法&#xff0c;那么今天小编再给大家进行部分内容的补充…

【C++】线程库

文章目录 线程库&#xff08;thread&#xff09;线程安全锁实现两个线程交替打印1-100 线程库&#xff08;thread&#xff09; 在C11之前&#xff0c;涉及到多线程问题&#xff0c;都是和平台相关的&#xff0c;比如Windows和Linux下各有自己的接口&#xff0c;这使得代码的可…

python函数的递归调用

引入 函数既可以嵌套定义也可以嵌套调用。嵌套定义指的是在定义一个函数时在该函数内部定义另一个函数&#xff1b;嵌套调用指的是在调用一个函数的过程中函数内部有调用另一个函数。而函数的递归调用指的是在调用一个函数的过程中又直接或者间接的调用该函数本身。 函数递归…

Python入门(三)变量和简单数据类型(二)

变量和简单数据类型&#xff08;二&#xff09; 1.数1.1 整数操作1.2 浮点数操作1.3 整数和浮点数1.4 数中的下划线1.5 同时给多个变量赋值1.6 常量 2.注释2.1 如何编写注释2.2 编写什么样的注释 作者&#xff1a;Xiou 1.数 数在编程中是经常使用的一种数据类型&#xff0c;可…

【目标检测论文阅读笔记】Dynamic Head: Unifying Object Detection Heads with Attentions

Abstract 在目标检测中结合定位和分类的复杂性导致了方法的蓬勃发展。以前的工作试图提高各种目标检测头的性能&#xff0c;但未能提出统一的观点。在本文中&#xff0c;我们提出了一种新颖的动态头部框架 来统一目标检测头部和注意力。通过在用于尺度感知的特征级别之间、用于…

嵌入式Linux:FrameBuffer 和 DRM/KMS(一)

文章目录 前言: Linux 的两种显示方案FrameBufferDRM1、GEM2、KMS 参考&#xff1a;RK3399 探索之旅 / Display 子系统 / 基础概念 参考&#xff1a;DRM架构介绍&#xff08;一&#xff09; 前言: Linux 的两种显示方案 包括&#xff1a; FBDEV: Framebuffer Device DRM/KM…

【MediaSoup c#】 worker的创建

js rust 不太熟,c# 似乎还好懂一些。学习media soup 的各个组件及大体使用方式学习其设计理念。MediasoupServer 管理worker列表 worker的表达是通过 IWorker 抽象类 拥有一个observer 实例 (EventEmitter): /// <summary>/// Observer instance./// </summary&g…