-
2020-07-17 10:05:03
在建模过程中遇到了这个问题,但在网上没有找到任何解决问题的方法,所以自己想了三天终于大彻大悟,特此分享一下。
当然这类问题最好不要用这种枚举的方法,时间复杂度为n的m次方,不能称得上是个好的算法,要具体问题具体分析。
这个问题就是在数组少时可以通过手写来解决
例如我们要排列的是a=[15 32 56 74]、b=[469 85 26 49 ] 可以通过:
a=[15 32 56 74] b=[469 85 26 49 ] m=1; for i=a for j=b temp{m}=[i,j]; m=m+1; end end
但 当数组比较多或者是n的时候就无法用手写的方式来解决了。
整体的思路就是循环嵌套,写一个程序自己调用自己,一直调用到最后一个数组,为了能够让每一个数组都调用一个数,必须产生一个全局变量来加以限制。为了保存每一个产生的组合,要定义一个全局变量的元胞数组,同样让每一次的存储地址加一也要声明全局变量的地址。matlab实现功能的主程序为:
clc clear all dd=[8,12,5,3; 1 ,2, 3 ,4; 17 85 65 66];%这里每一行作为一个数组;便于书写,如果长度不一,可以直接复制给下面的aaa元胞数组 nx=size(dd,1); ny=size(dd,2); for i=1:nx [aaa{i} ]=sort_change( dd(i,:) ) end xxx=1 ; %行数 yyy=[]; global nnn; %组合序号 nnn=1; global zuhe; %存放所有组合 zuhe={}; temp=zeros(1,nx); for i=aaa{xxx} yyy(xxx)=i ; diedai( aaa, xxx, nx, yyy); end
function []=diedai(aaa,xxx,nx,yyy) global zuhe global nnn xxx=xxx+1;; for i=aaa{xxx} yyy(xxx)=i; if length(yyy)-length(unique(yyy))>0 continue; end if xxx<nx diedai(aaa,xxx,nx,yyy); end if xxx==nx zuhe{nnn}=yyy; nnn=nnn+1; end end end
function [ shu]=sort_change(temp) [a,b]=sort(temp) ; shu=a; % shunxu=b(1:3) end
得到的元胞数组结果:
你可能会问了,4×4×4 ,不应该是64个组合吗,我这里把组合中有重复的组合进行了删除,在递归调用的过程之中加入了判断,在最优化问题中通常会出现不能产生重复的要求。还有为什么组合组合是每一个数组从小大大抽取的,这是在解决最优化问题时找出最小组合写的,删减了不少附带的条件,来专门解释排列组合问题。当然要是需要不删除重复数字的全排列也可以自行更改条件。
少熬夜,追光者。
更多相关内容 -
MySQL 8 复制(七)——组复制基本原理
2019-07-09 16:41:352. 组复制 二、组复制使用场景 三、组复制相关服务 1. 故障检测 2. 组成员服务 3. 容错 四、组复制技术细节 1. 组复制插件体系结构 2. 复制组 3. 数据操作语言(Data Manipulation Language,DML) 4. ...目录
3. 数据操作语言(Data Manipulation Language,DML)
4. 数据定义语句(Data Definition Language,DDL )
MySQL Group Replication(MGR)是MySQL 5.7.17版本引入的一个服务器插件,可用于创建高可用、可扩展、容错的复制拓扑结构。组复制可以在单主模式下操作,其中只有一个服务器接受更新,这个单主是系统自动选举出来的。对于高级用户,也可以部署为多主模式,其中所有服务器都可以接受更新。内置的组成员服务可以在任何给定的时间点保持组的视图一致并可供所有服务器使用。当服务器加入或离开组时,视图也会相应更新。当服务器宕机,故障检测机制会检测到此情况并通知组其视图已更改。这些都是自动进行的。
创建容错系统最常见的方法是组件冗余。换句话说,一个组件被移除时,系统应该继续按预期运行。这产生了一系列挑战,将这种系统的复杂性提高到了一个完全不同的水平。数据复制必须维护和管理多个服务器,还必须处理若干其它经典的分布式系统问题,如网络分区或脑裂。对MySQL而言,最终的挑战是将数据复制逻辑与协调多个服务器的逻辑相融合。这可以概括为让每个服务器的状态在数据变化时达成一致,即便它们都作为单个数据库系统运行,但最终都收敛到相同的状态。
MGR对属于同一组的服务器自动进行协调。对于要提交的事务,组成员必须就全局事务序列中给定事务的顺序达成一致。提交或回滚事务由每个服务器单独完成,但所有服务器都必须做出相同的决定。如果存在网络分区,导致成员无法达成事先定义的分割策略,则在解决此问题之前系统不会继续进行,这是一种内置的自动裂脑保护机制。MGR由组通信系统(Group Communication System,GCS)协议支持。该系统提供故障检测机制、组成员服务以及安全且有序的消息传递。所有这些属性都是创建系统的关键,可确保在服务器组中一致地复制数据。该技术的核心是Paxos算法的实现,它充当组通信引擎。
一、MySQL复制技术
在深入了解MySQL组复制的细节之前,先介绍一些其产生的背景以及工作原理,以帮助理解组复制,以及传统异步复制、半同步复制和组复制之间的区别。
1. 主从复制
传统的MySQL复制提供了一种简单的主从复制方法。有一个主库,一个或多个从库。主库执行并提交事务,然后通过二进制日志将事务相关的事件异步发送到从库,以便重放。这是一个无共享系统,默认情况下所有服务器都拥有完整的数据副本。
图1 异步复制 半同步复制为异步复制协议添加了一个同步步骤。这意味着主库在提交时等待至少一个从库确认它已收到该事务,才会继续提交操作。
图2 半同步复制 图1图2分别表示MySQL异步复制协议以及它的半同步变体。对角箭头表示服务器之间交换的消息或服务器与客户端应用程序之间交换的消息。
2. 组复制
组由多个服务器构成,通过传递消息进行交互,通信层保证原子消息传递。MGR构建于此通信层抽象之上,并实现了多主更新复制协议。组中的每个服务器独立地执行事务,但是所有读写事务只有在得到组的批准后才会提交。只读事务在组内不需要协调,因此立即提交。对于任何读写事务,当事务准备好在始发服务器处提交时,服务器以原子方式广播写入值(更改的行)和对应的写入集(更新的行的唯一标识符),然后将该事务加入全局事务列表。最终所有服务器都以相同的顺序接收并应用相同的事务集,所以它们在组内保持一致。
不同服务器上并发执行的事务之间可能存在冲突。MGR在certify过程中检查并发事务的写集来检测这种冲突。如果在不同服务器上执行的两个并发事务更新同一行,则存在冲突。解决方案是先到事务提交,后到事务回滚,即按顺序第一个事务在所有服务器提交,而第二个事务在在原始服务器上回滚并在组中的其它服务器中删除。这实际上体现的是多主分布式事务的首个提交获胜原则。
图3 MGR协议 图3描述了MGR协议。组复制同样是一种无共享复制方案,其中每个服务器都有自己的整个数据副本。
二、组复制使用场景
组复制可用来创建具有冗余的容错系统。即使某些服务器发生故障,只要它不是全部或大多数,系统仍然可用。根据失败的服务器数量,可能会降低性能或可伸缩性,但它仍然可用。组成员服务跟踪服务器故障,该服务依赖于分布式故障检测器,能够在任何服务器脱离组时发出信号,无论是意外停止还是主动停止。分布式恢复过程确保当服务器加入组时能自动更新。单个服务器发生故障时不会停止服务,也无需服务器故障转移。总之,MGR保证数据库服务持续可用。
需要注意,尽管数据库服务可用,但在服务器崩溃的情况下,必须将连接到它的客户端重定向或转移到其它服务器。组复制不解决数据库连接重定向的问题,连接器、负载平衡器、路由器或某种形式的中间件更适合处理此问题,例如MySQL Router。
以下是组复制的典型使用场景。
- 弹性复制:服务器的数量能够动态增加或减少,并且尽可能减小副作用,例如云数据库服务。
- 高可用分片:分片是实现写扩展的流行方法。使用MGR实现高可用分片,其中每个分片映射到复制组。
- 主从复制的替代方案:在某些情况下,使用单个主服务器会使其成为热点,写入整个组会更具可扩展性。
三、组复制相关服务
1. 故障检测
组复制包括一种故障检测机制,该机制能够查找并报告哪些服务器已经宕机。当服务器A在给定时间内没有从服务器B接收到消息时,发生超时并引发怀疑。然后如果组同意怀疑是真的,那么该组决定给定服务器确实宕机了。这意味着该组中的其余成员将采取协调决策以排除给定成员。
如果一个服务器与组的其余部分隔离,它会怀疑所有其它服务器都已失败,但由于无法与该组达成协议(因为无法确保法定票数),其怀疑并没有结果。当服务器以这种方式与组隔离时,它无法执行任何本地事务。
2. 组成员服务
MGR依赖于组成员服务,该服务内置于插件中。它定义了哪些服务器在线并参与该组。在线服务器列表通常称为视图。因此,组中的每个服务器都具有一致的视图,其中是在给定时刻主动参与该组的成员。
服务器不仅必须同意事务提交,还要同意当前视图。因此,如果服务器同意新服务器成为组的一部分,则组本身将重新配置为将该服务器集成在其中,从而触发视图更改。相反的情况也会发生,如果服务器离开组,则组会动态更新配置并触发视图更改。
组成员离开组分主动与被动。主动离开会启动组的动态重新配置,这会触发所有其它成员必须在没有该服务器的情况下就新视图达成一致。被动离开(如已意外停止或断网)时,故障检测机制会建议重新配置组,这需要组中大多数服务器的同意。如果该组无法达成协议,为阻止脑裂,系统无法动态更改配置,这意味着管理员需要介入解决此问题。
3. 容错
MGR构建于Paxos分布式算法的实现之上,需要多数服务器处于活动状态才能达到法定票数,从而做出决定。这直接影响系统可以容忍的故障机数量,但不会影响组复制自身及其整体功能。容忍 f 个故障机所需的服务器数量 n 为:n = 2 * f + 1。
实践中为了容忍一个故障机,该组必须具有三个服务器,因为此时如果一个服务器发生故障,仍然有两个服务器构成多数,并允许系统继续自动做出决策并继续提供服务。但如果第二个服务器继续失败,那么该组(剩下一个服务器)会阻塞,因为没有多数票可以做出决定。
四、组复制技术细节
1. 组复制插件体系结构
MGR是一个MySQL插件,它以现有的MySQL复制架构为基础,利用二进制日志、基于行的日志记录和全局事务标识符(GTID)等功能。图4显示了MGR插件架构。
图4 MGR插件架构 MGR插件包含一组捕获、应用和生命周期API,用于控制插件与MySQL服务器的交互方式。这些接口将MySQL服务器核心与MGR插件隔离。服务器向插件通知启动、恢复、准备接收连接、即将提交事务等消息。插件指示服务器执行诸如提交事务、中止正在进行的事务、事务在中继日志中排队等动作。
组复制插件体系结构的下一层是一组组件。捕获组件负责跟踪与正在执行的事务相关的上下文。应用组件负责在数据库上执行远程事务。恢复组件管理分布式恢复,负责选择捐赠者,对故障做出反应,执行追赶程序,使加入该组的服务器获得更新。
堆栈下一层的复制协议模块包含复制协议的特定逻辑。它处理冲突检测,接收事务并将其传播到组。
组复制插件体系结构的最后两层是组通信系统(GCS)API,以及基于Paxos的组通信引擎(XCom)的实现。GCS API将消息传递层的实现与插件上层分离,组通信引擎处理与复制组成员的通信。
2. 复制组
MGR中的一组服务器构成一个复制组,组名形式为UUID。组是动态的,服务器可以离开(主动或被动)并随时加入组。服务器加入或离开时,组会自行调整。如果服务器加入组,组会通过从现有服务器获取状态自动更新新加入的服务器。状态通过MySQL异步复制进行传输。如果服务器离开该组,其余服务器会知道它已离开并自动重新配置该组。
3. 数据操作语言(Data Manipulation Language,DML)
组中的每个服务器都被允许随时执行读写事务。任何服务器都可以在没有任何事先协调的情况下执行事务。但在提交时,它与组中的其余服务器协调,以便就该事务的操作做出决定。这种协调有两个目的:一是检查事务是否可以提交;二是传播更新,以便其它服务器也可以应用该事务。
当事务通过原子广播发送时,组中的所有服务器都接收该事务,或者都不接收该事务。它们会以与之前发送的其它事务相同的顺序收到它,并通过检查和比较写入事务集来执行冲突检测。冲突解决遵循首个提交者获胜规则。例如,事务t1和t2在不同的站点同时执行,t2排在t1之前,并且两者都改变了同一行,那么t2赢得冲突被执行,t1被中止。如果两个事务经常发生冲突,最好在同一台服务器上启动它们,这样它们有机会在本地锁管理机制下并行执行,而不是稍后在复制协议中中止。
4. 数据定义语句(Data Definition Language,DDL )
在组复制拓扑中,执行数据定义语句时需要小心。MySQL 8.0 引入了对原子数据定义语言的支持,其中完整的DDL语句作为单个原子事务提交或回滚。但是,原子或其它DDL语句隐式结束当前会话中处于活动状态的任何事务,就好像在执行语句之前已完成COMMIT一样。这意味着DDL语句自成事务,不能在另一个事务中,或在START TRANSACTION ... COMMIT等事务控制语句中,或者与同一事务中的其它语句结合使用。
组复制基于乐观复制,其中语句被乐观执行(执行时不事先锁定对象)并在稍后必要时回滚。每个服务器首先执行和提交而不保护组协议。因此,在多主模式下复制DDL语句时需要更加小心。如果对同一对象进行结构更改(使用DDL)并更改对象包含的数据(使用DML),则需要通过同一服务器处理更改。如果不这样做,可能会因操作中断或部分完成,导致数据不一致。如果组以单主模式部署,则不会发生此问题,因为所有更改都是通过同一服务器(主服务器)执行的。
5. 分布式恢复
(1)分布式恢复基础
组复制分布式恢复可以概括为服务器从组中获得丢失事务的过程,以便它可以加入具有已处理相同事务集成员的组。在分布式恢复期间,加入组的服务器会缓冲其正在接收的,组中所需的事务和成员事件。一旦加入该组的服务器收到了该组的所有事务,它就会应用在恢复过程中缓冲的事务。此过程结束时,服务器随之作为在线成员加入组。分布式恢复分为两个阶段。第一阶段,加入该组的服务器选择该组上的一个在线服务器作为其缺失状态的捐赠者。捐赠者负责为新服务器提供加入该组的所有数据,直到它加入该组为止。这是通过在捐赠者和加入该组的服务器之间建立的标准异步复制通道来实现的。复制通道是MySQL 5.7 中提出的概念。简单讲一个复制通道表示从主库到从库的一条复制路径,在多源复制中主到从可以存在多条复制通道。通过此复制通道复制捐赠者的二进制日志,直到加入该组的服务器成为该组的一部分,并发生视图更改时。加入该组的服务器在收到捐赠者的二进制日志时应用它们。
复制二进制日志时,加入该组的服务器还会缓存在组内交换的每个事务。也就是说,它监听在加入该组之后发生的事务,同时应用来自捐赠者的数据。当第一阶段结束并且关闭捐赠者的复制通道时,加入该组的服务器开始第二阶段:追赶。在此阶段,加入组的服务器继续执行高速缓存的事务。排队等待执行的事务数最终达到零时,该成员将在线声明。
当加入组的服务器从捐赠者获取二进制日志时,恢复过程可以承受捐赠者故障。在这种情况下,捐赠者在第一阶段期间失败时,加入该组的服务器将故障转移到新的捐赠者并从新捐赠者恢复。加入该组的服务器将关闭与失败的捐赠者的连接,并打开与新捐赠者的连接,这些都是自动进行的。
(2)基于时间点的恢复
为了使加入组的服务器与捐赠者同步到特定时间点,加入组和捐赠者的服务器利用MySQL全局事务标识符(GTID)机制。但GTID仅提供了一种方法来发现加入该组的服务器缺少哪些事务,不会传达认证信息。这是二进制日志视图标记的工作,它标记二进制日志流中的视图更改,还包含其它元数据信息,如认证相关数据。视图对应于主动参与当前配置的一组成员,在特定时间点,这些组成员在系统中是正确的和在线的。视图更改发生在组配置修改(例如成员加入或离开)时。任何组成员身份更改都会导致在同一逻辑时间点向所有成员传达视图更改。视图标识符唯一标识视图。只要视图发生更改,就会生成一个视图标识符。
在通信层,视图更改及其关联的视图ID是成员加入之前和之后数据变化的边界。此概念通过新的二进制日志事件实现:“视图更改日志事件”。因此视图ID也成为在组成员资格发生变化之前和之后传输的事务的标记。视图标识符本身由两部分构成:随机部分和单调递增整数部分。第一部分在创建组时生成,并且在组中至少有一个成员时保持不变。每次视图更改发生时,第二部分都会递增。随机部分识别组的开始,增量部分标识组的改变。
(3)视图更改
视图更改时,执行以下步骤将标识符合并到二进制日志事件:1. 开始:稳定组
如图5所示,所有服务器都在线并处理来自组的传入事务。一些服务器复制的事务可能稍微落后,但最终它们会相同。此时该组充当一个分布式数据库副本。图5 稳定组 2. 视图更改:加入一个组成员
每当新成员加入组并因此执行视图更改时,每个联机服务器都会把视图更改日志事件排入队列以备执行。在视图更改之前,服务器上可能有一些属于旧视图的事务排队进行应用,将视图更改事件排在它们之后可确保正确标记何时发生了视图更改。同时,加入该组的服务器通过视图的在线服务器列表中选择捐赠者。如图6所示,成员加入时生成视图4,在线成员将此视图更改事件写入二进制日志。
图6 成员加入 3. 状态转移:追赶
一旦加入该组的服务器选择该组中的某服务器作为捐赠者,则在两者之间建立新的异步复制连接并且开始状态转移(第一阶段)。这种与捐赠者的交互一直持续到服务器加入组的应用程序线程,该线程处理服务器进入组时所触发的视图更改日志事件。加入该组的服务器从捐赠者复制,直到它到达与视图改变相匹配的视图标识符,如图7所示。图7 追赶 加入该组的服务器知道它应该在哪个视图标识符停止复制。由于视图标识符在相同的逻辑时间被发送到组中的所有成员,避免了复杂的GTID集合计算,因为视图ID清楚地标记了属于每个组视图的数据。
加入该组的服务器正在从捐赠者复制时,它也会缓存来自该组的传入事务。最后它停止从捐赠者复制并切换到应用缓存的那些事务,如图8所示。
图8 排队的事务 4. 完成:赶上
当加入组的服务器识别出具有预期视图标识符的视图更改日志事件时,终止与捐赠者的连接并开始应用缓存的事务。视图更改日志事件除了在二进制日志中充当分隔标记,还扮演另一个角色。当新服务器进入组时,它传达所有服务器感知的认证信息,即最后的视图改变。如果没有视图更改事件,加入该组的服务器将没有必要的信息对后续事务进行冲突检测。追赶的持续时间(第二阶段)是不确定的,它取决于负载和进入组的事务的多少。此过程完全联机,加入组的服务器在追赶时不会阻止组中的任何其它服务器。当进行到第二阶段时,加入该组的服务器的事务可能落后,落后的多少取决于负载。
当加入组的服务器达到零排队事务并且其存储的数据等于其它成员时,其公共状态将更改为联机,如图9所示。
图9 实例联机 (4)分布式恢复的使用建议和限制
分布式恢复基于传统的异步复制,如果加入组的服务器没有数据或者只有非常旧的备份数据,恢复过程可能很慢。这意味着要在第一阶段传输大量数据,新增服务器可能需要很长时间才能恢复。因此建议在将服务器添加到组之前,应该为其配置已经在组中的服务器的相当近的快照。这最小化了第一阶段的所需时间并减少了对捐赠服务器的影响,因为它只需保传输较少的二进制日志。
-
2. 组复制技术架构 | 深入浅出MGR
2022-01-26 10:56:43重配置组视图需要得到多数派成员的同意才行,当无法形成一致时,就无法实现自动重配置,需要人工介入处理。无法形成一致意见可能的原因有,剩下的节点数没达到总结点数的一半以上,也就是无法形成多数派。 在节点被...- GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。
1. 传统主从复制技术架构
传统主从复制的方式是在master节点上执行数据更新事务,而后记录这些事务到binlog中,再将binlog发送到slave节点转储成relay log,在slave节点上再有单独的线程读取这些relay log然后重新执行或应用这些事务,它是shared-nothing的,每个节点都有一份完整的数据副本,其技术流程图如下所示:
- 传统主从复制技术架构图
MySQL还提供了半同步复制,这是在传统主从复制的基础上增加了一个同步的步骤,master节点上提交事务前,要先等到slave节点确认收到事务信息才可以(所以前文才说当slave节点响应慢时会影响master节点的事务提交),其技术流程图如下所示:
- 半同步复制技术架构图
2. MGR组复制技术架构
MGR也是shared-nothing的,每个节点都有一份完整的数据副本,节点间通过GCS(Group Communication System)进行交互。GCS层提供了节点间的全局消息及其有序性的保证。
MGR可以做到在任何节点、任何时间都能执行读写事务(不含只读事务),不过读写事务要被整个复制组确认后才能提交。如果是只读事务则没有这个限制,任何节点都可以发起及提交。
当读写事务准备提交前,它会向复制组发出一个原子广播,内容包括:该事务修改的数据,及其所对应的writeset。复制组中所有节点要么接收该事务,要么都不接收。如果组中所有节点都接收该事务消息,那么它们都会按照与之前发送事务的相同顺序收到该广播消息。因此,所有组成员都以相同的顺序接收事务的写集,并为事务建立全局顺序。
在多个节点上并行执行的事务是可能产生冲突的,这时候就需要对比判断两个并行事务的writeset来确认,这个过程称为事务认证,也叫做冲突检测。事务冲突检测是行级别的,也就是说两个并行的事务更新同一行时,则视为产生冲突。这时的做法是全局顺序在前面的事务可以成功,所有节点都提交该事务。而全局顺序在后面的事务会失败回滚,各节点会删除该事务。这实际上是个分布式的谁先提交谁先赢得事务的规则。建议:如果经常发生节点间的事务冲突,那最好将这些事务放在同一个节点上执行,这样它们在本地事务并发控制协调下可能都可以提交成功,而不至于由于MGR的冲突检测而导致某个事务总是被回滚。
对于正在应用或外化的事务,MGR允许它们不一定按照原有顺序执行,只要不破坏事务的一致性和有效性即可。MGR默认要求是最终一致性,也就是说当所有事务都应用完毕后,所有节点的数据是一致的。当流量巨大时,事务可能会被外化而导致顺序轻微不一致。例如在多主模式下,一个本地事务在通过认证后会被立即外化,尽管此时可能还有个有这更早全局顺序的远程事务还没被应用,只要MGR的认证线程认为这个事务不会产生冲突即可。在单主模式下,在Primary节点上的本地并发事务,在不产生冲突的情况下,其提交和外化的顺序可能和该事物的全局事务顺序有轻微不一致。在Secondary节点上,由于没有写事务,因此它们的事务顺序和全局事务顺序是一致的。
下图描述了MGR的组复制协议,可以看到和传统主从复制(及半同步复制)的一些差异。为了简单起见,图中少了共识算法和Paxos相关的信息:
- MGR技术架构图
3. MGR的单主和多主模式
MGR支持单主或多主两种模式。
在启动时,通过设置选项
group_replication_single_primary_mode
来决定使用哪种模式,各节点中该值的设置要求一致。设置为 ON 时表示采用 单主模式,当设置为 OFF 时表示采用 多主模式。在运行过程中,不能在线修改
group_replication_single_primary_mode
的值,但是从MySQL 8.0.13开始,可以通过调用group_replication_switch_to_single_primary_mode()
和group_replication_switch_to_multi_primary_mode()
这两个udf在线修改运行模式,或者通过MySQL Shell修改。在 单主模式 下,有且只有一个(Primary)节点可以写入数据,其余(Secondary)节点都只能读数据。而在 多主模式 下,可以在任意节点上同时读写数据。
MGR最多只能支持9个节点,无论单主还是多主模式。
4. 节点管理
MGR由一组节点构成,每个节点都有唯一的名字,以 UUID 的格式表现。节点可以动态加入或离开(也可能是被动被驱逐)MGR。
MGR的组成员服务用于维护定义各活跃节点的信息,这些活跃节点信息也称之为组视图(view)。各节点的组视图是一致的,这表示在给定时刻组中有哪些活跃成员。
MGR各节点除了在事务提交时要保持一致外,也包括组视图发生变化时也要达成一致。当有新节点加入,或现有节点离开时,都会触发新的组视图变更。
当有节点主动离开集群时,它会触发集群自动重配置,剩下的节点会就新的组视图达成一致。但若节点是因为网络异常或宕机等原因意外离开集群时,则无法触发自动重配置,这时候集群故障检测机制会在该节点离开一段时间后识别到这个状态,并发出重配置组视图的提议。重配置组视图需要得到多数派成员的同意才行,当无法形成一致时,就无法实现自动重配置,需要人工介入处理。无法形成一致意见可能的原因有,剩下的节点数没达到总结点数的一半以上,也就是无法形成多数派。
在节点被确认故障之前,或在重新配置组以删除该故障节点前,允许该节点短暂离线,然后尝试重新加入集群。在这种情况下,该节点可能会丢失它以前的状态(事务数据),如果此时其他节点向它发送了包含崩溃前的消息,则这就可能会导致数据不一致等问题。
为了解决这个问题,从MySQL 5.7.22开始,MGR会检查具有相同地址+端口的节点再次以新身份加入集群的情况,确认当前是否还有其旧身份存在。这时候其新身份不能加入,直到旧身份能从集群中删掉。注意:,选项
group_replication_member_expel_timeout
的作用是设置一个等待期,使得节点在被正式驱逐前有更多时间尝试重新加回集群,也就是说处于被怀疑状态的节点,在超时之前还可尝试重新加入集群,再次作为活跃节点。当节点超过group_replication_member_expel_timeout
阈值并被从集群中驱逐时,或节点执行STOP GROUP_REPLICATION
退出集群,或因节点宕机等情况下,该节点必须以新身份重新加入集群。5. 故障检测
MGR自带故障检测机制,它能发现并报告哪个节点处于静默状态,达到一定条件后会认为这个节点已死。它是个分布式的故障检测服务,提供了哪个节点处于(被怀疑)已死状态的信息。
当一个节点静默(不主动发信息,也不回复其他节点的信息)时,可能会触发被怀疑。当节点A在给定时间内还没有收到节点B的消息时,则发生消息超时并引发怀疑。在这之后,集群内其他成员如果一致同意(多数派达成一致)对该节点的怀疑是确定的话,则会判定该节点发生了故障。
如果某个节点因为网络故障和其他节点断开连接了,那么它可能也会怀疑其他节点发生了故障。但由于它不能形成多数派决议,因此这个怀疑是无效的,此时该节点无法执行任何读写事务,最多只能执行只读事务。
当网络不稳定时,随意两个节点间可能频繁断开和重连,理论上说可能会导致所有节点都会标记为驱逐,集群会退出并需要重建。为了避免这种情况,从MySQL 8.0.20开始,GCS会跟踪标记为驱逐的节点,并决定某个可疑节点是否还留在多数派节点中,这使得集群中至少有一个节点而不会退出。当被驱逐节点正式被从集群中移出时,GCS会删掉起被标记为驱逐的记录,使得它后面还能重新加回。
6.容错机制
MGR是基于分布式的Paxos算法实现,因此要求有多数派节点存活以保证投票。这就决定了在不影响系统整体可用性前提下,可容忍发生故障的节点数量。假设总节点数是n,可容忍发生故障的节点数是f,则它们的关系是:n = 2*f + 1。简言之,容忍发生故障的节点数,不高于总节点数的一半。
下表展示了不同节点数的对应关系:
总节点数 多数派节点数 最大容忍故障节点数 1 1 0 2 2 0 3 2 1 4 3 1 5 3 2 6 4 2 7 4 3 8 5 3 9 5 4 参考资料、文档
免责声明
因个人水平有限,专栏中难免存在错漏之处,请勿直接复制文档中的命令、方法直接应用于线上生产环境。请读者们务必先充分理解并在测试环境验证通过后方可正式实施,避免造成生产环境的破坏或损害。
Enjoy GreatSQL :)
文章推荐:
GreatSQL季报(2021.12.26)
https://mp.weixin.qq.com/s/FZ_zSBHflwloHtZ38YJxbA技术分享|sysbench 压测工具用法浅析
https://mp.weixin.qq.com/s/m16LwXWy9bFt0i99HjbRsw故障分析 | linux 磁盘io利用率高,分析的正确姿势
https://mp.weixin.qq.com/s/7cu_36jfsjZp1EkVexkojw技术分享|闪回在MySQL中的实现和改进
https://mp.weixin.qq.com/s/6jepwEE0DnYUpjMYO17VtQ万答#20,索引下推如何进行数据过滤
https://mp.weixin.qq.com/s/pt6mr3Ge1ya2aa6WlrpIvQ关于 GreatSQL
GreatSQL是由万里数据库维护的MySQL分支,专注于提升MGR可靠性及性能,支持InnoDB并行查询特性,是适用于金融级应用的MySQL分支版本。
Gitee: https://gitee.com/GreatSQL/GreatSQL
GitHub: https://github.com/GreatSQL/GreatSQL
Bilibili: https://space.bilibili.com/1363850082/video
微信&QQ群: 可搜索添加GreatSQL社区助手微信好友,发送验证信息“加群”加入GreatSQL/MGR交流微信群
QQ群:533341697 微信小助手:wanlidbc
本文由博客一文多发平台 OpenWrite 发布!
-
组播及igmp/mld协议详解(二)
2018-05-10 09:59:01主机向本地的组播路由器发送IGMP消息来表明自己所属的组播组。在IGMP协议中,路由器侦听IGMP消息并周期的发出查询,以发现某个子网上哪些组是活动的,哪些是不活动的。 IGMP消息在IP数据报内发送,用IP协议号2来...1 IGMP 协议
IGMP用来动态的将各个主机注册到特定局域网中的一个组播组中。主机向本地的组播路由器发送IGMP消息来表明自己所属的组播组。在IGMP协议中,路由器侦听IGMP消息并周期的发出查询,以发现某个子网上哪些组是活动的,哪些是不活动的。
IGMP消息在IP数据报内发送,用IP协议号2来标识。同时,将IP存活时间(TTL)字段值设定为1,因此IGMP信息处于本地范围本子网内传送并且不会被路由器转发。
1989年,IGMP版本1(RFClll2)第一次详细定义了IGMP规范。后来施乐公司对最早的IGMP版本1进行了大幅更新,产生了IGMP版本2(RFC2236)。到目前为止IGMP版本3规范己经称为IETF正式标准(RFC3376),通用的是IGMPv2。IGMPvl实现简单,但是有离开延迟过大和选择查询路由器需要依赖组播路由协议的缺点,IGMPv2对此进行了改进。IGMPv3协议的主要目的是支持源特定组播,并进一步对IGMPv2进行完善。
1.1 IGMPv1协议
1.1.1 IGMPv1的工作原理
在IGMPvl中定义了基本规则、组成员查询机制和报告机制。当某接收主机希望接收到某个组播组的数据时,它会向本地链路上的查询路由器发送加入消息,通知查询路由器本机希望申请加入的组播组;查询路由器收到加入消息之后,把这条消息加入到查询路由器所维护的状态列表,同时向源发起建立组播分发树的请求;查询路由器在设定的周期内发起组成员查询消息;接收主机收到查询消息之后,会向查询路由器发送报告消息来应答查询,否则查询路由器会认为不存在接受主机;主机如果想离开某个组播组,就对路由器的查询保持沉默,经过一定时间,路由器便知道子网内没有组成员了。
1.1.2 IGMPv1报文格式
IGMPvl报文格式如图2-4所示,
图2-4 IGMPv1报文格式
其主要内容包括:
(1) 版本字段表示IGMP协议的版本号,在IGMP中置为1.
(2) 类型字段,在IGMPv1中,只有两个值:
取值为0x11,表示该报文为成员关系查询(Membership Query),主要是由路由器使用。
取值为0x12,表示该报文为成员关系报告(Membership Report),主要是主机使用。
(3) 校验和字段用于数据报文的校验。
(4) 组地址字段。当用于成员关系查询时,本字段置为0,并被主机忽略;当用于成员关系报告时,本字段包含组播组地址。
IGMPv1报文在网络中传输完整的报文格式如图2-5:
图2-5 在网络中传输的IGMPv1报文
1.1.3 IGMPv1工作过程
在IGMPv1中,路由器利用查询一响应过程来确定在本地子网中是否有加入某个组播组的主机存在,如果有,则这台路由器就要完成向本子网组播数据包的功能;如果没有,则这台路由器就不必向此子网转发组播包。路由器周期性地向子网上的所有主机发送组播成员关系查询报文,希望加入某个组播组的主机就响应该查询,发送一个组播成员关系报告报文到子网上,在IGMP报文的组地址地段中加入想要加入的组播组的地址。路由器接收到来自主机的成员关系报告报文后,就知道了在该子网上有主机要加入组播组,组播组地址在报文中可以获得,接下来,路由器就会根据所使用的路由协议建立起相应的转发状态。
当一个子网上有多台主机想加入同一个组播组时,就可以利用报告响应抑制功能,来减少子网中的重复信息传递。处理流程如下:
主机接收到IGMP成员关系查询报文后,对加入的每个组播组启动一个倒数计时器。当计时器的值为0时,主机发送IGMP成员关系报告报文,通知路由器子网内仍有处于活动状态的组播接收者。当计时器到达0之前,若主机接收到来自其他主机发送的同一组成员关系报告报文,那么它就停止与该计时器得到的数,重新计时,这样,就避免了发送同一个成员关系报告报文给路由器。
如果在一个子网中有多个组播路由器,那么多个路由器都发送IGMP查询报文是一种浪费,所以应当确定一个路由器作为查询路由器就可以了。但是在IGMPv1中,没有提供选举查询路由器的机制,而是把这一任务留给了组播路由协议。由于不同的协议使用不同的选举机制,会造成在一个子网中出现多个查询路由器,这也是IGMPv1的缺点之一。
但是IGMPv1的另一个缺点是缺乏显式的离开方式。当一台主机想要离开一个组播组时,并不显式地表示出来,而只是不再对路由器的查询报文进行响应。当一个网段内某个组播组的最后一个成员退出后,路由器还会继续组播这个组的数据,直到一段时间内路由器接收不到任何来自该组的成员响应,才会知道该组已经没有接收者了,然后停止转发该组的组播数据报文。因此,路由器中需要为子网中的每一个组维护一个计时器。当路由器接收到某台主机发送的报告报文时,就会将该组的计时器清零;当某个组的计时器超时后,就说明在本网段上已经没有接收者,于是停止转发该组报文。
下面是工作过程图解:
1) 组成员加入
图2-6 IGMPv1组成员加入
2) 查询与响应
图2-7 IGMPv1组成员查询
3) 响应抑制机制
图2-8 IGMPv1响应抑制机制
4) 组成员离开
图2-9 IGMPv1组成员离开
1.2 IGMPv2协议
1.2.1 IGMPv2的工作原理
IGMPv1的主要缺点是离开延迟过大和选择查询路由器需要依赖组播路由协议进行。针对这些缺点,IGMPv2做了相关的改进。在IGMPv2中,增加了离开组的报文格式,当主机想要离开某个组播组时,不必等待路由器发出查询报文,而是可以直接向路由器发送成员关系报告报文,这样可以有效地缩短离开延迟。另外在IGMPv2中,还明确了查询路由器的选举机制。除此之外,IGMPv2的工作原理与IGMPv1基本一致
1.2.2 IGMPv2的报文格式
IGMPv2的报文格式如图2-10所示:
图2-10 IGMPv2报文格式
它在IGMPvl的基础上,进行了两处改动:一个是将v1的版本字段和类型字段进行了合并;另一个是增加了最大响应时间字段 (MaxResponseTime)。其主要内容如下:
(1)类型字段,在兼容IGMPv1的基础上,IGMPv2中新增了两种报文类型。
·0xll:成员关系查询。与IGMPv1不同,IGMPv2的查询分为两种类型:①通用查询 (GeneralQuery),组地址字段置为全0,对所有的组进行组成员查询;②特定组查询 (GrouPpecificQuery),针对特定组进行组成员查询,组地址字段置为特定组的地址。
·0x12:IGMPv1成员关系报告(为了向后兼容IGMPv1)。
·0x16:IGMPv2成员关系报告。
·0xl7:离开组。
(2)最大响应时间字段,只有在成员关系查询报文中有效,主机必须在最大响应时间到达之前发出成员关系报告报文。通过该值,路由器可以调节组成员的离开延迟。
(3)校验和字段,与IGMPv1中的一样。
(4)组地址地段,与IGMPv1中的基本一样,当采用特定组查询时,该字段存放要查询的组播组的地址。
IGMPv2报文在网络中传输完整的报文格式如图2-11:
图2-11 在网络中传输的IGMPv2报文格式
1.2.3 IGMPv2工作过程
查询一响应过程与IGMPv1基本相同,但是有两点改进:①增加了特定组查询,特定组查询的目的是为了让路由器知道一个特定组在子网内是否还有组成员,以便判断是否还需要转发该组的数据报文;②IGMPv2的成员关系报告的类型代码不一样。
IGMPv2的组成员加入与 IGMPv1中的完全一样。IGMPv2离开过程与IGMPv1相比有了较大的改进。主机离开一个组时,需要显式地发送一个离开报文给路由器。其过程如下:
要离开的主机发送一个离开报文给子网上所有路由器(目的地址224.0.0.2),查询路由器接收到离开报文后,会立即发送一个特定组查询到子网上。如果子网上还有该组的成员,则会发回一个响应报文;如果子网上己经没有该组的成员,则没有主机回应,于是路由器就知道己经没有该组成员了,就停止转发该组的数据。
在IGMPv1中,选择查询路由器依赖于组播路由协议;而在IGMPv2中,明确了选择查询路由器的机制。其过程如下:
开始时,子网上的每个路由器都假定自己就是查询路由器,发送一个通用查询报文给所有主机(目的地址224.0.0.1)。每个路由器都可以接收到来自其他路由器的报文,然后进行IP地址的比较,具有最低IP地址的路由器就成为查询路由器;非查询路由器启动一个计时器,无论何时接收到来自当选的查询路由器的通用查询报文,就将计时器复位。如果计时器超时,就认为当选的查询路由器发生故障,转向开始重新选择。计时器的取值一般为查询间隔的2倍
图解工作过程如下:
1)组成员加入
图2-12 IGMPv2组成员加入
2)查询与响应
图2-13 IGMPv2组成员查询与响应
3)查询器选举
图2-14 IGMPv2查询路由器选择
4)成员离开
图2-15 IGMPv2组成员离开
1.3 IGMPv3协议
1.3.1 IGMPv3的工作原理
IGMPv3的提出,主要是为了配合源特定组播的实现,即组播组成员可以指定接收或指定不接收某些组播源的报文。这样主机就可以有选择性接收来自某个特定组播源的数据包,而不是被动接收该组中所有组播源的数据包。IGMPv3的这一特性,可以实现源特定组播SSM技术。源特定组播(Source Specific Multicast,SSM)是一种区别于传统组播的新的业务模型,SSM保留了传统PIM-SM模式中的主机显式加入组播组的高效性,但是跳过了PIM-SM模式中的共享树和RP规程。SSM直接建立由(S,G)标志的一个组播最短路径树。SSM的一个(S,G)对也被称为一个频道(Channel)。PIM-SSM是对传统PIM协议的扩展,使用SSM,用户能直接从组播源接收组播报文,需要汇聚点(RP)的帮助。
IGMPv3在IGMPvl/v2的基础上提供了额外的源过滤组播功能(Source FilteredMulticast,SFM)。在IGMPvl/v2中,主机只根据组地址来决定加入某个组,并从任何一个源接收发给该组地址的报文。具有源过滤组播功能(SFM)的主机使用IGMPv3来表示主机所希望加入的组播组,同时还表示该主机所希望接收的组播源的地址。主机可以使用一个包括列表(Inclusion List)或一个排除列表 (Exclusion List)来表示对源地址的限制。即组播组成员可以指定接收或指定不接收某些组播源的报文。这样主机就可以有选择性接收来自某个特定组播源的数据包,而不是被动接收该组中所有组播源的数据包。
1.3.2 IGMPv3的报文格式
IGMPv3的报文类型有以下几种:
0xll:成员关系查询报文 (MembershipQeury)。
0x22:版本3成员关系报告报文(version 3 Membership Report)
0x12:版本1成员关系报告报文(version 1 Membership Report)
0x16:版本2成员关系报告报文 (version 2 Membership Report)
0x17:版本2离开报文 (version 2 LeaveGroup)。
报文类型的值填写在报文中的类型字段。在IGMPv3中,查询报文和报告报文格式有较大差异,需要分别描述。图2-16为查询报文的格式
图2-16 IGMPv3查询信息格式
(1)类型字段,设置为0xll,代表该报文为查询报文。
(2)最大响应时间字段,指明了主机发出响应的最长时间。
(3)组地址字段,功能与IGMPv2一样,可以用于通用查询和组特定查询。
(4)s字段,置为1时,其他路由器不对该报文进行处理。
(5)QRV字段,查询路由器的健壮值(Querier’s RobustnessVariable),该值影响计时器和重试次数的取值。
(6)QQIC字段,查询路由器的查询间隔码(Querier’s QueryIntervalCode),该值影响查询路由器的查询间隔时间,非查询路由器按照此值更新自己的缺省值。
(7)源地址数目字段,该值表在这个报文中包含了多少个源地址。当进行通用查询(GeneraQuery)或者组特定查询 (GroupSpecific Query)时,该值置为0;当进行特定组和源查询 (Group Source pecific Query,用于PIM一SSM)时,该值为源特定地址的数目。虽然该值最大可为65536,但是实际上受限于数据链路层的MTU,例如在以太网上,1P数据报最长为1500字节,除去IP报头的24字节和IGMP报头的12字节,剩余1464字节,所以最多包含366(1464/4)个源地址。
(8)源地址地段。
IGMPv3查询信息报文在网络中传输完整的报文格式如图2-17:
图2-17 在网络中传输的IGMPv3查询报文格式
IGMPv3报告报文的格式较为复杂,如图2-18所示。
图2-18 IGMPv3侦听者报告格式
(1)类型字段,置为0x22,表示该报文为IGMPv3报告报文。
(2)组记录数目字段,表示此报文中包含的组记录数目。
(3)组记录字段。包含若干个组记录,每个组记录长度不固定,其内容如图2-19:
图2-19 IGMPv3组记录格式
①组记录类型字段,表示该组记录中包含的数据的类型,目前定义了六种类
型,分别是:
·MODE IS INCLUDE。表示该主机的过滤模式为INCLUDE.也就是说,后面列出的地址都是主机想要接收的组播源地址。
·MODE IS EXCLUDE。表示该主机的过滤模式为EXCLUDE,也就是说,后面列
出的地址都是主机想要拒绝的组播源地址。
·CHANGE TO INCLUDEMODE。表示该主机的过滤模式从EXCLUDE切换为INCLUDE模式。
·CHANGE TO EXCLUDEMODE。表示该主机的过滤模式从INCLUDE切换为EXCLUDE模式。
·ALLOW NEW SOURCES。表示该主机中新增的想要接收的源地址。
·BLOCK OLD SOURCES。表示从该主机中删除的不想接收的源地址。
②辅助数据长度字段,在组记录的最后,可以增加以4字节为单位的辅助数据,如果没有辅助数据,则置为0。
③源地址数目字段,表示该记录中包含了多少个组播源地址。
④组地址字段,与源地址共同表示特定源组播。
⑤源地址字段,每个长度为32bits。标志源地址,数目由源地址数目字段表示。
⑥辅助数据字段。为将来的应用预留,在IGMPv3中并不需要。一台主机在发送报告报文的时候,应当把自己的源IP地址包含在IP数据报中,当主机还没有获得IP地址的时候,可以使用0.0.0.0作为源IP地址,支持IGMPv3的路由器必须接收来自0.0.0.0的数据报。主机的IGMP报文的目的地址标志为224.0.0.22,代表子网中所有支持IGMPv3的路由器。
1.3.3 IGMPv3的主要改进
IGMPv3除了支持原特定组播外,其工作原理与IGMPv2相比,并没有本质的改变,只是在某些地方做了改进和优化。以下列出了IGMPv3的主要特点和改进:
①支持源特定组播SSM;
②向后兼容IGMPvl和IGMPv2;
③主机可以定义要接收的组播源地址;
④非查询路由器可以与查询路由器保持参数值同步;
⑤最大响应时问从25.5s增加到53min,适合于较大的网络;
⑥辅助数据字段为将来的应用预留了空间;
⑦关系成员报告报文发送给目的地址224.0.0.22,可以帮助二层交换机更有效地实现IGMP监听 (IGMPSnooping)功能;
⑧报告报文中可以包含多个组记录,可以有效地减少网络通信量;
⑨在IGMPv3中,取消了前面版本中的响应抑制功能,主要原因是:
第一,使用响应抑制时,路由器只知道子网上是否有组成员,而不知道有几个组成员,以及成员是哪些主机:取消响应抑制,路由器就可以记录每一个组成员的信息,可以开发一记账等增值服务功能。
第二,许多网桥或者二层/三层交换机在实现IGMP监听功能时,为了避免响应抑制,一般不转发网段问的IGMP报文。取消了响应抑制后,可以简化这些设备的设计。
第三,取消响应抑制后,主机不必处理来自其他主机的报文,简化了主机的实现。在查询报文中,增加S标志位,可以提高系统的健壮性。
IGMPv3报告报文在网络中传输完整的报文格式如图2-20:
图2-20 在网络中传输的IGMPv3报告报文
2 MLD协议
IPv6的组管理协议被称为MLD(组播监听者发现)。1999年,MLD版本l(RFC2710)被IETF发布。2004年,MLD版本2(RFC3810)标准出台,后一个版本可以向前一个兼容。MLD协议是专门针对基于IPv6的组播组管理协议。因为MLD使用全新的ICMPv6的报文格式,所以MLD协议就是ICMPv6协议的一个子集。MLD消息使用链路本地IPv6源地址发送,其跳数被限制为1。MLD消息的封装格式如图2-21所示:
图2-21 MLD消息封装格式
以下分别描述MLD协议的两个版本以及MLDSnooping,其中对于MLDSnooping的详细描述见第三章。
2.1 MLDv1协议
2.1.1 MLDv1的工作原理
MLDvl协议是从IGMPv2协议中派生出来的,其运行机制和IGMPv2协议相同,专门用于IPv6组播群组的管理,其主要是应用于ASM模式组播路由协议的组管理工作。
对于运行MLD协议的路由器,其接口要监听由IPv6组播地址产生的所有链路组播地址。路由器为它所在的每一条链路维护一个列表。表项有此链路中存在的组成员的组播地址,以及该地址相应的定时器。路由器周期性地发送通用请求 (general query),以查询该链路上是否存在某组播地址的组成员。节点收到路由器发送的常规请求后,经过随机时延后发出组播监听报告。这样是为了防止所有的节点都在同一时间发出报告分组,从而造成网络的突发性阻塞。当路由器收到链路上的报告分组时,如果报告地址不在路由器的列表上,则加入该项,否则计时器重新置位。如果某个地址的计时器过期,则从列表中删除。当节点要加入一个组播组时,主动发送组播监听报告,向路由器报告组成员的存在。节点退出组播组时,发送完成分组,删除有关路径。当请求状态的路由器从链路上接收到一个完成消息,如果消息中的组播地址在路由器的列表上,路由器发送一个特定组播地址查询。如果一段时延后没有报告分组,则认为该组播地址在此链路上没有组成员了。
2.1.2 MLDv1报文格式
MLDv1的报文格式如图2-22所示:
图2-22 MLDv1报文格式
(1)类型字段,MLDvl有如下三种报文类型:
·组播侦听查询消息(Type=130),分为两种类型:①通用查询(GeneralQuery),组地址字段置为全0,对所有的组进行组成员查询;②特定组查询(Group Pecific Query),用于判断一个特定的组播地址在本地链路上是否有组播侦听者。
·组播侦听报告消息(Type = 131)
·组播侦听完成消息(Type = 132)
(2)编码字段,初始值为0。
(3)校验和字段。
(4)最大响应时间字段,只有在组播监听者查询报文中有效,主机必须在最大响应时间到达之前发出成员关系报告报文。通过该值,路由器可以调节组成员的离开延迟。
(5)组地址字段,在通用组查询中,置为0;在特定组查询时,该字段存放要查询的组播组的地址。在报告和完成报文中,分别用于存放主机要加入和离开的组地址。
MLDv1报文在网络中传输完整的报文格式如图2-23:
图2-23 在网络中传输的MLDv1报文
以上这些查询消息和应答消息报文有三种不同的报文交互方式:
第一种交互方式是由路由器发起的。路由器作为询问者向与其相连接的所有主机发送一个一般查询消息报文。其目的地址是FF02::1。主机收到此消息后,应答一个包含当前组播地址状态记录的报文消息,此报文告诉路由器此主机希望接收哪个组播组或者哪些源发来的数据。
第二种交互方式是由主机发起的。当一个主机离开一个组播组时,它就要向路由器发送组播侦听者完成消息,该消息包括一个状态改变一记录。路由器收到此消息后,向其相连的链路上发送一个特定组播地址查询,询问是否还有主机加人了此特定的组。
第三种交互方式是由路由器发起的。如果在路由器的组播地址表中某一个组播地址的相关定时器超时后,仍然没有收到主机发来的包含状态变化记录的组播侦听者报告消息,路由器则向所有主机发送一个特定组查询消息,确认该组播组是否还有组播侦听者。
2.1.3 MLDv1工作流程
当一个网段内连接有多台路由器运行MLDvl协议时,必须选举一台路由器作为查询路由器,其余的自然成为非查询路由器。选举的机制是:地址最小的路由器当选。非查询路由器中有一个其他查询路由器存在计时器,当该计时器到期仍没有收到来自查询路由器的报文,则认为该查询路由器失效,重新开始新的选举。
路由器定期向子网内所有的主机广播查询报文(目的地址为FF02::1),目的是获得主机的报告报文。在路由器刚开始工作时,会快速连续地发送查询报文,以便尽快搜集到子网内的组成员信息。
当主机接收到一个查询报文后,就为每一个要接收的组地址启动一个延迟定时器。定时器的值在[0,最大响应时间]之间取一个随机数。如果查询报文中的最大响应时间字段被置为0,则定时器立刻到期。定时器到期后,主机会发送一个报告报文给路由器,通知路由器主机想接收的组播组地址。
如果一台主机在定时器还未到期时,就收到其它主机通告路由器的报告报文,则读取该报文的组地址,如果和自己需要通告的组地址相同,则立刻停止相应的定时器,并不再发送关于该组地址的报告报文,这样就可以避免多台主机发送相同内容的报告报文给路由器,这种机制称为“响应抑制”。路由器收到来自主机的报告报文后,查看其中的组地址,如果该地址未在路由器的组地址列表中,则将其添加到组地址列表中,同时为其启动一个相应的定时器;如果该地址已经在路由器的组地址列表中,则将相应的定时器恢复最大值。如果一个组地址的定时器到期了,则说明该组地址在子网内已经没有接收者了,路由器会将此组地址从列表中删除。
当一台主机想要加入某个组播组时,可以不必等待路由器的查询报文,而是直接向路由器发送报告报文。为了保障该报文的可靠性,一般会进行重传。当一台主机想要离开某个组播组时,必须发送一个离开报文给子网内的路由器。路由器收到离开报文后,会首先查看该组地址是否在组地址列表中,如果在,则发送一个特定组地址查询给子网内的所有主机。在一定的时间内,路由器收不到来自主机的应答,则会认为该组已经没有接收者,于是将该组地址从列表中删除。非查询路由器会忽略所有的离开报文。
2.2 MLDv2协议
MLDv2从IGMPv3中发展过来,和MLDvl相比,增加了IGMPv3所具有的源过滤功能,不仅能够支持ASM模式组播路由协议,而且还能够支持基于IPv6的SSM模式组播路由协议。
MLDv2在MLDv1的基础上添加了源组播 (Source Specific Multieast)的概念,主机可以组播源报告(Group-Source Report)报告感兴趣的源,路由器则只转发该链路上所有组成员感兴趣的源所发送的报文。当主机想退出某组播源时,主机发送离开组播源报告(Group-Source Leave),查询者在接收到该报告后可以发送指定组播源请求,确认是否仍有组成员关心该组播源。MLDv2支持源过滤 (SourceFiltering),因此比MLDvl具备更高的可管理性和安全性。此外MLDv2还可以与MLDvl兼容。
2.2.1 工作原理
MLD协议的目的是使每一个组播路由器知道在本地链路上监听者对哪些组播地址和源地址感兴趣。被MLD收集到的信息提供给在路由器上运行的任何组播路由协议,来保证组播数据包被传递到组播组成员。组播路由器只需要知道在一个链路上,至少有一个节点正在监听从一个特定的源发给一个特定组播地址的数据包,它不需要知道有多少个这样的节点。
MLDv2是一个不对称的协议,它包括分离的两部分:针对组播地址侦听者部分(监听组播数据包的主机或者路由器)和组播路由器部分。它为组播地址侦听者和组播路由器定义了不同的行为。当执行“组播路由器”功能时,收集组播路由协议所需要的组播侦听者信息;当执行“组播地址侦听者”功能时,通知本身和其他邻居组播路由器他的侦听状态。需要注意的是,一个组播路由器可以本身是一个或者多个组播地址的侦听者,执行“组播路由器”和“组播地址侦听者”双重功能。
组播地址监听者在所有的支持组播的接口上执行MLDv2协议的“组播地址监听者”功能。
组播路由器在它的每一个接口上执行MLDv2协议的“组播路由器”功能。如果在同一个子网上具有不止一个组播路由器,组播路由器将运行查询者选举机制来选择一个组播路由器来充当查询路由器。在这个子网上的所有的组播路由器都在监听由组播地址监听者发送的消息,并且维护相同的组播监听信息状态,以便于当目前的查询路由器不能工作时,接替查询路由器的工作,但是只有查询路由器在子网上能够发送周期的消息和被触发的查询消息。
2.2.2 MLDv2报文格式
为了支持以上的功能,MLDv2除了兼容支持MLDvl所有的三种报文:组播侦听查询(MLD消息类型值为130),包括一般查询和特定组播地址查询:组播侦听报告(MLD消息类型值为131);组播侦听完成(MLD消息类型值为 132)外,还增加了MLDv2查询消息(一般的查询、特定组播地址查询,特定组播地址和源查询)和“侦听者报告”报文。“侦听者报告”报文是向邻居路由器报告当前的组播侦听状态,或者声明侦听状态变化情况。
综上,MLDv2消息中的所有消息如下:
n 两种类型的MLDv2消息:
l 组播侦听查询(类型值=130)
l Version 2组播侦听报告(类型值=143)
n 为保证和MLDvl的兼容,MLDv2还要支持以下两种消息:
l Versionl组播侦听报告(类型值=131)[RFC2710]
l versionl组播侦听完成(类型值=132)[RFC2710]
以下详细介绍两种类型的MLDv2消息内容。
a) MLDv2侦听查询消息
组播侦听查询消息是在请求状态的路由器发出来的,它用来查询邻居接口的侦听状态。它的消息格式如图2-24所示:
图2-24 MLDv2侦听查询消息
(1)编码
被发送者初始化为0,接收者被忽略。
(2)校验和
标准的ICMPv6校验和;算它,它需要计算整个消息,包括IPv6的伪造报头。为了计算它,首先把它设置为0。收到包后,要先验证后处理。
(3)最大响应延迟
最大响应延迟编码域指定了在发送响应报告的允许的最大延迟。实际时间叫做最大响应延迟,单位为毫秒。它们之间编码方式如下:
l 如果最大响应延迟编码<32768,则最大响应延迟=最大响应延迟编码。
l 如果最大响应延迟编码>=32768,则最大响应延迟编码构成如图2-25所示:
图2-25 最大响应延迟编码构成图
而且最大响应延迟=(mant| 0x1000) << (exp+3)。
值比较小的最大响应延迟允许MLDv2路由器调整“离开潜伏期”(链路上最后一个节点停止侦听某个特定的组播地址到路由协议认为对于这个地址没有侦听者的时间)。值比较大的最大响应延迟,尤其是编码的那些,是用来调整在链路上的爆炸性的MLD消息的传输。
(4)预留
被发送者初始化为0,被接收者忽略。
(5)组播地址
对于一般的查询消息,组播地址为0。对于特定组地址或者特定组地址和源的查询消息来说,组播地址被设置成需要查询的地址。
(6)S Flag(抑制路由器端的处理)
如果设置成1,则表示对于所有接收在收到查询消息时抑制计时器更新。不过,并不抑制选举查询者和主机端对一类查询消息的处理,这类查询是路由器请求实现组播侦听者的功能。
(7)QRV(查询者活跃变量)
如果非0,那么查询者活跃变量域包含查询者使用的活跃变量值。如果活跃变量大于7(查询者活跃变量域的最大值),查询者活跃变量域被设置为0。
路由器把近期收到的查询消息中的查询者活跃变量值设置为自己的活跃变量值,除非近期收到的查询消息中多数的查询者活跃变量值都为0。在多数的查询者活跃变量值都为0的清况时,就使用缺省值,或者静态配置特定的值。
(8)QQIC(查询者查询间隔编码)
查询者查询间隔编码域指定了查询者使用的查询间隔。实际间隔被叫作查询者查询间隔(QQI),它是以秒为单位的。它们之间的转换如下:
l 如果QQIC<128,那么QQI=QQIC。
l 如果QQIC>=128,则查询者查询间隔编码构成如图2-26所示:
图2-26 查询者查询间隔编码构成图
而且QQI = (mant | 0x10) << (exp+ 3)。
不是当前查询者的组播路由器把最新收到的查询消息中的QQI值作为自己的查询间隔时间,除非近期收到的查询消息中多数的QQI值都为0。在都为0情况时,就使用缺省值
(9)源个数(N)
源个数(N)指出在当前处在查询的源地址的个数。在一般查询消息或者特定组播地址的查询消息中源个数都为0,在特定源和组地址的查询消息中不为0。源个数的大小由链路上可传输的最大报文决定。例如,在Ethernet上,可传输的最大报文是1500字节,携带路由警报选项的8字节的Hop-By-Hop扩展头和40字节的IPv6头一共48字节;源个数域前的MLD头共28字节,这样留给源个数域的有1424字节,所以源个数最大为89(1424/16)。
(10)源地址[i]
源地址[i]域是指向n的单播地址,n既是源个数域中的值。
(11)附加数据区
如果收到的查询消息中的IPv6头的负载长度域指定了需要有附加数据存在时,执行MLDv2时必须包含附加字节,用来确认收到MLD消息的校验和,否则就忽略附加字节。当发送查询消息时,一定不含附加消息。
(12)查询变量
查询消息中有三类查询变量:
l “一般查询消息”是查询路由器发出的,用来查询相连的链路上哪个组播地址有侦听者。组播地址和源个数都为0。
l “特定组地址查询消息”是查询路由器发出的,用来查询相连的链路上某个特定的组播地址是否有侦听者。在特定组地址查询消息中,包括希望查询的组地址,源个数为0。
l “特定组地址和源查询消息”是查询路由器发出的,用来查询相连的链路上某个特定的组播地址并且是来自特定的源的消息是否有侦听者。在特定组地址和源查询消息中,包括希望查询的组地址,源地址列表包含关注的源地址。
(13)查询消息源地址
所有MLDv2的查询消息的源地址为可用的IPv6链路本地地址。如果节点(路由器或主机)收到的来自不明的地址的查询消息,或者不是可用的IPv6链路本地地址时,它就丢弃这个消息,同时记录警报。
(14)查询消息目的地址
在MLDv2协议中,一般查询消息是发给链路内所有节点的地址的(FF02::1)。特定组地址查询消息和特定组地址和源查询消息都发给希望查询的组地址。同时节点必须接收和处理所有的IP目的地址是任意的单播或组播地址的查询消息,这样有利于调试。
MLDv2侦听查询报文在网络中传输完整的报文格式如图2-27:
图2-27 在网络中传输的MLDv2侦听查询报文
b) MLDv2组播侦听报告
MLDv2组播侦听报告是IP节点发送的,它可以用来向邻居报告当前的侦听状态和以身改变接口上的组播侦听状态。消息格式如图2-28所示:
图2-28MLDv2组播侦听报告报文
组地址记录的消息格式如图2-29所示:
图2-29 组地址记录的消息格式
(1) 类型字段,在MLDv2侦听报告置为143。
(2) 预留,被发送者初始化为0,被接收者忽略。
(3) 校验和
标准的ICMPv6校验和;它需要计算整个消息,包括IPv6的伪造报头。为了计算它,首先把它设置为0。收到包后,要先验证后处理。
(4) 组播地址记录个数(M)
组播地址记录个数(M)指出在当前侦听报告中的组播地址一记录的个数。
(5) 组播地址记录
组播地址记录是由来自报告发送的接口的发送者侦听每个组播地址的信息组成。具体记录如下:
① 记录类型
当前状态报告是用来在收到查询消息的接口上节点发出的消息。它报告接口对于每一个组地址的当前的侦听状态。以下两类,如表2-2所示
表2-2 当前状态报告类型
值
名字
作用
1
包括模式
指明在当前的接口上对于特定的组地址的过滤模式是包括模式的。相应的源地址域是对于特定的组地址的列表必须为非空。
2
排除模式
指明在当前的接口上对于特定的组地址的过滤模式是排除模式的。如果组地址列表为非空,则组地址域包含接口的对于特定组地址的源列表。
过滤模式变化报告是接点在本地的接口层对于特殊的组地址状态变化时引起的IPv6 Multicast Listen请求而发出的报告,这种报告无论源地址是否发生变化都要发送。只有状态变化的接口才发送这类的报告。以下两类,如表2-3所示:
表2-3 过滤模式变化报告
值
名字
作用
3
变为包括模式
指明在当前的接口上对于特定的组地址的过滤模式转变为包括模式的。如果组地址列表为非空,则组地址域包含接口的对于特定组地址的新的源列表。
4
变为排除模式
指明在当前的接口上对于特定的组地址的过滤模式转变为排除模式的。如果组地址列表为非空,则组地址域包含接口的对于特定组地址的新的源列表。
源列表改变报告是节点发出的,当源列表改变,过滤模式和组地址不改变时会触发发送此消息。以下两类,如表2-4示
表2-4 源列表改变报告
值
名字
作用
5
允许新的源
指明在当前组地址记录中的源地址域需要增加新的源。若是在包含模式下,则把这些源加入到列表中。若是在排除模式下,则把这些源从列表中删除。
6
阻止旧的源
指明在当前组地址记录中的源地址域需要删除旧的源。若是在包含模式下,则把这些源删除到列表中。若是在排除模式下,则把这些源从列表中删除加入。
② 附加数据长度
附加数据长度域包含在当前在侦听报告中的附加数据长度,单位是字(32bits)。如果为0,则表示没有附加数据。
③ 源个数(N)
源个数(N)指出在当前在侦听报告中的源地址记录的个数。
④ 组播地址
组播地址域包含组播侦听报告中有的组播地址。
⑤ 源地址[i]
源地址[i]域是指向n的单播地址,n既是源个数域中的值。
⑥ 辅助数据区
如果存在辅助数据区,那么它是用来补充说明组播地址报告的。在MLDv2协议中没有定义间接寻址数据,所以不需要包括辅助数据区。因此设置辅助数据区为0,收到时也忽略。留出这样一部分辅助数据区是为了以后能够扩展MLD。
MLDv2组播侦听报告报文在网络中传输完整的报文格式如图2-30:
图2-30 在网络中传输的MLDv2组播侦听报告报文
2.2.3 MLDv2工作过程
MLDv2的工作过程可以分成三部分:一是侦听者端建立“组播侦听状态”;二是侦听者端和路由器端之间交换各种报文;三是路由器端建立侦听者状态列表。
a) 建立侦听状态
侦听者根据自身的需要指定它所感兴趣的组播组和组的源地址,建立自身的侦听状态,即加入列表或排除列表,然后主动或被动通知邻居组播路由器它的侦听状态。
侦听者执行MLDv2协议的“侦听部分”的功能。无论节点上的接口是否在同一链路上.每个接口上都会执行侦听者功能。运行在侦听节点上层的协议或者应用通过“套接字组播状态”函数IPv6 Multicast Listen告知接口启用或禁用组播功能。
IPv6MuhieastListen的格式如下:
IPv6MulticastListen(套接口,接口,IPv6组播地址,过滤模式,源列表)
套接口:用来区分节点内不同的组播请求实体(如进程用程序等)。
接口:表示一个本地网络接口.通过该接口可以启用或禁用组播功能。
IPv6组播地址:表示请求加入的组播地址。如果一个接口接收多于一个组播的分组,IPv6Multicast Listen就会被每个组播单独调用。
过滤模式:过滤模式分为“INCLUDEMODE”(包含模式)和“EXCLUDEMODE”(排除模式)两种。
源列表:一个无序的地址列表,可以为空。
除了“套接字组播状态”之外,节点必须维护或计算出每个接口的组播侦听状态,称为“接口组播状态”,表示为(IPv6组播地址,过滤模式,源列表)。“接口组播状态”从“套接字组播状态”推导出来。从“套接字组播状态”导出“接口组播状态”的一般规律如下:根据具有相同接口(IPv6组播地址)的“套接字组播状态”记录,推导出一个“接口组播状态”。
(1)若有“套接字组播状态”记录处于排除模式,那么“接口组播状态”的过滤模式就是“排除模式”,且“源地址列表”等于所有“排除模式”“套接字组播状态”记录中的源地址列表的交集,减去所有“包含模式”“套接字组播状态”记录中的源地址列表。
(2)若所有“套接字组播状态”记录都是“包含模式”,那么“接口组播状态”就是“包含模式”,且“源地址列表”等于所有“套接字组播状态”记录中源地址列表的并集。
侦听者通过上述算法可以建立起自己的“接口组播状态”。当接口收到组播分组之后,就会根据应用或者进程的“套接字组播状态”判断是否将数据分组传给上层。MLDv2报文不受源地址过滤影响,能够被所有的主机或路由器处理。
b) 交换各种报文
侦听者端和路由器端之间交换各种报文是协议的重要部分,在此过程中计时器会保持各个功能实体的可用性,若计时器到期会重新选举新的侦听者和查询器。
同一子网内的运行MLDv2协议的路由器首先通过1P地址最小的接口为查询接口的“查询器选举机制”选举出一台“查询器”(Querierrouter),其余的路由自然成为非查询路由器(Non-Querier router)。被当选为“查询器”的接口周期性的在子网内发布一般查询报文,触发性地发布特定组播地址查询报文和特定组播地址和源查询报文,查询邻居接口的组播侦听者发出的组播侦听状态路由器周期性地发送一般查询报文,以查询该链路上是否存在某组播地址的组成员。
在接收到收到路由器发送的一般查询报文后,节点经过[0,最大响应时间〕之间的随机时延后发出组播侦听报告,通过“当前状态报告”报告自己的“接口组播状态”。经过随机时延是为了防止所有的节点都在同一时间发出报告分组,从而避免网络的突发性阻塞。当侦听者端改变自己的接口组播状态时,如想要加入某个组播组时或改变对某个源的接收状态,就可以不必等待路由器的查询报文,而是直接向路由器发送报告报文。如果节点状态发生变化,会立刻发送并重传“状态变化报告”。在旧的“状态变化报告”重传过程中.如果又有新的状态变化发生,这个新的状态变化就会和原来的状态变化合并,然后组成新的“状态变化报告”重新发送,同时重置“重传次数”。节点新旧状态的变化引起的状态变化报告算法表如表2-5所示:
表2-5状态变化报告算法表
旧状态
新状态
状态改变报告
包含(A)
包含(B)
允许(B-A),阻止(A-B)
排除(A)
排除(B)
允许(A-B),阻止(B-A)
包含(A)
排除(B)
变成排除(B)
排除(A)
包含(B)
变成包含(B)
在表中A和B为源地址列表,“包含”和“排除”指的是接口过滤模式,“允许”的意思是希望侦听的新增的源地址列表,“阻止”的意思是希望不再侦听的旧的源地址列表。
路由器收到侦听者针对某组播的“状态变化报告”后,查询器会发送“组播地址指定查询”或者“组播地址和源地址指定查询”,以确定链路上是否还有其他侦听者,如果在“查询超时时钟”超时之前,收到其他侦听者的“当前状态报告”,则重置计时器。否则将修改相应的“组播侦听者状态”。
c) 建立侦听状态列表
通过侦听来自侦听者的报告报文,子网内所有路由器维持相同的“组播侦听状态”。当路由器收到链路上的报告分组时,如果报告地址不在路由器的列表上,则加入该项,否则计时器重新置位。路由器为它所在的每一条链路维护一个列表,除了组播地址列表外,还维护着每个组播地址相关的过滤模式、源地址列表、该地址相应的源计时器等信息。如果某个计时器过期,则把相应的表项从列表中删除。
通过收到的侦听者报告,路由器(包括查询器和非查询器)可以在其接口上建立自己的“组播侦听者状态”信息表。与侦听者不同,如果路由器有多个接口处于同一子网内,则只在其中一个接口建立“侦听者状态”信息。“侦听者状态”由过滤模式、过滤时钟和源地址列表三部分组成。如果链路上某一组播的所有侦听者都是“包含模式”。则接口处于该组播的“包含模式”,用INCLUDE(A)表示。其中A表示源地址列表,INCLUDE(A)称为“包含列表”。如果“包含模式”侦听者发送了包含特定源地址的“当前状态报告”或者“状态变化报告”,该源地址就会被加入到“包含列表”中。“包含列表”中每一个源地址都有“源地址时钟”,一旦“包含模式”侦听者发送报告证实自己在侦听着某一特定源地址.“源地址时钟”就会被更新。如果“包含列表”中的“源地址时钟”由于长时间得不到更新而到期,该源地址就会从“包含列表”中删除。这称为“软离开”机制。
除了“软离开”之外,还有一种“快速离开”机制。当“包含模式”侦听者通过“状态变化报告”声明不再侦听特定源地址时,链路上所有的路由器就会降低该源地址的“源地址时钟”。随后查询器会发出“组播地址和源地址指定查询”,用以验证该链路是否还有该源地址的侦听者。如果“源地址时钟”到期,该源地址就会从“包含列表”中删除。
如果链路上存在处于“排除模式”的侦听者,则接口处于该组播的“排除模式”。收到第一个“排除模式”侦听者的报文时起,路由器就设定该组播的“过滤时钟”。每当“排除模式”侦听者通过“当前状态报告”证实自己的侦听状态时,“过滤时钟”被更新。当一个“包含模式”侦听者,通过“状态变化报告”声明自己变为“排除模式”时,该“过滤时钟”也会被更新。如果“过滤时钟”过期,路由器便将侦听者状态切换成“包含模式”。处于“排除模式”的路由器接口。侦听者状态由符号Exclude(X,Y)表示,其中X表示“请求列表”,Y表示“排除列表”。除了“排除列表”之外所有的源地址发出的组播分组,都会被路由器转发。虽然“请求列表”对于转发分组没有任何影响,但是路由器必须保持“请求列表”。原因如下:
(1)为了标明“包含模式”侦听者所侦听的源地址。当链路中没有“排除模式”侦听者时,能及时将路由器转换成“包含模式”。这种转换不应该中断向该组播“包含模式”侦听者传输的数据流。当路由器切换成“包含模式”时。“请求列表”中的源地址被转移到“包含列表”中,“排除列表”被删除。
(2)用来对原来没有“排除”的源地址进行快速的“排除”。如果路由器接收到一个特定源地址请求的报告。源地址就会被加入到“请求列表”,源地址时钟被赋值.随后查询器发出“组播地址和源地址指定查询”。用以检测该链路上是否还有对这些源地址感兴趣的节点。如果源地址的时钟就会到期时。仍没有侦听者响应。这时,这些源地址就会从“请求列表”移到“排除列表”。从这时起,该源地址就会被路由器阻断[15]。
-
高等代数--线性方程组
2020-08-12 16:58:44高等代数–线性方程组 消元法 明确基础名词的含义: 未知量,方程个数,系数,常数项,方程的解,解集合,同解,一般解,自由未知量,系数矩阵,增广矩阵。 消元法实际上是反复的对方程组进行如下的三种变换: 1.用... -
扩增子、宏基因组测序问题集锦
2017-07-01 11:52:23扩增子、宏基因组测序问题集锦原文转自诺禾致源,点我阅读原文。作者整理的非常好,值得学习。但本人又结合自己经验进行了修改,并对每个问题例举了实例和添加自己的理解(个人经验部分)。微生物,是地球上最古老的... -
狂神说Spring09:声明式事务
2020-04-24 11:23:03狂神说Spring系列连载课程,通俗易懂,基于Spring最新版本,欢迎各位狂粉转发关注学习。禁止随意转载,转载记住贴出B站视频链接及公众号链接!声明式事务回顾事务事务在项目开发过程非常... -
JavaScript 编程精解 中文第三版 四、数据结构:对象和数组
2018-05-01 23:12:36四、数据结构:对象和数组 原文:Data Structures: Objects and Arrays 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 部分参考了《JavaScript 编程精解(第 2 版)》 On two occasions I... -
超硬核!数据结构学霸笔记,考试面试吹牛就靠它
2021-03-26 11:11:21注意事项: 声明一个引用,同时必须初始化,及声明它代表哪一个变量。(作为函数参数时不需要初始化) 在声明一个引用后,不能再作为另一变量的引用。 3。不能建立引用数组。 1.2函数调用: 其实还是通过函数来理解... -
C语言0长度数组(可变数组/柔性数组)详解
2017-03-20 19:37:26C语言0长度数组(可变数组/柔性数组)详解 CSDN GitHub C语言0长度数组(可变数组/柔性数组)详解 AderXCoding/language/c/zero_length_array 本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议... -
-
华为C语言编程规范(精华总结)
2020-03-24 09:48:55头文件中应放置对外部的声明,如对外提供的函数声明、宏定义、类型定义等。 要求: 内部使用的函数(相当于类的私有方法)声明不应放在头文件中。 内部使用的宏、枚举、结构定义不应放入头文件中。 变量定义不应放在... -
JavaScript设计模式(一)单例模式、组合模式和外观模式
2016-07-25 00:18:09叶片包含绝大多数行为,但不能承载子项,至少在传统的组合示例中不可以。 图 1. 组合结构 另一个示例,我本人百分百确定您之前见到过组合模式,但从未真正进行... -
NOIP 2015 普及组 初赛
2016-11-22 10:07:12NOIP 2015 普及组 初赛 疑难点 学习 感悟。 -
数据结构编程笔记二:第一章 绪论 三元组的程序实现
2017-08-11 13:09:48第一章主要是让大家入门,理解ADT的作用,所以书的作者没有安排太难的程序,只安排了三元组。 写C的童鞋都知道,写指针程序是个麻烦事,且不说调试遇到的各种奇葩问题,仅仅是打*号字符就是一个麻烦,每次手都必须... -
63 Three.js 将多个网格合并成一个网格
2018-03-14 20:11:22多数情况下使用组可以很容易地操纵和管理大量网格。但是当对象的数量非常多时,性能就会成为一个瓶颈。使用组,每个对象还是独立的,仍然需要对它们分别进行处理和渲染。通过THREE.Geometry.merge()函数,你可以将多... -
Matlab符号计算与方程组求解
2018-07-24 23:26:002、可应用范围有限:实际科研和生产中遇到的问题绝大多数都无法获得精确的符号解,这时我们不得不求助数值计算。 3、对待符号计算态度:用其来完成公式推导和解决简单的对计算时效性要求不高... -
Android清单文件详解(二) ---- 应用程序权限声明
2016-08-03 10:52:41我们知道,Android系统的各个模块...接下来,我们就开始讲解另一个非常重要的知识点——应用程序权限声明,其中主要包括应用程序的权限声明,自定义应用程序的访问权限和SDK版本限定。 1.——应用程序的权限申请 -
❤️ 爆肝三万字《数据仓库体系》轻松拿下字节offer ❤️【建议收藏】
2021-09-11 16:18:20作者主页:不吃西红柿 简介:CSDN博客专家、信息技术智库公号作者✌ 华为云享专家、HDZ核心组成员。 简历模板、PPT模板、学习资料、面试题库、技术互助,点击下方「信息技术智库」跳转文末都给你! 目录 信息技术... -
【白嫖党】如何把前端学好?看完这篇,直呼:太强了!(历经半个月之作)
2020-04-04 21:35:42很少有文章做到将知识形成一个体系,于是需要翻阅很多资料,但这篇文章做到了!共34815字数。 -
老大说不要在项目中使用存储过程
2022-02-09 15:03:12本文在存储过程技术使用方面不进行赘述,需要声明的是,任何技术都是双刃剑,不能搞一刀切。 农历2022年的第一篇文章,给大家拜个年。虎年大吉,一起暴富!祝冬奥会顺利落幕,中国加油! 今天南方阴雨绵绵,... -
世上最全计算机网络面试整理(附答案),不服来战!!
2021-07-15 17:33:08如果要对响应进行缓存,需要满足以下条件: 请求报文的 HTTP 方法本身是可缓存的,包括 GET 和 HEAD,但是 PUT 和 DELETE 不可缓存,POST 在多数情况下不可缓存的。 响应报文的状态码是可缓存的,包括:200, 203, ... -
使用的角度来讲
2022-03-15 14:32:03安卓手机连接其他设备,不管从app开发方面,还有使用方面都非常麻烦,而且...版权声明:本文为CSDN博主「是一只萨摩耶」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://bl -
【思维导图】前端开发JavaScript-巩固你的JavaScript知识体系
2020-03-29 20:53:29} 常量 使用const来声明一个只读的常量 const声明的变量不能直接修改值,但是对于对象和数组,却是不受保护可以修改的 一个完整的javascript实现由3个部分组成:核心ECMAScript,文档对象模型DOM,浏览器对象模型BOM...