Gateway服务网关(二)

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
简介: 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

相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
11天前
|
监控 负载均衡 安全
微服务(五)-服务网关zuul(一)
微服务(五)-服务网关zuul(一)
|
8天前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
21 5
|
11天前
|
测试技术 微服务
微服务(八)-服务网关zuul(四)
微服务(八)-服务网关zuul(四)
|
11天前
|
监控 前端开发 Java
微服务(七)-服务网关zuul(三)
微服务(七)-服务网关zuul(三)
|
11天前
|
负载均衡 前端开发 安全
微服务(六)-服务网关zuul(二)
微服务(六)-服务网关zuul(二)
|
2月前
|
Java API 微服务
服务网关Gateway
该博客文章详细介绍了Spring Cloud Gateway的使用方法和概念。文章首先阐述了API网关在微服务架构中的重要性,解释了客户端直接与微服务通信可能带来的问题。接着,文章通过具体的示例代码,展示了如何在Spring Cloud Gateway中添加依赖、编写路由规则,并对路由规则中的基本概念如Route、Predicate和Filter进行了详细解释。最后,文章还提供了路由规则的测试方法。
服务网关Gateway
|
2月前
|
安全 API
【Azure API 管理】APIM Self-Host Gateway 自建本地环境中的网关数量超过10个且它们的出口IP为同一个时出现的429错误
【Azure API 管理】APIM Self-Host Gateway 自建本地环境中的网关数量超过10个且它们的出口IP为同一个时出现的429错误
|
2月前
|
存储 容器
【Azure 事件中心】为应用程序网关(Application Gateway with WAF) 配置诊断日志,发送到事件中心
【Azure 事件中心】为应用程序网关(Application Gateway with WAF) 配置诊断日志,发送到事件中心
|
2月前
|
监控 供应链 安全
构建高效微服务架构:API网关与服务熔断策略
【7月更文挑战第38天】随着现代应用程序向微服务架构的转型,系统的稳定性和效率成为了开发团队关注的焦点。本文将探讨在微服务环境中实现系统可靠性的关键组件——API网关,以及如何在服务间通讯时采用熔断机制来防止故障蔓延。通过分析API网关的核心功能和设计原则,并结合熔断策略的最佳实践,我们旨在提供一套提高分布式系统弹性的策略。
|
3月前
|
监控 负载均衡 Java
深入理解Spring Cloud中的服务网关
深入理解Spring Cloud中的服务网关