网关-基于SpringCloud Gateway的扩展开发_v0.0.1

版本号 作者 qq SpringCloud Gateway版本号 备注
v0.0.1 若布与宫合 8416837 2.1.0.RELEASE 反向代理 负载均衡 服务发现 监控埋点

原创文章转载请注明出处 https://blog.csdn.net/cc007cc009/article/details/92570640

用途

  • 安全
  • 微服务代理
  • 监控/埋点
  • 限流
  • 路径重写

技术栈

  • 动态路由
  • 熔断器
  • 服务模式

过滤器

简介

  • 过滤器种类
    内置
    自定义
  • Filter责任链
    过滤器责任链,管理过滤器执行顺序。
  • 过滤器工作原理
    在这里插入图片描述
    过滤器可以在发送代理请求之前或之后执行.

内置的系统过滤器

  • PrefixPath
  • StripPrefix
    springBoot yaml配置文件:
	routes:
      	- id: routetest1
        uri: https://www.baidu.com # 上游
        predicates:
        - Path=/service/baidu/s # 转发正确. 路由路径
        filters:
        - StripPrefix=2

路由定位器

  • Demo
    关键配置
spring:
  cloud:
    gateway:
      discovery:
      # 网关发现服务开启
        locator:
        # 默认关闭
          enabled: true
          # 默认关闭
          lower-case-service-id: true

微服务API地址http://localhost:8002/system/sysDept/list
经网关代理后的地址:http://localhost:9999/service-name/system/sysDept/list,service-name是服务注册名称。这时在网关添加过滤器,即可拦截请求了。
注意:通过服务名访问,不要定义路由特指。
通过网关访问成功:
在这里插入图片描述
注:localhost:9999为本地网关url。

  • 负载均衡
    通过网关访问微服务时,默认负载均衡。
    过滤器
@Slf4j
public class RequestTimeFilter implements GatewayFilter, Ordered {
    private static final String REQUEST_TIME_BEGIN = "requestTimeBegin";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        exchange.getAttributes().put(REQUEST_TIME_BEGIN, System.currentTimeMillis());
        return chain.filter(exchange).then(
                Mono.fromRunnable(() -> {
                    Long startTime = exchange.getAttribute(REQUEST_TIME_BEGIN);
                    if (startTime != null) {
                        log.debug("{}=={},{}=={}",
                                "请求路径", exchange.getRequest().getURI().getRawPath(),
                                "消耗时间 ", (System.currentTimeMillis() - startTime) + "ms");
                    }
                })
        );
    }

    @Override
    public int getOrder() { // 亦可注解
    // order值与优先级成反比
        return 0;
    }
}

启动访问,过滤器没起效,这是因为还需要将该过滤器加入责任链。见下文

	@Bean
    public RouteLocator myRoutes(RouteLocatorBuilder builder) {
        // 通过访问.../cecjx跳转到cecjx.com/cecjx cecjx路径会转交过去 PATH将作为远程应用的path
        return builder.routes()
                .route(p -> p
                        .path("/**/**")
                        .filters(f -> f.filter(new RequestTimeFilter()))
                        .uri("http://localhost:8002")
                        .order(0)// 起效了
                        .id("customer_filter_router")
                )
                .build();// 熔断测试要去掉contextPath
    }

这样就起效了。

14:56:09 [reactor-http-nio-3] DEBUG c.j.gateway.filter.RequestTimeFilter - 请求路径==/system/sysDept/list,消耗时间 ==17ms
14:56:19 [reactor-http-nio-3] DEBUG c.j.gateway.filter.RequestTimeFilter - 请求路径==/system/sysDept/list3,消耗时间 ==15ms
  • 全局过滤器
// 全局过滤器 优先执行
@Component
@Slf4j
public class TokenFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        if (token == null || token.isEmpty()) {
            log.debug("token 为空,无法进行访问.");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

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

不传Token,无法访问
在这里插入图片描述

15:49:37 [reactor-http-nio-2] DEBUG com.jxbd.gateway.filter.TokenFilter - token 为空,无法进行访问.

传Token,访问成功.Token应加密到Https请求头.
在这里插入图片描述

自带管理接口

# 配置
management:
  endpoints:
    web:
      exposure:
        include: "*"

访问http://localhost:9999/actuator/gateway/routes
在这里插入图片描述
不过这里我们不打算用自带的Restful接口,一来官方文档也没说新增网关参数怎么传,再者我们也不希望在网关暴露这些接口,第三路由信息也需要持久化起来.参照org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint,我们自己编程来动态改变网关。

跨域

熔断器

在路由配置熔断器

					.route(p -> p
                        .host("*.hystrix.com")
                        .filters(f -> f
                                .hystrix(config -> config
                                .setName("mycmd")
                                .setFallbackUri("forward:/fallback")))
                        .uri("http://httpbin3.org"))
curl --dump-header - --header Host:www.hystrix.com http://localhost:9999/any # 因上游地址访问不通,触发熔断

因为访问不到http://httpbin3.org/any,所以会触发熔断。

重试

限流

异常处理

* Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.http.codec.ServerCodecConfigurer' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

解决方法:
去除spring-web依赖

Caused by: org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.

路由

路由种类:
路径
参数
主机
请求头
范围路由
我们主要使用路径路由,其实是做了个路径的反向代理。
将路径路由转发到上游服务器,与上游服务器的url拼接起来。

动态路由

  • 是网关的核心功能之一,在不重启网关的情况下,即可更新路由和路由权限。
代码解释
RouteDefinitionLocator
public interface RouteDefinitionLocator { // 实现该接口:实时获取路由.不好用,因为调用时读取全部路由太耗资源了.
    Flux<RouteDefinition> getRouteDefinitions(); // 不过调用时,并不是每次都走这个方法.
}

同时也要考虑网关集群时,路由亦需要使用中心化的存储器存储起来:redis、etcd、postgresql

RouteDefinitionWriter
public interface RouteDefinitionWriter { // 开发者实现该接口
    Mono<Void> save(Mono<RouteDefinition> route); // 保存单个路由,只是将路由存入客户自定义的数据库.

    Mono<Void> delete(Mono<String> routeId); // 删除单个路由
}
RouteDefinitionRepository
public interface RouteDefinitionRepository extends RouteDefinitionLocator, RouteDefinitionWriter {
} // 继承上述两个接口
ApplicationEventPublisherAware
public interface ApplicationEventPublisherAware extends Aware {
    void setApplicationEventPublisher(ApplicationEventPublisher var1); // 监听
}
public interface Aware {
} // 空的
ApplicationEventPublisher
@FunctionalInterface
public interface ApplicationEventPublisher { // 事件监听 监听路由改变
    default void publishEvent(ApplicationEvent event) { // ApplicationEvent 它的超类是EventObject
        this.publishEvent((Object)event);
    }

    void publishEvent(Object var1);
}
RouteDefinition

按这个数据结构定义路由,并存储待使用。

@Validated
public class RouteDefinition {
	@NotEmpty
	private String id = UUID.randomUUID().toString(); // 默认uuid,一般会覆盖这个id,以便于后续操作.

	@NotEmpty
	@Valid
	private List<PredicateDefinition> predicates = new ArrayList<>();

	@Valid
	private List<FilterDefinition> filters = new ArrayList<>();

	@NotNull
	private URI uri;

	private int order = 0;
// ... 片段 ...
}
原创文章 59 获赞 8 访问量 4万+

猜你喜欢

转载自blog.csdn.net/cc007cc009/article/details/92570640