Soul网关源码分析-5期(番外)


分析请求时 RPCType 如何产生

调用网关转发服务的过程中, 具体是走入 Dubbo 转发, Http 转发, 亦或者 SpringCloud 转发, 都是由上下文中请求对象的 RpcType 属性来确定的, 这次分析下它的由来.

直接找到被 GlobalPlugin (插件链的第一个插件) 调用的 DefaultSoulContextBuilder 类:

public class DefaultSoulContextBuilder implements SoulContextBuilder {
    
  @Override
  public SoulContext build(final ServerWebExchange exchange) {
    final 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);
    }
    return transform(request, metaData);
  }
  
  private SoulContext transform(final ServerHttpRequest request, final MetaData metaData) {
    // ...
    if (Objects.nonNull(metaData) && metaData.getEnabled()) {
      // 根据 metaData 的 rpcType, 判断走 springcloud or dubbo
      if (RpcTypeEnum.SPRING_CLOUD.getName().equals(metaData.getRpcType())) {
        setSoulContextByHttp(soulContext, path);
        soulContext.setRpcType(metaData.getRpcType());
      } else {
        setSoulContextByDubbo(soulContext, metaData);
      }
    } else {
      // metaData 为空或者 enable=false 会直接写为 HTTP 请求
      setSoulContextByHttp(soulContext, path);
      soulContext.setRpcType(RpcTypeEnum.HTTP.getName());
    }
		// ...
    return soulContext;
  }
}

重点看这句 MetaDataCache.getInstance().obtain(path) :

public final class MetaDataCache {
  private static final ConcurrentMap<String, MetaData> META_DATA_MAP = Maps.newConcurrentMap();
  
  public MetaData obtain(final String path) {
    // 从缓存中取出对应资源的元数据信息
    MetaData metaData = META_DATA_MAP.get(path);
    if (Objects.isNull(metaData)) {
      // 没有则循环再次匹配, PathMatchUtils.match 不细分析
      String key = META_DATA_MAP.keySet().stream().filter(k -> PathMatchUtils.match(k, path)).findFirst().orElse("");
      return META_DATA_MAP.get(key);
    }
    return metaData;
  }
}

通过这里可以知道, 请求进入通过元数据缓存匹配拿到 RpcType 类型, 那么 META_DATA_MAP 缓存的数据从哪里来呢? 最终可以追踪到网关与管理后台的同步配置这, 网关这边的监听类 SoulWebsocketClient:

public final class SoulWebsocketClient extends WebSocketClient {
  @Override
  public void onMessage(final String result) {
    handleResult(result);
  }
  
  private void handleResult(final String result) {
    WebsocketData websocketData = GsonUtils.getInstance().fromJson(result, WebsocketData.class);
    // 得到更新数据的类型, 比如 metadata(元数据) 、plugins(插件)、ruleData(规则)、selector(选择器)等
    ConfigGroupEnum groupEnum = ConfigGroupEnum.acquireByName(websocketData.getGroupType());
    // 得到更新事件, 比如 refresh、update、myself 等
    String eventType = websocketData.getEventType();
    // 具体要更新的配置
    String json = GsonUtils.getInstance().toJson(websocketData.getData());
    // 通过 groupEnum 更新类型, 获得数据处理子类 DataHandler
    websocketDataHandler.executor(groupEnum, json, eventType);
  }
}

SoulWebsocketClient 类借助 WebSocket 客户端的实现, 与管理后台建立 WebSocket 连接并同步配置, 在收到信息之后, 判断是什么数据类型, 并调用具体的 DataHandler 数据处理子类.

它们都有共同的抽象父类 AbstractDataHandler, 作为更新事件分发器的 handler() 方法:

public abstract class AbstractDataHandler<T> implements DataHandler {
	@Override
  public void handle(final String json, final String eventType) {
    List<T> dataList = convert(json);
    if (CollectionUtils.isNotEmpty(dataList)) {
      DataEventTypeEnum eventTypeEnum = DataEventTypeEnum.acquireByName(eventType);
      switch (eventTypeEnum) {
        case REFRESH:
        case MYSELF:
          doRefresh(dataList);
          break;
        case UPDATE:
        case CREATE:
          doUpdate(dataList);
          break;
        case DELETE:
          doDelete(dataList);
          break;
        default:
          break;
      }
    }
  }
}

这次分析的是元数据, 就继续进入 MetaDataHandler 类查看代码, 一路追踪可以在 MetaDataAllSubscriber 类中找到元数据缓存的更新:

public class MetaDataAllSubscriber implements MetaDataSubscriber {
    @Override
    public void onSubscribe(final MetaData metaData) {
        MetaDataCache.getInstance().cache(metaData);
    }
}



TIPS1 如何主动触发元数据的更新?

在管理后台的元数据页, 点击"同步配置"即可
在这里插入图片描述



TIPS2 如果访问请求时出现404等情况如何排查?

在 MetaDataCache 的 obtain() 方法上断点:

public MetaData obtain(final String path) {
  // 检查下 META_DATA_MAP 里都有什么数据
  MetaData metaData = META_DATA_MAP.get(path);
  if (Objects.isNull(metaData)) {
    String key = META_DATA_MAP.keySet().stream().filter(k -> PathMatchUtils.match(k, path)).findFirst().orElse("");
    return META_DATA_MAP.get(key);
  }
  return metaData;
}
  1. 检查 META_DATA_MAP 元数据缓存中的路径, 是否能与你访问的 path 相匹配
  2. 查看管理后台的"元数据管理"页面, 是否存在相应元数据, 且状态是"开启"
  3. 检查服务是否启动, 如果不启动并注册, 后台管理项目不会发送相应元数据, 即使管理页面存在且开启. (启动服务项目后若元数据缓存中未及时触发更新, 可以手动点下同步数据)
  4. 一个小坑: 如果你要请求 dubbo 服务但输错了路径, 肯定会走入到Http匹配那块去, 因为如果元数据缓存匹配到的路径为 null, 默认 RpcType 为 HTTP.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值