Soul网关源码学习04
soul-client 数据同步
soul-client 将元数据进行同步到soul-admin,支持dubbo、springMVC、springCloud、sofa等协议。
客户端注解
@SoulDubboClient、@SoulSofaClient、@SoulSpringCloudClient、@SoulSpringMvcClient 客户端自定义注解,bean实例化后获取appName、地址、接口元数据等信息同步到soul-admin,注解作用域ElementType.TYPE, ElementType.METHOD,描述类、接口、方法。
//请求路径
String path();
//规则名称
String ruleName() default "";
//描述信息
String desc() default "";
//使用协议
String rpcType() default "http";
//注册开关
boolean enabled() default true;
//注册元数据
boolean registerMetaData() default false;
@SoulSpringCloudClient 微服务客户端自定义注解,功能和参数基本同上。
客户端类
各协议同步服务处理类:SpringMvcClientBeanPostProcessor、SpringCloudClientBeanPostProcessor、AlibabaDubboServiceBeanPostProcessor、ApacheDubboServiceBeanPostProcessor等。
SoulSpringMvcClientConfiguration 配置 SpringMvc 的客户端。
@Configuration
public class SoulSpringMvcClientConfiguration {
//bean对象初始化后,将使用自定义注解的客户端信息,同步到soul-admin
@Bean
public SpringMvcClientBeanPostProcessor springHttpClientBeanPostProcessor(SoulSpringMvcConfig config) {
return new SpringMvcClientBeanPostProcessor(config);
}
//事件的监听器
@Bean
public ContextRegisterListener contextRegisterListener(final SoulSpringMvcConfig soulSpringMvcConfig) {
return new ContextRegisterListener(soulSpringMvcConfig);
}
//配置文件
@Bean
@ConfigurationProperties(prefix = "soul.http")
public SoulSpringMvcConfig soulHttpConfig() {
return new SoulSpringMvcConfig();
}
}
SpringMvcClientBeanPostProcessor 实现 BeanPostProcessor 重写了 postProcessAfterInitialization 方法在bean初始化后被调用。
public Object postProcessBeforeInitialization( Object bean, String beanName) throws BeansException {
if (soulSpringMvcConfig.isFull()) {
return bean;
}
//查找类中,是否存在对应的注解,如果有,就返回此注解,没有就返回空
Controller controller = AnnotationUtils.findAnnotation(bean.getClass(), Controller.class);
RestController restController = AnnotationUtils.findAnnotation(bean.getClass(), RestController.class);
RequestMapping requestMapping = AnnotationUtils.findAnnotation(bean.getClass(), RequestMapping.class);
if (controller != null || restController != null || requestMapping != null) {
String contextPath = soulSpringMvcConfig.getContextPath();
SoulSpringMvcClient clazzAnnotation = AnnotationUtils.findAnnotation(bean.getClass(), SoulSpringMvcClient.class);
String prePath = "";
if (Objects.nonNull(clazzAnnotation)) {
//在类上使用通配符*
if (clazzAnnotation.path().indexOf("*") > 1) {
String finalPrePath = prePath;
//线程池执行同步
executorService.execute(() -> post(buildJsonParams(clazzAnnotation, contextPath, finalPrePath)));
return bean;
}
//获取对应path
prePath = clazzAnnotation.path();
}
final Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(bean.getClass());
for (Method method : methods) {
//方法上是否存在对应的注解
SoulSpringMvcClient soulSpringMvcClient = AnnotationUtils.findAnnotation(method, SoulSpringMvcClient.class);
if (Objects.nonNull(soulSpringMvcClient)) {
String finalPrePath = prePath;
//线程池执行同步
executorService.execute(() -> post(buildJsonParams(soulSpringMvcClient, contextPath, finalPrePath)));
}
}
}
return bean;
}
//组装接口参数
private String buildJsonParams(SoulSpringMvcClient soulClient, String contextPath, String prePath) {
String appName = soulClient.getAppName();
Integer port = soulSpringMvcConfig.getPort();
String path = contextPath + prePath + soulClient.path();
String desc = soulSpringMvcClient.desc();
String configHost = soulClient.getHost();
String host = ("".equals(configHost) || null == configHost) ? IpUtils.getHost() : configHost;
String configRuleName = soulSpringMvcClient.ruleName();
String ruleName = ("".equals(configRuleName)) ? path : configRuleName;
SpringMvcRegisterDTO registerDTO = SpringMvcRegisterDTO.builder()
.context(contextPath)
.host(host)
.port(port)
.appName(appName)
.path(path)
.pathDesc(desc)
.rpcType(soulClient.rpcType())
.enabled(soulClient.enabled())
.ruleName(ruleName)
.registerMetaData(soulSpringMvcClient.registerMetaData())
.build();
return OkHttpTools.getInstance().getGosn().toJson(registerDTO);
}
SpringMvc 和 SpringCloud 处理流程逻辑基本一致,SpringCloud不需要传递端口从注册中心获取。
AlibabaDubboServiceBeanPostProcessor 监听ContextRefreshedEvent事件,初始化加载完成后触发。
@Override
public void onApplicationEvent(final ContextRefreshedEvent contextRefreshedEvent) {
if (Objects.nonNull(contextRefreshedEvent.getApplicationContext().getParent())) {
return;
}
//获取dubbo所有服务提供者
Map<String, ServiceBean> serviceBean = contextRefreshedEvent.getApplicationContext().getBeansOfType(ServiceBean.class);
for (Map.Entry<String, ServiceBean> entry : serviceBean.entrySet()) {
//线程池执行接口与 soul-admin 的同步
executorService.execute(() -> handler(entry.getValue()));
}
}
//通过组装服务信息:appName、Interface、methodName、参数类型
private String buildJsonParams( ServiceBean serviceBean, SoulDubboClient soulDubboClient, Method method) {
String appName = dubboConfig.getAppName();
if (StringUtils.isEmpty(appName)) {
appName = serviceBean.getApplication().getName();
}
String path = dubboConfig.getContextPath() + soulDubboClient.path();
String desc = soulDubboClient.desc();
String serviceName = serviceBean.getInterface();
String configRuleName = soulDubboClient.ruleName();
String ruleName = ("".equals(configRuleName)) ? path : configRuleName;
String methodName = method.getName();
Class<?>[] parameterTypesClazz = method.getParameterTypes();
String parameterTypes = Arrays.stream(parameterTypesClazz).map(Class::getName).collect(Collectors.joining(","));
MetaDataDTO metaDataDTO = MetaDataDTO.builder()
.appName(appName)
.serviceName(serviceName)
.methodName(methodName)
.contextPath(dubboConfig.getContextPath())
.path(path)
.ruleName(ruleName)
.pathDesc(desc)
.parameterTypes(parameterTypes)
.rpcExt(buildRpcExt(serviceBean))
.rpcType(RpcTypeEnum.DUBBO.getName())
.enabled(soulDubboClient.enabled())
.build();
return OkHttpTools.getInstance().getGson().toJson(metaDataDTO);
}
//组装group、版本号、负载均衡算法、超时时间、重试次数
private String buildRpcExt(final ServiceBean<?> serviceBean) {
MetaDataDTO.RpcExt build = MetaDataDTO.RpcExt.builder()
.group(StringUtils.isNotEmpty(serviceBean.getGroup()) ? serviceBean.getGroup() : "")
.version(StringUtils.isNotEmpty(serviceBean.getVersion()) ? serviceBean.getVersion() : "")
.loadbalance(StringUtils.isNotEmpty(serviceBean.getLoadbalance()) ? serviceBean.getLoadbalance() : Constants.DEFAULT_LOADBALANCE)
.retries(Objects.isNull(serviceBean.getRetries()) ? Constants.DEFAULT_RETRIES : serviceBean.getRetries())
.timeout(Objects.isNull(serviceBean.getTimeout()) ? Constants.DEFAULT_CONNECT_TIMEOUT : serviceBean.getTimeout())
.url("")
.build();
return OkHttpTools.getInstance().getGson().toJson(build);
}
1、初始化加载完成后获取所有dubbo的服务提供者,使用自定义注解标记方法同步数据到soul-admin 。
2、组装dubbo的提供服务的接口之后 soul-web使用dubbo泛化的方式进行调用。