基于Eureka搭建Springcloud微服务-9.使用GateWay实现网关功能

lingwh原创2020年5月29日大约 12 分钟约 3632 字

9.使用GateWay实现网关功能

9.1.章节内容概述

本章节涉及主要内容有:
 9.1.章节内容概述
 9.2.章节内容大纲
 9.3.GateWay简介
 9.4.硬编码配置方式使用GateWay(非负载均衡模式)
 9.5.声明式配置方式使用GateWay(非负载均衡模式)
 9.6.硬编码配置方式使用GateWay(负载均衡模式)
 9.7.声明式配置方式使用GateWay(负载均衡模式)
具体每个小节中包含的内容可使通过下面的章节内容大纲进行查看。

9.2.章节内容大纲

9.3.GateWay简介

Gateway全称SpringCloud Gateway,它旨在为微服务架构提供一种简单有效的统一的API路由管理方式。作为Spring Cloud生态系统中的网关,目标是替代Zuul,为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。主要功能包含认证、鉴权、路由转发、安全策略、防刷、流量控制、监控日志等。
为什么要使用SpringCloud Gateway?
以鉴权为例,一个大的应用由很多服务组成,不可能为每一个服务都加上鉴权的代码,这样仅是鉴权部分的代码维护工作就是非常庞大的工作量,同时如果为所有的微服务都加上鉴权代码,这样会破坏REST服务的无状态特征,有一个思路是把鉴权的代码写在一个公共的模块,所有的模块都引入这个模块,但是这样仅仅是实现了鉴权操作,所以最好的处理方案是把认证、鉴权、路由转发、安全策略、防刷、流量控制、监控日志等功能都放在网关中实现,网关在进行请求转发的同时还实现一个负载均衡的效果,服务消费端在调用服务提供端的时候可以调用同一个服务提供端的不同节点,进而实现负载均衡,而网关可以在进行请求转发的时候将请求转发到多个服务消费端,实现服务消费端的负载均衡调用。

官方网站(SPRING.IO)

https://github.com/spring-cloud/spring-cloud-gateway

官方网站(SPRING.IO)

https://spring.io/projects/spring-cloud-gateway/

9.4.硬编码配置方式使用GateWay(非负载均衡模式)

9.4.1.模块简介

使用SpringCloud Gateway实现网关功能,配置方式为硬编码配置,实现了简单的请求转发功能,即请求经过网关之后会转发到单个服务消费者的单个节点,没有实现在请求转发的同时做负载均衡处理,启动端口: 9527

9.4.2.模块目录结构

springcloud-router-connect-direct-hardcode-gateway9527
|-- src
|   •-- main
|       |-- java
|       |   •-- org
|       |       •-- openatom
|       |           •-- springcloud
|       |               |-- config
|       |               |   •-- GateWayConfig.java
|       |               |-- filter
|       |               |   •-- LoginFilter.java
|       |               •-- RouterConnectDirectHardcodeGateWay9527.java
|       •-- resources
|           •-- application.yml
•-- pom.xml

9.4.3.创建模块

在父工程(springcloud-eureka)中创建一个名为springcloud-router-connect-direct-hardcode-gateway9527的maven模块,注意:当前模块创建成功后,在父工程pom.xml中<modules></modules>中会自动生成有关当前模块的信息

9.4.4.编写模块pom.xml

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud-eureka</artifactId>
        <groupId>org.openatom</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-router-connect-direct-hardcode-gateway9527</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.openatom</groupId>
            <artifactId>springcloud-api-commons</artifactId>
        </dependency>
    </dependencies>
    <!--热部署需要加这个-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <fork>true</fork>
                    <addResources>true</addResources>
                </configuration>
            </plugin>
        </plugins>
        <!--打包多环境-->
        <resources>
            <resource>
                <directory>src/main/resources/</directory>
                <includes>
                    <!--不区分环境:直接加载application.yml配置文件-->
                    <include>application.yml</include>
                </includes>
            </resource>
        </resources>
    </build>
</project>

9.4.5.编写模块application.yml

server:
  port: 9527

spring:
  application:
    name: SPRINGCLOUD-ROUTER-CONNECT-DIRECT-HARDCODE-GATEWAY9527

eureka:
  instance:
    hostname: ${spring.application.name}
  client: #服务提供端provider注册进eureka服务列表内
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://localhost:7001/eureka



9.4.6.编写模块config

package org.openatom.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;

@Configuration
public class GateWayConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder){
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        routes.route("payment_routh",
                r -> r.path("/consumer/payment/**/get/**")
                        .uri("http://localhost")).build();
        return routes.build();
    }
}

9.4.7.编写鉴权LoginFilter

package org.openatom.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.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;


/**
 * 判断是否登录的Filter
 */
@Component    //注掉这行代码,这样过滤器就不会生效了
@Slf4j
public class LoginFilter implements GlobalFilter,Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取请求路径中的uname参数
        String uname = exchange.getRequest().getQueryParams().getFirst("uname");
        if(uname == null) {
            log.info("请求未携带uname参数,校验失败,不允许通过");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    /**
     * 设置多个Filter的执行顺序
     * @return
     */
    @Override
    public int getOrder(){
        return 0;
    }
}

9.4.8.编写模块主启动类

package org.openatom.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * 网关直连服务模式:Java硬编码设置路由规则
 */
@SpringBootApplication
@EnableEurekaClient
public class RouterConnectDirectHardcodeGateWay9527 {
    public static void main(String[] args) {
        SpringApplication.run(RouterConnectDirectHardcodeGateWay9527.class, args);
    }
}

9.4.7.测试模块

启动相关服务
在浏览器中访问
http://localhost:9527/consumer/payment/ok/get/1?uname=zhangsan
第一次访问返回结果
{"code":200,"message":"查询成功,serverPort:  8001","data":{"id":1,"serial":"15646546546"}}
第二次访问返回结果
{"code":200,"message":"查询成功,serverPort:  8002","data":{"id":1,"serial":"15646546546"}}
第三次访问返回结果
{"code":200,"message":"查询成功,serverPort:  8001","data":{"id":1,"serial":"15646546546"}}
第四次访问返回结果
{"code":200,"message":"查询成功,serverPort:  8002","data":{"id":1,"serial":"15646546546"}}
可以看到四次访问返回的结果中,第一次和第三次是相同的,第二次和第四次是相同的,之所以会出现这样的结果,是因为上面编写RestTemplate时使用了默认的配置,默认的配置使用负载均衡策略是轮询策略,所以接连访问该服务四次会出现上面的情况。但是要注意,这里并没有直接访问服务消费者,而是访问了网关,这些返回的数据是服务消费端返回给网关,网关返回给浏览器的。

9.5.声明式配置方式使用GateWay(非负载均衡模式)

9.5.1.模块简介

使用SpringCloud Gateway实现网关功能,配置方式为声明式配置,实现了简单的请求转发功能,即请求经过网关之后会转发到单个服务消费者的单个节点,没有实现在请求转发的同时做负载均衡处理,启动端口: 9527

9.5.2.模块目录结构

springcloud-router-connect-direct-configuration-gateway9527
|-- src
|   •-- main
|       |-- java
|       |   •-- org
|       |       •-- openatom
|       |           •-- springcloud
|       |               |-- filter
|       |               |   •-- LoginFilter.java
|       |               •-- RouterConnectDirectConfigurationGateWay9527.java
|       •-- resources
|           •-- application.yml
•-- pom.xml

9.5.3.创建模块

在父工程(springcloud-eureka)中创建一个名为springcloud-router-connect-direct-configuration-gateway9527的maven模块,注意:当前模块创建成功后,在父工程pom.xml中<modules></modules>中会自动生成有关当前模块的信息

9.5.4.编写模块pom.xml

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud-eureka</artifactId>
        <groupId>org.openatom</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-router-connect-direct-configuration-gateway9527</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.openatom</groupId>
            <artifactId>springcloud-api-commons</artifactId>
        </dependency>
    </dependencies>
    <!--热部署需要加这个-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <fork>true</fork>
                    <addResources>true</addResources>
                </configuration>
            </plugin>
        </plugins>
        <!--打包多环境-->
        <resources>
            <resource>
                <directory>src/main/resources/</directory>
                <includes>
                    <!--不区分环境:直接加载application.yml配置文件-->
                    <include>application.yml</include>
                </includes>
            </resource>
        </resources>
    </build>
</project>

9.5.5.编写模块application.yml

server:
  port: 9527

spring:
  application:
    name: SPRINGCLOUD-ROUTER-CONNECT-DIRECT-CONFIGURATION-GATEWAY9527
  cloud:
    gateway:
      routes:
        #第一个路由,多个路由依靠不同的路由ID区分
        - id: payment_routh   #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost          #匹配后提供服务的路由地址
          predicates:
            # 断言,路径相匹配的进行路由,可以匹配下面四个路径:
            # 路径1:http://localhost:9527/consumer/payment/ok/get/{id}
            # 路径2:http://localhost:9527/consumer/payment/timeout/get/{id}
            # 路径3:http://localhost:9527/consumer/payment/degradation_in_provider/get/{id}
            # 路径3:http://localhost:9527/consumer/payment/circuitbreaker/get/{id}
            - Path=/consumer/payment/**/get/**

        #第二个路由,多个路由依靠不同的路由ID区分
        #- id: payment_routh2   #路由的ID,没有固定规则但要求唯一,建议配合服务名
        #  uri: http://localhost          #匹配后提供服务的路由地址
        #  predicates:
        #    - Path=/consumer/payment/**/get/**

eureka:
  instance:
    hostname: ${spring.application.name}
  client: #服务提供端provider注册进eureka服务列表内
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://localhost:7001/eureka



9.5.6.编写鉴权LoginFilter

package org.openatom.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.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;


/**
 * 判断是否登录的Filter
 */
@Component    //注掉这行代码,这样过滤器就不会生气了
@Slf4j
public class LoginFilter implements GlobalFilter,Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取请求路径中的uname参数
        String uname = exchange.getRequest().getQueryParams().getFirst("uname");
        if(uname == null) {
            log.info("请求未携带uname参数,校验失败,不允许通过");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    /**
     * 设置多个Filter的执行顺序
     * @return
     */
    @Override
    public int getOrder(){
        return 0;
    }
}

9.5.7.编写模块主启动类

package org.openatom.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * 网关直连服务模式:YML文件设置路由规则
 */
@SpringBootApplication
@EnableEurekaClient
public class RouterConnectDirectConfigurationGateWay9527 {
    public static void main(String[] args) {
        SpringApplication.run(RouterConnectDirectConfigurationGateWay9527.class, args);
    }
}

9.5.8.测试模块

启动相关服务
在浏览器中访问
http://localhost:9527/consumer/payment/ok/get/1?uname=zhangsan
第一次访问返回结果
{"code":200,"message":"查询成功,serverPort:  8001","data":{"id":1,"serial":"15646546546"}}
第二次访问返回结果
{"code":200,"message":"查询成功,serverPort:  8002","data":{"id":1,"serial":"15646546546"}}
第三次访问返回结果
{"code":200,"message":"查询成功,serverPort:  8001","data":{"id":1,"serial":"15646546546"}}
第四次访问返回结果
{"code":200,"message":"查询成功,serverPort:  8002","data":{"id":1,"serial":"15646546546"}}
可以看到四次访问返回的结果中,第一次和第三次是相同的,第二次和第四次是相同的,之所以会出现这样的结果,是因为上面编写RestTemplate时使用了默认的配置,默认的配置使用负载均衡策略是轮询策略,所以接连访问该服务四次会出现上面的情况。但是要注意,这里并没有直接访问服务消费者,而是访问了网关,这些返回的数据是服务消费端返回给网关网关返回给浏览器的。

9.6.硬编码配置方式使用GateWay(负载均衡模式)

9.6.1.模块简介

使用SpringCloud Gateway实现网关功能,配置方式为硬编码配置,实现了简单的请求转发功能,即请求经过网关之后会转发到单个服务消费者的单个节点,实现了在请求转发的同时做负载均衡处理,启动端口: 9527

9.6.2.模块目录结构

springcloud-router-connect-loadbalance-hardcode-gateway9527
|-- src
|   •-- main
|       |-- java
|       |   •-- org
|       |       •-- openatom
|       |           •-- springcloud
|       |               |-- config
|       |               |   •-- GateWayConfig.java
|       |               |-- filter
|       |               |   •-- LoginFilter.java
|       |               •-- RouterConnectLoadbalanceHardcodeGateWay9527.java
|       •-- resources
|           •-- application.yml
•-- pom.xml

9.6.3.创建模块

在父工程(springcloud-eureka)中创建一个名为springcloud-router-connect-loadbalance-hardcode-gateway9527的maven模块,注意:当前模块创建成功后,在父工程pom.xml中<modules></modules>中会自动生成有关当前模块的信息

9.6.4.编写模块pom.xml

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud-eureka</artifactId>
        <groupId>org.openatom</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-router-connect-loadbalance-hardcode-gateway9527</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.openatom</groupId>
            <artifactId>springcloud-api-commons</artifactId>
        </dependency>
    </dependencies>
    <!--热部署需要加这个-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <fork>true</fork>
                    <addResources>true</addResources>
                </configuration>
            </plugin>
        </plugins>
        <!--打包多环境-->
        <resources>
            <resource>
                <directory>src/main/resources/</directory>
                <includes>
                    <!--不区分环境:直接加载application.yml配置文件-->
                    <include>application.yml</include>
                </includes>
            </resource>
        </resources>
    </build>
</project>

9.6.5.编写模块application.yml

server:
  port: 9527

spring:
  application:
    name: SPRINGCLOUD-ROUTER-CONNECT-LOADBALANCE-HARDCODE-GATEWAY9527
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由

eureka:
  instance:
    hostname: ${spring.application.name}
  client: #服务提供端provider注册进eureka服务列表内
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://localhost:7001/eureka

9.6.6.编写config

package org.openatom.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;

@Configuration
public class GateWayConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder){
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        routes.route("payment_routh",
                r -> r.path("/consumer/payment/**/get/**")
                        .uri("lb://SPRINGCLOUD-CONSUMER-HYSTRIX-LOADBALANCE-OPENFEIGN-CONFIGURATION-ORDER80")).build();
        return routes.build();
    }
}

9.6.7.编写鉴权LoginFilter

package org.openatom.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.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;


/**
 * 判断是否登录的Filter
 */
@Component    //注掉这行代码,这样过滤器就不会生气了
@Slf4j
public class LoginFilter implements GlobalFilter,Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取请求路径中的uname参数
        String uname = exchange.getRequest().getQueryParams().getFirst("uname");
        if(uname == null) {
            log.info("请求未携带uname参数,校验失败,不允许通过");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    /**
     * 设置多个Filter的执行顺序
     * @return
     */
    @Override
    public int getOrder(){
        return 0;
    }
}

9.6.8.编写模块主启动类

package org.openatom.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * 网关连接注册中心实现负载均衡模式:Java硬编码设置路由规则
 */
@SpringBootApplication
@EnableEurekaClient
public class RouterConnectLoadbalanceHardcodeGateWay9527 {
    public static void main(String[] args) {
        SpringApplication.run(RouterConnectLoadbalanceHardcodeGateWay9527.class, args);
    }
}

9.6.9.测试模块

启动相关服务
在浏览器中访问
http://localhost:9527/consumer/payment/ok/get/1?uname=zhangsan
第一次访问返回结果
{"code":200,"message":"查询成功,serverPort:  8001","data":{"id":1,"serial":"15646546546"}}
第二次访问返回结果
{"code":200,"message":"查询成功,serverPort:  8002","data":{"id":1,"serial":"15646546546"}}
第三次访问返回结果
{"code":200,"message":"查询成功,serverPort:  8001","data":{"id":1,"serial":"15646546546"}}
第四次访问返回结果
{"code":200,"message":"查询成功,serverPort:  8002","data":{"id":1,"serial":"15646546546"}}
可以看到四次访问返回的结果中,第一次和第三次是相同的,第二次和第四次是相同的,之所以会出现这样的结果,是因为上面编写RestTemplate时使用了默认的配置,默认的配置使用负载均衡策略是轮询策略,所以接连访问该服务四次会出现上面的情况。但是要注意,这里并没有直接访问服务消费者,而是访问了网关,这些返回的数据是服务消费端返回给网关网关返回给浏览器的。

9.7.声明式配置方式使用GateWay(负载均衡模式)

9.7.1.模块简介

使用SpringCloud Gateway实现网关功能,配置方式为声明式配置,实现了简单的请求转发功能,即请求经过网关之后会转发到单个服务消费者的单个节点,实现了在请求转发的同时做负载均衡处理,启动端口: 9527

9.7.2.模块目录结构

springcloud-router-connect-loadbalance-configuration-gateway9527
|-- src
|   •-- main
|       |-- java
|       |   •-- org
|       |       •-- openatom
|       |           •-- springcloud
|       |               |-- config
|       |               |   •-- GateWayConfig.java
|       |               |-- filter
|       |               |   •-- LoginFilter.java
|       |               •-- RouterConnectLoadbalanceConfigurationGateWay9527.java
|       •-- resources
|           •-- application.yml
•-- pom.xml

9.7.3.创建模块

在父工程(springcloud-eureka)中创建一个名为springcloud-router-connect-loadbalance-configuration-gateway9527的maven模块,注意:当前模块创建成功后,在父工程pom.xml中<modules></modules>中会自动生成有关当前模块的信息

9.7.4.编写模块pom.xml

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud-eureka</artifactId>
        <groupId>org.openatom</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-router-connect-loadbalance-configuration-gateway9527</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.openatom</groupId>
            <artifactId>springcloud-api-commons</artifactId>
        </dependency>
    </dependencies>
    <!--热部署需要加这个-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <fork>true</fork>
                    <addResources>true</addResources>
                </configuration>
            </plugin>
        </plugins>
        <!--打包多环境-->
        <resources>
            <resource>
                <directory>src/main/resources/</directory>
                <includes>
                    <!--不区分环境:直接加载application.yml配置文件-->
                    <include>application.yml</include>
                </includes>
            </resource>
        </resources>
    </build>
</project>

9.7.5.编写模块application.yml

server:
  port: 9527

spring:
  application:
    name: SPRINGCLOUD-ROUTER-CONNECT-LOADBALANCE-CONFIGURATION-GATEWAY9527
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        #第一个路由,多个路由依靠不同的路由ID区分
        - id: payment_routh   #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: lb://SPRINGCLOUD-CONSUMER-HYSTRIX-LOADBALANCE-OPENFEIGN-CONFIGURATION-ORDER80     #匹配后提供服务的路由地址,负载均衡前缀为lb://
          predicates:
            # 断言,路径相匹配的进行路由,可以匹配下面四个路径:
            # 路径1:http://localhost:9527/consumer/payment/ok/get/{id}
            # 路径2:http://localhost:9527/consumer/payment/timeout/get/{id}
            # 路径3:http://localhost:9527/consumer/payment/degradation_in_provider/get/{id}
            # 路径3:http://localhost:9527/consumer/payment/circuitbreaker/get/{id}
            - Path=/consumer/payment/**/get/**

        #第二个路由,多个路由依靠不同的路由ID区分
        #- id: payment_routh2   #路由的ID,没有固定规则但要求唯一,建议配合服务名
        #  uri: http://localhost          #匹配后提供服务的路由地址
        #  predicates:
        #    - Path=/consumer/payment/**/get/**

eureka:
  instance:
    hostname: ${spring.application.name}
  client: #服务提供端provider注册进eureka服务列表内
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://localhost:7001/eureka

9.7.6.编写config

package org.openatom.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;

@Configuration
public class GateWayConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder){
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        routes.route("payment_routh",
                r -> r.path("/consumer/payment/**/get/**")
                        .uri("http://localhost")).build();
        return routes.build();
    }
}

9.7.7.编写鉴权LoginFilter

package org.openatom.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.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;


/**
 * 判断是否登录的Filter
 */
@Component    //注掉这行代码,这样过滤器就不会生气了
@Slf4j
public class LoginFilter implements GlobalFilter,Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取请求路径中的uname参数
        String uname = exchange.getRequest().getQueryParams().getFirst("uname");
        if(uname == null) {
            log.info("请求未携带uname参数,校验失败,不允许通过");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    /**
     * 设置多个Filter的执行顺序
     * @return
     */
    @Override
    public int getOrder(){
        return 0;
    }
}

9.7.8.编写模块主启动类

package org.openatom.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * 网关连接注册中心实现负载均衡模式:YML文件配置方式设置路由规则
 */
@SpringBootApplication
@EnableEurekaClient
public class RouterConnectLoadbalanceConfigurationGateWay9527 {
    public static void main(String[] args) {
        SpringApplication.run(RouterConnectLoadbalanceConfigurationGateWay9527.class, args);
    }
}

9.7.9.测试模块

启动相关服务
在浏览器中访问
http://localhost:9527/consumer/payment/ok/get/1?uname=zhangsan
第一次访问返回结果
{"code":200,"message":"查询成功,serverPort:  8001","data":{"id":1,"serial":"15646546546"}}
第二次访问返回结果
{"code":200,"message":"查询成功,serverPort:  8002","data":{"id":1,"serial":"15646546546"}}
第三次访问返回结果
{"code":200,"message":"查询成功,serverPort:  8001","data":{"id":1,"serial":"15646546546"}}
第四次访问返回结果
{"code":200,"message":"查询成功,serverPort:  8002","data":{"id":1,"serial":"15646546546"}}
可以看到四次访问返回的结果中,第一次和第三次是相同的,第二次和第四次是相同的,之所以会出现这样的结果,是因为上面编写RestTemplate时使用了默认的配置,默认的配置使用负载均衡策略是轮询策略,所以接连访问该服务四次会出现上面的情况。但是要注意,这里并没有直接访问服务消费者,而是访问了网关,这些返回的数据是服务消费端返回给网关网关返回给浏览器的。
上次编辑于: 2022/9/6 00:07:50
贡献者: lingwh
评论