Longhorn,企业级云原生容器分布式存储 - 备份与恢复
Longhorn,企业级云原生容器分布式存储 - 备份与恢复
- 快照
- 手动快照
- 周期性快照和备份
- 使用 Longhorn UI 设置周期性快照
- 使用 StorageClass 设置 Recurring Jobs
- 分离卷时允许 Recurring Job
- 容灾卷
- 创建容灾(DR)卷
- 备份
- 设置备份目标
- 使用阿里云OSS备份存储
- 准备工作
- 为 S3 兼容的备份存储启用 virtual-hosted-style 访问
- 创建secret
- 在LonghornUI中配置BackupTarget和Secret
- NFS 备份存储
- 创建备份
- 从备份恢复
- 为 Kubernetes StatefulSets 恢复卷
快照
手动快照
snapshot 是 Kubernetes Volume 在任何给定时间点的状态。
要创建现有集群的快照,
- 在 Longhorn UI 的顶部导航栏中,单击 Volume。
- 单击要为其创建快照的卷的名称。这会导致卷详细信息页面。
- 在Snapshots and Backups板块中,单击 Take Snapshot 按钮即可创建快照。
创建快照后,您将在卷头(Volume Head)之前的卷的快照列表中看到它。
周期性快照和备份
从 Longhorn UI,可以安排周期性快照和备份。
要设置时间表(schedule),您将转到 Longhorn 中的卷详细信息视图。然后你将设置:
• schedule 类型,备份(backup)或快照(snapshot)
• 将创建备份或快照的时间,以 CRON expression 的形式
• 要保留的备份或快照的数量
• 应应用于备份或快照的任何标签(Any labels)
然后 Longhorn 会自动为当时的用户创建快照或备份,只要该卷附加到一个节点。
可以使用 Longhorn UI 或使用 Kubernetes StorageClass 配置周期性快照。
注意:为了避免当卷长时间没有新数据时,recurring jobs 可能会用相同的备份和空快照覆盖旧的备份/快照的问题,Longhorn 执行以下操作:
1. Recurring backup job 仅在自上次备份以来卷有新数据时才进行新备份。
2. Recurring snapshot job 仅在卷头(volume head)中有新数据(实时数据)时才拍摄新快照。
使用 Longhorn UI 设置周期性快照
可以从卷详细信息页面配置周期性快照和备份。要导航到此页面,请单击 Volume,,然后单击卷的名称。
使用 StorageClass 设置 Recurring Jobs
可以在 StorageClass 的 recurringJobs 参数中配置计划备份和快照。
使用这个 StorageClass 创建的任何未来卷都将自动设置这些 recurring jobs。
recurringJobs 字段应遵循以下 JSON 格式:
kind: StorageClassapiVersion: storage.k8s.io/v1metadata:name: longhornprovisioner: driver.longhorn.ioparameters:numberOfReplicas: "3"staleReplicaTimeout: "30"fromBackup: ""recurringJobs: '[{"name":"snap","task":"snapshot","cron":"*/1 * * * *","retain":1},{"name":"backup","task":"backup","cron":"*/2 * * * *","retain":1}]'
应为每个 recurring job 指定以下参数:
- name:一项 job 的名称。不要在一个 recurringJobs 中使用重复的名称。 并且 name 的长度不能超过 8 个字符。
- task:一项 job 的类型。它仅支持 snapshot(定期创建快照)或backup(定期创建快照然后进行备份)。
- cron:Cron 表达式。它告诉一项 job 的执行时间。
- retain:Longhorn 将为一项 job 保留多少快照/备份(snapshots/backups)。应该不少于 1。
分离卷时允许 Recurring Job
Longhorn 提供了 allow-recurring-job-while-volume-detached 设置,即使卷已分离,您也可以进行周期性备份(recurring backup)。您可以在 Longhorn UI 中找到该设置。
启用该设置后,Longhorn 将自动附加卷并在需要执行周期性快照/备份(recurring snapshot/backup)时进行快照/备份。
请注意,在卷自动附加(attached automatically)期间,卷尚未准备好处理工作负载。Workload 必须等到 recurring job 完成。
容灾卷
容灾 (DR) 卷是一种特殊卷,主要用于在整个主集群出现故障时将数据存储在备份集群中。灾难恢复卷用于提高 Longhorn 卷的弹性。
对于灾难恢复卷,Last Backup 表示其原始备份卷的最新备份。
如果代表灾难卷的图标为灰色,则表示该卷正在恢复 Last Backup,并且该卷无法激活。如果图标为蓝色,则表示该卷已恢复 Last Backup。
创建容灾(DR)卷
先决条件: 设置两个 Kubernetes 集群。它们将被称为集群 A 和集群 B。在两个集群上安装 Longhorn,并在两个集群上设置相同的备份目标。
- 在集群 A 中,确保原始卷 X 已创建备份或已安排 recurring backups。
- 在集群 B 的备份页面,选择备份卷 X,然后创建容灾卷 Y。强烈建议使用备份卷名作为容灾卷名。
- Longhorn 会自动将 DR 卷 Y 附加到随机节点。然后 Longhorn 将开始轮询卷 X 的最后一次备份,并将其增量恢复到卷 Y。
备份
设置备份目标
备份目标是用于访问 Longhorn 中 backupstore 的端点。backupstore 是 NFS 服务器或 S3 兼容服务器,用于存储 Longhorn 卷的备份。备份目标可以在 Settings/General/BackupTarget 中设置。
Longhorn 还支持通过 Longhorn UI 或 Kubernetes Storage Class 为卷设置周期性快照/备份(recurring snapshot/backup)作业。
使用阿里云OSS备份存储
准备工作
- 首先在阿里云开通OSS服务,并创建相应的Bucket
- 创建服务子账号,授予该账号对应Bucket的访问权限
- 记住子账号对应的accessKeyID、accessKeySecret,下边会用到
为 S3 兼容的备份存储启用 virtual-hosted-style 访问
在以下情况下,您可能需要为 S3 兼容的备份存储启用这种新的寻址方法
-
您想立即切换到这种新的访问方式,这样您就无需担心 Amazon S3 路径弃用计划;
-
您使用的 backupstore 只支持 virtual-hosted-style 的访问,例如:Alibaba Cloud(Aliyun) OSS;
-
您已配置 MINIO_DOMAIN 环境变量以启用 MinIO 服务器的 virtual-host-style 请求;
-
这个错误 … error: AWS Error: SecondLevelDomainForbidden Please use virtual hosted style to access. … 被触发。
启用 virtual-hosted-style 访问的方法 -
将值为 true 的新字段 VIRTUAL_HOSTED_STYLE 添加到您的备份目标 secret。例如:
apiVersion: v1kind: Secretmetadata:name: oss-backup-target-secret namespace: longhorn-systemtype: Opaquedata:AWS_ACCESS_KEY_ID: bG9uZ2hvcm4tdGVzdC1hY2Nlc3Mta2V5AWS_SECRET_ACCESS_KEY: bG9uZ2hvcm4tdGVzdC1zZWNyZXQta2V5AWS_ENDPOINTS: aHR0cHM6Ly9taW5pby1zZXJ2aWNlLmRlZmF1bHQ6OTAwMA==VIRTUAL_HOSTED_STYLE: dHJ1ZQ== # true
创建secret
在 Longhorn 所在的命名空间(默认为 longhorn-system)中创建一个名称为 oss-backup-target-secret的 Kubernetes secret。secret 必须在 longhorn-system 命名空间中创建,以便 Longhorn 访问它:
kubectl create secret generic oss-backup-target-secret --from-literal=AWS_ACCESS_KEY_ID=<your accessKeyID > --from-literal=AWS_SECRET_ACCESS_KEY=<your accessKeySecret> --from-literal=AWS_ENDPOINTS=<阿里云ENDPOINTS地址> --from-literal=VIRTUAL_HOSTED_STYLE=true -n longhorn-system
在LonghornUI中配置BackupTarget和Secret
-
转到 Longhorn UI。在顶部导航栏中,单击 Settings。 在 Backup 部分中,将 Backup Target 设置为:
s3://<your-bucket-name>@<your-aws-region>/mypath/attention:your-bucket-name: Bucket名称your-aws-region:Bucket所在区域,例如oss-cn-beijing确保末尾有 /,否则会报错。可以使用子目录(前缀):
-
在备份部分将 备份目标凭据 Secret(Backup Target Credential Secret) 设置为:oss-backup-target-secret
NFS 备份存储
要将 NFS 服务器用作备份存储,NFS 服务器必须支持 NFSv4。
目标 URL 应如下所示:
nfs://longhorn-test-nfs-svc.default:/opt/backupstore
创建备份
Longhorn 中的 Backups 是在集群外备份存储中的对象。快照的备份被复制到备份存储,访问备份存储的端点是备份目标。
先决条件: 必须设置备份目标。有关更多信息,请参阅设置备份目标。如果尚未设置 BackupTarget,则会出现错误。
要创建备份,
- 导航到 Volume 菜单。
- 选择要备份的卷。
- 单击 Create Backup。
- 添加适当的标签并单击 OK。
Result: 备份已创建。要查看它,请单击顶部导航栏中的 Backup。
从备份恢复
Longhorn 可以轻松地将备份恢复到一个卷。
还原备份时,默认情况下会创建一个同名的卷。如果已存在与备份同名的卷,则不会恢复备份。
要恢复备份,
- 导航到 Backup 菜单
- 选择您要恢复的备份,然后单击 Restore Latest Backup
- 在 Name 字段中,选择要恢复的卷
- 单击 OK
Result: 恢复的卷在 Volume 页面上可用。
为 Kubernetes StatefulSets 恢复卷
Longhorn 支持恢复备份,该特性的一个用例是恢复 Kubernetes StatefulSet 中使用的数据,这需要为备份的每个副本恢复一个卷。
要恢复,请按照以下说明操作。下面的示例使用一个 StatefulSet。
- 连接到 Web 浏览器中的 Longhorn UI 页面。在 Backup 选项卡下,选择 StatefulSet 卷的名称。单击卷条目的下拉菜单并恢复它,将卷命名为稍后可以轻松引用的 Persistent Volumes。
Backup Name | Restored Volume |
---|---|
pvc-01a | statefulset-vol-0 |
pvc-01b | statefulset-vol-1 |
对需要恢复的每个卷重复此步骤。
例如,如果使用具有名为 pvc-01a 和 pvc-02b 的卷的两个副本恢复 StatefulSet,则恢复可能如下所示:
1. 在 Kubernetes 中,为每个恢复的 Longhorn 卷创建一个 Persistent Volume来绑定它。storage 容量、numberOfReplicas、storageClassName 和 volumeHandle 必须在下面替换。在这个例子中,我们在 Longhorn 中引用了 statefulset-vol-0 和 statefulset-vol-1,并使用 longhorn 作为我们的 storageClassName。
kind: PersistentVolumeapiVersion: v1metadata:name: statefulset-vol-0spec:capacity:storage: 20Gi # 和Longhron中恢复的卷的大小保持一致storageClassName: longhorn # must be same name that we will use latervolumeMode: FilesystemaccessModes:- ReadWriteOncepersistentVolumeReclaimPolicy: Retaincsi:driver: driver.longhorn.io # driver must match thisfsType: ext4volumeAttributes:numberOfReplicas: '3' # Longhorn恢复的卷的副本数staleReplicaTimeout: '30'volumeHandle: statefulset-vol-0 # 在Longhorn中恢复的卷的名称---kind: PersistentVolumeapiVersion: v1metadata:name: statefulset-vol-1spec:capacity:storage: 20Gi # 和Longhron中恢复的卷的大小保持一致storageClassName: longhorn # must be same name that we will use latervolumeMode: FilesystemaccessModes:- ReadWriteOncepersistentVolumeReclaimPolicy: Retaincsi:driver: driver.longhorn.io # driver must match thisfsType: ext4volumeAttributes:numberOfReplicas: '3' # Longhorn恢复的卷的副本数staleReplicaTimeout: '30'volumeHandle: statefulset-vol-1 # 在Longhorn中恢复的卷的名称
-
在 namespace 中,将部署 StatefulSet,为上面创建的每个 Persistent Volume 创建 PersistentVolume Claims来进行绑定。Persistent Volume Claim 的名称必须遵循以下命名方案:
<name of Volume Claim Template>-<name of StatefulSet>-<index>
-
StatefulSet Pod 是零索引(zero-indexed)的。 在这个例子中,Volume Claim Template 的名字是 data,StatefulSet 的名字是 webapp, 并且有两个副本,分别是索引 0 和 1。
kind: PersistentVolumeClaimapiVersion: v1metadata:name: data-webapp-0namespace: databasespec:accessModes:- ReadWriteOnceresources:requests:storage: 20Gi # 必须和上边定义的PV中的大小保持一致storageClassName: longhornvolumeName: statefulset-vol-0 # 上边创建的PV的名字---kind: PersistentVolumeClaimapiVersion: v1metadata:name: data-webapp-1namespace: databasespec:accessModes:- ReadWriteOnceresources:requests:storage: 20Gi # 必须和上边定义的PV中的大小保持一致storageClassName: longhornvolumeName: statefulset-vol-1 # 上边创建的PV的名字
-
创建 StatefulSet:
apiVersion: apps/v1beta2kind: StatefulSetmetadata:name: webapp # match this with the PersistentVolumeClaim naming schemespec:selector:matchLabels:app: nginx # has to match .spec.template.metadata.labelsserviceName: "nginx"replicas: 2 # by default is 1template:metadata:labels:app: nginx # has to match .spec.selector.matchLabelsspec:terminationGracePeriodSeconds: 10containers:- name: nginximage: k8s.gcr.io/nginx-slim:0.8ports:- containerPort: 80name: webvolumeMounts:- name: datamountPath: /usr/share/nginx/htmlvolumeClaimTemplates:- metadata:name: data # match this with the PersistentVolumeClaim naming schemespec:accessModes: [ "ReadWriteOnce" ]storageClassName: longhorn # must match name from earlierresources:requests:storage: 20Gi # must match size from earlier
Result: 现在应该可以从 StatefulSet Pods 内部访问恢复的数据。
整个恢复流程如下:
- 在Longhorn中恢复对应的卷,并为其取一个名字
- 在Kubernetes中创建PV,通过volumeHandle关键字来绑定Longhorn中恢复的卷
- 在Kubernetes中创建PVC,通过volumeName关键字绑定特定的PV
- 创建Deployment或者StatefulSet来使用定义的PVC
相关文章:
Longhorn,企业级云原生容器分布式存储 - 备份与恢复
Longhorn,企业级云原生容器分布式存储 - 备份与恢复快照手动快照周期性快照和备份使用 Longhorn UI 设置周期性快照使用 StorageClass 设置 Recurring Jobs分离卷时允许 Recurring Job容灾卷创建容灾(DR)卷备份设置备份目标使用阿里云OSS备份存储准备工作为 S3 兼容…...

亿级高并发电商项目-- 实战篇 --万达商城项目 十(安装与配置Elasticsearch和kibana、编写搜索功能、向ES同步数据库商品数据)
亿级高并发电商项目-- 实战篇 --万达商城项目搭建 一 (商家端与用户端功能介绍、项目技术架构、数据库表结构等设计) 亿级高并发电商项目-- 实战篇 --万达商城项目搭建 一 (商家端与用户端功能介绍、项目技术架构、数据库表结构等设计&#x…...

windwos安装spring-cloud-alibaba-nacos
windwos安装spring-cloud-alibaba-nacos前言一、预备环境二、下载源码或者安装包1.启动2.关闭总结前言 这个快速开始手册是帮忙您快速在您的电脑上,下载、安装并使用 Nacos。 一、预备环境 Nacos 依赖 Java 环境来运行。如果您是从代码开始构建并运行Nacos&#x…...
Spring Boot 项目如何统一结果,统一异常,统一日志
1 统一结果返回目前的前后端开发大部分数据的传输格式都是json,因此定义一个统一规范的数据格式有利于前后端的交互与UI的展示。1.1 统一结果的一般形式是否响应成功;响应状态码;状态码描述;响应数据;其他标识符&#…...
Ubuntu下用Lean源码编译openwrt及一行命令u盘启动openwrt安装x86硬盘上
Ubuntu下用Lean源码编译openwrt 源码地址:https://github.com/coolsnowwolf/lede 1:首先微软云服务器装好 Ubuntu 64bit,推荐 Ubuntu 20.04 LTS x64,免费一年。ip设置在地球某处。总结就是每一步需要下载的都得下载完,…...
JavaScript Number 对象
JavaScript 是一门非常强大的编程语言,它提供了许多内置对象来帮助开发者在编写 JavaScript 应用时更轻松地处理数据。其中一个非常有用的对象是 JavaScript Number 对象,它可以帮助我们处理数值类型的数据,例如整数和浮点数。在本文中&#…...

【原创】java+swing+mysql银行ATM管理系统
本文主要介绍使用javaswingmysql去设计一个银行ATM管理系统,模仿实现存款、取款、转账、余额查询等功能。 功能分析: 隐含ATM管理系统一般分为管理员和用户角色,管理员可以进行用户管理、账单管理,用户可以进行转取存款等功能如…...
博弈论--总结
博弈分类 按照是否对外产出或消耗 零和博弈:博弈过程作为整体对外无产出也无消耗。非零和博弈:博弈过程作为整体对外有产出或有消耗。 按照博弈参与人数 1人博弈2人博弈3人博弈n人博弈 按照博弈是否重复 注:同一规则的同一博弈过程反复…...

AMBA低功耗接口规范(Low Power Interface Spec)
1.简介 AMBA提供的低功耗接口,用于实现power控制功能。目前AMBA里面包含2种低功耗接口: Q-Channel:实现简单的power控制,如上电,下电。 P-Channel:实现复杂的power控制,如全上电,半上…...

matlab-汽车四分之一半主动悬架模糊控制
1、内容简介汽车四分之一半主动悬架模糊控制651-可以交流、咨询、答疑2、内容说明半主动悬架汽车 1/4 动力学模型建立 本章主要对悬架类型进行简要介绍,并对其进行对比分析,提出半主动悬架的优越性,论述半主动悬架的工作原理,并对…...

【安全加密】通信加密算法介绍
加密常用于通信中,如战争中电台通讯有明码和密码,密码需要不断更换密码本;另外,商用软件也需要用到加密技术,如根据电脑的mac地址设置权限,防止软件被恶意传播。 文章目录一、介绍1. 单向散列/哈希算法2. 对…...

kubernetes教程 --组件详细介绍
组件详细介绍 NameSpace 在 Kubernetes 中,名字空间(Namespace) 提供一种机制,将同一集群中的资源划分为相互隔离的组。 同一名字空间内的资源名称要唯一,但跨名字空间时没有这个要求。 名字空间作用域仅针对带有名字…...

数字化系统使用率低的原因剖析
当“数字化变革”成为热门话题,当“数字化转型”作为主题频频出现在一个个大型会议中,我们知道数字化时代的确到来了。但是,根据Gartner的报告我们看到一个矛盾的现象——85%的企业数字化建设与应用并不理想、但对数字化系统的需求多年来持续…...

<<Java开发环境配置>>7-Apache Tomcat安装教程环境变量配置IDEA配置
一.Apache Tomcat简介: Apache是普通服务器,本身只支持html即普通网页。不仅可以通过插件支持php,还可以与Tomcat连通(单向Apache连接Tomcat,就是说通过Apache可以访问Tomcat资源。反之不然)。Apache只支持静态网页,但像php,cgi,jsp等动态网页就需要Tomc…...

互联网大厂测开面试记,二面被按地上血虐,所幸Offer已到手
在互联网做了几年之后,去大厂“镀镀金”是大部分人的首选。大厂不仅待遇高、福利好,更重要的是,它是对你专业能力的背书,大厂工作背景多少会给你的简历增加几分竞争力。 如何备战面试的? 第一步:准备简历…...

网络管理之设备上线技术的发展现状和趋势
网络和网络设备无处不在 随着社会的发展和技术的进步,人类文明开始向信息时代演进,网络逐渐变成现代社会不可或缺的一部分,极大程度影响了人类的认知形式、思维方式与生活模式。从家庭网,到企业网;从无线网࿰…...
SQL67 返回固定价格的产品
描述有表Productsprod_idprod_nameprod_pricea0018sockets9.49a0019iphone13600b0018gucci t-shirts1000【问题】从 Products 表中检索产品 ID(prod_id)和产品名称(prod_name),只返回价格为 9.49 美元的产品。【示例结…...

webpack 开发环境的基本配置(webpack打包样式资源、html、图片、devserver、开发环境配置、以及其他资源)
A.打包样式资源 1. 创建文件 2. 下载安装 loader 包 npm i css-loader style-loader less-loader less -D 3. 修改配置文件 /*webpack.config.js webpack的配置文件作用: 指示 webpack 干哪些活(当你运行 webpack 指令时,会加载里面的配置ÿ…...
刷题记录:牛客NC14402求最大值
传送门:牛客 题目描述: 给出一个序列,你的任务是求每次操作之后序列中 (a[j]-a[i])/(j-i)【1<i<j<n】的最大值。 操作次数有Q次,每次操作需要将位子p处的数字变成y. 输入: 5 2 4 6 8 10 2 2 5 4…...

javaEE 初阶 — 传输层 TCP 协议 中的延迟应答与捎带应答
文章目录1. 延迟应答2. 捎带应答TCP 工作机制:确认应答机制 超时重传机制 连接管理机制 滑动窗口 流量控制与拥塞控制 1. 延迟应答 延时应答 也是提升效率的机制,也是在滑动窗口基础上搞点事情。 滑动窗口的关键是让窗口大小大一点,传输…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...

Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...

C++ 设计模式 《小明的奶茶加料风波》
👨🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...