JAVA后端知识点碎片化整理 基础篇(十五) 认识分布式的一些小问题

目录

(1)从分布式系统部署的角度考虑,分哪几层

(二)如何解决业务层的数据访问问题?(dao数据访问层 Serevice业务层)

(三)为了解决数据库服务器的负担,如何做数据的分布? (分数据库   分表)

(四)为什么要使用ORM

(五)CAP原理与BASE思想

(六)最终一致性、强一致性和弱一致性

(七)分布式系统设计你会考虑哪些策略

(8)最常见的数据分布方式是什么?

(九)浅谈一致性hash算法

(十)Paxos是什么?(分布式数据一致性协议)

(十一)Lease机制(判断节点状态,提高系统可用性)

(十二)如何理解选举算法

(十三)什么事著名的拜占庭将军问题。拜占庭连接(在知道自己存在可靠队友的同时,让可靠的队友达成一致性协议,而并不是去找出不可靠的点)


上面一节(十三)讲了什么是分布式,这一节主要讲分布式中一些问题

(1)从分布式系统部署的角度考虑,分哪几层

一、通信,这个任务包括设计适当的网络间进程通信机制,一些样例机制:远程过程调用(RPC)、远程对象调用(ROI)、面向流的通信和面向消息的通信,底层通信机制的实现。

二、进程、设计的问题包括:在客户端服务端的进程和线程管理、代码迁移、软件和代理对象的设计等、

三、命名。为了以透明盒可扩展的方式来定位资源的进程,设计一个易于的名字分配标识符和地址是必不可少的。在移动系统中进行命名带来了进一步挑战,这是由于名字是不能容易绑定到任何静态地地理拓扑上。

四、同步。在进程之间的同步与合作机制是必不可少的。互斥是同步的典型例子,但还需要许多其他形式的同步,如领导者选举。此外,物理时钟的同步,能对所经过时间的本质进行刻画的逻辑时钟,以及全局记录算法,这些都需要不同形式的同步机制。

五、数据存储和访问。数据存储的方式以及所隐含的通过网络对数据的快速和可扩展的访问机制,是影响性能的重要因素。文件系统设计的一些传统问题在分布式系统环境下需要重新考虑。

六、一致性与副本。为了避免出现瓶颈,提供数据的快速访问以及提供可扩展性,对数据对象进行复制是非常必要的。这会导致出现管理副本的问题,以及在分布式环境下维护副本/缓存之间一致性问题。一个简单的例子就是如何取舍数据访问粒度。(大小)

七、容错。容错机制就是在任何连接、节点、进程错误的情况下保持操作正确、高效、弹性过程、可靠的通信、分布式提交、检查点和恢复、协商和共识、故障检测等一定容错的机制。

八、安全。分布式系统的安全包括安全通道、控制访问、密钥管理-生成、分发以及认证。

九、API透明性,易于使用的通信等专业化服务的api对分布式系统服务被非专业用户广泛接受是很重要的。透明性用来隐藏实现策略,避免让使用者卷进细节,可以按照下面方式分类。类似于dubbo中服务暴露的过程。

十、可扩展性和模块化。算法、数据(对象)以及服务必须尽可能地分布。诸如复制、缓存、缓存管理和异步处理等技术能够增加可扩展性。

(二)如何解决业务层的数据访问问题?(dao数据访问层 Serevice业务层)

传统数据的访问是面向过程的,分为connection、statement、通过statement的参数sql语句来访问数据库,返回数据进行处理。

传统数据库访问模式缺点显而易见 1、各个模块间的耦合太紧,statement要依赖connection,connection还依赖数据库的种类。2、如果我改变的数据库种类,或者提供不同的数据库服务,那么我就要提供大量的复制代码。

使用dao与service分层后,dao叫数据访问层,全称data access object属于一种比较底层基础的操作,具体到对于某个表,某个实体的增删改查。     service被称为服务层,被称之为服务,相比之下是一个比较高层次的结构,相当于把几种操作封装起来,service层要使用接口来定义的几点好处: 由ServiceImpl继承service接口进行业务操作。1、在java接口是多继承的,而类是单继承的,如果你需要一个实现类实现多个Service你用接口可以实现,用接口实现没那么灵活。2、要提供不同的数据库服务时,我们只需要面对接口用不同的实现类即可,而不是重复的定义类。3、编程规范问题,接口化的编程就是将实现封装起来,然后调用者只关心接口不关心实现。(dao只是封装了一个个数据库的调用方法,service只是在dao层上进行了一层包装实现相对高级的操作,最后将这些操作再ServiceImpl类中实现)(简而言之:好处就是如果 业务层需要修改无需改变dao层的代码,数据库访问有了新需求也不需要在service去改动)

(三)为了解决数据库服务器的负担,如何做数据的分布? (分数据库   分表)

为了解决数据库服务器的负担,我们可能希望吧数据分布存储在多个服务器上,我设想的数据库方案是,各服务器上的数据库在结构上一模一样,而在表中的数据存储到不同的服务器上,这样数据访问层在查数据的时候根据某一种设计好的计算在几个数据库机器中实现路由功能,找到数据存储的机器。

横向扩展:为了软解大量的并发访问,处理网站实现分布式负载均衡,远远不够,到了数据业务层,数据访问层,如果还是传统的数据存储结构,那么一台服务器很容易崩溃。一方面采用优秀的代码框架,进行代码的优化,采用优秀的数据缓存技术如memecached来分担主数据库的压力。另一方面举例我们可以用3台服务器共同构建一个数据集群,主从服务器利用Mysql二进制日志文件实现数据同步,二进制由主服务器产生,从服务器响应同步数据库。

(四)为什么要使用ORM

当我们实现一个应用程序的时候(不使用O/R Mapping),我们可能会写特别多的数据访问代码,从数据保存、删除、读取对象信息,而这些代码都是复制的。而使用ORM会大大减少重复性代码。对象关系映射ORM,主要实现程序对象到关系型数据的映射。

优点:1、提高开发效率,降低开发成本、2、使开发更加对象化、3、可移植、4、可以很方便地引入数据缓存之类的附加功能。

缺点:1、自动化进行关系数据库的映射需要消耗系统性能。其实这里的性能消耗还好,一般说是可以忽略的。2、在处理多表联查,where条件复杂之类的查询ORM会非常复杂。

(五)CAP原理与BASE思想

分布式领域的CAP理论就是指在一个分布式系统中,不可能同时满足Consistency(一致性)、Availability(可用性)、Partition tolerance(分区容错性)这三个基本需求,最多只能满足其中两项。一般而言对于分布式系统而言,分区容错性是一个最基本的要求,因为分布式系统中的组件必然需要部署到不通的节点,必然出现子网络,在分布式系统中,网络问题是必定会出现的异常。因此分布式系统只能在C(一致性)和A(可用性)之间做一个权衡。

1、一致性:指数据在多个副本之间能否保持一致的特性,当执行数据更新操作后,仍然保证系统数据处于一致的状态。

2、可用性:系统提供的服务必须一直处于可用的状态,对于用户的每一个操作请求总是能够在“有限的时间内”返回结果,这个有限时间是系统设计之初就订好的系统运行标准。能给出一个正常的响应结果。

3、分区容错性:分布式系统在遇到任何网络分区故障的时候,仍然需要保证对外提供一致性和可用性的服务,除非整个网络都发生了故障。组成分布式系统的每个节点的加入与退出都可以看成一个特殊的网络分区。

BASE理论是指:Basically Available(基本可用)、Soft-state(软状态)、Eventual Consistency(最终一致性)是基于CAP定理演化而来,是对CAP中一致性和可用性权衡的结果。

核心思想:即时无法做到强一致性,但每个业务根据自身特点,采用适当的方式使系统达到最终一致性。

1、基本可用:指分布式系统在出现故障的时候,允许损失部分可用性,保证核心可用,但不等价于不可用。比如:搜索引擎0.55秒返回查询结果,故障出现,导致2秒响应查询结果。访问过大部分用户提供降级服务。

2、软状态:软状态是指允许系统中存在中间状态,并且该中间状态不会影响系统整体的可用性,即允许系统在不同节点间副本同步的时候存在延时。

3、最终一致性:系统中所有的数据经过一定的时间后,最终能够达到一致的状态,不需要实时保证系统数据的强一致性。最终一致性是弱一致性的一种特殊情况。

(六)最终一致性、强一致性和弱一致性

对于一致性,可以分为客户端和服务端两个不同的视角。从客户端来看,多进程并发访问同时,更新过的数据在不同进程如何获取不同的策略,决定了不同的一致性。对于关系型数据库,要求更新过的数据能被后序的访问都能看到,这是强一致性。如果能容忍后续的部分或者全部访问不到,则是弱一致性。如果经过一段时间后要求能访问到更新后的数据,则是最终一致性。

强一致性:读操作可以立即读到提交的更新操作。

弱一致性:提交的更新操作,不一定立即会被读操作读到,此种情况存在一个不一致窗口,指的是读操作可以读到最新值的一段时间。

最终一致性:是弱一致性的特例,事务更新一份数据,最终一致性保证在没有其他事务更新同样的值的话,最终所有的事务都回到之前事务更新的最新值。如果没有错误发生,不一致窗口大小依赖于通信延时,系统负载。

最终一致性根据更新数据后各进程访问到数据的时间和方式不同,又可以区分为:

因果一致性。如果进程A通知进程B它已经更新一个数据项,那么进程B的后序访问将返回更新后的值,且一次写入将保证取代前一次写入,与进程A无因果关系的进程C的访问遵守一般的一致性规则。

单调一致性:如果进程已经读到一个值,那么后序就不会读到更早的值。(只会读到这个值之后修改的值)

会话一致性:保证客户端和服务器交互的会话过程中,读操作可以读到更新操作后的最新纸。

(七)分布式系统设计你会考虑哪些策略

分布式系统中说白了就是由很多机器组成的集群,彼此依靠之间的网络通信,当担的角色可能不同,共同完成同一个事情的系统,如果按“实体”来划分的话,就是如下(1)节点-系统中按照协议完成计算工作的一个逻辑实体,可能是执行某些工作的进程或机器。2、网络-系统的网络传输通道,用来彼此通信。通信是具有方向性的。3、存储-系统中持久化数据的数据库或者文件存储。

基于这些,分布式系统设计策略。

(1)重试机制:一般情况下,写一段网络交互代码,发起RPC和http,都会遇到请求超时而失败的情况,此种模式可以防止网络暂时的都动,一般停顿时间很多,并重试多次后,请求成功!但不能对防止对端不能连接。

(3)心跳机制,以固定频率向其他节点汇报当前节点的状态的方式,收到心跳,一般可以认为一个节点和现在网络拓扑良好的,当然心跳汇报时,一般也会携带一些附加状态,元数据信息,以便管理。(收到心跳不是万能的,收到心跳可以确认OK,但是收到心跳却不能确认节点不存在或者挂掉,因为可能是网络原因导致链路不同但是节点依旧可以工作。)

3、副本

副本值的是针对一份数据的多份冗余拷贝,在不同节点上持久化同一份数据,当某一个节点的数据丢失时,可以从副本上获取数据,数据副本是分布式系统解决异常数据的唯一途径。当然对于多份副本你的写入会带来一致性和可用性问题,比如规定副本数为3,同步写3分,会带来3次IO的性能问题。还是同步写一份,然后异步写两份,会带来一致性问题。

4、去中心化/无中心化

系统模型的设计:中心节点,例如Mysql的单主双从,MongDB Master,HDFS NameNode,有一个或者几个节点充单整个系统的核心元数据及节点管理工作,其他节点都和中心节点进行交互。这种方式的好处显而易见,数据的管理高度统一集中在一个地方,容易聚合,相当于领导者一样,其他人服从就好,简单可行。

缺点就是高度集中,容易形成性能瓶颈,如果出现异常,就会群龙无首。

无中心化的设计,例如cassandra、zookeeper,系统中不存在一个领导者,节点之间彼此通信需要合作完成任务,好处在于如果出现异常,不影响整体系统,局部不可用,缺点就是比较协议复杂,而且需要各个节点间同步信息。

(8)最常见的数据分布方式是什么?

一、hash方式,hash方式其方法按照数据某一特征计算hash值,并将hash值与机器中的机器建立映射关系,从而将不同hash值的数据分配到不同的机器上。所谓数据特征是key-value系统的key,也可以是其他应用业务逻辑相关的值。只要hash散列性比较好,数据就能均匀分发到不同机器中,同时需要管理元信息很少,只需要知道hash函数的模,通常这个模式机器总数。

二、按照数据范围分布:按照数据分布式另一个常见的数据分布式,将数据按特征值的值阈范围划分为不同的区间,使得集群中每台服务器处理不同区间的数据。例如节点1【1,33】,节点2【33,90】,节点4【91,100】

三、按数据量分布:数据量分布数据与具体的数据特征无关,而是将数据视位一个顺序增长的文件,并将这个文件按某一较为固定的大小划分为数据块,不同的数据块分布到不同的服务器上。

四、(哈希环)一致性哈希的基本方式是使用一个哈希函数计算数据或者数据特征的哈希值,令该哈希函数的输出值域为一个封闭的环。即哈希函数输出的最大值是最小值的前序。将节点随机分不到这个环上,每个节点负责处理从自己开始顺时针指向一个节点的全部hash值域。

(九)浅谈一致性hash算法

关于一致性Hash算法,先构造一个长度为2的32次方的整数环(这个环被称之为一致性Hsah环),根据节点名称的Hash值将服务器节点放置在这个Hash环上,然后根据数据的Key值计算得到其Hash值(其分布也为0-2的32次方),接着在Hash环上顺时针查找距离这个key的Hash值最近的服务器接到哪,完成Key到服务器的映射查找。

(十)Paxos是什么?(分布式数据一致性协议)

Paxos是一个用于实现可容错的分布式系统的算法,主要用于保证分布式集群中多备份系统之间的操作的数据的一致性。这个集群中的每台机器相当于完全一致,从而其互相之间可以互为备份,从而使得系统能够容忍一定数量的机器出问题

paxos的算法中的几个角色:

proposer提案者:在实际应用中可以理解为request的接受者,该角色接受的用户请求,并向集群提出提案要求执行该request。

acceptor接受者:接受到来自其他的proposer的提案,并按照一定的逻辑决定是否接受当前天,也就是是否接受request请求并且准备执行request的角色。

learner学习者:能够从其他节点获取某个达成共识的提案。

paxos解决的问题:假设n个独立进程能够分别提出一个值,那么paxos算法用于保证这些进程提出的其中之一能够被正确处理,paxox算法的安全性前提条件为:1、只有被提议的值,才可以被选中,也就是说不可能凭空出现的值出现。2、一个只有一个值被选中3、进程不会learn到一个没有被选中的值。

(十一)Lease机制(判断节点状态,提高系统可用性)

问题出现在于心跳机制可能不能完全满足分布式系统的需要:如果3副本A B C通过中心节点M来管理,其中A为主副本。未接触过分布式的直观的处理方法在每个副本与中心节点M之间维护一个心跳,通过心跳是否存在而判断对方是否依旧存活。

心跳方法其实根本无法解决分布式下这个问题。考虑如下问题:M在某时刻未能预期收到主节点A的心跳,M认为A已经异常,于是从B、C中选取一个作为主节点。但是实际上并未异常,这时候由于网络瞬时阻塞,或是M本身出现异常使A者消息暂时未收到。这时候出现了双主问题,从节点C可能同时从这个主节点同步数据,这会引发很严重的错误数据。(要么容忍双主节点的情况,要么用lease机制)

基于lease的cache,客户端节点读取元数据的流程。

lease机制定义:lease是由颁发者授予的在某一有效期限内的承诺。被颁发者在承诺期限可放心使用承诺内容,期限过了以后,被办法这一定不能再行使用承诺。

1、颁发者一旦发出lease,无论接收方是否收到,也无论后序接收方处于何种状态,只要lease不过期,颁发者一定严守承诺,另一方面,接收方在lease的有效期内可以使用颁发者的承诺,一旦lease过期,接收方一定不能继续使用办法这的承诺。

2、lease机器具有很高的容错机制,首先通过引入有效期,lease机制能否非常好的容错网络异常,再者,lease机制能较好的容错节点宕机,最后lease不依赖存储。

3、Lease机制依赖于有效期,这就要求颁发者和接受者时钟是同步的。对于这种时钟不同步,实践中通常做法是将办法这的有效期设置比接受者略大,只需要大过时钟误差就可以避免对lease的有效性影响。

需要在这个有效期失效期,向颁发者申请新的时期。

举例:在中心密钥服务器维护者全局的密钥生成和发放,所有需要使用密钥的外围系统向密钥服务器申请密钥,用于本系统的加解密工作。处于性能和可用性考虑,不能每个请求都向中心服务器申请,因此密钥通常被缓存在本地系统中,那么如何需要修改中心系统密钥时,如何保证所有使用该密钥的本地系统都立刻丢弃过期的密钥,而立刻向中心密钥服务器重新申请最新的密钥,并保持所有系统中密钥的一致性?

这种场景非常适合使用 lease 机制来解决,中心服务器发放密钥的时候,同时发放一个 lease 承诺在一定时间内不修改该密钥。本地系统获取密钥时,同时根据 lease 的约定只在其有效期内使用密钥,lease 一旦过期立刻重新申请密钥。当变更密钥时,在所有已颁发的 lease 全部过期前修改不能生效,并且在变更密钥生效期间不能颁发新的 lease,避免形成活锁(永远等不到所有 lease 失效)。

(十二)如何理解选举算法

在一个分布式系统中,因为各种意外的因素,有的服务器可能会崩溃或变得不可靠,它就不能和其他服务器达成一致的状态。因而这样就需要一种Consensus协议,来确保服务器的容错性,也就是说即使系统中有一两个服务器节点crash,也不会影响其处理过程。为了让容错方式达成一致,我们不可能要求所有的服务器节点都达成一致,只要半数的节点达成一致就可以了。

以Raft算法举例:所有的一致性算法都会涉及状态机,而状态机保证系统从一个一致的状态开始,以相同的顺序执行一些指令最终会达到另一个一致的状态。

Leader 负责接收客户端请求,将日志复制到其他节点并告知其他节点合适应用这些日志是安全的。

Candidate 用于选举Leader的一种角色

Follower 负责响应来自Leader或者Candidate的请求。

首先所有节点初始状态都是Follower角色,每一个节点设置的超时时间检测不同,超时没有收到Leader的请求转换为Candidate进行选举,Candidate收到大多数节点的选票则转换为Leader,发现Leader或者收到更高任期的请求则转换成Follower,Leader再收到更高任期的请求后转换为Follower。

细节:

Leader Election

1、timeout一定要是随机性的,如果是一起的那么大家一起竞选真的太麻烦了。。。。

2、timeout的选取范围,必须远大于rpc请求的平均时间,不然可能很久都选不出主,通常RPC请求在ms级别,所以可设置为150~300ms。

3、选主请求发送结束之后,由于有可能在选主请求(RequestVote)的返回或者别的节点的选主请求中发现较大的term(任期),这个选主就会被重置成Follower,这时即使投票超过半数也应该放弃成为leader,因为当前选主请求的term已经过时,成为Leader可能导致在新的term中出现两个Leader

4、每次发现较大的term时,自身重置为Follower,更新Term的同时,需要重置votedFor,以便在新的term中可以参与投票。

5、每次选主成功背后,发送一条日志复制请求,让Leader提交之前应该提交的日志,从而让Leader的状态机为最新,这样为读请求提供Linearializablity,不会返回state data。(以新的term的日志为主)

总结:一个工程级别的分布式一致性协议实现并不容易,要注意的细节很多,不仅要保证正确地实现协议,还要考虑优化点,在优化整个系统的性能时保证系统的正确性。分布式系统尤其像分布式一致性协议这样的复杂系统需要大量的测试来保证系统的正确性,算法本身的简洁描述忽略很多实现的细节。      Raft相对于Paxos/Multi-Paxos会明白Raft为了简单做的trace-off,在保证safety的性质的前提下,通过增加以下三个条件来简化Leader恢复或者说View Change过程中的状态恢复,保证日志从Leader上单向流动到Follower,这个过程其实也是最关键和复杂的步骤。

比如:primary其实是可以正常工作并对提供对client服务的,primary对其他secondary之间也是Ok的,但master和primiary之间连接突然闪断了,这是master收到primary的心跳,认为primary挂了,然后重新选择primary,这个这个系统就出现了两个primiary,也就双主问题。其实最基本的原因还是单纯用心跳机制master是无法确定节点状态的。   引用primary机制,master向primary颁发lease,承诺“这个lease有效期内我不会重新选取primiary”,其他都是secondary。master用于管理谁是primiary,假设我们用常规的心跳机制就会出现转态问题。

(十三)什么事著名的拜占庭将军问题。拜占庭连接(在知道自己存在可靠队友的同时,让可靠的队友达成一致性协议,而并不是去找出不可靠的点)

之前看到的漫画讲解如何处理拜占庭将问题,生动形象raft。https://blog.csdn.net/bjweimengshu/article/details/80222416

这是一个共识问题,说明如下:(3t+1)个总体,可以容忍t个不可靠队友

拜占庭将军问题点对点通信中的基本问题,含义是存在消息丢失的不可靠信道上试图通过消息传递的方式达到一致性是不可能的。拜占庭假设是对现实世界的模型化,由于硬件错误,网络拥塞或者断开以及遭到恶意攻击,计算机网络出现一些不可预测行为,拜占庭协议必须处理这些问题,并且协议还要满足所解决问题的规范要求。

什么事拜占庭将军问题,拜占庭一座富饶的城堡,他的周围有是个部落,都想要攻占城堡,但是只有其中五个以上的部落同时进攻才能取得胜利。部落之间的通信只能通过互派信使传递信息,那么每个部落都可以在任意时间给任意其他信件的部落发送信息,信息类似于“我将在明天早上六点进攻,你同意么”,收到信件的人盖上自己的章后,然后把信息拷贝,发送给九个邻居。如果每个部落发送9封信件,系统中一共有九封信件在传输,每个部落收到收到9封信,可能每一封都有一个不同的攻击事件,另外一些部落统一超过一个攻击时间故意背叛。此时整个系统变得混乱。

1。叛徒数大于或等于1/3,拜占庭问题不可解。

情况一:A,B,C三个司令,C是叛徒。A发消息给B,C“进攻”,C发消息给B“撤退”(因为是叛徒)。B收到两个矛盾的命令,无法作出决策。

情况二:A,B,C三个司令,A是叛徒。A发消息给B“进攻”,发消息给C“撤退”(因为是叛徒)。B。C收到不同的命令。

2.用口头信息,叛徒数少于1/3,拜占庭问题可解.

口头信息三条件
      传送正确
      接收者知道是谁发的
      沉默(不发信息)可被检测
什么叫可解?
     IC1:所有忠诚副官(B.C,指消息接受者)遵循同一命令。 
     IC2:若司令(A,消息)是忠诚的,所有忠诚副官遵循其命令

可以证明,多项式复杂性算法OM(m)可以解决拜占庭问题(L Lamport, R Shostak, and M Pease. The Byzantine generals problem. ACM Transactions on Programming Languages and Systems, 1982, 4(3))

如果记容忍t个叛国者的协议叫t弹性协议(即,在t弹性协议存在的情况下,可以胜利),则:

Ø    当n=3时,不存在1弹性协议

Ø    当n>=1,不存在t>=n/3t弹性协议

n如果记容忍t个叛国者的协议叫t弹性协议,则:

Ø    当n=3时,不存在1弹性协议

Ø    当n>=1,不存在t>=n/3t弹性协议

    当n>=1,不存在t>=n/3t弹性协议

nOM(m),m>0

               发送者发送他的值给每个接收者

               发送者发送他的值给每个接收者

        如果第i个接收者获得的值是vi, 接收者i执行算法OM(m-1)发送vin-2个其他的接收者

          i个接收者会收到从不同n-1人发来的n-1个值, 取多数认同的值就可以

nOM(m),m>0

 

               发送者发送他的值给每个接收者

               发送者发送他的值给每个接收者

        如果第i个接收者获得的值是vi, 接收者i执行算法OM(m-1)发送vin-2个其他的接收者

          i个接收者会收到从不同n-1人发来的n-1个值, 取多数认同的值就可以

3。用书写信息,至少两个忠诚,拜占庭问题可解
在口头信息的基础上, 书写信息又增加了两个条件
     忠诚司令的签名不能伪造,内容修改可检测
     任何人都可以识别司令的签名, 叛徒可以伪造叛徒司令的签名
SM(m)算法
    接收者信息收到后,签上自己的名字,再送给别人
    用书写信息, 只要有两个忠诚的司令, 拜占庭问题就可解

--------

可以证明,算法SM(m)可以解决m个叛徒的拜占庭问题。

SM(1):如A是叛徒。A给B发“进攻”,给C发“撤退”命令(都被A签名)。B比较从C发来的命令(“撤退”,该命令被C签名了)知A是叛徒。C比较从B发来的命令(“进攻”,该命令由B签名),知A是叛徒。

情况2:B是叛徒。A给B,C发“进攻”命令

猜你喜欢

转载自blog.csdn.net/weixin_39893439/article/details/81148879