Gateway服务网关(二)

简介: Gateway服务网关

过滤器

关于过滤器这块我举个例子,更多的内容请小伙伴自己查阅文档

官方文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories

举一个用的比较多的过滤器:

StripPrefix

顾名思义,除去前缀的过滤器,将匹配的请求的前缀去除,将去除后的请求转发给下游服务

spring:
  cloud:
    gateway:
      # 路由配置
      routes:
        # 路由id, 保证唯一性
        - id: my-goods
          # 路由的地址,格式:协议://服务名 lb: load balance,my-goods: 商品服务名
          uri: lb://my-goods
          # 断言
          predicates:
            # 匹配goods开头的请求
            - Path=/api/goods/**
          filters:
            # 1表示去除一个前缀
            - StripPrefix=1

组合来看,意思是当客户端发起请求:http://localhost:5555/api/goods/get-goods, 匹配该路由,然后将第一个前缀api去除,然后转发给商品服务,转发的路径为:/goods/get-goods

测试

自定义断言工厂

上面提到过:所有的断言工厂都是继承于AbstractRoutePredicateFactory, 并且命名规则为:XxxRoutePredicateFactory, 比如Path的类名为:PathRoutePredicateFactory

我们现在就来尝试实现一个自定义的请求头断言工厂吧

编写代码

package com.my.micro.service.gateway.filter;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
/**
 * @author Zijian Liao
 * @since 1.0.0
 */
@Component
public class MyHeaderRoutePredicateFactory extends AbstractRoutePredicateFactory<MyHeaderRoutePredicateFactory.Config> {
    /**
     * Header key.
     */
    public static final String HEADER_KEY = "header";
    /**
     * Regexp key.
     */
    public static final String REGEXP_KEY = "regexp";
    public MyHeaderRoutePredicateFactory() {
        super(MyHeaderRoutePredicateFactory.Config.class);
    }
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList(HEADER_KEY, REGEXP_KEY);
    }
    @Override
    public Predicate<ServerWebExchange> apply(MyHeaderRoutePredicateFactory.Config config) {
        return new GatewayPredicate() {
            @Override
            public boolean test(ServerWebExchange exchange) {
                // 获取请求头
                List<String> values = exchange.getRequest().getHeaders()
                        .getOrDefault(config.header, Collections.emptyList());
                if (values.isEmpty()) {
                    return false;
                }
                // 判断请求头中的值是否与配置匹配
                return values.stream()
                        .anyMatch(value -> value.matches(config.regexp));
            }
            @Override
            public String toString() {
                return String.format("Header: %s=%s ", config.header, config.regexp);
            }
        };
    }
    public static class Config {
        private String header;
        private String regexp;
        public String getHeader() {
            return header;
        }
        public void setHeader(String header) {
            this.header = header;
        }
        public String getRegexp() {
            return regexp;
        }
        public void setRegexp(String regexp) {
            this.regexp = regexp;
        }
    }
}

编写配置

spring:
  cloud:
    gateway:
      # 路由配置
      routes:
        # 路由id, 保证唯一性
        - id: my-goods
          # 路由的地址,格式:协议://服务名 lb: load balance,my-goods: 商品服务名
          uri: lb://my-goods
          # 断言
          predicates:
            # 匹配goods开头的请求
            - Path=/api/goods/**
            # 匹配header为name=aljian的请求
            - MyHeader=name,ajian
          filters:
            # 1表示去除一个前缀
            - StripPrefix=1

测试

直接在浏览器中访问

改用postman访问

自定义过滤器

自定义过滤器的方式与自定义断言工厂的方式大致相同,所以过滤器继承于AbstractGatewayFilterFactory或者AbstractNameValueGatewayFilterFactory, 命名规则为XxxGatewayFilterFactory

比如内置的添加请求头过滤器

public class AddRequestHeaderGatewayFilterFactory
    extends AbstractNameValueGatewayFilterFactory {
  @Override
  public GatewayFilter apply(NameValueConfig config) {
    return new GatewayFilter() {
      @Override
      public Mono<Void> filter(ServerWebExchange exchange,
          GatewayFilterChain chain) {
        // 获取到需要添加的header value
        String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
        // 将header添加到request中
        ServerHttpRequest request = exchange.getRequest().mutate()
            .header(config.getName(), value).build();
        // 重新构建出一个exchange
        return chain.filter(exchange.mutate().request(request).build());
      }
      @Override
      public String toString() {
        return filterToStringCreator(AddRequestHeaderGatewayFilterFactory.this)
            .append(config.getName(), config.getValue()).toString();
      }
    };
  }
}

全局过滤器

以上内容都是针对于每一个router,Spring Cloud Gateway提供了一个针对所有router的全局过滤器

实现方式如下

package com.my.micro.service.gateway.filter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
 * @author Zijian Liao
 * @since 1.0.0
 */
@Slf4j
@Component
public class MyGlobalFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getURI().getPath();
        log.info("进入全局过滤器,请求路径为:{}", path);
        // 编写任何你想要实现的逻辑,比如权限校验
        return chain.filter(exchange);
    }
}

测试

自定义异常处理器

小伙伴应该发现了,在遇到错误时,Spring Cloud Gateway返回给客户端的异常并不优雅,所以我们需要自定义异常处理

编写自定义异常处理器

package com.my.micro.service.gateway.exception;
import com.my.micro.service.gateway.result.BaseResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
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.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.lang.NonNull;
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;
/**
 * @author Zijian Liao
 */
@Slf4j
public class JsonExceptionHandler extends DefaultErrorWebExceptionHandler {
    /**
     * Create a new {@code DefaultErrorWebExceptionHandler} instance.
     *
     * @param errorAttributes    the error attributes
     * @param resourceProperties the resources configuration properties
     * @param errorProperties    the error configuration properties
     * @param applicationContext the current application context
     */
    public JsonExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties,
                                          ErrorProperties errorProperties, ApplicationContext applicationContext) {
        super(errorAttributes, resourceProperties, errorProperties, applicationContext);
    }
    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }
    @NonNull
    @Override
    protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
        Throwable throwable = getError(request);
        return ServerResponse.status(HttpStatus.OK)
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(this.handleError(throwable)));
    }
    private BaseResult<Void> handleError(Throwable throwable){
        return BaseResult.failure(throwable.getMessage());
    }
}

BaseResult

package com.my.micro.service.gateway.result;
import lombok.Data;
/**
 * @author Zijian Liao
 * @since 1.0.0
 */
@Data
public class BaseResult<T> {
    private Integer code;
    private String message;
    public BaseResult(Integer code, String message){
        this.code = code;
        this.message = message;
    }
    public static <T> BaseResult<T> failure(String  message){
        return new BaseResult<>(-1, message);
    }
}

编写配置类

package com.my.micro.service.gateway.exception;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
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.context.annotation.Primary;
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.stream.Collectors;
/**
 * @author Zijian Liao
 * @since 1.0.0
 */
@Configuration
public class ExceptionConfiguration {
    @Primary
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes, ServerProperties serverProperties, ResourceProperties resourceProperties,
                                                             ObjectProvider<ViewResolver> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer,
                                                             ApplicationContext applicationContext) {
        DefaultErrorWebExceptionHandler exceptionHandler = new JsonExceptionHandler(errorAttributes,
                resourceProperties, serverProperties.getError(), applicationContext);
        exceptionHandler.setViewResolvers(viewResolversProvider.orderedStream().collect(Collectors.toList()));
        exceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters());
        exceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders());
        return exceptionHandler;
    }
}

测试

小结

本编介绍了关于微服务架构中——客户端如何访问的解决方案:Spring Cloud Gateway

其中介绍了Gateway的三个核心概念:Route,Predicate,Filter。并演示了如何配置及使用他们,还讲解了如何自定义Predicate和Filter。

最后介绍了Spring Cloud Gateway的全局过滤器,以及如何实现自定义异常处理。

以上

希望大家有所收获,我们下期再见~

gittee: https://gitee.com/lzj960515/my-micro-service-demo

相关实践学习
部署高可用架构
本场景主要介绍如何使用云服务器ECS、负载均衡SLB、云数据库RDS和数据传输服务产品来部署多可用区高可用架构。
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
7天前
|
算法 NoSQL API
SpringCloud&Gateway网关限流
SpringCloud&Gateway网关限流
24 7
|
1月前
|
缓存
SpringCloud Gateway 网关的请求体body的读取和修改
SpringCloud Gateway 框架中,为了处理请求体body,实现多次读取与修改,创建了一个名为`RequestParamGlobalFilter`的全局过滤器。这个过滤器使用`@Component`和`@Slf4j`注解,实现了`GlobalFilter`和`Ordered`接口,设置最高优先级以首先读取body。它通过缓存请求体并创建装饰过的`ServerHttpRequest`来实现body的动态获取。
45 4
|
2月前
|
负载均衡 应用服务中间件 API
Nginx、Kong、Apisix、Gateway网关比较
Nginx、Kong、Apisix、Gateway网关比较
87 1
Nginx、Kong、Apisix、Gateway网关比较
|
2月前
|
运维 网络协议 安全
长连接网关技术专题(十):百度基于Go的千万级统一长连接服务架构实践
本文将介绍百度基于golang实现的统一长连接服务,从统一长连接功能实现和性能优化等角度,描述了其在设计、开发和维护过程中面临的问题和挑战,并重点介绍了解决相关问题和挑战的方案和实践经验。
85 1
|
2月前
|
SpringCloudAlibaba Java 网络架构
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(七)Spring Cloud Gateway服务网关
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(七)Spring Cloud Gateway服务网关
108 0
|
5天前
|
运维 Java 应用服务中间件
整合服务网关gateway
整合服务网关gateway
11 0
|
6天前
|
前端开发 Java 应用服务中间件
Springboot解决跨域问题方案总结(包括Nginx,Gateway网关等)
Springboot解决跨域问题方案总结(包括Nginx,Gateway网关等)
|
1月前
|
Java Maven 微服务
使用Spring Cloud Gateway构建微服务网关
以上简要示例介绍了使用Spring Cloud Gateway构建微服务网关的基本步骤,包括添加依赖、配置路由、编写过滤器等。通过配置和自定义过滤器,可以实现请求转发、过滤、鉴权等功能。
47 5
|
2月前
|
Java 网络架构 微服务
SpringCloud GateWay网关-学习笔记
SpringCloud GateWay网关-学习笔记
33 0
|
6月前
|
负载均衡 应用服务中间件 API
微服务技术系列教程(25) - SpringCloud- 接口网关服务Zuul
微服务技术系列教程(25) - SpringCloud- 接口网关服务Zuul
59 0