• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

SpringCloud-Gateway整合SpingCloud-Alibaba入

武飞扬头像
AMANO
帮助191

环境搭建

服务环境搭建

  1. Maven依赖配置

建立Maven项目结构如下:

--springcloud-alibaba-gateway
|----springcloud-provider
|----springcloud-gateway
  • 父项目springcloud-alibaba-gateway的POM依赖
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.9</version>
    <relativePath/>
</parent>
<packaging>pom</packaging>
<properties>
    。。。
    <!-- 此处仅列出关键依赖版本,版本对应关系参考SpringCloud-Alibaba官网 -->
    <spring.boot.version>2.7.9</spring.boot.version>
    <spring.cloud.version>2021.0.4</spring.cloud.version>
    <spring.cloud.alibaba.version>2021.0.4.0</spring.cloud.alibaba.version>
</properties>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring.cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>${spring.cloud.alibaba.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
  • 应用服务springcloud-provider的POM依赖
<dependencies>
    <!--springcloud-alibaba-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-bootstrap</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-loadbalancer</artifactId>
    </dependency>
    <!--springboot-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>
  • 网关服务springcloud-gateway的POM依赖
<dependencies>
    <!--springcloud-alibaba-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-bootstrap</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <!-- 整合nacos需要添加负载均衡依赖 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-loadbalancer</artifactId>
    </dependency>
</dependencies>
  1. 接口代码编写

应用服务测试接口编写, 该接口将通过gateway服务进行负载均衡

package com.amano.springcloudalibabaproduct.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
@RequestMapping("/test")
public class GateWayController {
    @Value("${server.port}")
    private Integer port;

    @GetMapping
    public String testGateWay() {
        return "service:"   port   " access";
    }
}

应用服务启动类,添加 @EnableDiscoveryClient 注解启用服务发现

package com.amano.springcloudalibabaproduct;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class SpringcloudAlibabaProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringcloudAlibabaProviderApplication.class, args);
    }
}

网关服务同样开始服务发现

package com.amano.springcloudgateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class SpringcloudGatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringcloudGatewayApplication.class, args);
    }

}
  1. 服务配置

应用服务的配置文件,采用bootstrap.yaml配置Nacos服务发现和配置中心的信息

server:
  port: 8080

spring:
  application:
    name: springcloud-provider
  profiles:
    active: local
  cloud:
    nacos:
      # nacos服务注册中心
      discovery:
        server-addr: localhost:8848
        namespace: ${nacos配置的namespace,默认则不填}
        group: SPRING-CLOUD-ALIBABA
      # nacos配置中心
      config:
        group: SPRING-CLOUD-ALIBABA
        name: product-config
        file-extension: yaml
        server-addr: localhost:8848
        namespace: ${nacos配置的namespace,默认则不填}

网关服务的配置文件,同样采用bootstrap.yaml配置Nacos服务发现和配置中心的信息

server:
  port: 10080
spring:
  application:
    name: springcloud-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        namespace: ${nacos配置的namespace,默认则不填}
        group: SPRING-CLOUD-ALIBABA
      config:
        server-addr: localhost:8848
        namespace: ${nacos配置的namespace,默认则不填}
        group: SPRING-CLOUD-ALIBABA
        file-extension: yaml
  profiles:
    active: local

网关路由信息配置到nacos配置中心, 实现网关路由的动态刷新

spring:
  cloud:
    gateway:
      discovery:
        locator:
          # 允许gateway从nacos中查找微服务
          enabled: true
      routes:
        # 确定路由唯一性的id
        - id: provider_router
          # lb允许才nacos中通过该名称查找到指定服务,并实现负载均衡
          uri: lb://springcloud-provider
          predicates:
            # 路由断言
            - Path=/api/provider/**
          filters:
            # 过滤器
            - StripPrefix=2

功能测试

通过配置IDEA启动两个应用服务, 通过代码获取的端口号来验证接口是否实现负载均衡

分别启动两个springcloud-provider示例和springcloud-gateway服务

多次通过Postman访问网关服务的路由地址,查看结果

可以看到接口通过Gateway网关顺利实现了负载均衡

SpringCloud-Gateway的配置及拓展

断言

Gateway断言(Predicate)用于判断路由的匹配条件。只有满足的断言的条件,该请求才会被发到指定的服务上进行处理。

使用断言需要注意以下情况:

  • 一个路由可以配置多个断言。
  • 请求必须满足所有断言条件才能被转发到指定的路由。
  • 当一个请求同时满足多个路由的断言条件时,请求只会被首个成功匹配的路由转发。
  1. Gateway内置了11种的断言工厂,所有这些断言都与 HTTP 请求的不同属性匹配。
断言 格式 说明 断言工厂
Path Path=/api/provider/** 当请求路径与参数匹配时 PathRoutePredicateFactory
After - After=2021-10-20T11:47:34.255 08:00[Asia/Shanghai] 接收一个日期参数,判断请求日期是否晚于指定日期 AfterRoutePredicateFactory
Before - Before=2021-10-20T11:47:34.255 08:00[Asia/Shanghai] 接收一个日期参数,判断请求日期是否早于指定日期 BeforeRoutePredicateFactory
Between - Between=2021-10-20T11:47:34.255 08:00[Asia/Shanghai],2021-10-20T13:47:34.255 08:00[Asia/Shanghai] 接收两个日期参数以逗号隔开,判断请求日期是否在指定日期之间 BetweenRoutePredicateFactory
Cookie - Cookie=key,vlue 接收两个参数,判断cookie是否具有给定的键值 CookieRoutePredicateFactory
Header - Header=key,value 接接收两个参数,判断请求header是否具有给定的键值 HeaderRoutePredicateFactory
Host - Host=*.host.com,... 接收多个URI模版变量,判断请求主机是否匹配指定的域名 HostRoutePredicateFactory
Method - Method=GET,POST 接收一个参数, 判断请求类型是否匹配 MethodRoutePredicateFactory
Query - Query=key,value 接收两个参数,判断请求param种是否包含指定key,value的参数 QueryRoutePredicateFactory
Remote - Remote=192.168.0.1/16 接收一个参数,判断请求主机是否在指定的IP段中 RemoteAddrRoutePredicateFactory
Weight - Weight=group1, 7 接收一个[组名,权重的]的参数,对指定组内的路由按权重进行转发 WeightRoutePredicateFactory
  1. 自定义断言

当Gateway默认提供的断言无法满足我们的需求时,我们可以自定义断言工厂, 对路由条件进行灵活拓展。

  • 创建一个类继承AbstractRoutePredicateFactory类,实现 apply方法
package com.amano.springcloudgateway.predicate;

import lombok.Data;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;

@Component
public class AreaGatewayFilterFactory extends AbstractRoutePredicateFactory<AreaGatewayFilterFactory.Config> {

    public AreaGatewayFilterFactory() {
        super(AreaGatewayFilterFactory.Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Collections.singletonList("area");
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return serverWebExchange -> {
            String area = serverWebExchange.getRequest().getQueryParams().getFirst("area");
            if (StringUtils.isEmpty(area)) {
                return false;
            }
            return Objects.equals(config.getArea(), area);
        };
    }

    @Data
    public static class Config {
        private String area;
    }

    @Override
    public String name() {
        return "Area";
    }
}
  • 修改路由配置
spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: nacos_product_router
          uri: lb://nacos-product
          predicates:
            - Path=/api/provider/**
            # 使用自定义断言
            - Area=Shanghai
          filters:
            - StripPrefix=2
  • 修改测试接口, 添加参数
@GetMapping()
public String testGateWayArea(String area) {
    return "service:"   port   " access:"   area;
}
  • 测试 可以看到路由参数中的area不匹配配置中的参数时无法访问指定微服务的接口 而满足条件时可以正常访问

过滤器

通常情况下,出于安全方面的考虑,服务端提供的服务往往都会有一定的校验逻辑,例如用户登陆状态校验、签名校验等。SpringCloud-Gateway 提供了以下两种类型的过滤器,可以对请求和响应进行精细化控制。

过滤器类型 说明
Pre 类型 这种过滤器在请求被转发到微服务之前可以对请求进行拦截和修改,例如参数校验、权限校验、流量监控、日志输出以及协议转换等操作。
Post 类型 这种过滤器在微服务对请求做出响应后可以对响应进行拦截和再处理,例如修改响应内容或响应头、日志输出、流量监控等。

按照作用范围划分,SpringCloud-gateway的Filter可以分为 2 类:

  • GatewayFilter(局部过滤器):应用在单个路由或者一组路由上的过滤器。
  • GlobalFilter(全局过滤器): 应用在所有的路由上的过滤器。

局部过滤器

  1. 局部过滤器的使用方式
spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: nacos_product_router
          uri: lb://nacos-product
          predicates:
            - Path=/api/provider/**
          filters:
            # 截断原始路径
            - StripPrefix=2
            # 通过过滤器,修改返回状态
            - SetStatus=201
  1. 在SpringCloud-Gateway中内置了31种局部过滤器, 以下列举部分常用的过滤器及其用法
过滤器工厂 功能 参数
AddRequestHeader 为原始请求添加Header Header的名称及值
AddRequestParameter 为原始请求添加请求参数 参数名称及值
AddResponseHeader 为原始响应添加Header Header的名称及值
DedupeResponseHeader 剔除响应头中重复的值 需要去重的Header名称及去重策略
PrefixPath 为原始请求路径添加前缀 前缀路径
RequestRateLimiter 用于对请求限流, 限流算法为令牌桶 keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus
RedirectTo 将原始请求重定向到指定的URL http状态码及重定向的url
StripPrefix 用于截断原始请求的路径 使用数字表示要截断的路径的数量
Retry 针对不同的响应进行重试 retries、 statuses、methods、 series
ModifyRequestBody 在转发请求之前修改原始请求体内容 修改后的请求体内容
ModifyResponseBody 修改原始响应体的内容 修改后的响应体内容
SetStatus 修改原始响应的状态码 HTTP 状态码, 可以是数字, 也可以是字符串
  1. 自定义局部过滤器
// 自定义的过滤器类名需要满足, 过滤器名称   GatewayFilterFactory
@Component
@Slf4j
public class LogGatewayFilterFactory extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.Config> {
    public LogGatewayFilterFactory() {
        super(LogGatewayFilterFactory.Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("open");
    }

    @Override
    public GatewayFilter apply(Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                if (config.open) {
                    // 过滤器逻辑处理
                    log.info("日志。。。。")
                }

                return chain.filter(exchange);
            }
        };
    }
    
    @Data
    static class Config {
        private boolean open;
    }
}

全局过滤器

全局过滤器作用于所有路由,所以无需配置,通过全局路由器可以权限的统一校验,安全性验证等功能。SpringCloud-Gateway同样也内置了一些全局过滤器, 这些过滤器都实现了GlobalFilter和 Ordered接口,感兴趣的可以自行查看源码。

  1. 自定义全局过滤器
@Component
@Slf4j
public class TokenGlobalFilter implements GlobalFilter, Ordered {

    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        if (token == null || token.length() == 0 || !token.equals("token")) {
            log.warn("鉴权失败");
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.OK);
            response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
            // 返回失败信息
            Map<String, Object> map = new HashMap<>();
            map.put("code", HttpStatus.UNAUTHORIZED.value());
            map.put("message", HttpStatus.UNAUTHORIZED.getReasonPhrase());

            DataBuffer buffer = response.bufferFactory().wrap(new ObjectMapper().writeValueAsBytes(map));
            return response.writeWith(Flux.just(buffer));
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

自定义路由匹配失败处理

在前面的测试中,当我们定义的路由规则不匹配时,Gateway会默认返回一个错误页面,这种页面通常无法满足我们的业务需求。我们通过自定义返回一个友好的提示信息。

  1. 创建一个类继承DefaultErrorWebExceptionHandler, 重写方法
package com.amano.springcloudgateway.handler;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.context.ApplicationContext;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.Map;

@Slf4j
public class CustomErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler {
    public CustomErrorWebExceptionHandler(ErrorAttributes errorAttributes,
                                          WebProperties.Resources resources,
                                          ErrorProperties errorProperties,
                                          ApplicationContext applicationContext) {
        super(errorAttributes, resources, errorProperties, applicationContext);
    }

    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }

    @Override
    protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
        boolean includeStackTrace = isIncludeStackTrace(request, MediaType.ALL);
        Map<String, Object> errorMap = getErrorAttributes(request, includeStackTrace);
        int status = Integer.parseInt(errorMap.get("status").toString());
        Map<String, Object> response = response(status, errorMap.get("error").toString(), errorMap);
        return ServerResponse.status(status).contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(response));
    }

    // 响应内容
    public static Map<String, Object> response(int status, String errorMessage, Map<String, Object> errorMap) {
        Map<String, Object> map = new HashMap<>();
        map.put("code", status);
        map.put("message", errorMessage);
        map.put("data", errorMap);
        return map;
    }
}
  1. 覆盖Gateway默认配置
package com.amano.springcloudgateway;

import com.amano.springcloudgateway.handler.CustomErrorWebExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;

import java.util.Collections;
import java.util.List;

@Configuration
@EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class})
public class GateWayConfiguration {
    private final ServerProperties serverProperties;
    private final ApplicationContext applicationContext;
    private final ResourceProperties resourceProperties;
    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public GateWayConfiguration(ServerProperties serverProperties,
                                ApplicationContext applicationContext,
                                ResourceProperties resourceProperties,
                                ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                ServerCodecConfigurer serverCodecConfigurer) {
        this.serverProperties = serverProperties;
        this.applicationContext = applicationContext;
        this.resourceProperties = resourceProperties;
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }


    @Bean("customErrorWebExceptionHandler")
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public ErrorWebExceptionHandler myErrorWebExceptionHandler(ErrorAttributes errorAttributes) {

        CustomErrorWebExceptionHandler exceptionHandler = new CustomErrorWebExceptionHandler(
                errorAttributes,
                this.resourceProperties,
                this.serverProperties.getError(),
                this.applicationContext);

        exceptionHandler.setViewResolvers(this.viewResolvers);
        exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
        exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
        return exceptionHandler;
    }
}

总结

通过这个简单的实例,我们实现了springcloud-alibaba和springcloud-gateway的整合,通过路由访问nacos注册的微服务。springcloud-alibaba和gateway相关详细的文章将在后续进行更新。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanffaga
系列文章
更多 icon
同类精品
更多 icon
继续加载