【高性能网关soul学习】4. soul-client 客户端信息上传
本文主要目标:
分析soul-client 中client 端如何注册相关信息到 soul-admin
1. dubbo服务
SoulAlibabaDubboClientConfiguration
配置类加载了两个bean DubboConfig
和 AlibabaDubboServiceBeanPostProcessor
/ApacheDubboServiceBeanPostProcessor
1.1 DubboConfig
@Data
public class DubboConfig {
/** 为启动的soul-admin 项目的ip + 端口,注意要加 http:// */
private String adminUrl;
/** contextPath: 为该项目在soul网关的路由前缀,用于标识调用端标识当前项目 */
private String contextPath;
/** 应用名称 不配置的话,会默认取 dubbo配置中application 中的名称*/
private String appName;
1.2 SoulDubboClient 和 处理器
dubbo服务的核心注解为 @SoulDubboClient
, 该注解有两个 handler
- alibaba-dubbo:AlibabaDubboServiceBeanPostProcessor
- apache-dubbo:ApacheDubboServiceBeanPostProcessor
上述两个类代码极其相似,因此我们只针对一个做解析 AlibabaDubboServiceBeanPostProcessor 做解析
public class AlibabaDubboServiceBeanPostProcessor implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(final ContextRefreshedEvent contextRefreshedEvent) {
if (Objects.nonNull(contextRefreshedEvent.getApplicationContext().getParent())) {
return;
}
Map<String, ServiceBean> serviceBean = contextRefreshedEvent.getApplicationContext().getBeansOfType(ServiceBean.class);
// 轮询所有的bean,放入线程池异步处理
for (Map.Entry<String, ServiceBean> entry : serviceBean.entrySet()) {
executorService.execute(() -> handler(entry.getValue()));
}
}
}
AlibabaDubboServiceBeanPostProcessor 实现了接口 ApplicationListener<ContextRefreshedEvent>
, spring ioc 初始化完所有的bean之后,会发送事件 ContextRefreshedEvent
, AlibabaDubboServiceBeanPostProcessor
接收上下文刷新事件之后就回去扫描所有的 dubbo ServiceBean,丢进线程池做异步处理
- dubbo 每个暴露出去的服务都会生成一个的
ServiceBean
private void handler(final ServiceBean serviceBean) {
Class<?> clazz = serviceBean.getRef().getClass();
final Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(clazz);
for (Method method : methods) {
SoulDubboClient soulDubboClient = method.getAnnotation(SoulDubboClient.class);
if (Objects.nonNull(soulDubboClient)) {
RegisterUtils.doRegister(buildJsonParams(serviceBean, soulDubboClient, method), url, RpcTypeEnum.DUBBO);
}
}
}
-
可以看到 handler 的处理非常简单,获取到serviceBean中所引用的实际的类型;然后扫描类中每个方法,判断是否有包含注解
@SoulDubboClient
。对于包含的 method ,调用RegisterUtils.doRegister
注册到 soul-admin 中-
url:dubboConfig.getAdminUrl() + “/soul-client/dubbo-register”;
-
buildJsonParams(serviceBean, soulDubboClient, method) 最后拼装成如下的一个对象,然后进行序列化
MetaDataDTO metaDataDTO = MetaDataDTO.builder() .appName(appName) // 应用名 .serviceName(serviceName) // 暴露的服务名 .methodName(methodName) // 暴露的方法名 .contextPath(dubboConfig.getContextPath()) // 配置的soul上下文路径 .path(path) // 类似于当前服务在外部看来的路径 .ruleName(ruleName) // 规则名,没有配置则取path .pathDesc(desc) // 描述 .parameterTypes(parameterTypes) // 参数名,多个参数 `,` 分割 .rpcExt(buildRpcExt(serviceBean)) // 扩展字段,dubbo的分组、超时、版本号、重试次数等信息 .rpcType("dubbo") .enabled(soulDubboClient.enabled()) .build();
-
RegisterUtils.doRegister
public static void doRegister(final String json, final String url, final RpcTypeEnum rpcTypeEnum) { // 删除异常处理代码之后,可以发现,本质就是http调用admin-url方法进行注册 String result = OkHttpTools.getInstance().post(url, json); }
-
1.3 soul-admin端 的 dubbo-register
上面我们发现实际上调用的是soul-admin 中的 /soul-client/dubbo-register
接口,很容易就能发现实际上调用的是 SoulClientController
中的 registerRpc
方法
@RestController
@RequestMapping("/soul-client")
public class SoulClientController {
@PostMapping("/dubbo-register")
public String registerRpc(@RequestBody final MetaDataDTO metaDataDTO) {
return soulClientRegisterService.registerDubbo(metaDataDTO);
}
}
@Service("soulClientRegisterService")
public class SoulClientRegisterServiceImpl implements SoulClientRegisterService {
private final MetaDataMapper metaDataMapper;
private final ApplicationEventPublisher eventPublisher;
private final SelectorService selectorService;
private final RuleService ruleService;
private final RuleMapper ruleMapper;
private final UpstreamCheckService upstreamCheckService;
private final SelectorMapper selectorMapper;
private final PluginMapper pluginMapper;
@Override
@Transactional
public String registerDubbo(final MetaDataDTO dto) {
// 取数据库查询元数据是否已存在
MetaDataDO exist = metaDataMapper.findByPath(dto.getPath());
// 新建或者更新元数据信息
saveOrUpdateMetaData(exist, dto);
// 下面selector和rule都只会注册一次,如果已经存在,则不会重复注册
// 注册seletor信息,从元数据中获取到selectorId (根据 dto.contextPath 进行搜索, 没有则进行注册)
String selectorId = handlerDubboSelector(dto);
// 注册seletor下属的rule信息
handlerDubboRule(selectorId, dto);
return SoulResultMessage.SUCCESS;
}
private void saveOrUpdateMetaData(final MetaDataDO exist, final MetaDataDTO metaDataDTO) {
DataEventTypeEnum eventType;
MetaDataDO metaDataDO = MetaDataTransfer.INSTANCE.mapToEntity(metaDataDTO);
// 对于已存在的进行更新,不存在的进行创建
if (Objects.isNull(exist)) {
Timestamp currentTime = new Timestamp(System.currentTimeMillis());
metaDataDO.setId(UUIDUtils.getInstance().generateShortUuid());
metaDataDO.setDateCreated(currentTime);
metaDataDO.setDateUpdated(currentTime);
metaDataMapper.insert(metaDataDO);
eventType = DataEventTypeEnum.CREATE;
} else {
metaDataDO.setId(exist.getId());
metaDataMapper.update(metaDataDO);
eventType = DataEventTypeEnum.UPDATE;
}
// 发送一个元数据变更事件
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.META_DATA, eventType,
Collections.singletonList(MetaDataTransfer.INSTANCE.mapToData(metaDataDTO))));
}
// 如果不存在,则进行注册(会保存在数据库,并发送一个 ConfigGroupEnum.RULE 的 DataChangedEvent)
private void handlerDubboRule(final String selectorId, final MetaDataDTO metaDataDTO) {
RuleDO existRule = ruleMapper.findByName(metaDataDTO.getPath());
if (Objects.isNull(existRule)) {
registerRule(selectorId, metaDataDTO.getPath(), metaDataDTO.getRpcType(), metaDataDTO.getRuleName());
}
}
}
- 数据变更事件的核心分发器 DataChangedEventDispatcher,对所有监听到的事件进行分发处理,这里暂时略过,后续再详细分析
public class DataChangedEventDispatcher implements ApplicationListener<DataChangedEvent>, InitializingBean {
@Override
@SuppressWarnings("unchecked")
public void onApplicationEvent(final DataChangedEvent event) {
for (DataChangedListener listener : listeners) {
switch (event.getGroupKey()) {
case APP_AUTH:
listener.onAppAuthChanged((List<AppAuthData>) event.getSource(), event.getEventType());
break;
case PLUGIN:
listener.onPluginChanged((List<PluginData>) event.getSource(), event.getEventType());
break;
case RULE:
listener.onRuleChanged((List<RuleData>) event.getSource(), event.getEventType());
break;
case SELECTOR:
listener.onSelectorChanged((List<SelectorData>) event.getSource(), event.getEventType());
break;
case META_DATA:
listener.onMetaDataChanged((List<MetaData>) event.getSource(), event.getEventType());
break;
default:
throw new IllegalStateException("Unexpected value: " + event.getGroupKey());
}
}
}
}
2. 其他服务的注册
其他服务的注册逻辑大致相似,唯一的区别是有些通过 实现BeanPostProcessor,在每个 bean 初始化完毕之后直接触发元数据的注册。
配置类 | 注解 | 处理类 | |
---|---|---|---|
SpringCloud | SoulSpringCloudConfig | SoulSpringCloudClient | SpringMvcClientBeanPostProcessor |
SpringMVC | SoulSpringMvcClient | SoulSpringMvcConfig | SpringMvcClientBeanPostProcessor |
Sofa | SofaConfig | SoulSofaClient | SofaServiceBeanPostProcessor |
Tars | TarsConfig | SoulTarsClient / SoulTarsService | TarsServiceBeanPostProcessor |