Android GPU纹理数据拷贝
在 Android 开发中读取纹理数据有以下几种方法:
- glReadPixels
- ImageReader
- PBO(Pixel BufferObject)
HardwareBuffer
1. glReadPixels
glReadPixels 是 OpenGL ES 的 API,通常用于从帧缓冲区中读取像素数据,OpenGL ES 2.0 和 3.0 均支持。使用非常方便,但是效率也是最低的。
- 当调用 glReadPixels 时,首先会影响 CPU 时钟周期,同时 GPU会等待当前帧绘制完成,读取像素完成之后,才开始下一帧的计算,造成渲染管线停滞。
- glReadPixels 读取的是当前绑定 FBO 的颜色缓冲区图像,所以当使用多个 FBO(帧缓冲区对象)时,需要确定好我们要读那个FBO 的颜色缓冲区。
- glReadPixels 性能瓶颈一般出现在大分辨率图像的读取,所以目前通用的优化方法是在 shader 中将处理完成的 RGBA 转成YUV(一般是 YUYV 格式),然后基于 RGBA 的格式读出 YUV 图像,这样传输数据量会降低一半,性能提升明显。
下面我们介绍两种使用 glReadPixels 来进行 RGBA 转换 NV21 的示例:
- 直接获取 RGBA 数据
这种方式 GPU 传输数据到 CPU 耗时比较长。
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, rgbaByteAddr);
libyuv::ABGRToNV21(rgbaByteAddr, width * 4, yByte, width, uvByte, width, width, height);;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
- OpenGL 扩展格式 YUV
// Draw Y
TextureAttributes textureAttriburesY = {.minFilter = GL_LINEAR,.magFilter = GL_LINEAR,.wrapS = GL_CLAMP_TO_EDGE,.wrapT = GL_CLAMP_TO_EDGE,.internalFormat = GL_RED_EXT,.format = GL_RED_EXT,.type = GL_UNSIGNED_BYTE
};varying vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
void main()
{vec4 color = texture2D(inputImageTexture,textureCoordinate);gl_FragColor.r = color.r*0.2990+color.g*0.5870+color.b*0.1140;
}// Draw UV
TextureAttributes textureAttriburesVU = {.minFilter = GL_LINEAR,.magFilter = GL_LINEAR,.wrapS = GL_CLAMP_TO_EDGE,.wrapT = GL_CLAMP_TO_EDGE,.internalFormat = GL_RG_EXT,.format = GL_RG_EXT,.type = GL_UNSIGNED_BYTE
};varying vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
void main()
{vec4 color = texture2D(inputImageTexture,textureCoordinate);gl_FragColor.rg = vec2(0.6150*color.r - 0.5150*color.g - 0.1000*color.b+0.5000,-0.1471*color.r - 0.2889*color.g + 0.4360*color.b+0.5000);
}glBindFramebuffer(GL_FRAMEBUFFER, yFbo);
glReadPixels(0, 0, width, height, GL_RED_EXT, GL_UNSIGNED_BYTE, yuv_byte);
glBindFramebuffer(GL_FRAMEBUFFER, 0);glBindFramebuffer(GL_FRAMEBUFFER, uvFbo);
glReadPixels(0, 0, width / 2, height / 2, GL_RG_EXT, GL_UNSIGNED_BYTE, yuv_byte + width * height);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
2. ImageReader
2.1 ImageReader 基础描述
ImageReader 是 Android 中的一个类,用于获取相机设备的图像数据。它可以用于捕获相机拍摄的静态图像或实时预览帧,并提供对图像数据的访问和处理。以下是一些 ImageReader 的特点和用法:
- 获取图像数据:通过创建一个 ImageReader 实例,可以指定要获取的图像的宽度、高度和图像格式。然后,可以使用ImageReader 的 acquireLatestImage() 或 acquireNextImage() 方法获取最新的图像或下一帧图像。这些方法返回一个 Image 对象,它包含了图像的数据和相关信息。
- 图像数据访问:通过 Image 对象,可以访问图像的像素数据。可以使用 getPlanes() 方法获取图像的平面数组,每个平面对应于图像的不同颜色通道。然后,可以使用 getBuffer() 方法获取每个平面的 ByteBuffer,从中读取或修改像素数据。
- 回收资源:使用完 Image 对象后,应调用其 close() 方法释放资源,以避免内存泄漏。
- 设置图像可用监听器:可以为 ImageReader 设置一个 OnImageAvailableListener 监听器,在新图像可用时收到通知,这样可以实现对图像数据的实时处理和分析。
- 配置图像输出:可以使用 ImageReader 的 setOnImageAvailableListener() 方法设置监听器,并通过 ImageReader 的 getSurface() 方法获取一个 Surface 对象,将其用于预览或拍照时的图像输出目标。
2.2 ImageReader 如何使用
我们可以使用 ImageReader 对象的 Surface 对象搭配 OpenGL 进行数据渲染。
mImageReader = ImageReader.newInstance(width, height, ImageFormat.YUV_420_888, 2);
mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mHandler);
mSurface = mImageReader.getSurface();
private ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {@Overridepublic void onImageAvailable(ImageReader reader) {Image image = reader.acquireLatestImage();if (image != null) {image.close();}}
};
部分重要 API:
- acquireLatestImage() 从 ImageReader 队列中获取最新的一帧 Image ,并且将老的 Image 丢弃,如果没有新的可用的 Image 则返回 null 。此操作将会从 ImageReader 中获取所有可获取到的 Images ,并且关闭除了最新的 Image 之外的 Image 。此功能大多数情况下比 acquireNextImage 更推荐使用,更加适用于视频实时处理。需要注意的是 maxImages 应该至少为 2 ,因为丢弃除了最新的之外的所有帧需要至少两帧。换句话说,(maxImages - currentAcquiredImages < 2) 的情况下,丢帧将会不正常。
- acquireNextImage() 从 ImageReader 的队列中获取下一帧 Image ,如果没有新的则返回 null。Android 推荐我们使用 acquireLatestImage 来代替使用此方法,因为它会自动帮我们 close 掉旧的 Image,并且能让效率比较差的情况下能获取到最新的 Image 。acquireNextImage 更推荐在批处理或者后台程序中使用,不恰当的使用本方法将会导致得到的 images 出现不断增长的延迟。
- close() 释放所有跟此 ImageReader 关联的资源。调用此方法后,ImageReader 不会再被使用,再调用它的方法或者调用被 acquireLatestImage 或 acquireNextImage 返回的 Image 会抛出 IllegalStateException,尝试读取之前 Plane#getBuffer 返回的 ByteBuffers 将会导致不可预测的行为。
- newInstance(int width, int height, int format, int maxImages) 创建新的 reader 以获取期望的 size 和 format 的 Images。maxImages 决定了 ImageReader 能同步返回的最大的 Image 的数量,申请越多的 buffers 会耗费越多的内存空间,使用合适的数量很重要。
- newInstance(int width, int height, int format, int maxImages) 创建新的 reader 以获取期望的 size 和 format 的 Images。maxImages 决定了 ImageReader 能同步返回的最大的 Image 的数量,申请越多的 buffers 会耗费越多的内存空间,使用合适的数量很重要。
- maxImages:缓存的最大帧数,必须大于 0。
3. PBO(Pixel Buffer Object)
3.1 PBO 基础介绍
OpenGL PBO(Pixel Buffer Object),被称为像素缓冲区对象,主要被用于异步像素传输操作。PBO 仅用于执行像素传输,不连接到纹理,且与 FBO (帧缓冲区对象)无关。OpenGL PBO(像素缓冲区对象) 类似于 VBO(顶点缓冲区对象),PBO 开辟的也是 GPU 缓存,而存储的是图像数据。PBO 是 OpenGL ES 3.0 开始提供的一种方式,主要应用于从内存快速复制纹理到显存,或从显存复制像素数据到内存。
在使用 OpenGL 的时候经常需要在 GPU 和 CPU 之间传递数据,例如在使用 OpenGL 将 YUV 数据转换成 RGB 数据时就需要先将 YUV 数据上传到 GPU ,一般使用函数 glTexImage2D ,处理完毕后再将 RGB 结果数据读取到 CPU , 这时使用函数 glReadPixels 即可将数据取回。但是这两个函数都是比较缓慢的,特别是在数据量比较大的时候。PBO 就是为了解决这个访问慢的问题而产生的。
不使用 PBO 加载纹理:
使用 PBO 加载纹理:
3.2 PBO 如何使用?
int imgByteSize = m_Image.width * m_Image.height * 4;//RGBAglGenBuffers(1, &uploadPboId);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboId);
glBufferData(GL_PIXEL_UNPACK_BUFFER, imgByteSize, 0, GL_STREAM_DRAW);glGenBuffers(1, &downloadPboId);
glBindBuffer(GL_PIXEL_PACK_BUFFER, downloadPboId);
glBufferData(GL_PIXEL_PACK_BUFFER, imgByteSize, 0, GL_STREAM_DRAW);
使用两个 PBO 从帧缓冲区读回图像数据:
如上图所示,利用 2 个 PBO 从帧缓冲区读回图像数据,使用 glReadPixels 通知 GPU 将图像数据从帧缓冲区读回到 PBO1 中,同时 CPU 可以直接处理 PBO2 中的图像数据。
// 交换 PBO
int index = m_FrameIndex % 2;
int nextIndex = (index + 1) % 2;// 将图像数据从帧缓冲区读回到 PBO 中
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_DownloadPboIds[index]);
glReadPixels(0, 0, m_RenderImage.width, m_RenderImage.height, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);// glMapBufferRange 获取 PBO 缓冲区指针
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_DownloadPboIds[nextIndex]);
GLubyte *bufPtr = static_cast<GLubyte *>(glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0,dataSize,GL_MAP_READ_BIT));
if (bufPtr) {nativeImage.ppPlane[0] = bufPtr;//NativeImageUtil::DumpNativeImage(&nativeImage, "/sdcard/DCIM", "PBO");glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
}
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
4. HardwareBuffer
4.1 HardwareBuffer 基础介绍
HardwareBuffer 官方介绍为一种底层的内存 buffer 对象,可在不同进程间共享,可映射到不同硬件系统,如 GPU、传感器等,从构造函数可以看出,其可以指定 format 和 usage,用来让底层选择最合适的实现,目前 format 主要是渲染相关的纹理格式,Android 11 之后支持了 BLOB 格式,可用来做 NN 相关的数据共享。
如果看一下 HardwareBuffer 的实现,会发现其只是 GraphicBuffer 的一个包装,只是 Android 低版本并没有开放 GraphicBuffer 相关 API,而前面提到的 Surface ,其底层就是基于 GraphicBuffer 来实现的,因此本质上是 Android 系统开放了更底层的 API,我们才可以有更高效的实现,接下来看具体如何基于 HardwareBuffer 跨进程传输纹理。
4.2 HardwareBuffer 如何使用?
AHardwareBuffer 创建纹理:
if(textureID == 0){AHardwareBuffer_Desc h_buffer_desc = {0};h_buffer_desc.stride = frameData->i32Width;h_buffer_desc.height = frameData->i32Height;h_buffer_desc.width = frameData->i32Width;h_buffer_desc.layers = 1;h_buffer_desc.format = 0x11;h_buffer_desc.usage = AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;int ret = AHardwareBuffer_allocate(&h_buffer_desc, &inputHWBuffer);EGLint attr[] = {EGL_NONE};EGLDisplay edp;edp = (EGLDisplay)eglGetCurrentDisplay();inputEGLImage) = eglCreateImageKHR(edp, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, eglGetNativeClientBufferANDROID(inputHWBuffer), attr);glGenTextures(1, &textureID);glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureID);glTexParameteri(GL_TEXTURE_EXTERNAL_OES , GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_EXTERNAL_OES , GL_TEXTURE_MAG_FILTER, GL_LINEAR);glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES , (GLeglImageOES)inputEGLImage);
}
AHardwareBuffer_Planes planes_info = {0}; int ret = AHardwareBuffer_lockPlanes(inputHWBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK, -1,nullptr,&planes_info);
if (ret == 0) {memcpy(planes_info.planes[0].data,frameData->ppu8Plane[0],frameData->i32Width * frameData->i32Height*3/2);ret = AHardwareBuffer_unlock(inputHWBuffer, nullptr);
}
glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureID);
AHardwareBuffer 读取纹理图像数据:
unsigned char *ptrReader = nullptr;
ret = AHardwareBuffer_lock(inputHWBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, -1, nullptr, (void **) &ptrReader);
memcpy(dstBuffer, ptrReader, imgWidth * imgHeight * 3 / 2);
ret = AHardwareBuffer_unlock(inputHWBuffer, nullptr);
ImageReader、 PBO 和 HardwareBuffer 明显优于 glReadPixels 方式,HardwareBuffer、ImageReader 以及 PBO 三种方式性能相差不大,但是理论上 HardwareBuffer 性能最优。
补充:OPENGL NCNN GPU零拷贝实现
相关文章:

Android GPU纹理数据拷贝
在 Android 开发中读取纹理数据有以下几种方法: glReadPixelsImageReaderPBO(Pixel BufferObject) HardwareBuffer 1. glReadPixels glReadPixels 是 OpenGL ES 的 API,通常用于从帧缓冲区中读取像素数据,OpenGL ES…...
浏览器端直播推流实现——系统篇
浏览器端用vue3.5.12写,服务器端用php8.2+swoole5.1.4+thinkphp8写,流媒体服务器使用nginx-rtmp模块,拉流App端用uniapp(其他端各自实现吧,这里以App端为例) 操作系统基于opencloudos8,还用到了ffmpeg,该安装就安装,这里不啰嗦安装步骤 以下是vue的代码,比较简陋,各…...

HDFS和HBase跨集群数据迁移 源码
HDFS集群间数据迁移(hadoop distcp) hadoop distcp \ -pb \ hdfs://XX.14.36.205:8020/user/hive/warehouse/dp_fk_tmp.db/ph_cash_order \ hdfs://XX.18.32.21:8020/user/hive/warehouse/dp_fksx_mart.db/HBase集群间数据(hbase ExportSnap…...

opencv实时弯道检测
项目源码获取方式见文章末尾! 600多个深度学习项目资料,快来加入社群一起学习吧。 《------往期经典推荐------》 项目名称 1.【基于CNN-RNN的影像报告生成】 2.【卫星图像道路检测DeepLabV3Plus模型】 3.【GAN模型实现二次元头像生成】 4.【CNN模型实现…...

计算机网络综合题
IP数据报的划分 CRC差错检测 冗余码的计算 因此,余数是1110,传输的数为11010110111110。在传输过程中最后两位变成o,接收端能够发现,因为11010110111110除以10011余数不为0。 子网划分 暴力求解法 (定长子网划分大量…...

【ARM Linux 系统稳定性分析入门及渐进 1.2 -- Crash 工具依赖内容】
请阅读:【Linux 维测及Crash使用专栏】 文章目录 Prerequisites1. 内核对象文件2. 内存镜像3. 平台处理器类型4. Linux 内核版本 Prerequisites crash 工具需要依赖下面的内容: 1. 内核对象文件 vmlinux 文件:需要一个 vmlinux 内核对象文件ÿ…...

「C/C++」C++标准库 之 #include<exception> 异常处理库
✨博客主页何曾参静谧的博客📌文章专栏「C/C」C/C程序设计📚全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…...
YOLOv7-0.1部分代码阅读笔记-experimental.py
experimental.py models\experimental.py 目录 experimental.py 1.所需的库和模块 2.class CrossConv(nn.Module): 3.class Sum(nn.Module): 4.class MixConv2d(nn.Module): 5.class Ensemble(nn.ModuleList): 6.def attempt_load(weights, map_locationNone): 1…...

【大数据学习 | kafka】简述kafka的消费者consumer
1. 消费者的结构 能够在kafka中拉取数据进行消费的组件或者程序都叫做消费者。 这里面要涉及到一个动作叫做拉取。 首先我们要知道kafka这个消息队列主要的功能就是起到缓冲的作用,比如flume采集数据然后交给spark或者flink进行计算分析,但是flume采用的…...
系统架构设计师论文:论湖仓一体架构及其应用
试题四 论湖仓一体架构及其应用 随着5G、大数据、人工智能、物联网等技术的不断成熟,各行各业的业务场景日益复杂,企业数据呈现出大规模、多样性的特点,特别是非结构化数据呈现出爆发式增长趋势。在这一背景下,企业数据管理不再局限于传统的结构化 OLTP (On-Line Transact…...

电磁兼容(EMC):GB 4343.1喀呖声 详解
目录 1. 喀呖声的危害 2. 喀呖声 Click定义 3. 中频参考电平 4. 开关操作 5. 最小观察时间 6. 喀呖声率 7. 喀呖声限值 8. 上四分位法 1. 喀呖声的危害 喀呖声作为一种电压骚扰,其危害主要体现在以下几个方面: 对电子设备的干扰:喀呖…...
纯血鸿蒙Native层支持说明
本文所有描述均参考鸿蒙官方文档:传送门 1.对C库的支持 C标准函数库在C语言程序设计中,提供符合标准的头文件,以及常用的库函数实现(如I/O输入输出和字符串控制)。 HarmonyOS采用musl作为C标准库,musl库…...

learn C++ NO.31——类型转换
C语言中的类型转换 在C语言中,当赋值符号两边的类型不匹配的时候,或者是形参类型和实参类型不匹配时,返回值类型与接受返回值类型不匹配时,都会需要类型转换。C语言的类型转换有两种:显示类型转换和隐式类型转换。 显…...

重学 Android 自定义 View 系列(三):自定义步数进度条
前言 本篇文章主要是实现仿QQ步数View,很老的一个View了,但技术永不落后,开搂! 最终效果如下: 1. 结构分析 QQStepView 主要由三个元素组成: 显示一个圆环进度条,通过外环和内环的角度变化来…...

海南华志亿星电子商务有限公司赋能抖音商家成长
在当今瞬息万变的电商时代,抖音凭借其短视频与直播电商的独特模式,迅速崛起并引领潮流。在这场电商变革中,海南华志亿星电子商务有限公司以其卓越的服务质量和创新的运营模式,在抖音电商领域大放异彩,成为众多商家的首…...

数据结构-并查集专题(1)
一、前言 因为要开始准备年底的校赛和明年年初的ACM、蓝桥杯、天梯赛,于是开始按专题梳理一下对应的知识点,先从简单入门又值得记录的内容开始,并查集首当其冲。 二、我的模板 虽然说是借用了jiangly鸽鸽的板子,但是自己也小做…...

共享汽车管理新纪元:SpringBoot框架应用
4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式,是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示: 图4-1系统工作原理…...
道可云人工智能元宇宙每日资讯|《中国生成式人工智能应用与实践展望》白皮书发布
道可云元宇宙每日简报(2024年11月6日)讯,今日元宇宙新鲜事有: 《重庆市“机器人”应用行动计划(2024—2027年)》发布 近日,重庆市经济和信息化委员会、重庆市教育委员会等八部门印发《重庆市“…...

kaggle学习 eloData项目(1)-数据校验
文章目录 kaggle学习 eloData项目(1)-数据校验(1) 数据基本情况查看(2) 数据校验(3) 数据探究 小结 kaggle学习 eloData项目(1)-数据校验 不能懈怠࿰…...
ORACLE RAC用DNS服务器的配置
一、搭建本地YUM源 二、安装DNS全部组建 yum -y install bind* 三、规划您RAC集群所有IP #public 192.168.16.111 rac1.ntt.com rac1 192.168.16.112 rac2.ntt.com rac2 192.168.16.121 rac3.ntt.com rac3 192.168.16.122 rac4.ntt.com rac4 #private 10.10.10.111 rac1-pr…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...

Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...

Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践
在 Kubernetes 集群中,如何在保障应用高可用的同时有效地管理资源,一直是运维人员和开发者关注的重点。随着微服务架构的普及,集群内各个服务的负载波动日趋明显,传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...