-
Notifications
You must be signed in to change notification settings - Fork 498
使用Alibaba Draognwell多租户特性管控运行时资源
本文将介绍如何修改应用、中间件等Java代码和配置来使用Alibaba Dragonwell多租户特性。 针对版本: Alibaba Dragonwell 8.3.3 及更高
在有些场景中,Java应用并非作为单一业务用途的程序存在的,而是作为一个平台存在,在他上面运行着一些更加轻量的应用或函数。 在这类Java应用上面,一般通过动态类加载机制把和应用无关、但遵循一定编程接口规范、和Java字节码技术兼容的软件模块(可以是从Java、Scala、Kotlin等语言编译而来)加载到应用进程,并运行其中的业务逻辑,一个典型的例子是Tomcat的动态加载机制。
这类架构有一个缺点:就是平台型Java应用缺乏对其上轻量应用所使用资源的管控能力。容易出现某个应用占用过多资源——比如CPU时间——而导致其他同进程应用无法响应。 JDK多租户技术的目的就是为平台型Java应用提供一个细粒度资源管控的能力。
多租户技术的目的是为基于JVM技术栈的PaaS、SaaS、FaaS应用平台提供的底层资源管控能力; 并不是为了普通单目的业务应用设计的,也不会让此类Java应用受益。
Alibaba Dragonwell多租户技术通过在JDK中创建虚拟的进程内容器“租户”,来让JVM可以识别出运行时代码所持有的资源组。
- 初始化多租户能力 多租户CPU管控的能力在Linux上面是依赖于cgroup(Linux control groups)这个特性来实现的,所以需要root权限做一些初始化工作。
主要目的是为了特定用户创建一个顶级的有读写权限的cgroup目录,这样后续启动租户就不再需要root权限。
如果不使用基于线程(未来会开源基于协程的管控能力)的CPU管控能力,可以忽略这一步。
这个目录层级是面向单个用户的运行时环境,目前不支持为同一个容器中多个用户同时初始化。
- 修改JVM参数添加多租户相关选项; Alibaba Dragonwell JDK的多租户特性以及每个子特性,都可以通过JVM选项的方式打开或关闭,这样应用可以方便的根据实际需求要求JVM只管控部分资源或提供部分隔离支持。
- 修改使用应用、中间件等组件的Java代码,把相关代码逻辑改到租户中运行。 Alibaba Dragonwell多租户特性面向上面第一部分介绍的使用场景,所以依靠平台型应用通过调用多租户的Java API来实现把资源管控能力嵌入到平台型应用之中。
这一步需要依赖于libcgroup-tools这个包,请在您所使用的发行版上安装对应软件包。 这一步可以写到Dockerfile中用来简化流程,但是需要在执行的时候指定
docker run --privileged
参数。 如果不需要CPU资源支持(-XX:+TenantCpuThrottling)可以跳过这一步 本步骤需要安装libcgroup-tools这个rpm包 一般情况下使用下面命令可以初始化cgroupsudo <jdk_home>/bin/jgroup -g <group> -u <user>
-
<user>,<group>
是执行多租户Java进程的Linux用户对应的Linux用户名和组。 - 一般需要sudo权限了来操作cgroup
如果下面命令能正常执行就证明初始化成功了
<jdk_home>/bin/java -XX:+MultiTenant -XX:+TenantCpuThrottling -version
如果无法执行成功,可以用下面命令调试下看看哪一步出错
export JGROUP_DEBUG=true
<jdk_home>/bin/java -XX:+MultiTenant -XX:+TenantCpuThrottling -version
不同集群cgroup配置五花八门,配置错误的也不在少数,请先参考文档保证cgroup配置正确 https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/resource_management_guide/ch01
Dragonwell 8.3.3中添加了一些新的JVM参数用来精细的控制多租户特性的行为,应用可以根据需要打开、关闭部分的资源控制特性。
JVM选项 | 说明 | 必填? |
---|---|---|
-XX:+MultiTenant | 打开多租户功能,必须添加此参数才能使用任何多租户功能 | 是 |
-XX:+TenantHeapThrottling | 启用租户内存资源隔离,必须和-XX:+UseG1GC配合使用,不能和CMS、PS等GC策略混合使用 | 否 |
-XX:+TenantCpuThrottling | 启用租户CPU资源控制,基于Linux cgroup实现,RHEL兼容的发行版上需要安装libcgroup、libcgroup-tools RPM包才可以工作,其他发行版请安装对应的软件包。 | 否 |
-XX:+TenantDataIsolation | 启动系统静态变量隔离功能,JDK里面效果上静态的变量(static变量,threadlocal变量)将会被按照租户隔离,不同租户看到的是不同静态变量的副本,应用代码的静态变量不受影响; JDK的properties也会被按租户隔离,租户里调用System.setProperty不会影响其他租户 | 否 |
平台型Java应用需要使用多租户API,通过编程的方式配置受控代码可以使用的资源上限,并把受控代码包装起来执行,这样受控代码才消耗的资源才可以被管控起来。
Dragonwell 8.3.3提供了一套新的API位于com.alibaba.tenant
和com.alibaba.rcm
包内,用来把代码放到租户空间执行,后面会详细介绍。
下面是一个简单的HelloWorld例子
Iterable<Constraint> constraints = Stream.of(ResourceType.CPU_PERCENT.newConstraint(40),
ResourceType.HEAP_RETRAINED.newConstraint(64 * 1024 * 1024))
.collect(Collectors.toList());
ResourceContainer tenant = TenantContainerFactory.instance().createContainer(constraints);
Runnable task = ()->{
System.out.println("Hello from tenant container!");
}
tenant.run(task);
上面的task是一段Java逻辑,可以看作受控的轻量级应用代码,tenant.run(task)
调用将会在当前线程执行这段代码,并且控制它消耗的CPU资源不超过单核心的40%,最大内存资源不超过64MB。
上面三步展示了让平台型应用使用多租户能力的基本步骤,下面部分将详细介绍多租户资源管控框架。
Alibaba Dragonwell提供了一套抽象的管控受控代码执行的框架,我们称之为RCM框架(Resource Control Management),他为平台型应用提供了统一的抽象接口,多租户功能(MultiTenant)是针对RCM框架的一个具体实现。
-
TenantContainer
即多租户特性提供的“租户”概念,用于将“一段执行的代码”所消耗的资源进行控制,这个控制不光包括了CPU、内存等资源,也包括了静态变量隔离、线程管控等更高级的特性。
未来会根据反馈开源更多经过验证的高级特性
-
ResourceContainer
ResourceContainer
是TenantContainer
的子集,用于提供抽象的接口来 只 做好“资源管理”的工作,用于管理一段执行的代码所消耗的CPU、内存、IO等资源。ResourceContainer
在多租户场景下,和TenantContainer
是1:1
的对应关系。
由于有
ResourceContainer
这层抽象的存在,使得其他非多租户场景的资源管理也成为可能,比如协程场景。
-
Thread
和TenantContainer
TenantContainer
是一个虚拟的“容器”的概念,并不会在内部隐式创建线程池,所有被管控的代码仍旧在自己本来的线程上执行,TenantContainer.run()
方法只相当于在受控代码开始和结束的时候给所在线程打上/取消了一个特殊的标记,JVM的OS可以利用这个标记进行资源计费、管控工作。
租户场景下只支持线程级别的管控。
代码可以能通过静态方法TenantContainer.create()
来创建租户对象,示例代码如下
TenantContainer tenant = TenantContainer.create(new TenantConfiguration()
.limitMemory(64 * 1024 * 1024)
.limitCpuShares(1024));
TenantConfiguration
是用来记录租户所适用资源限制的配置对象。
由于TenantContainer
和ResourceContainer
是1:1
的关系,可以从一个已有的ResourceContainer
对象获取租户对象
ResourceContainer rc = TenantContainerFactory.createContainer(constraints);
TenantContainer tc = TenantContainerFactory.tenantContainerOf(rc);
注意:每次新建一个ResourceContainer
对象,会有一个TenantContainer
对象被隐式的创建出来。
List<Constraint> cs = new ArrayList<>();
cs.add(ResourceType.CPU_PERCENT.newConstraint(50)); // 50% single-core CPU resource
cs.add(ResourceType.HEAP_RETAINED.newConstraint(32 * 1024 * 1024)); // 32MB retained heap
ResourceContainer rc = TenantContainerFactory.createContainer(cs);
代码如下
TenantContainer tc = TenantContainer.create(new TenantConfiguration().limitMemory(64 * 1024 * 1024));
ResourceContainer rc = tc.getResourceContainer();
多租户JVM启动之后并不会自动创建任何租户,如果没有显示的调用TenantContainer.run,所有的Java代码运行于默认的非租户空间,我们称之为“ROOT租户”,这些代码是不受多租户资源管控的。
ROOT租户和一般租户的分界线就在TenantContainer.run(Runnable task)
中task参数的run方法,如下面代码所示:
/*租户外*/
tenant.run (
()-> {
/*租户内*/
}
}
/*租户外*/
已经运行起来的线程,只能通过TenantContainer.run()
把应用的代码逻辑放到租户空间执行,这个方法实现上是修改了当前线程对象的一些状态(在JVM中,java层不可见),使得在TenantContainer.run()
这个方法执行期间所有资源消耗挂靠在该租户上。
Thread t = new Thread(
()->{
tenant.run(
()-> {
/*这个作用域的代码将被Root租户创建的线程t执行,并且运行在tenant租户里,受tenant租户的资源管控*/
});
});
t.start();
目前禁止嵌套执行TenantContainer.run()方法,不能从租户A的run方法里面调用租户B的run方法。
如下面代码所示的线程创建出来后就会一直租户空间执行,一般Java线程的入口Thread.run()会被多租户功能修改掉。
tenant.run(
()-> {
Thread threadInTenant = new Thread(task);
threadInTenant.start(); /* threadInTenant这个线程会一启动就在租户tenant里运行 */
});
从租户内临时切换到ROOT租户执行一段代码
tenant.run(()-> {
/*租户内*/
TenantContainer.primitiveRunInRoot(()->{
/*这里面的代码会临时切换到ROOT租户执行,所消耗资源不会算在当前租户*/
});
/*租户内*/
long data = TenantContainer.primitiveRunInRoot(()->{
/*这里面的代码会临时切换到ROOT租户执行,所消耗资源不会算在当前租户*/
return 0;
});
/*租户内*/
});
多租户特性为平台型应用提供了资源管控的能力,但并非没有代价。
- 由于资源管控会指定资源上限,如果被管控模块使用的资源超过了给定上限,将会通过限制手段控制其可用资源,会导致诸如分配到的CPU时间减少、发生GC等现象,应用层可能也会表现出无法响应等现象。
这些负面现象是受控应用应有的惩罚,被认为是正常现象,如果您的应用上面嵌入的目标应用不能接受这些控制,请三思。
- JVM执行资源管控也是需要占用较少资源
内存管理部分是通过修改GC实现的,基本可以认为不占用运行时资源; CPU部分是通过cgroup实现的,单线程单次切换租户会有十几微秒的开销,多线程竞争切换会更大些。
经过实践检验,一般推荐如下架构会更好的利用这个特性
线程池的线程一创建就在租户中运行,通过提交任务把相关业务的任务使用的资源进行管控。
线程处理单次请求时间较长,十秒以上,动态绑定到租户上可以方便管控资源消耗。
当然,如果您有更多奇思妙想,也可以到Alibaba Dragonwell钉钉群和我们一起探讨,共同探索更多玩法
如果您在使用过程中有发现Alibaba Dragonwell的问题,欢迎给我们提Issue,也欢迎使用钉钉群一起更轻松的探讨相关技术问题。
Alibaba Dragonwell