Soul网关源码学习(9)- 请求解析 GlobalPlugin

文章目录

前言

在前一篇文章《Soul网关源码学习(8)- 代理转发入口 SoulWebHandler》 中,我们详细分析了网关的入口 SoulWebHandler,并且就代码中使用的几个技术进行了扩展延申学习。如果小伙伴看过前面两篇关于 soul 代理转发流程的介绍文章,都应该知道请求进入 SoulWebHandler 之后,下一站就是天后了!噗!开玩笑的!是 GlobalPlugin !

GlobalPlugin

GlobalPlugin 到底是干什么的?

请求解析!这是我对 GlobalPlugin 功能的一个理解。Soul 对外统一使用 Http 接口,所以单纯的直接转发无法实现对不同协议的代理,比如,你总不能直接把 Http 请求转给 dubbo服务器吧,那样 dubbo 服务根本无法识别,需要 soul 通过客户端的请求和控制台配置的元数据进行匹配和结合才能实现。所以这里总结一下其功能:

  • 通过 Http 的请求信息匹配相应的元数据,并且添加到 ServerWebExchange 属性中。元数据是 soul 对具体目标服务接口的描述信息,比如 App Name、MethodName、Path等,像 dubbo 的泛化调用就需要使用到元数据,否则仅仅靠客户端请求信息是无法完成代理的。
  • 通过分析 Http 请求信息 和 上面获取的元数据构建 SoulContext 对象,并且存储到 ServerWebExchange(Spring WebFlux 的请求上下文) 的属性中,通过后者在整个 请求流程中传递。

SoulContext 的构建

SoulContext 按照字面理解就是 Soul 请求的上下文,它的层次在 ServerWebExchange 之上,毕竟 SoulContext 就存储在 ServerWebExchange 的属性中。

// GlobalPlugin execute 方法中的代码
exchange.getAttributes().put(Constants.CONTEXT, soulContext);

它主要存储一些与目标服务协议相关的信息,比如 Http 目标服务的 HttpMethod、参数、真实Url,又或者 Dubbo 目标服务的服务名称,接口名称、接口参数等待。

SoulContext 的构建代码:

    @Override
    public Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
    
    
    	...
    	// 如果不是 websocket 请求 通过 SoulContextBuilder 构建
        if (StringUtils.isBlank(upgrade) || !"websocket".equals(upgrade)) {
    
    
            soulContext = builder.build(exchange);
        } else {
    
    
        	// websocket 
            final MultiValueMap<String, String> queryParams = request.getQueryParams();
            soulContext = transformMap(queryParams);
        }
        // 存储到 ServerWebExchange 属性中
        exchange.getAttributes().put(Constants.CONTEXT, soulContext);
        return chain.execute(exchange);
    }

上面代码逻辑很简单,通过 Http header "upgrade " 来判断是否是 webSocket 请求,从而选择不同的构建方法。

我们这里主要看非 webSocket 的构建逻辑:

@Override
public SoulContext build(final ServerWebExchange exchange) {
    
    
   final ServerHttpRequest request = exchange.getRequest();
   String path = request.getURI().getPath();
   //获取元数据,并且存到 ServerWebExchange  属性中
   MetaData metaData = MetaDataCache.getInstance().obtain(path);
   if (Objects.nonNull(metaData) && metaData.getEnabled()) {
    
    
       exchange.getAttributes().put(Constants.META_DATA, metaData);
   }
   //通过请求信息和元数据共同构建 soulContext 对象并且返回
   return transform(request, metaData);
}

从上面的代码和注释中,可以看到一个关键单词就是 MetaData。

MetaData 的获取

  • 首先,MetaData 是什么?

    在这里插入图片描述
    MetaData 可以在 soul admin 中直接查看,其实就是对每个被代理接口的具体描述(例如:dubbo 泛化调用就需要用到),普通 Http 的 MetaData 是空的,这个可以参考一下官方文档说明。

  • MetaData 来自哪里?

    源头来自服务注册中心,网关服务获取的是本地副本!Soul Admin 通过配置的服务注册中心,获取到相应的接口描述信息(比如 dubbo 注册在 zookeeper 的接口信息),然后同步到网关服务端,服务端再缓存在本地 JVM 中。数据的同步可能是推或者拉的模式,这个后面“数据同步”相关文章再分析。

    PS:源头来自服务注册中心,目前是我推测的,我还没深入看这部分源码(这部分代码应该在 soul admin 中,目前优先解读 soul 服务端)。但是这些信息来自注册中心可能性最大,毕竟网关代理具体的微服务时,需要配置相应的注册中心。后面我回去看这部分代码,求证了再回来修改这里了。

    // 从本地缓存中获取 MetaData
    MetaData metaData = MetaDataCache.getInstance().obtain(path);
    

生成 SoulContext 对象

SoulContext 对象是通过 DefaultSoulContextBuilder#transform(ServerHttpRequest,MetaData) 方法构建的,代码比较长但是逻辑很简单,所以这里就不贴了,就和开头说的一样,其属性都是通过解析 request 和 MetaData中获取的,因此都是一些 set 操作。

总结

本篇文章,详细介绍了 GlobalPlugin 的作用,通过解析客户端请求,匹配缓存里的元数据,为后续的流程操作提供必要的信息。GlobalPlugin 是所有协议代理都会经过的插件节点,再往后,流程就可能会因为协议的不同而出现分叉了,这也是后面的文章的主要内容。

猜你喜欢

转载自blog.csdn.net/u012180773/article/details/113065826