七、soul源碼學習-http服務注冊數據同步機制源碼解析

前面我們已經本地啟動了SpringBoot服務,并將Controller的接口注冊到了SoulAdmin,并通過網關調用成功轉發到了我們的服務,這一節我們從http項目是如何注冊到SoulAdmin,SoulAdmin又如何將注冊信息同步到網關的整個流程來剖析下源代碼

首先啟動SoulAdmin。接下來在啟動我們的SpringBoot項目

第二節,我們由于http注冊問題,已經大體走完了,服務啟動注冊到SoulAdmin的流程,通過http請求到SoulAdmin的/soul-client/springmvc-register接口

接下來在SoulAdmin中定位到該接口

//org.dromara.soul.admin.controller.SoulClientController
/**
* Register spring mvc string.
*
* @param springMvcRegisterDTO the spring mvc register dto
* @return the string
*/
@PostMapping("/springmvc-register")
public String registerSpringMvc(@RequestBody final SpringMvcRegisterDTO springMvcRegisterDTO) {
  return soulClientRegisterService.registerSpringMvc(springMvcRegisterDTO);
}

我們看下SpringMvcRegisterDTO類,這個是之前org.dromara.soul.client.springmvc.init.SpringMvcClientBeanPostProcessor#buildJsonParams構造的信息

@Data
public class SpringMvcRegisterDTO implements Serializable {
  //我們SpringBoot項目配置的appName
  private String appName;
 //
  private String context;

  private String path;

  private String pathDesc;

  private String rpcType;

  private String host;

  private Integer port;

  private String ruleName;

  private boolean enabled;

  private boolean registerMetaData;
}

進入到service中

//org.dromara.soul.admin.service.impl.SoulClientRegisterServiceImpl
@Override
@Transactional
public String registerSpringMvc(final SpringMvcRegisterDTO dto) {
  //是否注冊元數據信息,這里http默認是false,元數據主要是為dubbo的泛化調用使用的,這里不是主要的關注重點
  if (dto.isRegisterMetaData()) {
    //根據path看元數據信息是否存在
    MetaDataDO exist = metaDataMapper.findByPath(dto.getPath());
    if (Objects.isNull(exist)) {
      saveSpringMvcMetaData(dto);
    }
  }
  //1
  String selectorId = handlerSpringMvcSelector(dto);
  handlerSpringMvcRule(selectorId, dto);
  return SoulResultMessage.SUCCESS;
}

1.進入到org.dromara.soul.admin.service.impl.SoulClientRegisterServiceImpl#handlerSpringMvcSelector

private String handlerSpringMvcSelector(final SpringMvcRegisterDTO dto) {
  //我們在SpringBoot項目中配置的contextPath
  String contextPath = dto.getContext();
  SelectorDO selectorDO = selectorService.findByName(contextPath);
  String selectorId;
  String uri = String.join(":", dto.getHost(), String.valueOf(dto.getPort()));
  if (Objects.isNull(selectorDO)) {
    //1.1 注冊選擇器
    selectorId = registerSelector(contextPath, dto.getRpcType(), dto.getAppName(), uri);
  } else {
    selectorId = selectorDO.getId();
    //update upstream
    String handle = selectorDO.getHandle();
    String handleAdd;
    DivideUpstream addDivideUpstream = buildDivideUpstream(uri);
    SelectorData selectorData = selectorService.buildByName(contextPath);
    if (StringUtils.isBlank(handle)) {
      handleAdd = GsonUtils.getInstance().toJson(Collections.singletonList(addDivideUpstream));
    } else {
      List<DivideUpstream> exist = GsonUtils.getInstance().fromList(handle, DivideUpstream.class);
      //如果已存在的規則和這次新增加的相等,則不需要更新
      for (DivideUpstream upstream : exist) {
        if (upstream.getUpstreamUrl().equals(addDivideUpstream.getUpstreamUrl())) {
          return selectorId;
        }
      }
      exist.add(addDivideUpstream);
      handleAdd = GsonUtils.getInstance().toJson(exist);
    }
    selectorDO.setHandle(handleAdd);
    selectorData.setHandle(handleAdd);
    // update db
    selectorMapper.updateSelective(selectorDO);
    // submit upstreamCheck
    upstreamCheckService.submit(contextPath, addDivideUpstream);
    // publish change event.
    eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, DataEventTypeEnum.UPDATE,
                                                     Collections.singletonList(selectorData)));
  }
  return selectorId;
}

1.1 org.dromara.soul.admin.service.impl.SoulClientRegisterServiceImpl#registerSelector

private String registerSelector(final String contextPath, final String rpcType, final String appName, final String uri) {
  //構造選擇器默認類,無論我們在SpringBoot項目配置的full是true或者false,在選擇器這里沒有區別。都是CUSTOM_FLOW,區別是在規則那里
  SelectorDTO selectorDTO = SelectorDTO.builder()
    .name(contextPath)
    .type(SelectorTypeEnum.CUSTOM_FLOW.getCode())
    .matchMode(MatchModeEnum.AND.getCode())
    .enabled(Boolean.TRUE)
    .loged(Boolean.TRUE)
    .continued(Boolean.TRUE)
    .sort(1)
    .build();
  if (RpcTypeEnum.DUBBO.getName().equals(rpcType)) {
    selectorDTO.setPluginId(getPluginId(PluginEnum.DUBBO.getName()));
  } else if (RpcTypeEnum.SPRING_CLOUD.getName().equals(rpcType)) {
    selectorDTO.setPluginId(getPluginId(PluginEnum.SPRING_CLOUD.getName()));
    selectorDTO.setHandle(GsonUtils.getInstance().toJson(buildSpringCloudSelectorHandle(appName)));
  } else if (RpcTypeEnum.SOFA.getName().equals(rpcType)) {
    selectorDTO.setPluginId(getPluginId(PluginEnum.SOFA.getName()));
    selectorDTO.setHandle(appName);
  } else if (RpcTypeEnum.TARS.getName().equals(rpcType)) {
    selectorDTO.setPluginId(getPluginId(PluginEnum.TARS.getName()));
    selectorDTO.setHandle(appName);
  } else {
    //我們這次是http服務注冊,rpcType是http
    //1.1.1 這里構造DivideUpstream代表http上游
    DivideUpstream divideUpstream = buildDivideUpstream(uri);
    String handler = GsonUtils.getInstance().toJson(Collections.singletonList(divideUpstream));
    //DivideUpsteam的json字符串,對應選擇器中的配置
    selectorDTO.setHandle(handler);
    //選擇器對應的就是Divide控件
    selectorDTO.setPluginId(getPluginId(PluginEnum.DIVIDE.getName()));
    //這里是 上游檢查服務,之后單獨寫一篇文章
    upstreamCheckService.submit(selectorDTO.getName(), divideUpstream);
  }
  //選擇器條件默認就是Match對應的contextPath+/**
  SelectorConditionDTO selectorConditionDTO = new SelectorConditionDTO();
  selectorConditionDTO.setParamType(ParamTypeEnum.URI.getName());
  selectorConditionDTO.setParamName("/");
  selectorConditionDTO.setOperator(OperatorEnum.MATCH.getAlias());
  selectorConditionDTO.setParamValue(contextPath + "/**");
  selectorDTO.setSelectorConditions(Collections.singletonList(selectorConditionDTO));
  //1.1.2 注冊
  return selectorService.register(selectorDTO);
}

SelectorDto的handle是存儲的就是DivideUpsteam的json字符串,對應選擇器的Configuration

image.png

1.1.1 org.dromara.soul.admin.service.impl.SoulClientRegisterServiceImpl#buildDivideUpstream

//uri 是 前面傳遞過來的,String.join(":", dto.getHost(), String.valueOf(dto.getPort()));
//實際上就是代表咱們注冊服務上游的http訪問地址
private DivideUpstream buildDivideUpstream(final String uri) {
  return DivideUpstream.builder()
    .upstreamHost("localhost")
    .protocol("http://")
    .upstreamUrl(uri)
    .weight(50)
    .build();
}

1.1.2 org.dromara.soul.admin.service.impl.SelectorServiceImpl#register

@Override
public String register(final SelectorDTO selectorDTO) {
  //這里就是簡單的數據庫操作
  SelectorDO selectorDO = SelectorDO.buildSelectorDO(selectorDTO);
  List<SelectorConditionDTO> selectorConditionDTOs = selectorDTO.getSelectorConditions();
  if (StringUtils.isEmpty(selectorDTO.getId())) {
    selectorMapper.insertSelective(selectorDO);
    selectorConditionDTOs.forEach(selectorConditionDTO -> {
      selectorConditionDTO.setSelectorId(selectorDO.getId());
     selectorConditionMapper.insertSelective(SelectorConditionDO.buildSelectorConditionDO(selectorConditionDTO));
    });
  }
  //1.1.2.1 這里發布事件
  publishEvent(selectorDO, selectorConditionDTOs);
  return selectorDO.getId();
}

1.1.2.1 org.dromara.soul.admin.service.impl.SelectorServiceImpl#publishEvent

private final ApplicationEventPublisher eventPublisher;

private void publishEvent(final SelectorDO selectorDO, final List<SelectorConditionDTO> selectorConditionDTOs) {
  PluginDO pluginDO = pluginMapper.selectById(selectorDO.getPluginId());
  List<ConditionData> conditionDataList =
    selectorConditionDTOs.stream().map(ConditionTransfer.INSTANCE::mapToSelectorDTO).collect(Collectors.toList());
  // 發布數據變化事件,用到了Spring的EventBus機制,之后單獨拿一章節講一下EventBus
  eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, DataEventTypeEnum.UPDATE,
                                                   Collections.singletonList(SelectorDO.transFrom(selectorDO, pluginDO.getName(), conditionDataList))));
}

總結下來就是:

同步數據到數據庫,并將數據的變化事件發送出去,具體發送后做的事情我們下篇文章在分析

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容