Spring Cloud Gateway--用GlobalFilter实现动态路由--实例/方案

原文网址:Spring Cloud Gateway--用GlobalFilter实现动态路由--实例/方案_IT利刃出鞘的博客-CSDN博客

简介

        本文用示例介绍Spring Cloud Gateway的动态路由(强制路由)的方案。

为什么需要动态路由

场景1:开发环境:提高调试效率

        在开发软件(例如Idea)上打断点调试是最快的调试方法。如果是与前端联调,前端一般将请求地址设置为网关,这样会负载均衡到不同的机器上,不能指定到自己的电脑。

        如果有了动态路由,那么可以指定某个url直接路由到自己电脑。

场景2:线上环境:可以快速将请求切到某个服务器

        如果没有动态路由,代码上线时如果出了问题,需要回滚代码,重新部署,很慢。

        如果有了动态路由,代码上线时,只更新部分实例,然后将流量切过去,如果有问题,立马切到其他未更新的实例即可。

实例

gateway项目

自定义GlobalFilter(核心)

package com.knife.dynamic;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import java.net.URI;

/**
 * 动态路由
 */
@Slf4j
@Component
public class Router4jClientFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest originalRequest = exchange.getRequest();

        // 可获得所有请求参数
        // Map<String, String> cachedRequestBody = exchange
        // .getAttribute(ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR);

        //获取域名+端口后的path
        String rawPath = originalRequest.getURI().getRawPath();

        // todo 从redis中取出所有url,然后用rawPath去匹配

        String host = "localhost";
        int port = 9012;

        URI originUri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);

        URI newUri = UriComponentsBuilder.fromUri(originUri)
                .host(host)
                .port(port)
                .build()
                .toUri();

        //重新封装request对象
        ServerHttpRequest newRequest = originalRequest.mutate().uri(newUri).build();

        // NettyRoutingFilter 最终从GATEWAY_REQUEST_URL_ATTR 取出uri对象进行http请求
        // 所以这里要将新的对象覆盖进去
        exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, newUri);

        return chain.filter(exchange.mutate().request(newRequest).build());

        // 也可以加回调方法
        // return chain.filter(exchange.mutate().request(newRequest).build())
        //         .then(Mono.fromRunnable(() -> {
        //             //请求完成回调方法 可以在此完成计算请求耗时等操作
        //         }));
    }

    /**
     * 这里不能用@Order,必须实现Ordered接口
     * 值必须大于10150。原因:Gateway有自己的过滤器,两个比较重要的如下:
     *      RouteToRequestUrlFilter:将根据Route将网关请求转为真实的请求。order = 10000
     *      ReactiveLoadBalancerClientFilter:负载均衡。order = 10150
     */
    @Override
    public int getOrder() {
        return 15000;
    }
}

配置文件

server:
  port: 6001

spring:
  application:
    name: gateway
  cloud:
    gateway:
      discovery:
        locator:
          # 解析微服务名称为主机名和端口,实现动态路由
          enabled: true
          # 服务名使用小写
          lower-case-service-id: true
    nacos:
      discovery:
        server-addr: localhost:8848

# 配置Gateway日志等级,输出转发细节信息
logging:
  level:
    org.springframework.cloud.gateway: debug

依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <!--<version>2.3.7.RELEASE</version>-->
        <version>2.4.13</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.knife</groupId>
    <artifactId>gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>gateway</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-loadbalancer</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>com.knife</groupId>
            <artifactId>router4j-client</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2020.0.1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2021.1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.4.13</version>
            </plugin>
        </plugins>
    </build>

</project>

nacos项目

nacos直接从官方下载,然后运行即可。

见:Nacos 快速开始

业务项目

Controller

package com.knife.order.controller;

import com.knife.common.entity.Result;
import com.knife.order.entity.Order;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * http://localhost:9011/order/create/?userId=1&productId=1&count=10&money=100
 */
@Slf4j
@RestController
@RequestMapping("order")
public class OrderController {
    // 正常流程
    @PostMapping("create")
    public Result create(Order order) {
        log.info("订单服务:创建订单:{}", order);

        return new Result().message("创建订单成功");
    }

}

配置文件

bootstrap.yml

spring:
  application:
    name: order
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848

application.yml

server:
  port: 9012

spring:
  application:
    name: order
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.13</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <packaging>pom</packaging>
    <groupId>com.knife</groupId>
    <artifactId>parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2020.0.1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2021.1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencyManagement>


        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2021.1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

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

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <scope>provided</scope>
        </dependency>

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

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <exclusions>
                <exclusion>
                    <groupId> springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-loadbalancer</artifactId>
        </dependency>

        <dependency>
            <groupId>com.knife</groupId>
            <artifactId>common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.4.13</version>
            </plugin>

        </plugins>
    </build>

</project>

测试

启动程序

1.启动nacos服务

执行:startup.cmd -m standalone

2.启动gateway服务

3.启动两个业务服务(order)

分别以不同的端口号启动。

先以上边的配置启动,再修改application.yml里的端口号,然后启动。修改后的为:

​server:
  port: 9013

spring:
  application:
    name: order
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

Idea同一个服务启动两个实例的方法见:Intellij IDEA--项目设置--导入项目/导入包/乱码/JDK版本_IT利刃出鞘的博客-CSDN博客​​​​​​

搜索关键字“同一服务启动多个实例”

测试

POST方式请求:http://localhost:6001/order/order/create/?userId=1&productId=1&count=10&money=100

请求10次,结果:请求全部到了指定的服务:9012端口

9012端口的服务:

9013端口的服务:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT利刃出鞘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值