Ceph分布式存储系统以及高可用原理

news2024/11/17 6:30:00

Ceph分布式存储系统以及高可用原理

  • 1. Ceph原理和架构
    • 1.1 分布式存储系统抽象
    • 1.2 Ceph基本组件
  • 2 Ceph中的策略层
    • 2.1 CRUSH进行数据分发和定位
    • 2.2 PG(Placement Group): 集群管理的基本单元
    • 2.3 PG的代理primary OSD
    • 2.4 轻量级的集群元数据ClusterMap
    • 2.5 对PG的罗辑分组:Pool
    • 2.6 集群自治
    • 2.7 良好的扩展性
    • 2.8 EC(Erasure Coding)编码的支持
  • 3. 多存储引擎的支持
  • 4. 三种接口简介
    • 4.1 RadowGW
    • 4.2 RBD
    • 4.3 CephFS
  • 5. 疑问和思考
  • 6. 参考文档

Ceph是目前开源社区中非常火的一款分布式存储系统。其最初是Sage Weil的博士课题,并且在2004-2006两年多的时间开发出Ceph的原型。2007年Sage博士毕业以后继续全职从事Ceph的开发工作。在2012年Sage创立了专门提供Ceph服务和技术支持的公司Inktank,并且在当年7月份发布了Ceph的第一个稳定版本V0.48.1(代号Argonaut,Ceph和许多开源软件一样,选择首字母从A ~ Z依次递增的单词作为稳定版本的代号)。当前最新的稳定版本是0.94.1(Hammer)。目前,Ceph社区非常活跃,基本保持每三到六个月发行一个稳定版本的速率。Redhat公司于2014年4月份以1.75亿美金的价格收购了Ceph开发团队。Ceph和其他存储系统最大的不同就是它同时为用户提供了块、对象以及Posix文件三种通用的存储接口,并且Ceph对OpenStack,Hadoop等开源平台也提供了内在的支持。早在2010年,Linus就将Ceph的Posix接口的客户端代码merge进了Linux内核,用户在不需要另外安装软件包就可以直接通过Ceph的Posix文件系统接口挂载使用Ceph。

和大多数的由GFS(Google File System)衍生出来的分布式存储系统相比,Ceph采用了无中心元数据服务器的架构,在诸多概念上有自己的创新,这篇文章就从原理和架构和相关概念上对Ceph进行一个梳理。

关于常见分布式组件高可用设计原理的理解和思考


1. Ceph原理和架构

1.1 分布式存储系统抽象

自2003年谷歌发布了GFS论文以后,分布式存储系统在互联网业内有了快速的发展和广泛的使用。应该说,分布式存储系统的出现很好的满足了互联网公司诸多应用场景下的数据存储的需求。数据是互联网公司非常宝贵的财富,在进行数据存储时,首先要考虑的是其高可靠性,也就是在正常情况下,写入的数据不会无故丢失。但是计算机设备和磁盘是有一定的故障率的,特别是互联网公司普遍使用的比较廉价的x86通用服务器。在实际应用中,各大公司保证数据高可靠性的方法通常有两种。第一,花钱省事,购买价钱比较昂贵的EMC,NetApp等公司的商业存储。第二,进行及时的数据备份。考虑到互联网公司数据量太大,并且增长快,如果采用第一种方式,想必是一笔非常大的开销。第二种方式是比较合,但是在分布式存储系统出现之前,通常是基于单机存储进行数据备份,数据的维护和管理比较繁琐和复杂,还有可能增加业务代码的复杂性(通常在业务层就考虑数据的备份)。谷歌GFS论文出来以后,给业界带来了很大的震动,一批有理想的程序员心想:其实也不复杂嘛,就是用一台机器作为管理机进行数据定位和集群管理,一批机器进行数据存储,并且让备份的过程自动化并且对用户透明。于是业界就很快出现了一批类GFS的分布式存储系统。比较有名的开源分布式存储系统有HDFS,MooseFS和淘宝TFS等。

然而Ceph和业内其它比较流行的类GFS分布式存储系统是不一样的,在结构上它没有类似GFS中单点的管理机,而是独树一帜,采用独特的哈希算法进行数据的定位以及存储节点分组的方式进行节点的自我管理。但是和其他分布式存储系统一样,它解决的最重要问题也是让数据的备份工作自动化,高效的保证数据的高可靠性。

总体来说,分布式存储系统解决的最重要的问题是将数据高效高可靠的存储在由通用硬件组成的计算机集群中。根据当前大多数分布式存储的设计和实现,我们可以抽象出分布式存储系统的三个层次,如图1所示:
在这里插入图片描述

  • 接口层:定义分布式存储系统怎样被客户来进行使用,比较通用的接口有Posix文件系统接口,块存储接口和对象接口。当然,许多的存储系统还定义了自己原生的API接口。

  • 策略层:定义数据通过接口层写入系统时,系统本身要采用相应的策略对写入的数据进行处理和分配,以保证数据的高可用性,安全性,读写高性能等。同时,在集群某个节点发生故障时,需要策略层定义故障的修复机制。

  • 引擎层:定义了数据持久化的方式,数据经过策略层的处理最终都会被分配到存储集群中的相应节点上。数据到达节点之后,以什么样的方式持久化到磁盘上,是引擎层定义的。

在这三个层次中,引擎层通常采用目前比较成熟的一些单机存储软件,例如整存整取的文件存储通常使用比较常用的ext4,xfs等文件系统作为引擎层。Key-Value存储通常采用Leveldb,MySQL等单机数据库系统作为引擎层。可以说,在分布式存储系统出现之前,引擎层单机的技术发展已经比较成熟了。所以,引擎层通常不是分布式存储系统需要重点考虑一层,通常都会根据业务的需求,选择一款比较合适的引擎层的存储技术。那么,分布式存储系统所需要解决的问题主要集中在接口层和策略层。其中,以策略层所要解决的问题最为复杂。接下来就以Ceph策略层所要解决的数据定位,数据备份,错误检测和恢复等方面的问题为出发点,结合Ceph本身的架构设计对Ceph作一个较为详细的介绍。

1.2 Ceph基本组件

Ceph中所包含的基本组件如图2所示
在这里插入图片描述
我们从下往上看,Sage在当初发表关于Ceph的论文最重要的一个概念就是RADOS,意在构造一个高可靠的,可扩展的自治的分布式存储系统。Ceph中RADOS的实现是由OSD和Monitor两个组件完成的:

  • OSD:OSD(Object Storage Device)是Ceph中的一个核心概念,是Ceph自治系统进行自我管理和修复的基本单元。它要完成的任务就是负责策略层策略的最终落地。本质上来说,它是一个用户态进程,管理着一片数据存储区域(通常是一块磁盘)。对外,OSD接收或者发送策略层的集群管理信号;对内,OSD利用引擎层的读写接口实现数据的最终写入和读出。在实际的部署中,通常都是一个OSD进程管理一块磁盘。

  • Monitor:Monitor类似于Hadoop中的Zookeeper服务,是Ceph分布式系统的协调器,主要负责对集群中的各个组件进行状态一致性的协调。同时,集群中的安全认证机制也是由Monitor完成的。Monitor本身也有高可用的机制,在生产环境中,一个Ceph集群通常使用三个Monitor同时工作。这里要注意的是Monitor不是元数据服务器,以HDFS作为类比,它和HDFS中的Namenode有非常大的不同,它主要负责集群状态的监测和数据一致性的协调,而不负责存储在集群中数据的元数据管理,但是会负责轻量的集群状态元数据(主要是一些Map结构的数据Ceph称之为ClusterMap,后面讲到的CrushMap和PGMap都包含其中)的维护和存储。

当你的集群中拥有了OSD和Monitor你就拥有了一个高可用的数据存储的集群。当然,你得需要一个读写的接口,才能把自己的数据放上去。Ceph提供了一个统一的读写API:librados,目前,librados支持C/C++、Java、Python等大部分的语言。客户应用程序可以通过这个API实现数据高可靠的写入和快速的读取。然而,考虑到用户的使用习惯,和librados本身比较复杂,Ceph在librados之上开发出了三种比较常用的数据存取接口:CephFS、RBD、RadosGW,分别为用户提供Posix文件系统接口,RBD块存储接口和RadosGW对象存储接口。

2 Ceph中的策略层

2.1 CRUSH进行数据分发和定位

Ceph能够实现去中心化的架构,主要得益于CRUSH算法,客户端通过CRUSH算法定位每一个数据块所在的位置,进行读写, 从而省去需要保存元数据的中心组件(容易成为瓶颈)

  • 在有中心元数据节点的分布式存储系统中,如果要定位想要的数据,客户端一般先与元数据服务器(比如HDFS中Namenode)进行交互,获取数据的位置信息。在这种结构的分布式系统中通常存在单点瓶颈,导致集群的扩展性、高可用性、性能等一些方面的问题
  • 在Ceph中是通过CRUSH(Controlled Replication Under Scalable Hashing)算法来进行数据分发和定位的,从而去除了中心节点(实际上,ceph也是有中心节点的,但是在设计上保存的数据量很少,因此在monitor协调一致性的前提下,保存到了所有的osd节点,并保证数据一致性)

CRUSH:CRUSH是一个伪随机哈希算法,其实现目标是通过哈希的方式快速快速找到数据应该存入的存储设备,并且使数据在整个集群中比较均匀的分布。对其简单的描述如图3所示:
在这里插入图片描述

CRUSH算法中有两个重要的输入

  • 一个是要计算对象的ID(可以是一个对象或者一个对象组)
  • 另一个是从上到下描述存储集群拓扑结构的CrushMap。CrushMap是根据集群当前的拓扑结构进行定制的。比如一个CrushMap可以包含这些信息:集群中多少个机架,每个机架上有多少个服务器,每个服务器上有多少个磁盘。

Sage把CRUSH算法设计成了一个通用的哈希算法,也就是它也可以被用在和Ceph有类似需求的存储系统中。那么在Ceph中CRUSH算法完成了什么样的工作呢。如图4所示,当要将一个对象存入Ceph集群时,Client端先对该对象进行一次哈希,得到一个pgid(后面会讲到),然后将pgid作为CRUSH算法的输入,此时的CrushMap包含的信息是:此集群中拥有4个存储节点,每个存储节点上有三个osd。由于此时各个osd之间的隔离域(failure domain)只是到了机器(host)层。所以此时的CRUSH算法只是从不同机器上选取3个不同的osd,然后将对象写入。以上只是对CRUSH的工作过程做一个简单的描述,具体算法的实现,可以参考相应的源代码或文档。

在这里插入图片描述

2.2 PG(Placement Group): 集群管理的基本单元

图4中我们可以看到,数据映射到osd的过程中有个中间的PG层,PG是Ceph中的一个非常重要的概念,是对写入对象进行管理的基本单元。数据读写、集群状态监测,错误恢复等过程都是以PG为单位进行的。PG与OSD是多对多的映射关系(例如图中,PG1对应了[osd2, osd5, osd6],而osd5又可以同时属于PG1和PG100),这种映射关系被称为PGMap,是系统在运行过程中维护的一个重要信息。

通常在这种映射关系中,PG的密集度远高于osd的,一个PG所对应的一组OSD本质上是CRUSH算法对一个pgid计算出来的结果,集群中pgid的数目是由系统管理员进行设置的,Ceph的官方建议值是:pg_num = (osd总数 * 100)/3

PG概念的引入为集群的管理带来了很大的灵活性,这也是无中心Metadata Server架构设计的关键所在。前面我们讲到PGMap是保存集群状态信息的一个重要的元数据,也是ClusterMap中最大的一个Map。PGMap对每一个PG保存一个条目,该条目指示PG与OSD的映射关系以及其所处的状态。如图5所示就是截取的我们测试集群中PGMap中的一段。
在这里插入图片描述

其每个PG条目主要字段的意思如下:

  • pg_stat:该PG的pgid,pgid是一个RADOS集群中PG的唯一标识,由点(.)隔成的两部分组成,第一部分“1”是Pool号(见2.3.5),第二部分是0 ~ n-1(n是由管理员设定的PG数目)的十六进制表示。
  • state:标示PG当前所处的状态,active+clean表示该PG目前处于活动状态,并且由该PG所管理的数据都处于安全一致的状态。
  • up:表示该PG会映射到的OSD集合,由于我们设置的数据备份数是2,所以每个PG会映射到两个OSD。例如写入到pgid为1.17c的数据会对应的写入到osd53和osd14中。
  • up_primary:标识该PG所映射的OSD中primary OSD是osd53。通常,写在up集合中的第一个OSD就是primary OSD。
  • acting,acting_primary和对应的up以及up_primary含义基本相同。

pg有多种状态,可以通过命令ceph pg dump 获取

  • active+clean:表示PG正常运行,并且数据在Ceph存储集群中的OSD(Object Storage Device)上是一致的。该状态是最理想的状态。
  • active+recovery:表示PG正在从故障中恢复。这可能是因为OSD出现故障,或者数据丢失等原因导致的。
  • active+remapped:表示PG在进行重映射,即数据正在从一个OSD迁移到另一个OSD。
  • active+undersized:表示PG中的副本数量低于所需数量。这可能是因为OSD出现故障,或者手动调整了PG的副本数。
  • active+stale:表示PG中的数据副本已经过期,需要进行重新同步。
  • active+degraded:表示PG中的数据丢失或不一致,需要进行修复。
  • inactive:表示PG当前处于非活动状态,即没有进行任何操作。

2.3 PG的代理primary OSD

上一节提到的primary OSD是实现PG功能的总代理。客户端对RADOS中数据的读写最终会落到某一个PG中,我们知道,PG是一个逻辑概念,需要一个实体(进程)完成其功能,这个实体就是primary OSD。在数据读写,PG状态维护等方面,primary OSD都发挥了积极的主导作用。

在数据读写时,客户端都是直接与primary OSD进行沟通

  • 在数据读取时,客户端首先通过哈希算法计算到数据所在的PG,然后查找当前的PGMap,找到PG的primary OSD,直接从该OSD中读取数据。
  • 在写入数据写入时,Ceph的写入是强一致性的。客户端也是先找到primary OSD然后,将数据写入到primary OSD,然后再通过primary OSD将数据同步到PG中其他的OSD。在PG对应的OSD都获得写入的数据之后,再由primary OSD对用户返回。

在数据读写以外的大量集群状态维护的工作中,PG状态的检测和错误恢复过程通常也是由primary OSD进行发起。

2.4 轻量级的集群元数据ClusterMap

Ceph中RADOS集群虽然不像HDFS那样存储标示数据位置信息的元数据,但也会存储标示集群构成和状态的元数据。Ceph中维护的几种元数据都是由Map数据结构构成,用ClusterMap作为这些Map的统称。Ceph的ClusterMap由以下Map组成:

  • MonitorMap:MonitorMap主要标示当前monitor集群由哪些monitor节点组成,各个monitor节点是否正常服务等信息。
  • OSDMap:标示集群由哪些OSD节点组成,以及各自的健康信息。
  • MDSMap:标示集群中mds(专为CephFS提供服务的组件)节点的组成,以及各自的健康信息。
  • CrushMap: CrushMap定义了数据在存储集群中的分布方式,通过将数据分散到多个存储设备上,提高了系统的并发访问能力和容错性。CrushMap描述了存储集群中的各个设备之间的层次结构,包括存储节点、机架和数据中心等,以及各个设备的权重。当存储设备出现故障时,CrushMap能够自动将受影响的数据迁移到其他可用设备上,从而实现故障恢复。CrushMap可以根据存储设备的负载情况,实现数据的动态负载均衡。通过动态分配数据到负载较低的存储设备上,避免了系统中某些设备负载过高的情况
  • PGMap: PGMap记录了每个PG的当前状态,包括活动(active)、未激活(inactive)、恢复(recovering)等状态。通过监控PGMap的变化,可以及时了解到PG的状态变化和健康状况。PGMap记录了每个PG的位置和复制策略,包括PG所在的OSD(Object Storage Daemon)和PG副本的分布情况。通过PGMap可以掌握到数据的分布情况,确保数据的可靠性和高可用性。当有OSD故障或数据损坏时,PGMap会提供故障恢复所需的信息,如需要恢复的PG和副本的位置等。通过PGMap可以方便地进行故障恢复操作,保证数据的完整性和可靠性

这5中Map共同构成了Ceph集群所需要维护的元数据ClusterMap。并且,这些Map所需要维护的数据量都比较小(在一个上百节点的集群中ClusterMap通常不过几MB的数据)。这给集群的维护带来了巨大的好处,集群中就不需要中心的元数据服务节点来存储和维护元数据。

Ceph集群中每一个节点都保存着ClusterMap,由Monitor维护着这些ClusterMap的协调一致,负责调整ClusterMap并同步到osd节点。这样,正常情况下,任意节点的元数据查找都只在本地进行Map查找,这也提高了集群的性能。

2.5 对PG的罗辑分组:Pool

当我们的Ceph集群资源比较充裕,单个业务用不完这么多资源,而需要多种业务混合使用单集群;或者集群中有些数据需要3份备份,而数据可靠性不需要那么高只需要2份备份该怎么办呢?针对这些相关的场景,Ceph引入了Pool的概念对PG进行了罗辑划分。在图5展示的pgid部分,第一部分的数字“1”就是Pool号。当Client要将数据写入到RADOS集群时,必须先指定相应的Pool号,这样就对不同类型的数据进行了划分。同时,还可以对Pool设置配额,设置对不同用户的读写权限等。

2.6 集群自治

Ceph集群中最多的节点就是存储数据的OSD节点,这些OSD节点的状态如果都由Monitor节点进行维护,会给Monitor节点带来沉重的负担。Ceph采取OSD节点自治的方式来进行自身状态的维护。具体操作方案就是每一个OSD节点会有自己的一组peer节点,OSD节点会定时的给自己的peer节点发出heartbeat,这样就能获取peer节点的状态信息。如果OSD节点获知peer节点有异常,就立即向Monitor节点进行报告,这样就能使集群的信息得到及时的更新。当前OSD所处的PG是其peer节点选取的重要依据,当然其中还有相关的算法策略,以确保用最少的heartbeat来保证不遗漏集群中每一个OSD节点状态的监测。

2.7 良好的扩展性

当Ceph进群需要扩容时,我们只需要向集群中增加OSD节点。新的OSD节点加入时,会主动告知Monitor,并被加入到OSDMap中。同时,Monitor会根据当前集群的状态,采取相应的策略,将新增的OSD节点加入到合适的一些PG中以保证集群中数据的负载均衡,同时,Monitor会更改PGMap。然后该OSD所对应的PG会进行相应的数据迁移(将PG中的数据迁移到该OSD节点上)。这样,OSD节点就能很快速的加入到了Ceph集群中,以实现对集群的扩容。

2.8 EC(Erasure Coding)编码的支持

和许多其他的分布式存储系统类似,Ceph原始的设计是基于存储对象的多份拷贝来保证数据高可用性的。随着EC编码在分布式存储系统中应用的增多,Ceph也顺应潮流加入了对EC编码的支持。得益于Ceph良好的结构设计,EC编码这个特性也很快的被Ceph所吸纳。

Ceph中的EC编码是基于Pool这一层做的。我们知道,RADOS可以通过对Pool指定数据对象的备份数来保证数据的高可用性。与之对应,RADOS还支持用户创建EC Pool,这样用户通过EC Pool写入RADOS集群的数据就能由EC编码的方式保证数据的高可用性。同时,在创建EC Pool时,还支持指定m,k值以及使用的具体EC编码算法等。

3. 多存储引擎的支持

前面的章节我们看到,Ceph中非常完善的策略层的设计,CRUSH算法以及RADOS的设计确保了数据的高可用性,集群的可靠性和扩展性。得益于Ceph良好的架构设计,在引擎层,Ceph也支持多种存储产品。如图6所示,我们可以将单OSD节点分成三个层
在这里插入图片描述

PGs层表示在PG与OSD多对多的关系中,一个OSD节点通常被多个PG所包含,OSD节点上的存储资源在逻辑上被落在该节点上的各个PG划分成了多个部分。

OSD层表示OSD主进程,前面讲到RADOS中数据的读写以及状态的维护都是以PG为单位,落到该OSD节点上PG的操作,都由OSD主进程进行完成。同时,数据的读写等操作OSD层会调用相应的引擎层进行完成。

目前,Ceph的引擎层不仅支持btrfs,xfs等文件系统,还支持一些单机的Key-Value存储如LevelDB,RocksDB等。这样Ceph不仅可以用于高吞吐的文件存储,也可以用于低延时的Key-Value存储。由于Ceph有强大的策略层的设计,和良好的架构设计,我们可以把Ceph看做是一个强大的分布式存储框架,用户可以根据自己的业务需求选择底层的存储引擎。

4. 三种接口简介

在Sage设计Ceph之初,业界使用最多的存储系统接口还是文件系统接口,文件系统接口最流行的标准是Posix。当时,对象存储还没有流行开来,并且也缺少统一的接口。因此,最初Sage仅仅实现了把Posix文件系统接口(CephFS)作为Ceph对外提供服务的接口。在亚马逊推出S3之后,对象存储迅速流行,并且S3的对象存储接口也基本成为了事实上的标准。并且随着以虚拟机作为服务形式的云计算服务的流行,块存储接口的需求也迅速增长。Sage很敏锐感觉到存储接口的设计是满足用户需求和用户体验的大事,于是在Ceph原有的librados原生API的基础上又开发出了对象存储接口RadosGW和块存储接口RBD。

4.1 RadowGW

谈到对象存储接口,那么什么是对象存储呢?目前,业内还没有对象存储的统一定义。通常可以认为,对象存储和其他存储最大的不同在于因用户需求的不同而导致的接口语义以及内部设计的不同。不同对象存储系统内部的设计千差万别,但是接口的语义基本相同:不提供文件的随机读写,数据的存取过程以对象的方式进行整存整取为主。并且,维护数据信息的元数据和数据本身进行分开存储和管理(在传统的文件系统中,元数据的操作占据了大量数据磁盘的IO)。这种存储方式天生的迎合了互联网中的诸多服务,例如图片、视频、音频等的存储。

RadosGW(Rados GateWay)是Ceph中的对象存储接口,同时兼容S3接口和Swift接口。其包含的主要模块的结构如图7所示:
在这里插入图片描述
图中我们可以看到RadosGW支持Civetweb,Apache等HTTP服务器作为接收REST请求的服务器,然后根据请求的类型,进行相应的解析。最终,RadosGW会将REST请求转化成对librados的调用,据此对RADOS集群进行数据的读写操作。

4.2 RBD

RBD(RADOS Bolck Device)是Ceph对外提供的块存储接口。通常块存储是由系统管理员进行管理和维护,主要是给虚拟机或者物理机提供虚拟磁盘服务。目前RBD被广泛应用于OpenStack中,为虚拟机提供虚虚拟磁盘服务。RBD接口的结构如图8所示:
在这里插入图片描述
我们可以看到,RBD接口是通过RBD Kernel Module将对存储块的操作转换成通过librados对RADOS集群进行的读写操作。RBD接口也是Ceph三种接口技术栈最简单的一种接口,并且RBD Kernel Module的代码已经被merge进了Linux内核当中,配置起来也比较简单。

4.3 CephFS

文件系统接口是历史悠久,使用最广泛的存储接口,Posix文件系统接口是最流行的文件系统接口的标准。Ceph文件系统接口是符合Posix标准的。相比于块和对象接口,文件系统接口是一种语义更复杂的存储接口。其支持的元数据操作、文件夹操作等给存储系统带来了比较大的额外负担,并且给实现也增加了很大的复杂性。在Ceph中,CephFS接口也是实现起来最复杂的一种接口,其结构图9如下所示:
在这里插入图片描述
由图我们可以看到,CephFS接口同时支持Kernel级的文件系统接口和用户态的文件系统接口(FUSE)。在CephFS接口中增加了一个非常重要的组件MDS(Metadata Server),负责对CephFS中的元数据进行管理。根据集群的负载情况,我们可以使用一个或多个MDS节点。并且,MDS本身并不进行数据的存储,而是将所有的元数据都存储到RADOS中对应的Metadata Pool中,依赖于RADOS中数据的高可用性,MDS所管理的数据也是高可用的。还可以为MDS节点添加热备用节点,在节点失效时,备用节点可以立即接收工作,以保证集群的高可用性。

5. 疑问和思考

一些总结

Ceph分布式存储系统具有优秀的策略层的设计,无中心元数据节点,无单点问题,完善的错误恢复机制,良好的扩展性等特性是它的核心竞争力。同时,良好的架构设计为其带来的多存储接口以及多存储引擎的支持也为其吸引了大量的用户和开发者。目前从社区以及我们自己的测试情况来看,Ceph已经具备非常高的可靠性。并且从社区来看,RadosGW和RBD接口已经在生产环境中得到了很好的检验,CephFS接口目前还有待得到生产环境的检验。当前,Ceph社区也非常活跃,并且背靠OpenStack这棵大树,相信Ceph的发展会越来越好。

6. 参考文档

  • 暂无

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1567950.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

面试总结------2024/04/04

1.面试官提问:你说你在项目中使用springsecurity jwt 实现了登录功能,能简单讲一下怎么实现的吗? 2.使用RabbitMQ实现订单超时取消功能 订单状态定义 首先,我们需要定义订单的不同状态。在这个示例中,我们可以定义以下…

分享three.js实现乐高小汽车

前言 Web脚本语言JavaScript入门容易,但是想要熟练掌握却需要几年的学习与实践,还要在弱类型开发语言中习惯于使用模块来构建你的代码,就像小时候玩的乐高积木一样。 应用程序的模块化理念,通过将实现隐藏在一个简单的接口后面&a…

shell的编写

文章目录 1.框架2.命令行3.获取用户命令字符串4.命令行字符串分割5.执行命令和内建命令6.完整代码: 1.框架 我们知道shell是一直存在的,所以首先我们第一步就是要搭建一个框架,使其一直存在。 那么也很简单,一个while循环就可以完…

(科研实践篇)大模型相关知识

1.embedding 1.介绍: embedding就是用一个低纬的向量表示一个物品。而这个embedding向量的实质就是使距离相似的向量所对应的物品具有相似的含义(参考皮尔逊算法和cos余弦式子:计算相似度)简单来说,就是用空间去表示…

1.Docker简介和安装

1 Docker 简介 1.1 Docker 是什么? docker是一个开源的应用容器引擎。 1.2 容器是什么? 容器是一种轻量级的虚拟化技术 ,它是一个由应用运行环境、容器基础镜像组成的集合。 以 Web 服务 Nginx 为例,如下图所示:Ngin…

【并发编程】CountDownLatch

📝个人主页:五敷有你 🔥系列专栏:并发编程 ⛺️稳中求进,晒太阳 CountDownLatch 概念 CountDownLatch可以使一个获多个线程等待其他线程各自执行完毕后再执行。 CountDownLatch 定义了一个计数器,…

贝锐蒲公英企业路由器双机热备,保障异地组网可靠、不中断

对于关键业务,比如:在线支付系统、远程医疗监控系统、重要数据中心等,一旦网络发生故障,可能导致巨大的损失或影响,因此需确保网络拥有极高的可靠性、稳定性和容错能力。 面对此类场景和需求,贝锐蒲公英异…

优秀网站收藏——持续更新

1、Uiverse.io 官网:Open-Source UI elements for any project Uiverse.io是一个开源免费的UI组件库,直接使用HTML和CSS组成,可以方便的使用在任何前端框架上。它包含了丰富的UI组件类型,如按钮、复选框、开关、卡片、加载动画、…

在s390x架构机器上构建frps/frpc镜像 —— 筑梦之路

源码:GitHub - fatedier/frp: A fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet. # 克隆代码git clone https://github.com/fatedier/frp.git# 切换目录cd frp# 构建frps服务端docker build -t frps:s390x -f …

ALPHA开发板上的PHY芯片驱动:LAN8720驱动

一. 简介 前面文章了解到,Linux内核是有提供 PHY通用驱动的。 本文来简单了解一下ALPHA开发板上的 PHY网络芯片LAN8720的驱动。是 LAN8720芯片的公司提供的 PHY驱动。 二. ALPHA开发板上的PHY芯片驱动:LAN8720驱动 我 们 来 看 一 下 LAN8720A 的 …

【算法每日一练]-数论(保姆级教程 篇1 埃氏筛,欧拉筛)

目录 保证给你讲透讲懂 第一种:埃氏筛法 第二种:欧拉筛法 题目:质数率 题目:不喜欢的数 思路: 问题:1~n 中筛选出所有素数(质数) 有两种经典的时间复杂度较低的筛法&#xff0…

LeetCode-98. 验证二叉搜索树【树 深度优先搜索 二叉搜索树 二叉树】

LeetCode-98. 验证二叉搜索树【树 深度优先搜索 二叉搜索树 二叉树】 题目描述:解题思路一:中序遍历解题思路二:0解题思路三:0 题目描述: 给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树…

Exchanger 怎么用J.U.C

Exchanger简介 Exchanger通常用来解决以下类似场景的问题,如下:两个线程间需要交换数据的问题,在多线程编程中,经常会有这样的场景:两个线程各自持有一些数据,并且需要在某个点上交换这些数据,…

不借助三方工具,修改Windows的CapsLock键为其他功能键

0. 背景交代 在我的Deepin上实现了CapsLock键切换输入法后,再用Windows会有点别扭,于是在一番查找资料和自行摸索后,找到了不借助第三方工具来实现修改CapsLock键的方法。 1. 修改CapsLock键为F15 1.1 Win R呼出运行窗口 1.2 输入Regedi…

Spring Boot 学习(2)——HelloWorld

HelloWorld!全宇宙码农的第一个(行)程序(代码)。 1、创建项目 打开idea,新建一个maven项目。 1)选择项目sdk(本例是1.8) 2)输入GroupId(co…

TCP的十个重要的机制

注:TCP不是只有十个机制 TCP 可靠传输是tcp最为重要的核心(初心) 可靠传输,并不是发送方把数据能够100%的传输给接收方 而是退而求其次 让发送方发送出去数据之后,能够知道接收方是否收到数据。 一但发现对方没有…

智慧公厕:提升城市公卫管理效率与环境舒适度的利器

公厕作为城市基础设施的重要组成部分,一直以来备受市民们的关注与诟病。然而,随着科技的发展和城市智慧化进程的推进,智慧公厕作为一种集成了物联网等技术的创新型公厕逐渐走入人们的视野。智慧公厕不仅实现了信息化、数字化和智慧化&#xf…

基于STC12C5A60S2系列1T 8051单片机的带字库液晶显示器LCD12864数据传输并行模式显示自定义字符应用

基于STC12C5A60S2系列1T 8051单片机的带字库液晶显示器LCD12864显示自定义字符应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍液晶显示器LCD12864简单介绍一、LCD…

WordPress 6.5 “里贾纳”已经发布

WordPress 6.5 “里贾纳”已经发布,其灵感来自著名爵士小提琴家Regina Carter的多才多艺。雷吉娜是一位屡获殊荣的艺术家和著名的爵士乐教育家,以超越流派而闻名,她在古典音乐方面的技术基础和对爵士乐的深刻理解为她赢得了大胆超越小提琴所能…

备战蓝桥杯--数论与搜索刷题2

话不多说,直接看题: 1.辗转相减法 我们不妨假设原等比数列a,a*(q/p),a*(q/p)^2.... 那么x1,,,,xn就是其中的n项,xi/x1(q/p)^b,假设最大比例为(q/p)^k,,那么一定有(q/p)^(k*s)(q/p)^b,即k是b的…