当前位置: 首页 > news >正文

服务端高并发分布式结构进阶之路

序言

  • 在技术求知的旅途中,鉴于多数读者缺乏在中大型系统实践中的亲身体验,难以从宏观角度把握某些概念,因此,本文特选取“电子商务应用”作为实例,详细阐述从百级至千万级并发场景下服务端架构的逐步演变历程。同时,将逐一列出每个演进阶段所涉及的关键技术,旨在为大家构建一个关于架构演进的全面认知框架,以便大家在后续的深入学习中能够拥有更为广阔的视野和坚实的基础。

目录

 (一)常见概念

1.1 基本概念

1.1.1 应用(Application)/系统(System)

1.1.2 模块(Module)/组件(Component)

1.1.3 分布式(Distributed)

1.1.4 集群(Cluster)

1.1.5 主(Master)/从(Slave)

1.1.6 中间件(Middleware)

1.2 评价指标(Metric)

1.2.1 可用性(Availability)

1.2.2 响应时长(ResponseTimeRT)

1.2.3 吞吐(Throughput)vs并发(Concurrent)

(二)架构演进

2.1 单机架构

2.2 应用数据分离架构

2.3 应用服务集群架构

2.4 读写分离/主从分离架构 

2.5 引入缓存⸺冷热分离架构

2.6 垂直分库 

2.7 业务拆分⸺微服务

(三)总结 


 (一)常见概念

 在正式引入架构演进之前,为避免大家对架构中的概念完全不了解导致低效沟通,优先对其中⼀ 些比较重要的概念做前置介绍:

1.1 基本概念

1.1.1 应用(Application)/系统(System)

为了实现一系列服务的功能,可能需要开发一个程序或是多个相互协作的程序集合。以日常生活中的例子来类比:就像是为了达成一个目标,而组建的一个单独个体或者一群彼此配合的人员所形成的团队。


1.1.2 模块(Module)/组件(Component)

当应用程序变得相当复杂时,为了明确划分职责,我们会把那些职责明确且内部联系紧密的部分提取出来,形成抽象概念,以便于理解和管理。用生活中的实例来比喻:就像在军队中,为了攻占某个据点,会将士兵划分为不同的专业小组,如突击小组、爆破小组、掩护小组和通信小组等。


1.1.3 分布式(Distributed)

当一个系统的多个模块被分别部署在不同的服务器上运行时,这样的系统就被称为分布式系统。例如,Web服务器与数据库服务器各自独立运行在不同的服务器上,或者多台Web服务器也被分别部署在各自独立的服务器上。用生活中的实例来比喻:为了更有效地满足实际需求,原本在同一办公地点工作的一个小组被分散到多个城市的不同工作地点,通过远程协作来完成目标。这些分散在不同地点的模块之间的通信,基本上需要依赖网络支持来实现。


1.1.4 集群(Cluster)

部署在多台服务器上,旨在实现特定目标的单个或一组特定组件的集合,整体上被称为集群。例如,多个MySQL实例运行在不同的服务器上,共同提供数据库服务,这样的组合可以被称为一个数据库集群。用生活中的实例来比喻:为了达成攻克防御坚固的大城市的军事目标,指挥部将大量炮兵部队集结起来,形成一个强大的炮兵打击集群。

关于分布式与集群的区别,通常我们不必过于拘泥于两者概念的细微差别。若深究,分布式更多强调的是物理分布状态,即工作在不同物理位置上的服务器通过网络通信相互协作以完成任务;而集群则更侧重于逻辑上的组合形态,即是否为了完成某个特定的服务目标而将多个组件或系统集合在一起。


1.1.5 主(Master)/从(Slave)

在集群中,往往会有一个程序或节点被赋予更多的职责,这个角色通常被称为主节点;而其他承担辅助或次要职责的节点则被称为从节点。以MySQL集群为例,如果只有其中一台服务器上的数据库允许执行数据的写入操作(如增加、删除、修改等),而其他数据库的数据修改都需要从这台数据库进行同步,那么这台数据库就被称为主库,而其他数据库则被称为从库。


1.1.6 中间件(Middleware)

一类软件,其作用是促进不同应用程序之间的通信,充当着不同技术、工具和数据库之间的连接桥梁。以生活中的实例来比喻:一家饭店在初创时,可能每天都会亲自前往市场挑选食材;但随着业务的扩大,饭店成立了一个专门的采购部门,这个部门专门负责食材的采购工作,从而成为了厨房与菜市场之间的连接纽带。


1.2 评价指标(Metric)

1.2.1 可用性(Availability)

可用性衡量的是在单位时间段内,系统能够正常提供服务的概率或期望值。例如,年化系统可用性可以通过系统正常提供服务的时间与一年总时间的比值来计算。这里隐含了一个关键指标,即如何评估系统服务是否正常,但对此我们不进行深入探讨。通常,我们会用“几个9”来简要描述系统的可用性,如4个9代表系统能提供99.99%的可用性,5个9则是99.999%的可用性,以此类推。而在日常交流中,我们更常用“高可用性(High Availability, HA)”这一非量化指标来表达我们对系统可用性的追求。


1.2.2 响应时长(ResponseTimeRT)

响应时长是指用户完成输入到系统给出反馈的时间间隔。以点外卖业务为例,响应时长就是从用户完成点单到拿到外卖的时刻之间的时间差。通常,我们需要关注的最长响应时长、平均响应时长和中位数响应时长。这个指标原则上越小越好,但在实际实现中,可能需要根据具体情况进行权衡。


1.2.3 吞吐(Throughput)vs并发(Concurrent)

吞吐衡量的是单位时间段内,系统能够成功处理的请求数量。而并发则是指系统在同一时刻能够支持的最大请求量。以高速公路为例,如果一条车道一分钟内可以通过20辆车,那么这条车道的吞吐量就是20辆/分钟,而并发量可以理解为车道上同时能容纳的车辆数(虽然在实际中,这个并发量很难直接获取,很多时候会用极短时间内的吞吐量来近似代替)。在日常交流中,我们常用“高并发(High Concurrent)”这一非量化指标来简要表达系统对并发处理能力的追求。


(二)架构演进

2.1 单机架构

在初创阶段,我们会依靠一支精炼的技术团队,迅速将业务系统推向市场以接受检验,并能够敏捷的应对需求的变化。幸运的是,由于前期用户访问量较少,系统性能和安全性方面的压力并不大,加之系统架构相对简单,无需配备专业的运维团队。因此,在这个阶段,采用单机架构是一个明智的选择。

当用户在浏览器中输入www.taobao.com时,首先会通过DNS服务将这一域名转换成对应的IP地址,然后浏览器会访问这个IP地址所对应的应用服务。 


2.2 应用数据分离架构

系统上线后,我们取得了预期的成功,吸引了一批忠实用户,导致系统访问量持续增长,逐渐接近硬件资源的承载上限。与此同时,团队也积累了丰富的业务流程经验。面对日益增长的性能压力,我们未雨绸缪,决定进行系统重构和架构升级,以增强系统的承载能力。然而,由于预算仍然有限,我们选择了成本效益最高的方案,即将应用与数据分离,以此来实现系统承载能力的有效提升。

和之前架构的主要区别在于将数据库服务独立部署在同⼀个数据中心的其他服务器上,应用服务通过网络访问数据。


2.3 应用服务集群架构

我们的系统深受用户喜爱,甚至出现了爆款产品,导致单台应用服务器已无法满足需求。此时,单机应用服务器首先遭遇了性能瓶颈。技术团队面临两种解决方案,并就此展开了热烈讨论:

  • 垂直扩展(Scale-Up):通过采购性能更强大但价格更高的应用服务器来应对增加的流量。此方案的优势在于无需对系统软件做任何调整;然而,劣势同样显著:硬件性能与价格的增长关系非线性,即性能提升两倍可能需要花费超过四倍的价格,且硬件性能提升存在明显上限。

  • 水平扩展(Scale-Out):通过调整软件架构,增加应用层硬件,将用户流量分散到不同的应用服务器上,从而提升系统的承载能力。此方案的优势在于成本相对较低,且提升的空间很大。但劣势是增加了系统的复杂性,要求技术团队具备更丰富的经验。

经过团队的深入研究、调研和讨论,最终决定采用水平扩展方案来解决此问题。但这需要引入一个新的组件——负载均衡器。为了解决用户流量应分发到哪台应用服务器的问题,需要一个专门的系统组件进行流量分发。实际上,负载均衡不仅应用于应用层,还可能涉及其他网络层。同时,流量调度算法也多种多样,这里简要介绍几种常见的:

  • 轮询(Round-Robin)算法:非常公平地将请求依次分配给不同的应用服务器。

  • 加权轮询(Weighted Round-Robin)算法:为不同的服务器(如性能差异)分配不同的权重,实现“能者多劳”。

  • 一致性哈希(Consistent Hashing)算法:通过计算用户特征值(如IP地址)的哈希值,根据哈希结果进行分发。其优点是确保来自同一用户的请求总是被分配给指定的服务器,类似于我们平时遇到的专属客户经理服务。


2.4 读写分离/主从分离架构 

在上一节中,我们讨论了通过负载均衡将用户请求分发到不同的应用服务器进行并行处理,并可根据业务需求动态增加服务器数量以减轻压力。然而,当前的架构中,无论服务器数量如何扩展,这些请求最终都会涉及数据库的读写操作。当数据量达到一定程度时,数据库的压力将成为系统承载能力的瓶颈。那么,我们能否像扩展应用服务器那样扩展数据库服务器呢?答案是否定的,因为数据库服务具有其独特性:一旦数据被分散到不同的服务器上,数据的一致性就无法得到保证。这里的数据一致性是指,对于同一个系统,无论何时何地访问,我们都应该看到一个统一且一致的数据状态。以银行管理的账户金额为例,如果收到一笔转账后,一个数据库的数据更新了,而另一个数据库没有更新,那么用户查询到的存款金额将是错误的。

为了解决这个问题,我们采用了主从数据库架构。我们保留一个主要的数据库作为写入数据库(主库),其他数据库作为从属数据库(从库)。从库的所有数据都来自主库,并通过同步机制保持与主库数据的一致性。为了分担数据库的压力,我们将写数据请求全部交给主库处理,而将读请求分散到各个从库中。由于大多数系统中读写请求的比例是不均衡的,例如100次读请求对应1次写请求,因此通过将读请求分散到各个从库处理后,数据库的整体压力会显著降低。当然,这个过程并非没有成本,主库到从库的数据同步需要一定的时间,但这个问题我们在此暂不深入讨论。

应用中需要对读写请求做分离处理,所以可以利用⼀些数据库中间件,将请求分离的职责托管出去。 


2.5 引入缓存⸺冷热分离架构

随着访问量的持续增长,我们发现业务中某些数据的读取频率远高于其他数据,我们将这部分数据定义为热点数据,而与之相对的是冷数据。为了提升热点数据的读取响应时间,我们可以采取增加本地缓存以及在外部部署分布式缓存的策略,缓存热门商品信息或热门商品的HTML页面等内容。通过缓存机制,我们可以在读写数据库之前拦截掉绝大部分请求,从而极大地减轻数据库的压力。

在这一过程中,我们会运用到多种技术,比如使用memcached作为本地缓存解决方案,使用Redis作为分布式缓存系统。然而,缓存的引入也带来了一系列需要关注的问题,如缓存一致性、缓存穿透/击穿现象、缓存雪崩问题以及热点数据集中失效等。这些问题都需要我们在设计和实现缓存策略时给予充分的考虑和应对。


2.6 垂直分库 

随着业务数据量的膨胀,单一数据库已难以满足存储需求,因此,我们可以根据业务逻辑,将数据分散存储。例如,针对评论数据,我们可以采用商品ID进行哈希运算,然后路由到相应的表中存储;而对于支付记录,则可以按小时为单位创建表,并进一步将每个小时的数据拆分成小表,通过用户ID或记录编号来引导数据路由。只要确保实时操作的表数据量保持在一个较小的范围内,且请求能够均匀分配到部署在多台服务器上的小表上,数据库便可通过水平扩展的方式来提升性能。前面提及的Mycat也支持在大表拆分为小表的情况下的访问控制。

然而,这种做法显著提升了数据库运维的复杂性,对DBA的专业能力提出了更高要求。当数据库设计到这种架构时,它实际上已经是一个逻辑上的分布式数据库整体,但内部的不同组件是由不同的系统来实现的。比如,分库分表的管理和请求分发由Mycat承担,SQL的解析工作则由单机数据库完成,读写分离可能依赖于网关和消息队列,而查询结果的汇总则可能由数据库接口层负责。这种架构实质上是大规模并行处理(MPP)架构的一种具体实现。


2.7 业务拆分⸺微服务

随着团队规模的扩大和业务的发展,我们将业务分配给不同的开发团队进行独立维护。每个团队负责实现自己的微服务,并通过隔离数据直接访问的方式,确保各服务间的独立性。为实现服务间的相互调用与关联,我们可以采用Gateway网关、消息总线等技术手段。此外,为了提升效率和复用性,我们还可以将一些通用的业务功能,如用户管理、安全管理、数据采集等,提取出来作为公共服务提供给各个团队使用。


(三)总结 

至此,一个相对合理的高可用、高并发系统的基本框架已经显现。需要注意的是,上述架构的演变顺序是基于某个侧面进行的单独改进,但在实际情境中,可能会同时面临多个问题,或者首先遇到瓶颈的可能是其他方面。因此,在实际操作中,我们应针对具体问题采取相应解决方案。例如,在政府类项目中,尽管并发量可能不大,但业务需求却可能十分复杂,此时,高并发就不是首要解决的问题,而满足丰富的业务需求则成为优先考量。

对于一次性实施且性能指标明确的系统,其架构设计只需满足既定的性能指标要求即可,但应预留扩展接口以应对未来可能出现的需求。而对于不断发展的系统,如电商平台,其架构设计则需考虑未来一段时间内的用户量和性能指标,并随着业务的增长不断迭代升级,以支持更高的并发量和更丰富的业务功能。

“大数据”实际上是对海量数据采集、清洗、转换、存储、分析以及数据服务等场景解决方案的总称。在每个场景中,都有多种可选的技术,如数据采集工具Flume、Sqoop、Kettle等,数据存储方案包括分布式文件系统HDFS、FastDFS以及NoSQL数据库HBase、MongoDB等,数据分析则可能涉及Spark技术栈、机器学习算法等。总的来说,大数据架构是根据业务需求整合各种大数据组件而成的,它通常提供分布式存储、分布式计算、多维分析、数据仓库以及机器学习算法等能力。而服务端架构则更多地关注应用组织层面的设计,其底层能力往往由大数据架构来提供。

相关文章:

服务端高并发分布式结构进阶之路

序言 在技术求知的旅途中,鉴于多数读者缺乏在中大型系统实践中的亲身体验,难以从宏观角度把握某些概念,因此,本文特选取“电子商务应用”作为实例,详细阐述从百级至千万级并发场景下服务端架构的逐步演变历程。同时&am…...

分布式微服务项目,同一个controller不同方法间的转发导致cookie丢失,报错null pointer异常

源码: /***添加商品进入购物车*/ GetMapping("/addToCart") public String addToCart(RequestParam("num") Integer num, RequestParam("skuId") Long skuId, RedirectAttributes redirectAttributes) {System.out.println("nu…...

STM32 ADC --- 任意单通道采样

STM32 ADC — 单通道采样 文章目录 STM32 ADC --- 单通道采样cubeMX配置代码修改:应用 使用cubeMX生成HAL工程 需求:有多个通道需要进行ADC采样,实现每次采样只采样一个通道,且可以随时采样不同通道的功能。 cubeMX配置 这里我们…...

vscode中执行git合并操作需要输入合并commit信息,打开的nano小型文本编辑器说明-

1.前提: VScode中的git组件执行任何合并动作的时候需要提交远程合并的commit信息,然后编辑器自动打开的是nano文本编辑器 2.nano编辑器说明: 1.保存文件:按 Ctrl O,然后按 Enter 来保存文件。 2.退出编辑器&#xf…...

蓝桥杯每日真题 - 第7天

题目:(爬山) 题目描述(X届 C&C B组X题) 解题思路: 前缀和构造:为了高效地计算子数组的和,我们可以先构造前缀和数组 a,其中 a[i] 表示从第 1 个元素到第 i 个元素的…...

【Git】Git Clone 指定自定义文件夹名称:详尽指南

目录 引言一、git clone 基本语法二、默认行为:没有指定文件夹名称时三、如何指定自定义文件夹名称四、高级使用技巧:动态文件夹名称4.1 基于日期命名文件夹4.2 基于版本标签(Tag)动态命名文件夹4.1 基于日期命名文件夹4.2 基于版…...

终端快捷键学习笔记

以下是优化润色后的内容: 终端快捷键学习笔记 前言 终端(Terminal)是开发者、系统管理员以及技术人员常用的重要工具,它为我们提供了直接与操作系统交互的方式。不同操作系统中的终端使用体验存在差异,尤其在 Linux、…...

Go语言24小时极速学习教程(四)MySQL数据库的增删改查

通过前几篇想必你已经知道该如何使用Go语言写一些简单的程序了,那么从这一篇开始,我们开始探究如何用go语言能够写真正的企业级应用。第一步我们实现先能让程序对数据库进行增删改查,这里以MySQL为例。 1. 导入必要的包 首先需要导入databa…...

04 - Clickhouse-21.7.3.14-2单机版安装

目录 一、准备工作 1、确定防火墙处于关闭状态 2、CentOS 取消打开文件数限制 3、安装依赖 4、CentOS取消SELINUX 二、单机安装 2.1、下载安装 2.2、安装这4个rpm包 2.3、修改配置文件 2.4、启动服务 2.5、关闭开机自启 2.6、使用Client连接server 一、准备工作 1…...

多项式回归

以多元线性回归和特征工程的思想来想出一种称为多项式回归的新算法,它可以让您拟合曲线,非线性函数,您的数据。假设你有一个住房看起来像这样的数据集,其中特征x是以平方英尺为单位的大小。它看起来不像一条直线非常适合这个数据集…...

vscode报错:Connecting with SSH time-out.

当我们在vscode上远程连接(Remote_SSH)Linux时,如果直接点关闭vscode,下次远程登陆后,就会弹出以下界面, 点击重新加载window就会弹出以下报错: 这是因为我们没有正常关闭remote-ssh, 导致linux上有多个vsc…...

python可视化将多张图整合到一起(画布)

这周有点事忙着,没时间重温刚结束的Mathurcup数学建模,这两天也是再看了下,论文还是赶紧挺烂的,但比国赛又有进步(说起国赛又不得不抱怨了,基本其余省份都发了,但江西......哎)。哎&…...

C函数如何返回参数lua使用

返回基本数据类型 数字类型(整数和浮点数) 在C函数中,可以使用lua_pushnumber函数将一个数字(整数或浮点数)压入Lua栈。当C函数返回后,Lua会从栈顶获取这个数字作为返回值。例如,以下是一个简单…...

pytest在conftest.py中实现用例执行失败进行截图并附到allure测试报告

conftest.py文件简介 conftest.py文件用于定义共享设置、夹具和钩子函数。 可以跨.py文件调用,有多个.py文件调用时,可让conftest.py只调用了一次fixture,或调用多次fixture; conftest.py与运行的用例要在同一个pakage下&#xf…...

编程之路,从0开始:数据在内存中的存储

目录 1、整数在内存中的存储 (1)大小端 (2)数据存储读取练习 2、浮点数在内存中的存储 Hello大家好,很高兴我们又见面啦!给生活添点Passion,开始今天的编程之路! 1、整数在内存中的存储 之…...

二叉树+树的OJ题讲解

求第K层节点个数 思路:走到K1就不走了,一次传回得到的值 #include<stdio.h> #include<stdlib.h> //树的定义 typedef int BTDataType; typedef struct BinaryTreeNode {BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right; }BTNode;//手…...

信捷PLC转以太网连接电脑方法

信捷XC/XD/XL等系列PLC如何上下载程序?可以选择用捷米特JM-ETH-XJ模块轻松搞定,并不需要编程&#xff0c;即插即用&#xff0c;具体看见以下介绍&#xff1a; 产品介绍 捷米特JM-ETH-XJ是专门为信捷PLC转以太网通讯面设计&#xff0c;可实现工厂设备信息化需求&#xff0c;对…...

释放 PWA 的力量:2024 年的现代Web应用|React + TypeScript 示例

在2024年的Web开发领域&#xff0c;PWA&#xff08;Progressive Web Apps&#xff09;已经成为一个不可忽视的技术趋势。这篇文章将探讨PWA的最新发展&#xff0c;并通过实例展示如何构建一个现代PWA应用。 PWA的本质与优势 PWA本质上是一种将Web应用提升到接近原生应用体验的技…...

CVSS4与CVSS3的不同之二

在文章CVSS4与CVSS3的不同-CSDN博客中描述了CVSS3的缺点&#xff0c;以及CVSS4相对CVSS3做了哪些改进和带来了哪些优点。 但是具体CVSS4针对CVSS3做了哪些改动&#xff0c;还没有详细列举出来。 本文主要是针对CVSS4和CVSS的打分的大项和小项进行逐一对比&#xff0c;列出来具体…...

【Pip】如何清理 `pip` 包管理器 —— 完整指南

目录 引言1. 清理 pip 缓存2. 卸载不再需要的包2.1 如何查看已安装的包2.2 如何卸载不需要的包 3. 查看已安装的包及其依赖3.1 查看单个包的依赖3.2 查看所有包的依赖关系3.2 优化包依赖 4. 解决包冲突5. 合并和优化依赖5.1 优化 requirements.txt5.2 删除冗余依赖 6. pip 清理…...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...

微信小程序云开发平台MySQL的连接方式

注&#xff1a;微信小程序云开发平台指的是腾讯云开发 先给结论&#xff1a;微信小程序云开发平台的MySQL&#xff0c;无法通过获取数据库连接信息的方式进行连接&#xff0c;连接只能通过云开发的SDK连接&#xff0c;具体要参考官方文档&#xff1a; 为什么&#xff1f; 因为…...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

安卓基础(aar)

重新设置java21的环境&#xff0c;临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的&#xff1a; MyApp/ ├── app/ …...