之前一直很好奇,java是如何根据容器的外部设置的cpu限制来做自适应的。下面就跟随openjdk,看看系统是如何应对cgroup cpu设置的。
代码位置在jdk/src/hotspot/os/linux/cgroupSubsystem_linux.cpp的active_processor_count的方法。
目前cpu的设置,主要是3种使用方式,第一种是设置quota,第二种是设置share比率,第三种是设置cpu具体的核。以下就围绕着三种情况进行展开。
以下原来来自openjdk18
quota模式
if (quota > -1 && period > 0) {
quota_count = ceilf((float)quota / (float)period);
log_trace(os, container)("CPU Quota count based on quota/period: %d", quota_count);
}
在设置了quota和period。jdk就用quota/period作为cpu的核数。这里的处理是虚拟的核数。因为period的周期是可以调整的,是否真的用到了对应倍数的物理机核,这里其实是不确定的。
share模型
在share模式下,只是设置了竞争cpu的比率。竞争强跑满了,share才有限制,如果都跑不满,限制是不存在的。在这种情况下,就需要用户做一些自己系统的预判。第一个设置是UseContainerCpuShares,开启这个参数后,就会读取cpu.shares的配置值,然后和1024(默认值做倍数关系)
if (share > -1) {
share_count = ceilf((float)share / (float)PER_CPU_SHARES);
log_trace(os, container)("CPU Share count based on shares: %d", share_count);
}
这种就是模拟大家都跑满的场景。并且希望设置核数比率的时候,是按照默认值的倍数来进行设置。否者jdk拿到的核数是不对的。这里只是jdk自己的限制,cgroup并没有这么要求。
如果不开启UseContainerCpuShares,share就会用系统的核数。这里模拟的就是程序竞争不激烈的情况。
cpu_count = limit_count = os::Linux::active_processor_count();
直接限制物理核
通过cgroup设置cpuset.cpus,可以固定使用具体某几个核。这种设置在系统函数中就会返回被限制后的。
cpu_count = limit_count = os::Linux::active_processor_count();
混合模式
以上三种模式,是可以混合在一起设置的。我们来继续看看,当出现混合设置的时候,如何去做cpu个数的计算。
先处理share和quota
这里的share是开启了UseContainerCpuShares的情况,没开启的时候拿到的核数和设置了cpuset.cpus是同一个逻辑。同时引入了一个新的参数PreferContainerQuotaForCPUCount,这个参数开启了就会直接使用quota值。否则会根据share和quota谁的比较小来做判断。
if (PreferContainerQuotaForCPUCount) {
limit_count = quota_count;
} else {
limit_count = MIN2(quota_count, share_count);
}
最后需要和cpuset.cpus的结果在做对比。这里注意的一点是,share不开启UseContainerCpuShares之后获取物理核的方法和cpuset.cpus是同一个,所以接下来的对比是包含了2中情况的,但是代码是同一处。
第一种情况是没有cpuset.cpus,那么对比的就是和物理机的核数。
第二种情况是有cpuset.cpus,对比是就是cpuset.cpus的值,并且cpuset.cpus的值一定比物理机核数小,同时也就保证了怎么都不会超过物理真实核数。
result = MIN2(cpu_count, limit_count);
依旧最终选择最小。这里也就发现了一个点,最大值不能超过物理机核数,现在看第一个quota模式,如果period只有正常cpu的一半,然后想用光所有cpu,就必须把quota扩大一倍。其实这样的比率设置和跑满没有区别。这里建议peroid的设置还是和cpu的时钟保值一致最好。这样的比率就接近于物理机的真是核数了。
总结
- share和quota是设置参数相对比较多,需要根据自己的情况来开启设置UseContainerCpuShares,PreferContainerQuotaForCPUCount。直接限制cpuset.cpus是比较省心的,系统函数返回的就是已经被限制过的。
- jdk只是描述了虚拟核数,但是永远不会超过当前物理机的核数。
作者:xpbob