应该算是非常简单的HSF框架转Dubbo框架方案

举报
yd_272985578 发表于 2024/09/28 23:46:59 2024/09/28
【摘要】 为什么会这么说呢因为你的代码改动是最少的,只增加了两个依赖却可以让你的项目从HSF框架轻松转移到Dubbo框架。经历阿里巴巴集团业务广泛,涵盖多个领域。在电商领域,拥有淘宝、天猫等核心平台,提供丰富的商品选择和便捷的购物体验。金融领域,通过蚂蚁集团布局支付、贷款、理财等,支付宝成为主流移动支付工具。云计算与大数据方面,阿里云提供全面的云计算服务,助力企业数字化转型。物流领域,菜鸟网络整合物流...

为什么会这么说呢

因为你的代码改动是最少的,只增加了两个依赖却可以让你的项目从HSF框架轻松转移到Dubbo框架。

经历

阿里巴巴集团业务广泛,涵盖多个领域。在电商领域,拥有淘宝、天猫等核心平台,提供丰富的商品选择和便捷的购物体验。金融领域,通过蚂蚁集团布局支付、贷款、理财等,支付宝成为主流移动支付工具。云计算与大数据方面,阿里云提供全面的云计算服务,助力企业数字化转型。物流领域,菜鸟网络整合物流资源,提升物流效率。此外,阿里巴巴还积极布局新零售、智慧零售,推动线上线下融合,并致力于国际化战略,拓展全球市场。这些业务共同构成了阿里巴巴庞大的商业生态系统

普通人平常接触更多的则是购物,秉持着“让天下没有难做的生意”这一核心理念,成功引领了全中国普通小商家迈入全新的销售模式。而其旗下的淘宝平台,更是让全国人民享受到了足不出户便能尽收天下好物的极致便利。

作为一个程序员,我对于阿里云平台有着更为深入的了解。阿里云平台作为阿里巴巴集团旗下的云计算服务提供商,凭借其强大的技术实力和丰富的产品线,为广大企业和开发者提供了稳定、安全、高效的云计算解决方案。无论是弹性计算、存储与CDN、数据库、网络与安全,还是大数据、人工智能等领域,阿里云平台都展现出了其卓越的技术实力和市场竞争力。

2017年下半年我开始接触淘宝团队打造的HSF框架,作为项目的分布式框架,有太多的东西需要学习。不过,好在有同事的帮助下,很快了解了此框架是如何在项目中配置的,慢慢地对其源码也产生了好奇。在好奇心的作用下,利用开发工具可以下载源码的功能试图下载源码,可惜的是下载不下来,经过多次百度查询得知HSF框架属于私有框架。

刚开始接触HSF框架的时候正处于公司从Dubbo转换HSF的结尾期,大概是一开始使用Dubbo框架后来公司接入阿里私有云后就切换到了HSF框架,当我接手的时候已经就是HSF框架了,只不过新项目的时候偶尔还能看到Dubbo的影子(应该是之前的项目模板遗留下来的)。

算是从今年开始,看到公司内部有开始迁移分布式框架的动向,发现改动原始代码的代价有点高,再加上本人喜欢研究源码,本着能不动原来的代码尽量就不动原来的代码的原则,准备将HSF框架无缝对接到Dubbo框架。

动手

由于HSF框架的封闭性,从现有的框架代码上动手就有些难搞了,幸好阿里云官方已经实现了HSF、Dubbo注册中心双注册的实现方案,再加上这块实现方案是公开的,源码可以找到,因此,在改造的路上就借鉴了双注册的部分逻辑。

方向

随着SpringBoot的火爆以及公司内部封装了基于HSF的SpringBoot框架,因此,此次改动的目标是基于内部封装的框架由HSF无缝迁移到Dubbo。

SpringBoot版HSF叫做PandoraBoot,基于注解的形式完成HSF注册和消费,因此,着手点首先要从@HSFProvider和@HSFConsumer两个注解开始。

1. @HSFProvider

入口文件HSFProviderAnnotationRegistrar,由于实现了ImportBeanDefinitionRegistrar接口,启动项目会通过registerBeanDefinitions方法注册bean,具体逻辑如下:

HSFProvider HSFProvider = (HSFProvider)AnnotationUtils.findAnnotation(targetClass, HSFProvider.class);
// 省略部分代码...
HSFProviderBeanDefinitionBuilder builder = (new HSFProviderBeanDefinitionBuilder(targetBeanName, HSFProvider)).clazz(targetClass).properties(HSFProperties);
BeanDefinition beanDefinition = builder.build(registry);
if (beanDefinition != null) {
    if (registry.containsBeanDefinition(beanName)) {
        throw new BeanDefinitionValidationException("BeanDefinition with the same beanName already existed, please check your config! beanName:" + beanName);
    }

    registry.registerBeanDefinition(beanName, beanDefinition);
    // 省略部分代码...
}

通过HSFProviderBeanDefinitionBuilder构建BeanDefinition,其build方法通过BeanDefinitionBuilder注册HSFSpringProviderBean

这块改造逻辑借鉴了Dubbo的处理思路,通过@DubboService注解查找到ServiceAnnotationPostProcessorbean后置处理类,postProcessBeanFactory方法处理逻辑如下:

BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
// 查找通过@DubboService注解的类并获取属性
Map<String, Object> annotationAttributes = getServiceAnnotationAttributes(beanDefinition);
if (annotationAttributes != null) {
    // 注册BeanDefinition
    processAnnotatedBeanDefinition(
            beanName, (AnnotatedBeanDefinition) beanDefinition, annotationAttributes);
}
private void processAnnotatedBeanDefinition(
        String refServiceBeanName,
        AnnotatedBeanDefinition refServiceBeanDefinition,
        Map<String, Object> attributes) {

    Map<String, Object> serviceAnnotationAttributes = new LinkedHashMap<>(attributes);

    // 省略部分代码...

    // 构建AbstractBeanDefinition,其实就是对BeanDefinition的构建
    AbstractBeanDefinition serviceBeanDefinition =
            buildServiceBeanDefinition(serviceAnnotationAttributes, serviceInterface, refServiceBeanName);

    // set id
    serviceBeanDefinition.getPropertyValues().add(Constants.ID, serviceBeanName);

    // 注册BeanDefinition
    registerServiceBeanDefinition(serviceBeanName, serviceBeanDefinition, serviceInterface);
}

了解到Dubbo的注册过程,实际改造过程中也是通过实现BeanDefinitionRegistryPostProcessor接口完成BeanDefinition的构建与注册,其逻辑如下:

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    String[] beanNames = beanFactory.getBeanDefinitionNames();
    for (String beanName : beanNames) {
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
        // 消费者
        if (isHsfConsumerBean(beanDefinition)) {
            // 省略部分代码...
        }
        // 提供者
        else if (isHsfProviderBean(beanDefinition)) {
            // 注册Dubbo bean
            HsfProviderDubboService.registerBeanDefinition(beanName, this.applicationContext, beanDefinition, this.environment, registry);
            if (this.registry.getBeanDefinition(beanName) != null) {
                // 为了清除hsf注册中心注册实例
                this.registry.removeBeanDefinition(beanName);
            }
        }
        // 省略部分代码...
    }
}
public static void registerBeanDefinition(String beanName, ApplicationContext applicationContext, BeanDefinition providerBeanDefinition, Environment environment, BeanDefinitionRegistry registry) {
    BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(providerBeanDefinition, beanName);
    // 具体注册逻辑
    processScannedBeanDefinition(beanDefinitionHolder, applicationContext.getClassLoader(), environment, registry);
}
private static void processScannedBeanDefinition(BeanDefinitionHolder beanDefinitionHolder, ClassLoader classLoader, Environment environment, BeanDefinitionRegistry registry) {
    // The attributes of @Service annotation
    Map<String, Object> serviceAnnotationAttributes =
        ReferenceBeanSupport.convertPropertyValues(beanDefinitionHolder.getBeanDefinition().getPropertyValues());
    Map<String, Object> dubboAttributes = new HashMap<>(serviceAnnotationAttributes.size());
    cleanAttribute(serviceAnnotationAttributes, dubboAttributes, "serviceInterface", "interfaceClass");
    cleanAttribute(serviceAnnotationAttributes, dubboAttributes, "serviceGroup", "group");
    cleanAttribute(serviceAnnotationAttributes, dubboAttributes, "serviceVersion", "version");

    BeanDefinition beanDefinition = registry.getBeanDefinition(serviceAnnotationAttributes.get("target").toString());
    Class<?> beanClass = resolveClass(beanDefinition, classLoader);

    String serviceInterface = resolveInterfaceName(dubboAttributes, beanClass);

    String annotatedServiceBeanName = serviceAnnotationAttributes.get("target").toString();

    // ServiceBean Bean name
    String beanName = generateServiceBeanName(dubboAttributes, serviceInterface, environment);

    // 构建BeanDefinition
    AbstractBeanDefinition serviceBeanDefinition =
            buildServiceBeanDefinition(dubboAttributes, serviceInterface, annotatedServiceBeanName, environment);

    // 注册,由于代码逻辑简单就不再展示了
    // 核心代码:registry.registerBeanDefinition(serviceBeanName, serviceBeanDefinition)
    registerServiceBeanDefinition(beanName, serviceBeanDefinition, serviceInterface, registry);

}

2. 展示


3. @HSFConsumer

入口文件HsfConsumerPostProcessor,由于实现了BeanFactoryPostProcessor接口,启动项目会通过postProcessBeanFactory方法注册bean,消费者逻辑与Dubbo类似,具体逻辑如下:

private void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory, BeanDefinitionRegistry registry) {
    final HsfProperties properties = HsfPropertiesUtils.buildHsfProperties(this.environment);
    new HashMap();
    String[] var5 = beanFactory.getBeanDefinitionNames();
    int var6 = var5.length;

    for(int var7 = 0; var7 < var6; ++var7) {
        String beanName = var5[var7];
        BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
        String beanClassName = definition.getBeanClassName();
        if (beanClassName != null) {
            Class<?> clazz = ClassUtils.resolveClassName(definition.getBeanClassName(), this.classLoader);
            ReflectionUtils.doWithFields(clazz, new ReflectionUtils.FieldCallback() {
                public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                    // 解析@HSFConsumer属性
                    HsfConsumerPostProcessor.this.parseElement(field, properties);
                }
            });
        }
    }

    // 省略部分代码...

    while(var12.hasNext()) {
        String beanName = (String)var12.next();
        if (this.context.containsBean(beanName)) {
            throw new IllegalArgumentException("[HSF Starter] Spring context already has a bean named " + beanName + ", please change @HSFConsumer field name.");
        }

        // 注册bean
        registry.registerBeanDefinition(beanName, (BeanDefinition)this.beanDefinitions.get(beanName));
    }

}
private void parseElement(Field field, HsfProperties properties) {
    // 获取注解属性
    HSFConsumer annotation = (HSFConsumer)AnnotationUtils.getAnnotation(field, HSFConsumer.class);
    if (annotation != null) {
        HsfConsumerBeanDefinitionBuilder beanDefinitionBuilder = new HsfConsumerBeanDefinitionBuilder(field.getType(), annotation);
        beanDefinitionBuilder.context(this.context).beanFactory(this.beanFactory).properties(properties);
        // 构建BeanDefinition,解析注解属性
        BeanDefinition beanDefinition = beanDefinitionBuilder.build();
        if (this.checkFieldNameDuplicate4FirstTime(field.getName(), beanDefinition)) {
            logger.error("registered HSFConsumerBean with duplicate fieldName:{} in spring context.", field.getName());
        }

        this.beanDefinitions.put(field.getName(), beanDefinition);
    }
}

通过HsfConsumerBeanDefinitionBuilder解析@HSFConsumer属性,随后通过build方法并构建BeanDefinition

从上文注册@DubboService的过程可以发现,postProcessBeanFactory方法预留了处理Hsf消费者的逻辑,

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    String[] beanNames = beanFactory.getBeanDefinitionNames();
    for (String beanName : beanNames) {
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
        // 消费者
        if (isHsfConsumerBean(beanDefinition)) {
            try {
                // 构建BeanDefinition
                BeanDefinition consumerDefinition =
                    HsfConsumerDubboService.registerBeanDefinition(beanName, this.applicationContext, this.referenceBeanManager, beanDefinition);
                if (this.registry.getBeanDefinition(beanName) != null) {
                    // 为了清除hsf注册中心注册实例
                    this.registry.removeBeanDefinition(beanName);
                }
                // 构建bean
                this.registry.registerBeanDefinition(beanName, consumerDefinition);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        } else if (isHsfProviderBean(beanDefinition)) {
            // 服务提供者逻辑...
        }
        if (isAnnotatedReferenceBean(beanDefinition)) {
            
        }
    }
}
public static BeanDefinition registerBeanDefinition(String beanName, ApplicationContext applicationContext,
            ReferenceBeanManager referenceBeanManager, BeanDefinition consumerBeanDefinition)
            throws ClassNotFoundException {
    MutablePropertyValues mutablePropertyValues = consumerBeanDefinition.getPropertyValues();
    Map<String, Object> propertyAttributes = ReferenceBeanSupport.convertPropertyValues(mutablePropertyValues);

    // check reference key
    String referenceKey = ReferenceBeanSupport.generateReferenceKey(propertyAttributes, applicationContext);
    Object propertyValue = propertyAttributes.get("id");
    String referenceBeanName = beanName;
    if (propertyValue != null) {
        referenceBeanName = propertyValue.toString();
    }
    Map<String, Object> attributes = new HashMap<>();
    attributes.put(ReferenceAttributes.ID, referenceBeanName);

    // 将hsf属性转换为dubbo属性
    resolveDubboAttributes(propertyAttributes, attributes);

    // Register the reference bean definition to the beanFactory
    RootBeanDefinition beanDefinition = new RootBeanDefinition();
    beanDefinition.setBeanClassName(ReferenceBean.class.getName());
    beanDefinition.getPropertyValues().add(ReferenceAttributes.ID, referenceBeanName);

    // set attribute instead of property values
    beanDefinition.setAttribute(Constants.REFERENCE_PROPS, attributes);
    String interfaceName = attributes.get("interface").toString();
    Class interfaceClass = Class.forName(interfaceName);
    beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_CLASS, interfaceClass);
    beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_NAME, interfaceName);

    // create decorated definition for reference bean, Avoid being instantiated when getting the beanType of ReferenceBean
    // see org.springframework.beans.factory.support.AbstractBeanFactory#getTypeForFactoryBean()
    GenericBeanDefinition targetDefinition = new GenericBeanDefinition();
    targetDefinition.setBeanClass(interfaceClass);
    beanDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, referenceBeanName + "_decorated"));

    // signal object type since Spring 5.2
    beanDefinition.setAttribute(Constants.OBJECT_TYPE_ATTRIBUTE, interfaceClass);
    referenceBeanManager.registerReferenceKeyAndBeanName(referenceKey, referenceBeanName);
    logger.info("Register dubbo reference bean: " + referenceBeanName + " = " + referenceKey);
    return beanDefinition;
}

4. 展示


最终效果


结语

这次改动不仅仅只是适配SpringBoot框架,SpringMvc其实也是适配的,上述UserDemoService服务是通过注解注入的服务,而RoleDemoService服务是由xml配置提供的服务。

最终源码有时间会公开提供下,或者私信也可以。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。