soul 网关源码解析

一.soul网关引入的依赖分析

从上图可以看到我红线划分五个依赖区域

1.soul-common包:这里不是很重要,我们大概看一下他的作用就好了

从上图中可以看出,这个包里主要定义了一个常量,枚举类,配置类,自定义的DTO对象

 

2.soul-metrics这些依赖包主要是监控暴露出来的代码,这里也不是很重要,先略过

 

3.最重要的就是插件部分 soul-plugin依赖包,这里包含了你项目中会用到的插件源码

 

4.websocket和sync部分依赖包: 主要是网关和soul后台管理系统同步配置信息的

 

5.soul-web包也很重要,里面是soul网关的请求入口类,和一些拦截器,配置信息类.

 

二.请求流程:

可以看到exchange里面包含着request信息和response信息.

plugins插件里面会告诉你需要执行的有多少个插件

 

1.首先请求会到SoulWebHandler类里面的execute方法中


        public Mono<Void> execute(final ServerWebExchange exchange) {
            return Mono.defer(() -> {
                //循环执行插件
                if (this.index < plugins.size()) {
                    SoulPlugin plugin = plugins.get(this.index++);
                    //判断插件是否跳过执行
                    Boolean skip = plugin.skip(exchange);
                    if (skip) {
                        return this.execute(exchange);
                    }
                    //调用插件执行逻辑
                    return plugin.execute(exchange, this);
                }
                return Mono.empty();
            });
        }

 

2.执行插件代码

可以看到spring cloud项目一共需要执行10个插件,其中3,4,7是我自定义的插件

 

3.GlobalPlugin插件:

 @Override
    public Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
        final ServerHttpRequest request = exchange.getRequest();
        final HttpHeaders headers = request.getHeaders();
        final String upgrade = headers.getFirst("Upgrade");
        SoulContext soulContext;
        //判断是否是websocket同步的配置请求
        if (StringUtils.isBlank(upgrade) || !"websocket".equals(upgrade)) {
            //正常请求需要构建soulContext
            soulContext = builder.build(exchange);
        } else {
            final MultiValueMap<String, String> queryParams = request.getQueryParams();
            soulContext = transformMap(queryParams);
        }
        exchange.getAttributes().put(Constants.CONTEXT, soulContext);
        return chain.execute(exchange);
    }

继续看DefaultSoulContextBuilder类中的build方法

 public SoulContext build(final ServerWebExchange exchange) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        //根据请求的路径匹配到配置的元数据
        MetaData metaData = MetaDataCache.getInstance().obtain(path);
        if (Objects.nonNull(metaData) && metaData.getEnabled()) {
            exchange.getAttributes().put(Constants.META_DATA, metaData);
        }
        //构建本次请求的SoulContext上下文对象
        return Optional.ofNullable(metaData).map(e -> decoratorMap.get(e.getRpcType()))
                .orElse(decoratorMap.get(RpcTypeEnum.HTTP.getName()))
                .decorator(buildDefault(request), metaData);
    }

 

4.spring cloud plugin插件

 protected Mono<Void> doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) {
        if (Objects.isNull(rule)) {
            return Mono.empty();
        } else {
            SoulContext soulContext = (SoulContext)exchange.getAttribute("context");

            assert soulContext != null;

           //取出后台管理系统配置的选择器规则对象
            SpringCloudRuleHandle ruleHandle = (SpringCloudRuleHandle)GsonUtils.getInstance().fromJson(rule.getHandle(), SpringCloudRuleHandle.class);

           //取出后台管理系统配置的选择器对象
            SpringCloudSelectorHandle selectorHandle = (SpringCloudSelectorHandle)GsonUtils.getInstance().fromJson(selector.getHandle(), SpringCloudSelectorHandle.class);

            if (!StringUtils.isBlank(selectorHandle.getServiceId()) && !StringUtils.isBlank(ruleHandle.getPath())) {
                //调用spring cloud ribbon对象从eureka注册中心获取注册服务实例
                ServiceInstance serviceInstance = this.loadBalancer.choose(selectorHandle.getServiceId());

                if (Objects.isNull(serviceInstance)) {
                    Object error = SoulResultWrap.error(SoulResultEnum.SPRINGCLOUD_SERVICEID_IS_ERROR.getCode(), SoulResultEnum.SPRINGCLOUD_SERVICEID_IS_ERROR.getMsg(), (Object)null);
                    return WebFluxResultUtils.result(exchange, error);
                } else {
                    //根据服务实例负载均衡获取真正的请求服务的URL信息
                    URI uri = this.loadBalancer.reconstructURI(serviceInstance, URI.create(soulContext.getRealUrl()));

                    //构建真正的请求URL,这里有BUG,需要注意
                    String realURL = this.buildRealURL(uri.toASCIIString(), exchange.getRequest().getURI().getQuery());
                    exchange.getAttributes().put("httpUrl", realURL);
                    exchange.getAttributes().put("httpTimeOut", ruleHandle.getTimeout());
                    return chain.execute(exchange);
                }
            } else {
                Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_CONFIG_SPRINGCLOUD_SERVICEID.getCode(), SoulResultEnum.CANNOT_CONFIG_SPRINGCLOUD_SERVICEID.getMsg(), (Object)null);
                return WebFluxResultUtils.result(exchange, error);
            }
        }
    }

 

5.webClientPlugin插件

 @Override
    public Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
        final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);
        assert soulContext != null;
        String urlPath = exchange.getAttribute(Constants.HTTP_URL);
        if (StringUtils.isEmpty(urlPath)) {
            Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null);
            return WebFluxResultUtils.result(exchange, error);
        }
        //获取后台管理系统配置的超时时间
        long timeout = (long) Optional.ofNullable(exchange.getAttribute(Constants.HTTP_TIME_OUT)).orElse(3000L);
        //重试时间
        int retryTimes = (int) Optional.ofNullable(exchange.getAttribute(Constants.HTTP_RETRY)).orElse(0);
        log.info("The request urlPath is {}, retryTimes is {}", urlPath, retryTimes);
        HttpMethod method = HttpMethod.valueOf(exchange.getRequest().getMethodValue());
        WebClient.RequestBodySpec requestBodySpec = webClient.method(method).uri(urlPath);

        //构建request的handle
        return handleRequestBody(requestBodySpec, exchange, timeout, retryTimes, chain);
    }

 

6.插件中主要介绍上面三个插件,经过插件后,就进入reactor.netty.http.client 进行异步调用下游服务接口.具体的源码信息在reactor-netty里面.本章节就不进行介绍了

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值