前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >load-time-weaver & spring-configured

load-time-weaver & spring-configured

作者头像
MickyInvQ
发布2021-10-19 14:40:30
6440
发布2021-10-19 14:40:30
举报
文章被收录于专栏:InvQ的专栏InvQ的专栏

文章目录

  • load-time-weaver & spring-configured
    • javaagent
    • 解析
      • LoadTimeWeaver
      • LoadTimeWeaverBeanDefinitionParser
      • aspectj-weaving
      • 是否开启
      • AspectJWeavingEnabler
      • SpringConfiguredBeanDefinitionParser
        • 切点(pointcut)
          • inConfigurableBean
          • beanConstruction
          • preConstructionCondition
          • leastSpecificSuperTypeConstruction
          • preConstructionConfiguration
          • postConstructionCondition
        • advise
          • 前置
    • 运行
      • LoadTimeWeaverAware
      • BeanClassLoaderAware
        • 入口
        • setBeanClassLoader
          • 服务器agent
          • Spring agent
          • 反射
          • 总结
      • ClassFileTransformer
      • Aj
        • 缓存
        • WeavingAdaptor初始化
          • aop.xml
          • 解析
          • 注册
        • 总结

load-time-weaver & spring-configured

这两个配置是紧密相关的,所以在一起说了。

load-time-weaver用以开启aspectj类加载期织入,实际上是利用jdk1.6提供的instrument API实现的,原理就是jvm会在类加载之前将class暴露给我们制定的类,允许我们在此时对类进行修改。aspectj便利用此机会根据我们的配置生成对应的满足需求的子类。

可以参考:

Spring之LoadTimeWeaver——一个需求引发的思考

Spring LoadTimeWeaver 的那些事儿

javaagent

要想使用此功能需要配置jvm参数javaagent指定代理类的jar包,示例:

-javaagent:D:\Software\maven-repos\org\springframework\spring-agent\2.5.6.SEC03\spring-agent-2.5.6.SEC03.jar

此jar包的META-INF/MANIFEST.MF文件需要配置如下一行:

Premain-Class: org.springframework.instrument.InstrumentationSavingAge nt

Spring的这个jar包只有这一个类,premain方法便是jvm调用的入口,方法参数是固定的。源码:

代码语言:javascript
复制
public class InstrumentationSavingAgent {

    private static volatile Instrumentation instrumentation;

    public static void premain(String agentArgs, Instrumentation inst) {
        instrumentation = inst;
    }

    public static Instrumentation getInstrumentation() {
        return instrumentation;
    }
}	

所以,Spring在这里把Instrumentation给暴露了出来,供其它的类使用。

解析

解析的实现类是LoadTimeWeaverBeanDefinitionParser,其继承体系和property-override的PropertyOverrideBeanDefinitionParser类似。

LoadTimeWeaver

此接口用于向ClassLoader添加ClassFileTransformer对象,其继承体系:

在这里插入图片描述
在这里插入图片描述

LoadTimeWeaverBeanDefinitionParser的父类初始化了一个DefaultContextLoadTimeWeaver类型的BeanDefinition放入容器,类型的决定位于LoadTimeWeaverBeanDefinitionParser.getBeanClassName:

代码语言:javascript
复制
@Override
protected String getBeanClassName(Element element) {
    // 如果配置了weaver-class属性,那么使用其值
    if (element.hasAttribute(WEAVER_CLASS_ATTRIBUTE)) {
        return element.getAttribute(WEAVER_CLASS_ATTRIBUTE);
    }
    // org.springframework.context.weaving.DefaultContextLoadTimeWeaver
    return DEFAULT_LOAD_TIME_WEAVER_CLASS_NAME;
}

那么这个BeanDefinition的id/name又是什么呢?

LoadTimeWeaverBeanDefinitionParser.resolveId:

代码语言:javascript
复制
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext 	parserContext) {
    // loadTimeWeaver
    return ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME;
}

DefaultContextLoadTimeWeaver其实是个包装类,包装了真正的LoadTimeWeaver,使用这层包装的目的就是可以根据外部环境(服务器代理或是Spring自己的代理)确定内部LoadTimeWeaver的实现,具体参见后面运行-BeanClassLoaderAware-setBeanClassLoadery一节。

LoadTimeWeaverBeanDefinitionParser

LoadTimeWeaverBeanDefinitionParser.doParse:

代码语言:javascript
复制
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
    builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    if (isAspectJWeavingEnabled(element.getAttribute(ASPECTJ_WEAVING_ATTRIBUTE), parserContext)) {
        if (!parserContext.getRegistry().containsBeanDefinition(ASPECTJ_WEAVING_ENABLER_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(ASPECTJ_WEAVING_ENABLER_CLASS_NAME);
            parserContext.registerBeanComponent(
                    new BeanComponentDefinition(def, ASPECTJ_WEAVING_ENABLER_BEAN_NAME));
        }
        if (isBeanConfigurerAspectEnabled(parserContext.getReaderContext().getBeanClassLoader())) {
            new SpringConfiguredBeanDefinitionParser().parse(element, parserContext);
        }
    }
}

aspectj-weaving

这里便是加载其织入的开关,共有三个选项: on, off, autodect。前两个自不必说,autodect表示自动去检测/META-INF下是否存在aop.xml,如果有,那么开启。

此功能依赖于spring-aspectj包,此jar包下有aop.xml,所以autodect也是开启的。

是否开启

isAspectJWeavingEnabled方法用于判断是否启用:

代码语言:javascript
复制
protected boolean isAspectJWeavingEnabled(String value, ParserContext parserContext) {
    if ("on".equals(value)) {
        return true;
    } else if ("off".equals(value)) {
        return false;
    } else {
        // 寻找aop.xml
        ClassLoader cl = parserContext.getReaderContext().getResourceLoader().getClassLoader();
        return (cl.getResource(AspectJWeavingEnabler.ASPECTJ_AOP_XML_RESOURCE) != null);
    }
}

AspectJWeavingEnabler

从源码中可以看出,Spring向容器放了一个这东西,名字叫org.springframework.context.config.internalAspectJWeavingEnabler。这东西用来向LoadTimeWeaver设置aspectj的ClassPreProcessorAgentAdapter对象。其类图:

在这里插入图片描述
在这里插入图片描述

SpringConfiguredBeanDefinitionParser

如果isBeanConfigurerAspectEnabled方法返回true,那么将会生成一个此对象并调用其parse方法,查看ContextNamespaceHandler的init方法源码可以发现,spring-configured对应的解析器其实就是它:

代码语言:javascript
复制
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());

其parse方法:

代码语言:javascript
复制
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
    // org.springframework.context.config.internalBeanConfigurerAspect
    if (!parserContext.getRegistry().containsBeanDefinition(BEAN_CONFIGURER_ASPECT_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition();
         // org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect
        def.setBeanClassName(BEAN_CONFIGURER_ASPECT_CLASS_NAME);
        def.setFactoryMethodName("aspectOf");
        def.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        def.setSource(parserContext.extractSource(element));
        parserContext.registerBeanComponent(new BeanComponentDefinition
            (def, BEAN_CONFIGURER_ASPECT_BEAN_NAME));
    }
    return null;
}

很明显,把org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect添加到容器里了,这其实是一个切面,其类图:

在这里插入图片描述
在这里插入图片描述

AnnotationBeanConfigurerAspect及其父类其实是由aspectj源文件(.aj)编译而来,所以在spring-aspectj的源码包中看到的是.aj文件而不是.java。

下面就去aj文件中看看到底定义了哪些pointcut以及advise。

语法可以参考:

Spring 之AOP AspectJ切入点详解

切点(pointcut)
inConfigurableBean

在AnnotationBeanConfigurerAspect中定义,源码:

代码语言:javascript
复制
public pointcut inConfigurableBean() : @this(Configurable);

@this没找到相关说明,结合@以及this的语义,猜测是匹配带有@Configurable注解(以及作为元注解)的类

beanConstruction

源码:

代码语言:javascript
复制
public pointcut beanConstruction(Object bean) :
            initialization(ConfigurableObject+.new(..)) && this(bean);

initialization表示匹配构造器的调用,ConfigurableObject+表示ConfigurableObject及其子类,这就说明可以用实现ConfigurableObject接口的方式代替@Configurable注解。this(bean)表示this必须满足this instanceof bean,也就是说被代理的对象必须是bean的子类。

preConstructionCondition
代码语言:javascript
复制
private pointcut preConstructionCondition() :
            leastSpecificSuperTypeConstruction() && preConstructionConfiguration();

由两个pointcut与运算而来。

leastSpecificSuperTypeConstruction
代码语言:javascript
复制
public pointcut leastSpecificSuperTypeConstruction() : initialization(ConfigurableObject.new(..));
preConstructionConfiguration
代码语言:javascript
复制
public pointcut preConstructionConfiguration() : preConstructionConfigurationSupport(*);
private pointcut preConstructionConfigurationSupport(Configurable c) : @this(c) && if (c.preConstruction());

preConstruction表示@Configurable注解的preConstruction属性,此属性表示是否注入操作可以发生在构造之前,默认false。

postConstructionCondition
代码语言:javascript
复制
private pointcut postConstructionCondition() :
            mostSpecificSubTypeConstruction() && !preConstructionConfiguration();

mostSpecificSubTypeConstruction:

代码语言:javascript
复制
public pointcut mostSpecificSubTypeConstruction() :
            if (thisJoinPoint.getSignature().getDeclaringType() == thisJoinPoint.getThis().getClass());

advise可以声明JoinPoint类型的方法参数,thisJoinpoint指的就是这个。此pointcut的目的是匹配接口/抽象类的最具体的实现。

advise
前置
代码语言:javascript
复制
before(Object bean) :
    beanConstruction(bean) && preConstructionCondition() && inConfigurableBean()  {
    configureBean(bean);
}

运行

AspectJWeavingEnabler.postProcessBeanFactory:

代码语言:javascript
复制
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    enableAspectJWeaving(this.loadTimeWeaver, this.beanClassLoader);
}

enableAspectJWeaving:

代码语言:javascript
复制
public static void enableAspectJWeaving(LoadTimeWeaver weaverToUse, ClassLoader beanClassLoader) {
    // 不为空
    if (weaverToUse == null) {
        if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {
            weaverToUse = new InstrumentationLoadTimeWeaver(beanClassLoader);
        }
        else {
            throw new IllegalStateException("No LoadTimeWeaver available");
        }
    }
    weaverToUse.addTransformer(
            new AspectJClassBypassingClassFileTransformer(new ClassPreProcessorAgentAdapter()));
}

LoadTimeWeaverAware

AspectJWeavingEnabler实现了LoadTimeWeaverAware接口,那么何时由谁进行注入的呢?

当Context初始化时,AbstractApplicationContext.prepareBeanFactory部分源码:

代码语言:javascript
复制
// loadTimeWeaver
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
    beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
    // Set a temporary ClassLoader for type matching.
    beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}

很明显,关键在于LoadTimeWeaverAwareProcessor,类图:

在这里插入图片描述
在这里插入图片描述

postProcessBeforeInitialization方法:

代码语言:javascript
复制
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
    if (bean instanceof LoadTimeWeaverAware) {
        LoadTimeWeaver ltw = this.loadTimeWeaver;
        if (ltw == null) {
            Assert.state(this.beanFactory != null,
                "BeanFactory required if no LoadTimeWeaver explicitly specified");
             // 去容器找 
            ltw = this.beanFactory.getBean(
                ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME, LoadTimeWeaver.class);
        }
        ((LoadTimeWeaverAware) bean).setLoadTimeWeaver(ltw);
    }
    return bean;
}

可以看出,如果本地的loadTimeWeaver为空,那么会去容器找,调用了getBean方法,也就是说DefaultContextLoadTimeWeaver就是在这里初始化的。

BeanFactoryPostProcessor也是一个bean,所以它的初始化也会BeanPostProcessor的处理。不过注意一点:

BeanPostProcessor的注册是在BeanFactoryPostProcessor的调用之后进行的:

AbstractApplicationContext.refresh:

代码语言:javascript
复制
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);

那么BeanFactoryPostProcessor初始化的时候执行处理的BeanPostProcessor是哪里来的?

AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization源码:

代码语言:javascript
复制
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) {
    Object result = existingBean;
    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
        result = beanProcessor.postProcessBeforeInitialization(result, beanName);
        if (result == null) {
            return result;
        }
    }
    return result;
}

getBeanPostProcessors:

代码语言:javascript
复制
public List<BeanPostProcessor> getBeanPostProcessors() {
    return this.beanPostProcessors;
}

可以看出,并没有查找容器的过程,所以此处并不会导致BeanPostProcessor的初始化。问题的关键就在于LoadTimeWeaverAwareProcessor的添加方式:

代码语言:javascript
复制
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));

直接将实例添加到BeanFactory中,所以可以得出结论:

我们自定义的BeanPostProcessor不会对BeanFactoryPostProcessor的初始化造成影响,除非使用调用BeanFactory.addBeanPostProcessor的方式进行添加

BeanClassLoaderAware

入口

DefaultContextLoadTimeWeaver同样实现了此接口,那么哪里调用的呢?

AbstractAutowireCapableBeanFactory.initializeBean调用了invokeAwareMethods方法,源码:

代码语言:javascript
复制
private void invokeAwareMethods(final String beanName, final Object bean) {
    if (bean instanceof Aware) {
        if (bean instanceof BeanNameAware) {
            ((BeanNameAware) bean).setBeanName(beanName);
        }
        if (bean instanceof BeanClassLoaderAware) {
            ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
        }
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
        }
    }
}
setBeanClassLoader

这个方法很关键,对instrument的获取就是在这里。源码:

代码语言:javascript
复制
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
    LoadTimeWeaver serverSpecificLoadTimeWeaver = createServerSpecificLoadTimeWeaver(classLoader);
    if (serverSpecificLoadTimeWeaver != null) {
        this.loadTimeWeaver = serverSpecificLoadTimeWeaver;
    } else if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {
        this.loadTimeWeaver = new InstrumentationLoadTimeWeaver(classLoader);
    } else {
        this.loadTimeWeaver = new ReflectiveLoadTimeWeaver(classLoader);
    }
}

很明显分为三部分。

服务器agent

Spring首先会去检测是否存在服务器的agent代理。按照Spring doc里说的,支持下列服务器:

Oracle WebLogic 10,GlassFish 3, Tomcat 6, 7 and 8, JBoss AS 5, 6 and 7, IBM WebSphere 7 and 8.

createServerSpecificLoadTimeWeaver源码:

代码语言:javascript
复制
protected LoadTimeWeaver createServerSpecificLoadTimeWeaver(ClassLoader classLoader) {
    String name = classLoader.getClass().getName();
    if (name.startsWith("weblogic")) {
        return new WebLogicLoadTimeWeaver(classLoader);
    } else if (name.startsWith("org.glassfish")) {
        return new GlassFishLoadTimeWeaver(classLoader);
    } else if (name.startsWith("org.apache.catalina")) {
        return new TomcatLoadTimeWeaver(classLoader);
    } else if (name.startsWith("org.jboss")) {
        return new JBossLoadTimeWeaver(classLoader);
    } else if (name.startsWith("com.ibm")) {
        return new WebSphereLoadTimeWeaver(classLoader);
    }
    return null;
}

可以看出,对于服务器的判断是通过检测当前的类加载器来实现的,因为这些服务器都使用了自己的类加载器实现

这也从侧面说明,如果当前处于以上服务器所在的web应用环境,不需要spring-agent.jar便可以实现LTW(载入期织入)。

Spring agent

这个也是测试时使用的。InstrumentationLoadTimeWeaver.isInstrumentationAvailable:

代码语言:javascript
复制
public static boolean isInstrumentationAvailable() {
    return (getInstrumentation() != null);
}

private static Instrumentation getInstrumentation() {
    if (AGENT_CLASS_PRESENT) {
        return InstrumentationAccessor.getInstrumentation();
    } else {
        return null;
    }
}

AGENT_CLASS_PRESENT是一个布尔变量,就是判断org.springframework.instrument.InstrumentationSavingAgent是否存在,这个便是spring-agent.jar中唯一的类。

InstrumentationAccessor是InstrumentationLoadTimeWeaver的内部类:

代码语言:javascript
复制
private static class InstrumentationAccessor {
    public static Instrumentation getInstrumentation() {
        return InstrumentationSavingAgent.getInstrumentation();
    }
}

这里便是获取spring-agent.jar暴露的Instrumentation对象的地方了。

反射

在这种情况中,Spring寄托于当前的ClassLoader实现了LoadTimeWeaver的功能,也就是必须有addTransformer方法,如果有,Spring便会把LoadTimeWeaver的职责委托给ClassLoader,如果没有只能抛异常了(抱歉,我们没法支持LTW…),检测的源码位于ReflectiveLoadTimeWeaver的构造器:

代码语言:javascript
复制
public ReflectiveLoadTimeWeaver() {
    this(ClassUtils.getDefaultClassLoader());
}

public ReflectiveLoadTimeWeaver(ClassLoader classLoader) {
    Assert.notNull(classLoader, "ClassLoader must not be null");
    this.classLoader = classLoader;
    this.addTransformerMethod = ClassUtils.getMethodIfAvailable(
            this.classLoader.getClass(), ADD_TRANSFORMER_METHOD_NAME, ClassFileTransformer.class);
    if (this.addTransformerMethod == null) {
        throw new IllegalStateException();
    }
}
总结

其实可以不用Spring,只使用aspectj自己便可以实现LTW,只需要把代理jar包设为aspect-weaver.jar,并自己编写aop.xml文件以及相应的aspect类即可。可以参考官方文档:

Chapter 5. Load-Time Weaving

ClassFileTransformer

从enableAspectJWeaving方法的源码可以看出,实际上就是向DefaultContextLoadTimeWeaver添加了一个AspectJClassBypassingClassFileTransformer对象。根据java instrument API的定义,每当一个Class被加载的时候都会去调用挂在Instrumentation上的ClassFileTransformer的transform方法。所以LTW的核心便在这里了。

AspectJClassBypassingClassFileTransformer.transform:

代码语言:javascript
复制
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
        ProtectionDomain protectionDomain, byte[] classfileBuffer) {
    // aspectj自身的类无需检测(织入),直接跳过
    if (className.startsWith("org.aspectj") || className.startsWith("org/aspectj")) {
        return classfileBuffer;
    }
    return this.delegate.transform(loader, className, classBeingRedefined, 
        protectionDomain, classfileBuffer);
}

delegate是一个org.aspectj.weaver.loadtime.ClassPreProcessorAgentAdapter对象。这是一个适配器模式,其类图:

在这里插入图片描述
在这里插入图片描述

根据Aspectj的doc,ClassPreProcessor用于将Aspectj 5对于jdk5依赖代码抽取出来以便可以支持jdk1.3/1.4.

Aj

Aj的preProcess方法很长,其实只干了两件事,都是围绕着WeavingAdaptor进行的。对类的处理也转交给WeavingAdaptor的weaveClass方法。

缓存

Aj使用了WeavingAdaptor缓存机制,确保一个ClassLoader只有一个WeavingAdaptor对象,因为其初始化的成本很高,缓存利用一个key为AdaptorKey(包装了ClassLoader), value为WeavingAdaptor的HashMap来实现。

WeavingAdaptor初始化

初始化就是ClassLoaderWeavingAdaptor.initialize方法,初始化分部分来进行说明。Aspectj部分不再详细展开,只对关键的部分进行说明。

aop.xml
解析

aop.xml的解析便是在这里进行。解析的过程无非是xml的解析,下面是其结果如何存储的:

以org.aspectj.weaver.loadtime.definition.Definition为载体,我们以spring-aspects.jar下的aop.xml为例:

代码语言:javascript
复制
<aspectj>
    <aspects>
        <aspect name="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect"/>
        <aspect name="org.springframework.scheduling.aspectj.AnnotationAsyncExecutionAspect"/>
        <aspect name="org.springframework.transaction.aspectj.AnnotationTransactionAspect"/>
        <aspect name="org.springframework.transaction.aspectj.JtaAnnotationTransactionAspect"/>
        <aspect name="org.springframework.cache.aspectj.AnnotationCacheAspect"/>
        <aspect name="org.springframework.cache.aspectj.JCacheCacheAspect"/>
    </aspects>
</aspectj>

那么解析后的结果:

在这里插入图片描述
在这里插入图片描述
注册

入口方法在ClassLoaderWeavingAdaptor.registerDefinitions:

代码语言:javascript
复制
private boolean registerDefinitions(final BcelWeaver weaver, final ClassLoader loader, List<Definition> definitions) {
    //对应<weaver options="-verbose">
    registerOptions(weaver, loader, definitions);
    //对应<exclude>标签
    registerAspectExclude(weaver, loader, definitions);
    //对应<include>标签
    registerAspectInclude(weaver, loader, definitions);
    // <aspect>
    success = registerAspects(weaver, loader, definitions);
    registerIncludeExclude(weaver, loader, definitions);
    //对应<dump>标签
    registerDump(weaver, loader, definitions);
    //忽略返回
}
总结

Spring将切面以编译过的Aspectj语言形式定义,不过也可以用原生java类。spring-aspectj包定义的是供各个模块进行LTW的切面。Aspectj部分不再继续向下深入探究。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-10-16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • load-time-weaver & spring-configured
    • javaagent
      • 解析
        • LoadTimeWeaver
        • LoadTimeWeaverBeanDefinitionParser
        • aspectj-weaving
        • 是否开启
        • AspectJWeavingEnabler
        • SpringConfiguredBeanDefinitionParser
      • 运行
        • LoadTimeWeaverAware
        • BeanClassLoaderAware
        • ClassFileTransformer
        • Aj
    相关产品与服务
    容器服务
    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档