一、CFT简介

CFT(Crash Fault Tolerance),即故障容错,是非拜占庭问题的容错技术。

Paxos 问题是指分布式的系统中存在故障(crash fault),但不存在恶意(corrupt)节点的场景(即可能消息丢失或重复,但无错误消息)下的共识达成问题,是分布式共识领域最为常见的问题。最早由Leslie Lamport用 Paxon 岛的故事模型来进行描述而得以命名。解决Paxos问题的算法主要有Paxos系列算法和Raft算法,Paxos算法和Raft算法都属于强一致性算法。

二、Paxos算法

1、Paxos算法产生背景

在常见的分布式系统中,总会发生诸如机器宕机或网络异常(包括消息的延迟、丢失、重复、乱序,网络分区)等情况。Paxos算法需要解决的问题就是如何在一个可能发生异常的分布式系统中,快速且正确地在集群内部对某个数据的值达成一致,并且保证不论发生任何异常,都不会破坏整个系统的一致性。

Paxos算法用于解决分布式系统的一致性问题。

2、Paxos算法简介

1990年,Leslie Lamport 在论文《The Part-time Parliament》中提出了Paxos共识算法,在工程角度实现了一种最大化保障分布式系统一致性(存在极小的概率无法实现一致)的机制。Leslie Lamport作为分布式系统领域的早期研究者,因为相关成果获得了2013年度图灵奖。

Leslie Lamport在论文中将Paxos问题表述如下:

希腊岛屿Paxon上的执法者在议会大厅中表决通过法律(一次Paxos过程),并通过服务员(proposer)传递纸条的方式交流信息,每个执法者会将通过的法律记录在自己的账目上。问题在于执法者和服务员都不可靠,他们随时会因为各种事情离开议会大厅(服务器拓机或网络断开),并随时可能有新的执法者进入议会大厅进行法律表决(新加入机器),使用何种方式能够使得表决过程正常进行,且通过的法律不发生矛盾(对一个值达成一致)。

Paxos过程中不存在拜占庭将军问题(消息不会被篡改)和两将军问题(信道可靠)。

Paxos是首个得到证明并被广泛应用的共识算法,其原理类似两阶段提交算法,进行了泛化和扩展,通过消息传递来逐步消除系统中的不确定状态。

作为后续很多共识算法(如 Raft、ZAB等)的基础,Paxos算法基本思想并不复杂,但最初论文中描述比较难懂,甚至连发表也几经波折。2001年,Leslie Lamport专门发表论文《Paxos Made Simple》进行重新解释,其对Paxos算法的描述如下:

Phase1

(a) A proposer selects a proposal number n and sends a prepare request with number n to a majority of acceptors.

(b) If an acceptor receives a prepare request with number n greater than that of any prepare request to which it has already responded, then it responds to the request with a promise not to accept any more proposals numbered less than n and with the highest-numbered pro-posal (if any) that it has accepted.

Phase 2

(a) If the proposer receives a response to its prepare requests (numbered n) from a majority of acceptors, then it sends an accept request to each of those acceptors for a proposal numbered n with a value v , where v is the value of the highest-numbered proposal among the responses, or is any value if the responses reported no proposals.

(b) If an acceptor receives an accept request for a proposal numbered n, it accepts the proposal unless it has already responded to a prepare request having a number greater than n.

Paxos算法目前在Google的Chubby、MegaStore、Spanner等系统中得到了应用,Hadoop中的ZooKeeper也使用了Paxos算法,但使用的算法是原始Paxos算法的改进算法。通常以Paxos算法为基础,在实现过程中处理实际应用场景中的具体细节,可以得到一个Paxos改进算法。

3、Paxos算法原理

Paxos算法的基本思路类似两阶段提交:多个提案者先要争取到提案的权利(得到大多数接受者的支持);成功的提案者发送提案给所有人进行确认,得到大部分人确认的提案成为批准的结案。

Paxos协议有三种角色:Proposer(提议者),Acceptor(决策者),Learner(决策学习者)。

Paxos 是一个两阶段的通信协议,Paxos算法的基本流程如下:

第一阶段Prepare:
A、Proposer生成一个全局唯一的提案编号N,然后向所有Acceptor发送编号为N的Prepare请求。

B、如果一个Acceptor收到一个编号为N的Prepare请求,且N大于本Acceptor已经响应过的所有Prepare请求的编号,那么本Acceptor就会将其已经接受过的编号最大的提案(如果有的话)作为响应反馈给Proposer,同时本Acceptor承诺不再接受任何编号小于N的提案。

第二阶段 Accept
A、如果Proposer收到半数以上Acceptor对其发出的编号为N的Prepare请求的响应,那么Proposer就会发送一个针对[N,V]提案的Accept请求给半数以上的Acceptor。V就是收到的响应中编号最大的提案的value,如果响应中不包含任何提案,那么V由Proposer自己决定。

B、如果Acceptor收到一个针对编号为N的提案的Accept请求,只要该Acceptor没有对编号大于N的Prepare请求做出过响应,Acceptor就接受该提案。

Paxos 并不保证系统总处在一致的状态。但由于每次达成共识至少有超过一半的节点参与,最终整个系统都会获知共识结果。如果提案者在提案过程中出现故障,可以通过超时机制来缓解。

Paxos 能保证在超过一半的节点正常工作时,系统总能以较大概率达成共识。

4、提案ID的生成算法

对于提案ID的选择,《Paxos made simple》中提到的是让所有的Proposer都从不相交的数据集合中进行选择。

Google的Chubby论文中给出提案ID的生成算法如下:假设有n个Proposer,每个编号为ir(0<=ir<n),Proposal编号的任何值s都应该大于它已知的最大值,并且满足:

s %n = ir    =>     s = m*n + ir

Proposer已知的最大值来自两部分:Proposer自己对编号自增后的值和接收到Acceptor的拒绝后所得到的值。

以3个Proposer P1、P2、P3为例,开始m=0,编号分别为0,1,2。

1) P1提交的时候发现了P2已经提交,P2编号为1 >P1的0,因此P1重新计算编号:new P1 = 1*3+1 = 4;

2) P3以编号2提交,发现小于P1的4,因此P3重新编号:new P3 = 1*3+2 = 5。

5、Paxos算法的活锁

如果两个提案者恰好依次提出更新的提案,则导致活锁,系统会永远无法达成共识(实际发生概率很小)。活锁没有产生阻塞,但是一直无法达成一致。

活锁有三种解决方案:

A、在被打回第一阶段再次发起PrepareRequest请求前加入随机等待时间。

B、设置一个超时时间,到达超时时间后,不再接收PrepareRequest请求。

C、在Proposer中选举出一个leader,通过leader统一发出PrepareRequest和AcceptRequest。

6、Paxos算法异常处理

Paxos算法在执行过程中会产生很多的异常情况:Proposer宕机,Acceptor在接收Proposal后宕机,Proposer接收消息后宕机,Acceptor在Accept后宕机,Learner宕机,存储失败等等。

为保证Paxos算法的正确性,Proposer、Aceptor、Learner都需要实现持久存储,以做到Server恢复后仍能正确参与Paxos处理。

Proposer存储已提交的最大proposal编号、决议编号(instance id)。

Acceptor存储已承诺(promise)的最大编号、已接受(Accept)的最大编号和Value、决议编号。

Learner存储已学习过的决议和编号。

7、Paxos算法应用

Paxos算法只有两种情况下服务不可用:一是超过半数的Proposer异常,二是出现活锁。前者可以通过增加Proposer的个数来降低由于Proposer异常影响服务的概率,后者本身发生的概率就极低。

Paxos是分布式系统一致性协议的基础,其它的协议(raft、zab等)都是Paxos协议的改进版本。Paxos侧重理论,实现Paxos非常困难。

微信后台生产级Paxos类库PhxPaxos实现:

GitHub - Tencent/paxosstore: PaxosStore has been deployed in WeChat production for more than two years, providing storage services for the core businesses of WeChat backend. Now PaxosStore is running on thousands of machines, and is able to afford billions of peak TPS.

https://github.com/tencent-wechat/phxpaxos

基于Paxos算法的改进算法的资料集合:

GitHub - dgryski/awesome-consensus: Awesome list for Paxos and friends

三、三军问题

1、三军问题简介

三军问题的描述如下:

1) 1支红军在山谷里扎营,在周围的山坡上驻扎着3支蓝军;

2) 红军比任意1支蓝军都要强大;如果1支蓝军单独作战,红军胜;如果2支或以上蓝军同时进攻,蓝军胜;

3) 三支蓝军需要同步他们的进攻时间;但他们惟一的通信媒介是派通信兵步行进入山谷,在那里他们可能被俘虏,从而将信息丢失;或者为了避免被俘虏,可能在山谷停留很长时间;

4) 每支军队有1个参谋负责提议进攻时间;每支军队也有1个将军批准参谋提出的进攻时间;很明显,1个参谋提出的进攻时间需要获得至少2个将军的批准才有意义;

5) 问题:是否存在一个协议,能够使得蓝军同步他们的进攻时间?

三军问题符合Paxos问题场景,参谋和将军需要遵循一些基本的规则:

1) 参谋以两阶段提交(prepare/commit)的方式来发起提议,在Prepare阶段需要给出一个提议编号;

2) 在Prepare阶段产生冲突,将军以提议编号大小来裁决,提议编号大的参谋胜出;

3) 参谋在Prepare阶段如果收到将军返回的已接受进攻时间,在Commit阶段必须使用返回的进攻时间;

2、两参谋先后提议场景

A、参谋1发起提议,派通信兵带信给3个将军,内容为(编号1);

B、3个将军收到参谋1的提议,由于之前还没有保存任何编号,因此把(编号1)保存下来,避免遗忘;同时让通信兵带信回去,内容为(ok);

C、参谋1收到至少2个将军的回复,再次派通信兵带信给3个将军,内容为(编号1,进攻时间1);

D、3个将军收到参谋1的时间,把(编号1,进攻时间1)保存下来,避免遗忘;同时让通信兵带信回去,内容为(Accepted);

E、参谋1收到至少2个将军的(Accepted)内容,确认进攻时间已经被大家接收;

F、参谋2发起提议,派通信兵带信给3个将军,内容为(编号2);

G、3个将军收到参谋2的提议,由于(编号2)比(编号1)大,因此把(编号2)保存下来,避免遗忘;又由于之前已经接受参谋1的提议,因此让通信兵带信回去,内容为(编号1,进攻时间1);

H、参谋2收到至少2个将军的回复,由于回复中带来了已接受的参谋1的提议内容,参谋2因此不再提出新的进攻时间,接受参谋1提出的时间;

3、两参谋交叉提议场景

1) 参谋1发起提议,派通信兵带信给3个将军,内容为(编号1);

2) 3个将军的情况如下

A、将军1和将军2收到参谋1的提议,将军1和将军2把(编号1)记录下来,如果有其他参谋提出更小的编号,将被拒绝;同时让通信兵带信回去,内容为(ok);

B、负责通知将军3的通信兵被抓,因此将军3没收到参谋1的提议;

3) 参谋2在同一时间也发起了提议,派通信兵带信给3个将军,内容为(编号2);

4) 3个将军的情况如下

A、将军2和将军3收到参谋2的提议,将军2和将军3把(编号2)记录下来,如果有其他参谋提出更小的编号,将被拒绝;同时让通信兵带信回去,内容为(ok);

B、负责通知将军1的通信兵被抓,因此将军1没收到参谋2的提议;

5) 参谋1收到至少2个将军的回复,再次派通信兵带信给有答复的2个将军,内容为(编号1,进攻时间1);

6) 2个将军的情况如下

A、将军1收到了(编号1,进攻时间1),和自己保存的编号相同,因此把(编号1,进攻时间1)保存下来;同时让通信兵带信回去,内容为(Accepted);

B、将军2收到了(编号1,进攻时间1),由于(编号1)小于已经保存的(编号2),因此让通信兵带信回去,内容为(Rejected,编号2);

7) 参谋2收到至少2个将军的回复,再次派通信兵带信给有答复的2个将军,内容为(编号2,进攻时间2);

8) 将军2和将军3收到了(编号2,进攻时间2),和自己保存的编号相同,因此把(编号2,进攻时间2)保存下来,同时让通信兵带信回去,内容为(Accepted);

9) 参谋2收到至少2个将军的(Accepted)内容,确认进攻时间已经被多数派接受;

10) 参谋1只收到了1个将军的(Accepted)内容,同时收到一个(Rejected,编号2);参谋1重新发起提议,派通信兵带信给3个将军,内容为(编号3);

11) 3个将军的情况如下

A、将军1收到参谋1的提议,由于(编号3)大于之前保存的(编号1),因此把(编号3)保存下来;由于将军1已经接受参谋1前一次的提议,因此让通信兵带信回去,内容为(编号1,进攻时间1);

B、将军2收到参谋1的提议,由于(编号3)大于之前保存的(编号2),因此把(编号3)保存下来;由于将军2已经接受参谋2的提议,因此让通信兵带信回去,内容为(编号2,进攻时间2);

C、 负责通知将军3的通信兵被抓,因此将军3没收到参谋1的提议;

12) 参谋1收到了至少2个将军的回复,比较两个回复的编号大小,选择大编号对应的进攻时间作为最新的提议;参谋1再次派通信兵带信给有答复的2个将军,内容为(编号3,进攻时间2);

13) 将军1和将军2收到了(编号3,进攻时间2),和自己保存的编号相同,因此保存(编号3,进攻时间2),同时让通信兵带信回去,内容为(Accepted);

14) 参谋1收到了至少2个将军的(accepted)内容,确认进攻时间已经被多数派接受;

四、Multi-Paxos算法

1、Multi-Paxos算法简介

Paxos是对一个值达成一致,Multi-Paxos是连续多个Paxos instance来对多个值达成一致。Multi-Paxos协议中有一个Leader。Leader是系统中唯一的Proposal,在lease租约周期内所有提案都有相同的ProposalId,可以跳过prepare阶段,议案只有accept过程,一个ProposalId可以对应多个Value,所以称为Multi-Paxos。

Multi-Paxos协议是经典的Paxos协议的简化版本,将原来2-Phase过程简化为了1-Phase,从而加快了提交速度。Multi-Paxos要求在各个Proposer中有唯一的Leader,并由Leader唯一地提交value给各Acceptor进行表决,在系统中仅有一个Leader进行value提交的情况下,Prepare的过程就可以被跳过,而Leader的选举则可以由Paxos Lease来完成。

2、PaxosLease

在Paxos算法中,如果能够选举出一个leader,那么有助于提高投票的成功率。另外leader在多个决议的选举中有很重要的作用(用于得到决议的连续id)。因此,如何通过某种方法得到一个leader就是PaxosLease所说明的。

Master选举的过程如下:从众多的Node中选择一个作为Master,如果该Master 一直 alive则无需选举,如果master crash,则其他的node进行选举下一个master。选择正确性的保证是:任何时刻最多只能有一个master。

逻辑上Master更像一把无形的锁,任何一个节点拿到这个锁,都可成为master,所以本质上Master选举是个分布式锁的问题,但完全靠锁来解决选举问题也是有风险的:如果一个Node拿到锁,然后crash,会导致锁无法释放,即死锁。一种可行的方案是给锁加个时间(Lease),拿到锁的Master只能在Lease有效期内访问锁定的资源,在Lease timeout后,其他Node可以继续竞争锁,从根本上避免了死锁。

Master在拿到锁后,如果一直alive,可以向其他node”续租“锁,从而避免频繁的选举活动。

五、Raft算法

1、Raft算法简介

Paxos 算法的设计并没有考虑到一些优化机制,同时论文中也没有给出太多实现细节,因此后来出现了不少性能更优化的算法和实现,包括Fast Paxos、Multi-Paxos 等。

Raft算法由斯坦福大学的Diego Ongaro和John Ousterhout 2013年在论文《In Search of an Understandable Consensus Algorithm》中提出。Raft算法面向对多个决策达成一致的问题,是对Multi-Paxos的重新简化设计和实现,分解了领导者选举、日志复制和安全方面的考虑,并通过约束减少了不确定性的状态空间。

Raft算法将一致性问题分解为领导选举(leader election)、日志复制(log replication)、安全性(safety)三部分。

2、Raft算法角色

Raft算法包括领导者(Leader)、候选者(Candidate)和跟随者(Follower)三种角色,决策前通过选举一个全局的领导者来简化后续的决策过程。领导者决定日志(log)的提交,日志只能由领导者向跟随者单向复制。

Leader:集群中只有一个处于Leader状态的服务器,负责响应所有客户端的请求。
Follower:刚启动时所有节点为Follower状态,响应Leader的日志同步请求,响应Candidate的请求。
Candidate:Follower状态服务器准备发起新的Leader选举前需要转换到的状态,是Follower和Leader的中间状态。

所有节点初始状态都是Follower角色

超时时间内没有收到Leader的请求则转换为Candidate进行选举

Candidate收到大多数节点的选票则转换为Leader;发现Leader或者收到更高任期的请求则转换为Follower

Leader在收到更高任期的请求后转换为Follower

3、Term(任期)

Term(任期):每个Leader都有自己的任期,任期到期就需要开始新一轮选举,在每个任期内,可以没有leader,但是不能出现大于两个的leader。

Raft算法将整个系统执行时间划分为若干个不同时间间隔长度的Term(任期)构成的序列,以递增的数字来作为Term的编号;每个Term由Election开始,在任期内若干处于Candidate状态的服务器竞争产生新的Leader,如果一个候选人赢得了选举,就会在该任期的剩余时间担任领导人;在某些情况下,选票会被瓜分,有可能没有选出领导人,那么将会开始另一个任期,并且立刻开始下一次选举。Raft算法保证在给定的一个任期最多只有一个领导人。

4、Raft算法的流程

Raft算法典型的过程包括两个主要阶段:

(1)Leader选举

当整个系统启动时,所有服务器都处于Follower状态;如果系统中存在Leader,Leader会周期性的发送心跳(AppendEntries RPC)来告诉其它服务器自己是Leader;如果Follower经过一段时间没有收到任何心跳信息,则可以认为Leader不存在,需要进行Leader选举。
在选举前,Follower增加其Term编号并改变状态为Candidate状态,然后向集群内的其它服务器发出RequestVote RPC,Candidate状态持续到发生下面三个中的任意事件:
A、赢得选举:Candidate接受了大多数服务器的投票,成为Leader,然后向其它服务器发送心跳(AppendEntries RPC)告诉其它服务器。
B、其它服务器获得选举:Candidate在等待的过程中接收到自称为Leader的服务器发送来的心跳(AppendEntries RPC),如果RPC的Term编号大于等于Candidate自身的Term编号,则Candidate承认Leader,自身状态变成Follower;否则拒绝承认Leader,状态依然为Candidate。
C、一段时间过后,如果没有新的Leader产生,则Term递增,重新发起选举(因为有可能同时有多个Follower转为Candidate状态,导致分流,都没有获得多数票)。

(2)日志复制

Log复制主要作用是用于保证节点的一致性,所做的操作是为了保证一致性与高可用性;当Leader选举出来后便开始负责客户端的请求,所有请求都必须先经过Leader处理。Leader接受到客户端请求后,将其追加到Log的尾部,然后向集群内其它服务器发出AppendEntries RPC,引发其它服务器复制新请求的操作,当大多数服务器复制完后,Leader将请求应用到内部状态机,并将执行结果返回给客户端。

每个Log中的项目包含2个内容:操作命令本身和Term编号;还有一个全局的Log Index来指示Log项目在Log中的顺序编号。当大多数服务器在Log中存储了Log项目,则可认为Log项目是可以提交的,上图中的Log Index为7前的Log项目都可以提交。

(3)安全性

安全性是用来保证每个节点都执行相同序列的安全机制,如当某个Follower在当前Leader提交命令时不可用,稍后可能该Follower又会被选举为Leader,这时新Leader可能会用新的Log覆盖先前已提交的Log,这就是导致节点执行不同序列;安全性就是用于保证选举出来的Leader一定包含先前已经提交Log的机制。
为了达到安全性,Raft算法增加了两个约束条件:
A、要求只有其Log包含了所有已经提交的操作命令的那些服务器才有权被选为Leader。
B、对于新Leader来说,只有它自己已经提交过当前Term的操作命令才被认为是真正的提交。

5、Raft算法的应用

Raft算法已经被多种语言实现,如Go语言、C++、Python等主流开发语言。

Raft算法原理动画演示:

Raft

Raft共识算法资源如下:

Raft Consensus Algorithm

Raft算法的Go语言实现:

GitHub - goraft/raft: UNMAINTAINED: A Go implementation of the Raft distributed consensus protocol.

区块链快速入门(三)——CFT(非拜占庭容错)共识算法相关推荐

  1. 非拜占庭容错共识算法

    目录 一.Raft 算法 二.KRaft 算法 参考文献 一.Raft 算法 三种角色不同的节点:跟随者(follower).候选者(candidate).领导者(leader) 两个阶段: 1.领导 ...

  2. 区块链快速入门(五)——区块链技术的演化

    一.区块链技术的发展 比特币区块链面向转账场景,支持简单的脚本计算.如果引入更多复杂的计算逻辑,将能支持更多应用场景,即智能合约(Smart Contract).智能合约可以提供除了货币交易功能外更灵 ...

  3. 拜占庭容错共识算法介绍

    1. 前言 区块链的共识算法中,除了常见的工作量证明(PoW,Proof of Work)和权益证明(PoS,Proof of Stake)外,还有拜占庭容错(Byzantine Fault Tole ...

  4. 区块链快速入门(四)——BFT(拜占庭容错)共识算法

    一.BFT简介 1.拜占庭将军问题简介 拜占庭将军问题(Byzantine Generals Problem)是Leslie Lamport(2013年的图灵奖得主)用来为描述分布式系统一致性问题(D ...

  5. 区块链快速入门(六)——区块链密码学与安全相关技术

    一.区块链密码安全技术简介 区块链和分布式账本中大量使用了密码学和安全技术的最新成果,特别是身份认证和隐私保护相关技术.区块链使用了包括Hash 算法与摘要.加密算法.数字签名和证书.PKI体系.Me ...

  6. 【区块链】(三)之拜占庭将军问题

    区块链的最早应用是比特币,而比特币的概念最初由中本聪在2009年提出,根据中本聪的思路设计发布的开源软件以及建构其上的P2P网络构成比特币系统.区块链发源于比特币技术.在中本聪的白皮书里," ...

  7. 区块链快速入门(一)——区块链简介

    一.区块链简介 1.分布式记帐技术简介 2009年1月,基于区块链结构的比特币网络悄然问世,其融合了现代密码学和分布式网络技术等重要成果.在比特币出现后的数年里,在纯分布式场景下比特币网络稳定支持了海 ...

  8. 区块链快速入门(八)——以太坊

    一.以太坊简介 以太坊(Ethereum)项目的最初目标是打造一个运行智能合约的平台(Platform for Smart Contract),支持图灵完备的应用,按照智能合约的约定逻辑自动执行,理想 ...

  9. 以太坊区块链快速入门

    一.基本概念 1.什么是以太坊? 以太坊是由社区驱动的技术,为加密货币以太币(ETH)和成千上万的去中心化应用程序提供动力. 属于所有人的银行服务 现实生活中不是每个人都能使用金融服务.但是只要您有网 ...

最新文章

  1. gis怎么改鼠标滚轮缩放_ArcGIS鼠标滚轮方向之代码篇
  2. 【Google Play】APK 扩展包 ( 2021年09月02日最新处理方案 | 扩展文件名格式 | 扩展文件下载存放地址 )
  3. mysql创建一个表用来快速查询表_mysql数据库的创建表格、查询(多表查询)
  4. SQL中合并多行记录的方法总汇
  5. Python库:wordcloud库介绍、政府工作报告词云、自定义背景词云
  6. Sentinel降级_RT_分布式系统集群限流_线程数隔离_削峰填谷_流量控制_速率控制_服务熔断_服务降级---微服务升级_SpringCloud Alibaba工作笔记0039
  7. python+selenium自动化测试-Windows环境搭建
  8. 使用FastDFS在CentOS上搭建简易分布式文件系统
  9. 今天看了《一声叹息》
  10. 贪心算法--经典问题(java实现)
  11. 网络攻防技术——端口扫描
  12. 移位密码算法-c语言,凯瑟移位密码(C语言).doc
  13. 第七章——ICell的常用属性与方法
  14. 数字图像处理(16): 图像颜色空间转换 和 OpenCV图像灰度化处理
  15. 3、线性表的顺序存储结构(顺序表)
  16. R COOKBOOK 学习笔记
  17. #(二)、(三)规律用于预测是有前提要求条件的
  18. (01)ORB-SLAM2源码无死角解析-(64) BA优化(g2o)→闭环线程:Optimizer::OptimizeSim3→Sim3变换优化
  19. Springboot养老院管理系统
  20. Linux中创建python项目的虚拟环境virtual enviroment

热门文章

  1. mac QQ不加载历史记录
  2. PetShop 4.0
  3. explorer.exe 遇到问题需要关闭。我们对此引起的不便表示抱歉
  4. 【CALIPSO】星载激光雷达CALIPSO产品数据下载教程
  5. linux服务器怎么装windows系统,linux云服务器装windows系统
  6. CS和CSS的区别【吐血整理,疯狂推荐】
  7. 史上最全的Android性能优化面试题集锦
  8. 系统架构设计笔记(8)——数据库设计
  9. 良匠-手把手教你写NFT抢购软(二)
  10. 如何实现数据可视化分析?有这个解决方案就够了