zk 01之 ZooKeeper概述
ZooKeeper---译名为“动物园管理员”。动物园里当然有好多的动物,游客可以根据动物园提供的向导图到不同的场馆观赏各种类型的动物,而不是像走在原始丛林里,心惊胆颤的被动 物所观赏。为了让各种不同的动物呆在它们应该呆的地方,而不是相互串门,或是相互厮杀,就需要动物园管理员按照动物的各种习性加以分类和管理,这样我们才能更加放心安全的观赏动物。
回到企业级应用系统中,随着信息化水平的不断提高,企业级系统变得越来越庞大臃肿,性能急剧下降,客户抱怨频频。拆分系统是目前我们可选择的解决系统可伸缩性和性能问题的唯一行之有效的方法。但是拆分系统同时也带来了系统的复杂性——各子系统不是孤立存在的,它们彼此之间需要协作和交互,这就是我们常说的分布式系统0。各个子系统就好比动物园里的动物,为了使各个子系统能正常为用户提供统一的服务,必须需要一种机制来进行协调——这就是ZooKeeper(动物园管理员)。
Zookeeper的设计目的
我们知道要写一个分布式应用是非常困难的,主要原因就是局部故障。一个消息通过网络在两个节点之间传递时,网络如果发生故障,发送方并不知道接收方是否接收到了这个消息。他可能在网络故障迁就收到了此消息,也坑没有收到,又或者可能接收方的进程死了。发送方了解情况的唯一方法就是再次连接发送方,并向他进行询问。这就是局部故障:根本不知道操作是否失败。因此,大部分分布式应用需要一个主控、协调控制器来管理物理分布的子进程。目前,大部分应用需要开发私有的协调程序,缺乏一个通用的机制。协调程序的反复编写浪费,且难以形成通用、伸缩性好的协调器。协调服务非常容易出错,并很难从故障中恢复。例如:协调服务很容易处于竞态1甚至死锁2。Zookeeper的设计目的,是为了减轻分布式应用程序所承担的协调任务。
Zookeeper并不能阻止局部故障的发生,因为它们的本质是分布式系统。他当然也不会隐藏局部故障。ZooKeeper的目的就是提供一些工具集,用来建立安全处理局部故障的分布式应用。
ZooKeeper是一个分布式小文件系统,并且被设计为高可用性。通过选举算法和集群复制可以避免单点故障3,由于是文件系统,所以即使所有的ZooKeeper节点全部挂掉,数据也不会丢失,重启服务器之后,数据即可恢复。另外ZooKeeper的节点更新是原子的,也就是说更新不是成功就是失败。通过版本号,ZooKeeper实现了更新的乐观锁4,当版本号不相符时,则表示待更新的节点已经被其他客户端提前更新了,而当前的整个更新操作将全部失败。当然所有的一切ZooKeeper已经为开发者提供了保障,我们需要做的只是调用API。与此同时,随着分布式应用的的不断深入,需要对集群管理逐步透明化监控集群和作业状态,可以充分利ZK的独有特性。
Zookeeper简介
①Zookeeper是Google的Chubby一个开源的实现,是Hadoop的分布式协调服务
②它包含一个简单的原语集,分布式应用程序可以基于它实现同步服务,配置维护和命名维护等
③它的文件系统使用了我们所熟悉的目录树结构。
④Zookeeper的设计目的是为了减轻分布式应用程序所承担的协调任务
Zookeeper集群
ZK集群如下图2.1所示。这是实际应用的一个场景,该ZooKeeper集群当中一共有5台服务器,有两种角色Leader和Follwer,5台服务器连通在一起,客户端有分别连在不同的ZK服务器上。如果当数据通过客户端1,在左边第一台Follower服务器上做了一次数据变更,他会把这个数据的变化同步到其他所有的服务器,同步结束之后,那么其他的客户端都会获得这个数据的变化。
注意:
通常Zookeeper由2n+1台servers组成,每个server都知道彼此的存在。每个server都维护的内存状态镜像以及持久化存储的事务日志和快照。为了保证Leader选举能过得到多数的支持,所以ZooKeeper集群的数量一般为奇数。对于2n+1台server,只要有n+1台(大多数)server可用,整个系统保持可用。
2.3.1 集群中的角色
在ZooKeeper集群当中,集群中的服务器角色有两种Leader和Learner,Learner角色又分为Observer和Follower,具体功能如下:
1.领导者(leader),负责进行投票的发起和决议,更新系统状态
2.学习者(learner),包括跟随者(follower)和观察者(observer),
3.follower用于接受客户端请求并向客户端返回结果,在选主过程中参与投票
4.Observer可以接受客户端请求,将写请求转发给leader,但observer不参加投票过程,只同步leader的状态,observer的目的是为了扩展系统,提高读取速度。
5. 客户端(client),请求发起方
2 数据模型和分层的名字空间
ZooKeeper提供的名字空间与标准文件系统非常相似。名字是一个由斜杠/分隔的路径元素序列。ZooKeeper名字空间中的每个节点都由其路径标识。
3 Zookeeper中的节点
Znode会维护一个stat结构体,其中包含数据和ACL的版本号与时间戳,以便于进行缓存验证和协调更新。每次修改znode数据时,版本号会增长。客户端获取数据的时候,也同时获取数据的版本。
对znode数据的读写操作是原子的。读取操作获取节点的所有数据,写入操作替换所有数据。节点的访问控制列表(ACL)控制可以进行操作的用户。
ZooKeeper具有临时节点的概念。只要创建节点的会话是活动的,临时节点就存在。一旦会话终止,临时节点会被删除。临时节点对于实现tbd是很有用的。
一、Znode Znode是客户端访问Zookeeper的主要实体,每当Znode的数据改变时,他相应的版本号将会增加。
- stat: 此为状态信息, 描述该znode的版本, 权限等信息.
- data: 与该znode关联的数据.
- children:该znode下的子节点
(4)ZooKeeper的永久节点:该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除。
org.apache.zookeeper.CreateMode中定义了四种节点类型,分别对应:
- PERSISTENT:永久节点
- EPHEMERAL:临时节点
- PERSISTENT_SEQUENTIAL:永久节点、序列化
- EPHEMERAL_SEQUENTIAL:临时节点、序列化
ZooKeeper 节点是有生命周期的,这取决于节点的类型。在 ZooKeeper 中,节点类型可以分为持久节点(PERSISTENT )、临时节点(EPHEMERAL),以及时序节点(SEQUENTIAL ),具体在节点创建过程中,一般是组合使用,可以生成以下 4 种节点类型。
持久节点(PERSISTENT)
所谓持久节点,是指在节点创建后,就一直存在,直到有删除操作来主动清除这个节点——不会因为创建该节点的客户端会话失效而消失。
持久顺序节点(PERSISTENT_SEQUENTIAL)
这类节点的基本特性和上面的节点类型是一致的。额外的特性是,在ZK中,每个父节点会为他的第一级子节点维护一份时序,会记录每个子节点创建的先后顺序。基于这个特性,在创建子节点的时候,可以设置这个属性,那么在创建节点过程中,ZK会自动为给定节点名加上一个数字后缀,作为新的节点名。这个数字后缀的范围是整型的最大值。
临时节点(EPHEMERAL)
和持久节点不同的是,临时节点的生命周期和客户端会话绑定。也就是说,如果客户端会话失效,那么这个节点就会自动被清除掉。注意,这里提到的是会话失效,而非连接断开。另外,在临时节点下面不能创建子节点。
临时顺序节点(EPHEMERAL_SEQUENTIAL)
public enum CreateMode { /**
* 持久节点:节点创建后,会一直存在,不会因客户端会话失效而删除;
*/
PERSISTENT (0, false, false), /**
* 持久顺序节点:基本特性与持久节点一致,创建节点的过程中,zookeeper会在其名字后自动追加一个单调增长的数字后缀,作为新的节点名;
*/
PERSISTENT_SEQUENTIAL (2, false, true), /**
* 临时节点:客户端会话失效或连接关闭后,该节点会被自动删除,且不能再临时节点下面创建子节点,否则报如下错:org.apache.zookeeper.KeeperException$NoChildrenForEphemeralsException;
*/
EPHEMERAL (1, true, false), /**
* 临时顺序节点:基本特性与临时节点一致,创建节点的过程中,zookeeper会在其名字后自动追加一个单调增长的数字后缀,作为新的节点名;
*/
EPHEMERAL_SEQUENTIAL (3, true, true);
private static final Logger LOG = LoggerFactory.getLogger(CreateMode.class);
private boolean ephemeral;
private boolean sequential;
private int flag;
CreateMode(int flag, boolean ephemeral, boolean sequential) {
this.flag = flag;
this.ephemeral = ephemeral;
this.sequential = sequential;
}
public boolean isEphemeral() {
return ephemeral;
}
public boolean isSequential() {
return sequential;
}
public int toFlag() {
return flag;
}
static public CreateMode fromFlag(int flag) throws KeeperException {
switch(flag) {
case 0: return CreateMode.PERSISTENT;
case 1: return CreateMode.EPHEMERAL;
case 2: return CreateMode.PERSISTENT_SEQUENTIAL;
case 3: return CreateMode.EPHEMERAL_SEQUENTIAL ;
default:
LOG.error("Received an invalid flag value to convert to a CreateMode");
throw new KeeperException.BadArgumentsException();
}
}
}
二、Zxid
三、版本号
四,节点属性结构
4 条件更新和观察
ZooKeeper支持观察的概念。客户端可以在znode上设置观察。观察将在znode修改时被触发和移除。观察被触发时客户端会收到一个数据包,指示znode已经被修改。如果与ZooKeeper服务之间的连接断开,客户端会收到一个本地通知。这可用于tbd。
读操作exists、getChildren和getData都被设置了watch,并且这些watch都由写操作来触发:create、delete和setData。ACL操作并不参与到watch中。当watch被触发时,watch事件被生成,他的类型由watch和触发他的操作共同决定。ZooKeeper所管理的watch可以分为两类:
1.数据watch(data watches):getData和exists负责设置数据watch;
2.孩子watch(child watches):getChildren负责设置孩子watch;
我们可以通过操作返回的数据来设置不同的watch:
1.getData和exists:返回关于节点的数据信息
2.getChildren:返回孩子列表
因此,一个成功的setData操作将触发Znode的数据watch。
一个成功的create操作将触发Znode的数据watch以及孩子watch。
一个成功的delete操作将触发Znode的数据watch以及孩子watch。
watch由客户端所连接的ZooKeeper服务器在本地维护,因此watch可以非常容易地设置、管理和分派。当客户端连接到一个新的服务器上时,任何的会话事件都将可能触发watch。另外,当从服务器断开连接的时候,watch将不会被接收。但是,当一个客户端重新建立连接的时候,任何先前注册过的watch都会被重新注册。
exists操作上的watch,在被监视的Znode创建、删除或数据更新时被触发。
getData操作上的watch,在被监视的Znode删除或数据更新时被触发。在被创建时不能被触发,因为只有Znode一定存在,getData操作才会成功。
getChildren操作上的watch,在被监视的Znode的子节点创建或删除,或是这个Znode自身被删除时被触发。可以通过查看watch事件类型来区分是Znode还是他的子节点被删除:NodeDelete表示Znode被删除,NodeDeletedChanged表示子节点被删除。
watch设置操作及相应的触发器如图下图所示:
watch事件包括了事件所涉及的Znode的路径,因此对于NodeCreated和NodeDeleted事件来说,根据路径就可以简单区分出是哪个Znode被创建或是被删除了。为了查询在NodeChildrenChanged事件后哪个子节点被改变了,需要再次调用getChildren来获得新的children列表。同样的,为了查询NodeDeletedChanged事件后产生的新数据,需要调用getData。在两种情况下,Znode可能在获取watch事件或执行读操作这两种状态下切换,在写应用程序时,必须记住这一点。
(1)Zookeeper的watch实际上要处理两类事件:
1. 连接状态事件(type=None, path=null)
这类事件不需要注册,也不需要我们连续触发,我们只要处理就行了。
2. 节点事件
节点的建立,删除,数据的修改。它是one time trigger,我们需要不停的注册触发,还可能发生事件丢失的情况。
上面2类事件都在Watch中处理,也就是重载的process(Event event)
(2)节点事件的触发,通过函数exists,getData或getChildren来处理
这类函数,有双重作用:
1. 注册触发事件
2. 函数本身的功能
函数的本身的功能又可以用异步的回调函数来实现,重载processResult()过程中处理函数本身的的功能。
函数还可以指定自己的watch,所以每个函数都有4个版本。根据自己的需要来选择不同的函数,不同的版本。
3.3 ZooKeeper访问控制列表ACL
ZooKeeper使用ACL来对Znode进行访问控制。ACL的实现和Unix文件访问许可非常相似:它使用许可位来对一个节点的不同操作进行允许或禁止的权限控制。但是,和标准的Unix许可不同的是,Zookeeper对于用户类别的区分,不止局限于所有者(owner)、组 (group)、所有人(world)三个级别。Zookeeper中,数据节点没有“所有者”的概念。访问者利用id标识自己的身份,并获得与之相应的 不同的访问权限。
注意:
传统的文件系统中,ACL分为两个维度,一个是属组,一个是权限,子目录/文件默认继承父目录的ACL。而在Zookeeper中一个ACL和一个ZooKeeper节点相对应。并且,父节点的ACL与子节点的ACL是相互独立的。也就是说,ACL不能被子节点所继承,父节点所拥有的权限与子节点所用的权限都没有任何关系。
Zookeeper支持可配置的认证机制。它利用一个三元组来定义客户端的访问权限:(scheme:expression, perms) 。其中:
1.scheme:定义了expression的含义。
如:(host:host1.corp.com,READ),标识了一个名为host1.corp.com的主机,有该数据节点的读权限。
2.Perms:标识了操作权限。
如:(ip:19.22.0.0/16, READ),表示IP地址以19.22开头的主机,有该数据节点的读权限。
Zookeeper的ACL也可以从三个维度来理解:一是,scheme; 二是,user; 三是,permission,通常表示为scheme:id:permissions,如下图所示。
1.world : id格式:anyone。
如:world:anyone代表任何人,zookeeper中对所有人有权限的结点就是属于world:anyone的。
2.auth : 它不需要id。
注:只要是通过authentication的user都有权限,zookeeper支持通过kerberos来进行认证, 也支持username/password形式的认证。
3.digest: id格式:username:BASE64(SHA1(password))。
它需要先通过username:password形式的authentication。
4.ip: id格式:客户机的IP地址。
设置的时候可以设置一个ip段。如:ip:192.168.1.0/16, 表示匹配前16个bit的IP段
5.super: 超级用户模式。
在这种scheme情况下,对应的id拥有超级权限,可以做任何事情
ZooKeeper权限定义如下图所示:
ZooKeeper内置的ACL模式如下图所示:
当会话建立的时候,客户端将会进行自我验证。另外,ZooKeeper JavaAPI支持三种标准的用户权限,它们分别为:
1.ZOO_PEN_ACL_UNSAFE:对于所有的ACL来说都是完全开放的,任何应用程序可以在节点上执行任何操作,比如创建、列出并删除子节点。
2.ZOO_READ_ACL_UNSAFE:对于任意的应用程序来说,仅仅具有读权限。
3.ZOO_CREATOR_ALL_ACL:授予节点创建者所有权限。需要注意的是,设置此权限之前,创建者必须已经通了服务器的认证。
5 保证
ZooKeeper非常高效和简单。基于其目标:成为构建如同步这样的更复杂服务的基础,ZooKeeper提供下述保证:
在ensemble中的领导者和跟随着非常灵活,跟随者通过更新号来滞后领导者11,结果导致了只要大部分而不是所有的ensemble中的元素确认更新,就能被提交了。对于ZooKeeper来说,一个较好的智能模式是将客户端连接到跟着领导者的ZooKeeper服务器上。客户端可能被连接到领导者上,但他不能控制它,而且在如下情况时,甚至可能不知道。参见下图:
每一个Znode树的更新都会给定一个唯一的全局标识,叫zxid(表示ZooKeeper事务“ID”)。更新是被排序的,因此如果zxid的z1<z2,那么z1就比z2先执行。对于ZooKeeper来说,这是分布式系统中排序的唯一标准。
ZooKeeper是一种高性能、可扩展的服务。ZooKeeper的读写速度非常快,并且读的速度要比写快。另外,在进行读操作的时候,ZooKeeper依然能够为旧的数据提供服务。这些都是由ZooKeeper所提供的一致性保证的,它具有如下特点:
(1)顺序一致性
任何一个客户端的更新都按他们发送的顺序排序,也就意味着如果一个客户端将Znode z的值更新为值a,那么在之后的操作中,他会将z更新为b,在客户端发现z带有值b之后,就不会再看见带有值a的z。
(2)原子性
更新不成功就失败,这意味着如果更新失败了,没有客户端会知道。☆☆
(3)单系统映像
无论客户端连接的是哪台服务器,他与系统看见的视图一样。这就意味着,如果一个客户端在相同的会话时连接了一台新的服务器,他将不会再看见比在之前服务器上看见的更老的系统状态,当服务器系统出故障,同时客户端尝试连接ensemble中的其他机器时,故障服务器的后面那台机器将不会接受连接,直到它连接到故障服务器。
(4)容错性
一旦更新成功后,那么在客户端再次更新他之前,他就固定了,将不再被修改,这就会保证产生下面两种结果:
如果客户端成功的获得了正确的返回代码,那么说明更新已经成功。如果不能够获得返回代码(由于通信错误、超时等原因),那么客户端将不知道更新是否生效。
当故障恢复的时候,任何客户端能够看到的执行成功的更新操作将不会回滚。
(5)实时性
在任何客户端的系统视图上的的时间间隔是有限的,因此他在超过几十秒的时间内部会过期。这就意味着,服务器不会让客户端看一些过时的数据,而是关闭,强制客户端转到一个更新的服务器上。
解释一下:
由于性能原因,读操作由ZooKeeper服务器的内存提供,而且不参与写操作的全局排序。这一特性可能会导致来自使用ZooKeeper外部机制交流的客户端与ZooKeeper状态的不一致。举例来说,客户端A将Znode z的值a更新为a',A让B来读z,B读到z的值是a而不是a’。这与ZooKeeper的保证机制是相容的(不允许的情况较作“同步一致的交叉客户端视 图”)。为了避免这种情况的发生,B在读取z的值之前,应该先调用z上的sync。Sync操作强制B连接上的ZooKeeper服务器与leader保 持一致这样,当B读到z的值时,他将成为A设置的值(或是之后的值)
容易混淆的是:
sync操作只能被异步调用12。这样操作的原因是你不需要等待他的返回,因为ZooKeeper保证了任何接下去的操作将会发生在sync在服务器上执行以后,即使操作是在sync完成前被调用的。
这些已执行的保证后,ZooKeeper更高级功能的设计与实现将会变得非常容易,例如:leader选举、队列,以及可撤销锁等机制的实现。
6 简单的API
ZooKeeper的设计目标之一是提供非常简单的编程接口。ZooKeeper仅支持这些操作:
l create:在树中某位置创建一个节点。
l delete:删除一个节点。
l exists:测试某位置是否存在某节点。
l get data:读取节点数据。
l set data:向节点写入数据。
l get children:获取子节点列表。
l sync:等待数据传播。
关于这些操作的更深入讨论,以及如何使用它们来实现更高层的操作,请参看tbd。
7 实现
下图显示了ZooKeeper服务的高层组件。除了请求处理器(Request Processor)之外,组成ZooKeeper服务的每个服务器拥有每个组件的自有拷贝。
自我复制数据库(replicated database)是一个包含整个数据树的内存数据库。更新会记录到磁盘中以便可以恢复,并且将写操作应用到内存数据库之前会先写入到磁盘。
每个ZooKeeper服务器都为客户服务。客户端连接到一个服务器,提交请求。读请求由每个服务器数据库的本地拷贝进行服务。改变服务状态的请求和写请求由一致性协议处理。
作为一致性协议的一部分,客户端的所有写请求都被转发到单个服务器,也就是领导者。其他ZooKeeper服务器则是跟随者,它们接收来自领导者的建议,对传递的消息达成一致。消息层考虑了替换失败的领导者和跟随者与领导者同步的问题。
ZooKeeper使用定制的原子消息协议。因为消息层是原子的,ZooKeeper可保证本地拷贝不会发散(diverge)。收到写请求时,领导者计算写入操作后系统的状态,将其转换成一个捕获此状态的事务。
8 使用
ZooKeeper的编程接口非常简单。但是,可将其用于实现高层顺序操作,如同步原语、组成员管理、所有者关系管理等。更多信息请参看tbd。
9 性能
ZooKeeper被设计为高性能的。但它真的是高性能的吗?Yahoo研究中心的ZooKeeper开发团队证实了ZooKeeper的高性能,特别是在读操作比写操作多的应用中(见下图),因为写操作涉及在所有服务器间同步状态。(读操作比写操作多是协调服务的典型情况)
上图是ZooKeeper 3.2在配置有两个2GHz Xeon处理器和两个SATA 15K RPM驱动器的服务器上运行时的吞吐率图形。一个驱动器配置为ZooKeeper日志专用设备。快照写入到操作系统驱动器。读写操作1KB的数据。“服务器数”指的是ZooKeeper集群的大小,即组成服务的服务器个数。大约30个其他服务器用于模拟客户端。ZooKeeper集群配置为不允许客户端连接到领导者。
提示:3.2版的读写性能是3.1版的2倍。
Benchmarks也表明ZooKeeper是可靠的。下图(第10节的图)显示了ZooKeeper在各种失败情况下的反应。图中标记的各个事件是:
1.跟随者失败和恢复
2.另一个跟随者失败和恢复
3.领导者失败
4.两个跟随者失败和恢复
5.另一个领导者失败
10 可靠性
为揭示在有失败注入时系统的行为,我们在一个由7台机器组成的ZooKeeper服务上运行和先前一样的benchmark测试,但是让写操作的百分比固定为30%,这是预期负载比例的保守估计。
此图有几处值得仔细观察。首先,如果跟随者失败后快速恢复,则ZooKeeper可以维持高吞吐率。但更重要的是,领导者选举算法让系统可以足够快地恢复,以阻止吞吐率有实质性的下降。据我们观察,ZooKeeper选举一个新的领导者的时间小于200ms。第三,一旦跟随者恢复并且开始处理请求,ZooKeeper可以恢复高吞吐率。
11 ZooKeeper工程
ZooKeeper已经在很多工业应用中成功使用。Yahoo!在Yahoo! Message Broker中使用ZooKeeper作为协调和故障恢复服务。Yahoo! Message Broker是一个高度扩展的发布-订阅系统,管理着成千上万个需要拷贝和数据传递的话题。Yahoo!的很多广告系统也使用ZooKeeper来实现可靠服务。
我们鼓励用户和开发者加入社区,贡献技能。更多信息请看Apache的ZooKeeper工程。
zk 01之 ZooKeeper概述的更多相关文章
- ZooKeeper概述
1.Zookeeper概述 Zookeeper 是 Google 的 Chubby一个开源的实现,是 Hadoop 的分布式协调服务.它包含一个简单的原语集,分布式应用程序可以基于它实现同步服务,配置 ...
- Zookeeper概述和基本概念
一.Zookeeper背景 随着互联网技术的发展,企业对计算机系统的计算,存储能力要求越来越高,各大IT企业都在追求高并发,海量存储的极致,在这样的背景下,单纯依靠少量高性能单机来完成计算机,云计算的 ...
- ZooKeeper概述与安装
ZooKeeper笔记 ZooKeeper概述 背景: 现代企业对计算机系统的计算存储能力要求越来越高,单纯的高性能服务器已经无法满足要求.企业的IT架构从集中式向分布式过度. 所谓分布式,就是将一个 ...
- 【WCF--初入江湖】01 WCF编程概述
01 WCF编程概述 SOA的优点 1.服务独立于平台和工作环境.服务并不关心自己所处的环境,也不关心与之进行通信的服务所处的 环境. 2.服务相互隔离. 3.服务对协议.格式和传输中立. 4. ...
- Zookeeper概述、特点、数据模型
Zookeeper 1.Zookeeper概述 Zookeeper是一个工具,可以实现集群中的分布式协调服务. 所谓的分布式协调服务,就是在集群的节点中进行可靠的消息传递,来协调集群的工作. Zo ...
- 大数据之Zookeeper概述
Zookeeper概述 Zookeeper是一个开放源码的分布式应用程序协调服务,是 Google的Chubby一个开源的实现,是 Hadoop和 HBASE的重要组件.主要解决分布式应用一致性问题. ...
- 第01章 Spring概述
第01章 Spring概述 1.Spring概述 ①Spring是一个开源框架 ②Spring为简化企业级开发而生,使用Spring,JavaBean就可以实现很多以前要靠EJB才能实现的功能.同样的 ...
- 039 01 Android 零基础入门 01 Java基础语法 05 Java流程控制之循环结构 01 循环结构概述
039 01 Android 零基础入门 01 Java基础语法 05 Java流程控制之循环结构 01 循环结构概述 本文知识点:循环结构概述 循环结构主要内容 while 循环 do-whiile ...
- 034 01 Android 零基础入门 01 Java基础语法 04 Java流程控制之选择结构 01 流程控制概述
034 01 Android 零基础入门 01 Java基础语法 04 Java流程控制之选择结构 01 流程控制概述 本文知识点:Java中的流程控制相关概念的认识 三大流程控制语句结构的简介 顺序 ...
随机推荐
- 一条长l的笔直的街道上有n个路灯,若这条街的起点为0,终点为l,第i个路灯坐标为ai,每盏灯可以覆盖到的最远距离为d,为了照明需求,所有灯的灯光必须覆盖整条街,但是为了省电,要是这个d最小,请找到这个最小的d。
// ConsoleApplication3.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> ...
- 2018年EMUI系统能力分论坛来啦
为鼓励开发者创新,挖掘前沿创新能力的应用及服务,帮开发者打造爆款应用的同时丰富终端消费者的用户体验,由设立10亿激励基金耀星计划扶持的华为创新竞赛平台即将开启. 竞赛平台将滚动推出AI.HAG.AR. ...
- (个人开源)ffpanel --ffmpeg的GUI,让ffmpeg离开黑黑的命令行
程序及源代码下载地址 :https://github.com/langsim/ffpanel
- WinForm程序打包工具InnoSetup使用说明图文教程
WinForm程序打包工具InnoSetup使用说明图文教程 WinForm程序开发测试好了,如果将Debug/Release里面的文件发给客户使用,会让客户觉得你不够专业,但是使用VS自带的打包工具 ...
- Jenkins:使用Git Parameter插件实现tag或分支的选择性构建
Jenkins如何选择任意一个tag来构建代码 Jenkins如何选择任意一个branch(分支)来构建代码 苦恼了一段时间后,发现Git Parameter插件实现可以帮助我们来实现. 下面来介绍如 ...
- Spring MVC获得HttpServletRequest
以下代码是获得Spring MVC中的HttpServletRequest ServletRequestAttributes attr = (ServletRequestAttributes) Req ...
- 转载 -- iOS开发之JSON格式数据的生成与解析
本文将从四个方面对IOS开发中JSON格式数据的生成与解析进行讲解: 一.JSON是什么? 二.我们为什么要用JSON格式的数据? 三.如何生成JSON格式的数据? 四.如何解析JSON格式的数据? ...
- VI带行号查看
:set nu 带行号查看,并不改变文件内容 :set nonu 取消带行号查看 在每个用户的主目录下,都有一个 vi 的配置文件".vimrc"或 ...
- 【分享】自己写的一个可空的DateTimePicker控件-附源码
最近这段时间在重构以前的一个项目,其中有一项就是要把DateTimePicker控件值可空.大家都知道的DateTimePicker值为DateTime类型,DateTime类型值不能等于Null.但 ...
- string 转 LPCTSTR
(1)在ANSI字符集下 LPCTSTR想当于LPCSTR,当中L指long.P指Point,C指Const.在程序中能够直接用char*类型的数据对LPCSTR进行赋值,用下述语句: LPCSTR ...