打造一个全命令行的Android构建系统

“IDE都是给小白程序员的,大牛级别的程序员一定是命令行控,终端控,你看大牛都是使用vim,emacs 就一切搞定”

这话说的虽然有些绝对,但是也不无道理,做开发这行要想效率高,自动化还真是缺少不了命令行工具,因为只有命令行才是最佳的人机交互工具。其实IDE也是底层也是调用命令行工具而已,只不过给普通开发者呈现一个更友好的开发界面。这里可不是宣扬让大家放弃IDE都改命令行,只是每种事物都有他存在的理由,无论是编程语言还是工具都是一个原则 “没有最好的,只有最合适的”。

打造一个全命令行的Android构建系统
打造一个全命令行的Android构建系统

前一段时间做一个人产品 http://xbrowser.me ,发布产品的时候为了统计各渠道流量免不了要构建不通的渠道包,你懂得国内渠道上百个,靠IDE编译打包非吐血不可。这些重复劳动最适合交个程序来做,很多程序员想不明白这个问题,宁愿把大量的精力时间花在业务上,却不知道用工具提高工作效率。在这里写一篇简单的教程,告诉大家怎么脱离IDE环境完成一个android项目的编译构建,有了这基础开发什么自动化构建工具都不是什么难事了, 前一阵子做的一个打包html5应用的在线工具AppBuilder就是基于命令行构建完成的。

说到命令行自然是不需要图形界面,所以Android SDK的安装下载自然都是在终端上进行。下面是本文中使用的一些SDK和基本环境。

  • ubuntu server 14.04 (64位)
  • JDK 1.7
  • android-sdk_r24.0
  • gradle-2.2.1

进入正题,接下来一步一步介绍如何安装配置一个命令行下的编译构建系统.

step 1 安装 JDK 环境

配合android的JDK最好选用JDK官方版本而不是Open JDK,下面是在unbuntu下安装JDK 1.7的方法。

sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo apt-get install oracle-java7-installer

step 2 安装 Android SDK

android sdk 工具包的一些命令行工具是基于32位系统的,在64为平台运行32程序必须安装 i386 的一些依赖库,方法如下:

sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1

安装完成32位的依赖库后,我们使用wget 去官方下载最新的linux下android SDK包。

cd ~
wget http://dl.google.com/android/android-sdk_r24.0.1-linux.tgz
tar xvzf android-sdk_r24.0.1-linux.tgz

编辑 .profile 或者 .bash_profile 把下面的目录增加到 path的搜索路径中,确保android SDK的的一些命令工具可以直接在终端使用,比如 adb 命令。

ANDROID_HOME=$HOME/android-sdk-linux
export PATH="$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools"
exprot ANDROID_HOME

使环境变量生效

source ~/.profile

环境变量生效后,你可以使用android命令 列出sdk相关的列表,以便我们选择和自己项目匹配的SDK版本。(刚才只是安装了最基础的SDK,要完全满足你的开发环境需要还得从下面的列表中选择你需要的SDK和工具更新下载)

android list sdk --all

输出如下所示:

   1- Android SDK Tools, revision 24.0.1
   2- Android SDK Platform-tools, revision 21
   3- Android SDK Build-tools, revision 21.1.2
   4- Android SDK Build-tools, revision 21.1.1
   5- Android SDK Build-tools, revision 21.1
   6- Android SDK Build-tools, revision 21.0.2
   7- Android SDK Build-tools, revision 21.0.1
   8- Android SDK Build-tools, revision 21
   9- Android SDK Build-tools, revision 20
  10- Android SDK Build-tools, revision 19.1
  11- Android SDK Build-tools, revision 19.0.3
  12- Android SDK Build-tools, revision 19.0.2
  13- Android SDK Build-tools, revision 19.0.1
  14- Android SDK Build-tools, revision 19
  15- Android SDK Build-tools, revision 18.1.1
  16- Android SDK Build-tools, revision 18.1
  17- Android SDK Build-tools, revision 18.0.1
  18- Android SDK Build-tools, revision 17
  19- Documentation for Android SDK, API 21, revision 1
  20- SDK Platform Android 5.0.1, API 21, revision 2
  21- SDK Platform Android 4.4W.2, API 20, revision 2
  22- SDK Platform Android 4.4.2, API 19, revision 4
  23- SDK Platform Android 4.3.1, API 18, revision 3
  24- SDK Platform Android 4.2.2, API 17, revision 3
  ....

这里包括不同的Android API 版本和不同的构建工具,选择你想要安装项目的序号,这里我想安装 build tools 19.1 ,build tools 21 及 android 4.2.2以上的SDK所以选择序号 “1,2,3,20,21,22,23”

android update sdk -u -a -t  1,2,3,10,20,21,22,23

step 3 安装gradle构建环境

使用Ant构建项目已经是过去式了,这里我们选用更加强悍和方便的构建工具gradle 。

下载 grdle 二进制包

cd ~
wget https://services.gradle.org/distributions/gradle-2.2.1-bin.zip

释放到本地Home目录,创建名字为”gradle”的符号链接,符号连接的好处是方便版本更新,有了新的版本直接修改符号链接即可。

 unzip gradle-2.2.1-bin.zip
 ln -s gradle-2.2.1 gradle

配置gradle环境变量并使其生效,编辑 ~/.profje 文件增加下面内容

GRADLE_HOME=$HOME/gradle
export PATH=$PATH:$GRADLE_HOME/bin

保存后使环境变量使其生效

source ~/.profile

环境变量生效后你可以在终端敲入’gradle’命令并运行用以检测gradle是否安装成功。

gradle

如果安装配置的没有问题将会提示类似下面的信息

:help
Welcome to Gradle 2.2.1
To run a build, run gradle  ...
To see a list of available tasks, run gradle tasks
To see a list of command-line options, run gradle --help
BUILD SUCCESSFUL

验证是否可以编译android 应用

完成以上的环境配置,我们的一个Android下的基础构建环境就全部配置好了,接下来做的事情就是尝试使用gradle 编译一个android 应用出来验证我的编译环境是否OK,下载我写的一个gadle demo 例子进行测试。

git clone https://github.com/examplecode/gradle_demo
cd gradle_demo/hello-apk-with-gradle
gradle build

一切顺利的话,编译完成后,你会在 “hello-apk-with-gradle/build/outputs/apk” 目录下找到编译好的apk包。至于如何整合到你自己的项目中去,只需要仿照例子给你的项目提供一个”gradle.build” 脚本即可。

来源:http://www.jianshu.com/p/1694ea9a3f90

Linux:全方位对比 Mesos、Omega 和 Borg

谷歌最近公布了他们基础设施系统王冠上的宝石之一:Borg,集群调度系统。这促使我重新阅读了MesosOmega论文,它们与Borg的功能类似。我觉得对比下这三个系统一定会非常有趣。Mesos两级调度的突破性理念得到了认可,Omega使用类似数据库的技术有所改进,Borg可以看作是对所有这些思想的巅峰之作。

Linux:全方位对比 Mesos、Omega 和 Borg
Linux:全方位对比 Mesos、Omega 和 Borg

背景

集群调度系统在大数据出现之前就存在已久。在高性能计算(HPC)的世界中,关于上千核CPU的调度有丰富的文献,但他们的问题域比数据中心调度系统要解决什么更简单,到底该用Mesos/Borg还是与他们同类的其他产品。让我们从几个维度对他们进行一次对比。

为具体位置而调度

超级计算机将存储和计算分离,并使用近似于全等分带宽的网络连接起来,其运行速度接近内存速度(GB/秒)。这意味着我们的任务可以放置在集群上的任何地方,而不必考虑具体位置,因为所有计算节点都可以同样快速地访问数据。一些超优化的应用优化了网络拓扑,但这毕竟是非常罕见的。

数据中心调度系统会关心具体位置,实际上,这是GFS和MapReduce共同设计的结果。回眸2000年初,那时的网络带宽远比磁盘带宽昂贵。所以,为了经济上极大的节约,调度算法会将计算任务保持在与数据相同的节点上。这是调度主要的约束;在我们可以把任务放到任何地方之前,需要将其放在三个数据副本之一的节点上。

硬件配置

超级计算机通常由同质节点组成的,即它们都具有相同的硬件规格。这是因为,超级计算机一般是一次性购买的:实验室获得几百万美元的采购费用,然后一次全部预支出去。一些高性能计算应用的优化是针对超级计算机中特定型号的CPU的。新技术,如图形处理器和协处理器的推出,就要作为一个新的集群。

在大数据领域,集群主要受存储限制,因此运维不断地增加新的机架,更新规格来扩展群集容量。这意味着节点可以有不同的CPU、内存容量、磁盘数量等。这样的节点还可以配入指定的附加设备,如固态硬盘、图形处理器、重叠驱动器(shingled drive)等。一个数据中心可能要支持广泛的应用,而这一切又会强加额外的调度约束。

队列管理和调度

在超级计算机上运行应用程序时,你需要指定想要多少个节点、要提交作业的队列,以及作业将运行多长时间。队列存储可以请求多少资源、作业可以运行多久等不同的限制。同时,队列也有优先级或基于系统的预留来确定排序。由于作业的持续时间都是已知的,这是一个非常简单的打包到盒子的问题。如果队列很长(通常就是这样),并有可以很好融合的小作业回填大作业(也是典型)的剩余空间,则可以实现非常高的资源利用率。我很喜欢将这一描述用以时间为X轴,以资源使用为Y轴的可视化2D图呈现出来。

如前所述,数据中心的调度是一个比较普遍的问题。资源请求的“形状”可以是相当多样,并有更多的维度。作业也没有设定持续时间,所以很难预先计划队列。因此,我们有更复杂的调度算法,从而,调度器的性能变得非常重要。

利用率作为一般的规则将变得糟糕(除非你是谷歌,以及更多的后来者),但高性能计算之上的应用有一个优势是可以使用MapReduce和类似的技术逐渐代替组调度。高性能计算,会等到请求所需的N个节点都可用后,同时运行所有任务。MapReduce可以在多次波动中运行它的任务,这意味着它仍然可以有效地利用剩余的资源。单一的MR作业也可以基于集群的需求起伏,避免了抢占或资源预留,并且还有助于实现多用户之间的公平性。

Mesos

Mesos的出现早于YARN,并且在设计时考虑了原有MapReduce的问题。当时,Hadoop集群只可以运行一种单一的应用:MapReduce。这使得它很难运行不符合先是map阶段后是reduce阶段的应用程序。这里最好的例子是Spark。在Mesos出现之前,你必须为Spark安装一套全新的worker和master,这一套设置与MapReduce的worker和master放在一起。这么做,从利用率的角度来看非常不理想,因为他们通常是静态分区的。

通过为所有集群应用提供一个通用调度器,Mesos解决了这一问题。MapReduce和Spark被简单地作为不同的应用程序,他们可以使用相同的基础资源,共享Mesos的Framework。最简单的实现方法是写一个集中式的调度器,但具有如下一些缺点:

  • API的复杂性:我们需要一个单一的API,它是所有已知Framework调度器API的超集。这本身就是一个难题。资源请求也会变得非常复杂。
  • 性能:成千上万个节点和数百万的任务实在太多,特别是当调度问题很复杂的时候。
  • 代码的灵活性:要为新的需求,持续编写新的调度器和新的Framework。

相反,Mesos引入了两级调度的理念。Mesos将每个应用程序的调度工作委派给应用程序本身,同时,Mesos仍然负责应用和执行整体公平性之间的资源分配。因此,Mesos可以做得相当薄,只有10K行代码。

两级调度通过一个名为资源邀约的新API发起,Mesos会周期地为应用程序调度器提供一些资源。这咋听起来很落后(请求要从master到应用程序?),但其实这一点都不奇怪。在MR1中,TaskTracker worker是实际的运行节点。当TT心跳报告任务完成后,JobTracker会选择别的任务让TaskTracker运行。调度决策本质上是由worker上的资源邀约触发的。在Mesos中,资源邀约来自Mesos master,而不是slave,因为Mesos在管理集群。没有什么不同。

资源邀约扮演着对资源有时间限制的租约。基于优先级或公平份额策略​​,Mesos向应用程序提供资源。然后,应用程序会计算如何使用他们,并且告诉Mesos资源邀约中它想要的那部分资源。这给了应用程序很大的灵活性,因为它可以选择现在运行任务的一部分、等待后面更大的分配(组调度),或者调整任务的大小以适应可用的资源。由于邀约是有时间限制的,这也激励了应用程序去实现快速地调度。

一些问题及解决办法:

  • 长任务占用资源:Mesos允许为短任务预留一些资源,时间期限一到便杀死他们。这也激励了用户使用短任务,它具有很好的公平性。
  • 性能隔离:使用Linux容器(cgroups)。
  • 大型任务饥饿:很难得到一个节点的唯一访问权,因为一些具有较小任务的其他应用程序会蚕食它。可以通过设定请求资源的最小规模来修复这一问题。

尚未解决的/未知的决定:

  • 组调度:我认为,无法预知任务长度或者抢占不可能做到高利用率。可以使用低利用率来增量囤积资源,但这可能会导致死锁。
  • 跨应用程序抢占很也难:资源邀约API没有办法说“这里有一些低优先级的任务,如果你想要,我可以杀掉他们。”Mesos实现公平性取决于任务必须是短期的。

Omega

Linux:全方位对比 Mesos、Omega 和 Borg
Linux:全方位对比 Mesos、Omega 和 Borg

Omega是Mesos的继任者,事实上,是同一作者。由于论文采用模拟结果做评估,所以我怀疑它永远不会进入谷歌的生产,而其中的想法会进入下一代Borg中。改写API可能是太侵入性的变化,即使是谷歌。

Omega让资源邀约更进一步。在Mesos中,资源邀约是悲观的或独占的。如果资源已经提供给一个应用程序,同样的资源将不能提供给另一个应用程序,直到邀约超时。在Omega中,资源邀约是乐观的。每个应用程序可以请求群集上的所有可用资源,冲突在提交时解决。Omega的资源管理器基本上只是一个记录每个节点状态的关系数据库,使用不同类型的乐观并发控制解决冲突。这样的好处是大大增加了调度器的性能(完全并行)和更好的利用率。

所有这一切的缺点是,应用程序是在一个绝对自由的环境中,他们可以以最快的速度吞噬他们想要的资源,甚至抢占其他用户的资源。对谷歌来说这没问题,因为他们使用基于优先级的系统,并且可以向他们的内部用户咆哮。他们的应用大致只分为两种优先级:高优先级的服务性作业(如HBase、web服务器、长住服务等)和低优先级的批处理作业(MapReduce和类似技术)。应用程序可以抢占低优先级的作业,并且在协作执行限制的范围#内授信,以提交作业、计算资源分配等。我认为雅虎没法冲着用户大叫(当然是不可扩展的),但这在谷歌某种方式上是可行的。

论文的大部分在谈论这种乐观的分配方案如何与冲突一起工作的,这永远是个问题。有一些高级的注意事项:

  • 服务性作业都较大,对(跨机架的)容错有更严格的配置需求。
  • 由于在分配完全群集状态上的开销,Omega大概可以将调度器扩展到十倍,但是无法达到百倍。
  • 秒级的调度时间是典型的。他们还比较了十秒级和百秒级的调度,这是两级调度的好处真正发挥作用的地方。无法确定这样的场景有多普遍,也许是由服务性作业来决定?
  • 典型的集群利用率约为60%。
  • 在OCC实践中,冲突非常罕见。在调度器崩溃之前,他们能够将正常批处理作业上升6倍。
  • 增量调度是非常重要的。组调度明显更昂贵,因为要增加冲突处理的实现。显然,大多数的应用程序可以做好增量,通过实现部分资源分配进而达到他们所需的全额资源。
  • 即使执行复杂的调度器(每作业十余秒的费),Omega仍然可以在合理的等待时间内调度一个混合的作业。
  • 用一个新的MapReduce调度器进行实验,从经验上说,在Omega中会非常容易。

开放式问题

  • 在某些时候,因为高冲突率和重试导致的重复工作,乐观并发控制会崩溃。在实践中他们好像没有碰到这种问题,但我不知道是否有奇形怪状的任务致使出现最坏的场景。这是受服务和批处理作业联合影响的吗?在实践中他们做过什么调优吗?
  • 是否缺乏真正可以接受的全局策略?如公平性、抢占等。
  • 不同类型的作业调度的时间是多少?有人已经写出非常复杂的调度器吗?

Borg

这是一个具备生产经验的论文。与Omega有相同的负载作业量,因为它也出自谷歌,所以很多元点都是一样的。

高级功能

  • 谷歌的一切都在Borg中运行,包括像CFS和BigTable这样的存储系统。
  • 平均集群大小为10K个节点,虽然还有更大集群。
  • 节点可以是异构的。
  • Borg使用了Linux进程隔离技术(基本上就是容器技术),因为Borg的出现早于现代虚拟机基础架构。效率和启动时间是很重要的。
  • 所有作业都是静态链接的二进制文件。
  • 有非常复杂、非常丰富的资源规范语言。
  • 可以滚动更新运行的作业,更新可以是配置和二进制文件。有时这需要任务重新启动,所以容错很重要。
  • 通过SIGTERM支持“正常停止”,否则通过SIGKILL最终杀掉进程。软杀进程是可选的,但从正确性上说是不可靠的。

Alloc

  • 资源分配从进程活跃度中分离。Alloc可以用于为任务组资源分配,或者为重新启动的任务保留资源。
  • Alloc集合是一组在多台机器上的Alloc。多个作业可以在一个单一的Alloc中运行。
  • Alloc实际上是一种很常见的模式!对于分离问题与实现,多进程是有用的。

优先级和配额

  • 两级优先级:服务性的高优先级和批处理的低优先级。
  • 更高的优先级作业可以抢占低优先级的。
  • 高优先级的作业不能彼此抢占(在防止级联活锁的情况下)。
  • 配额用于接入控制。用户支付更多的配额获得更高的优先级。
  • 运行在最低优先级还提供了“自由”层,以鼓励高利用率和回填工作。
  • 这是一个简单易懂的系统!

调度

  • 两个阶段调度:找到可行的节点,然后为最终放置对这些节点评分。
  • 可行性在很大程度上由任务约束决定。
  • 评分主要由系统属性决定,比如最适合与最不适合、作业组成、故障域、具体位置等。
  • 一旦最终节点被选择,Borg将抢占该节点的资源以适应需求。
  • 典型的调度时间是25秒左右,这依赖于具体节点的本地化。下载二进制占这个时长的80%。这个具体位置很重要。 Torrent和树协议用于分发二进制文件。

伸缩性

  • 集中化一直是潜在的性能瓶颈。
  • 针对成千上万个节点,每分钟调度的速率为10K任务。
  • 典型的Borgmaster使用10-14个内核和50GB内存。
  • 随着时间推移,参照Omega和两级调度,Borg架构已经变成更多的多进程。
  • Borgmaster是独立的主节点,但有些责任依然共享:worker状态的更新、只读的RPC。
  • 一些明显的优化:缓存的机器分数、每种任务类型计算一次的可行性,在做调度决策时,不要试图全局最优。
  • 对较大单元的主要争论是隔离操作错误和失败的传播。架构保持良好的伸缩性。

利用率

  • 他们的主要的度量指标是单元压缩,或者说还有空间容纳一组任务的最小集群。从本质上讲是打包装箱。
  • 主要收益来自以下几点:不分隔作业或用户、有很大的共享集群、细粒度的资源请求。
  • 基于Borglet的乐观过量使用。 Borglets做资源估算,然后回填非prod工作。如果估计不正确,杀掉非prod工作。内存是无弹性的资源。
  • 共享不会显着影响CPI(CPU干扰),但我不知道对存储是否有影响。

经验教训

这里列出的问题多数在他们的公开的、开放源码的容器调度系统Kubernetes中已经修复。

缺点:

  • 对于跟踪和管理来说,调度多作业的工作流程要比调度单一的作业更好。关于工作流组件,还需要更灵活的方式。解决办法是可以将任意的键值对附加到每个任务上,并允许用户对其进行查询。
  • 每台机器一个IP。这会导致一台机器上的端口冲突,以及复杂的绑定和服务发现。可以通过Linux的命名空间、IPv6和SDN来解决。
  • 复杂的规范语言。知识点太多,作为一个普通用户很难上手。自动化确定资源请求需要一些工作。

优点:

Alloc非常牛!辅助服务可以很容易置入下一个主任务。后台服务,如负载均衡服务和命名服务是非常有用的。度量、调试、网页界面都很重要,用户能通过他们解决自己的问题。集中化的可扩展性很好,但需要将其拆分到多个进程中。为此,Kubernetes从新开始,为不同调度器组件提供一套干净的API。

结束语

YARN看上去借鉴了Mesos和Omega,使伸缩规模到达10K节点。与Mesos和Omega相比,YARN仍然是一个集中式的调度系统。Borg特别强调要分片伸缩。

为了实现高利用率而不损害服务水平目标(SLO),隔离是非常重要的。这可以在应用程序层面来实现,而应用服务本身需要设计为延迟容忍的。想想在BigTable中,为避免请求延迟(tail-at-scale)采用的向副本发起请求。最终,问题归结为是要硬件来做隔离还是软件来做。保持运行于利用率较低的状态可以回避这一问题。或者,你可以直接通过OS的隔离机制、资源评估和调整你的作业和调度器来解决。在谷歌的规模下,有足够的硬件、可以招一批内核开发工程师来实现。幸运的是,他们为我们做足了工作 🙂

我也想知道,是否谷歌的假设作业更具备普遍性。谷歌能很好地使用优先级分类、资源预留,以及抢占,但是我们的客户几乎全部使用公平共享调度器。雅虎使用容量调度器。 Twitter使用的公平调度器。我没有听说过的关于优先级+资源预留调度器的任何需求或使用。

最后,很少有客户会运行在谷歌预想的大共享集群上。我们的客户在使用千级的节点,但是这被分为百级的pod节点上。为单独的用户或者应用程序独立部署集群也是常见的。集群在硬件方面通常还是同构的。我认为这将开始改变,并且会很快。

来源:http://www.infoq.com/cn/articles/comparison-of-mesos-omega-and-borg

Linux:PHP 5.5新特性

默认开启OPCache

PHP 5.5安装时候会默认编译Zend OPcache做为OPCache,并默认开启OPCache.

PHP语言的一些改变

增加Generator

function getLinesFromFile($fileName) {
    if (!$fileHandle = fopen($fileName, 'r'))
    {
        throw new RuntimeException('Couldn't open file "' . $fileName . '"');
    }

    while (false !== $line = fgets($fileHandle))
    {
        yield $line;
    }

    fclose($fileHandle);
}
foreach (getLinesFromFile($fileName) as $line)
{
    // do something
}

增加finally关键字

try {
    echo '1';
    throw new Exception();
} catch (Exception $e) {
    echo '2';
} finally {
    echo '3';
}

完全限定类名

使用ClassName::class可以获得”完全限定类名”(fully qualified class name)

namespace FooBar;

class One {
    const A = self::class;
    const B = Two::class;
}

class Two extends One {
    public static function run()
    {
        var_dump(self::class);
        var_dump(static::class);
        var_dump(parent::class);
    }
}

var_dump(One::class);

$class = One::class;

$x = new $class;
var_dump($x);

$two = Two::class;
(new $two)->run();

empty( )函数允许直接调用函数

function test_false() {
    return false;
}

if (empty(test_false()))
{
    echo "output something.";
}

foreach中使用list( )

$users = [
    ['Foo', 'Bar'],
    ['Baz', 'Qux'],
];

foreach ($users as list($firstname, $lastname)) {
    echo "First name: $firstname, last name: $lastname.";
}

常量寻址(Constant dereferencing)-访问常量内元素

echo "hello"[1];
echo [1,2,3,4][3];

标准库或函数/方法的一些改变

增加密码哈希API

password hashing api的函数:password_get_info(), password_hash(),password_needs_rehash(),password_verify()

$receved_password = "zrwmpassword";
$pass_hash = password_hash($receved_password, PASSWORD_DEFAULT);

var_dump(password_get_info($pass_hash));

if (password_verify($receved_password, $pass_hash)) {
    echo 'Password is valid';
} else {
    echo 'Invalid password';
}

新增加的一些函数

PHP Core还新增了array_column(),boolval(),json_last_error_msg(),cli_get_process_title()等函数。

此外还新增一些有用的扩展函数/方法:

  • MySQLimysqli_begin_transaction()mysqli_release_savepoint()mysqli_savepoint()
  • IntlIntlDateFormatter::formatObject()IntlDateFormatter::getCalendarObject()IntlDateFormatter::getTimeZone()IntlDateFormatter::setTimeZone()
  • cURLcURL新增一些函数,其中curl_file_create()函数或CURLFile类都可以创建CURLFile对象,实现文件的上传功能.

新增的类/接口

查看PHP 5.5新增的类/接口.

对比DateTime与DateTimeImmutable的一个简单例子:

function printUTC1(DateTime $dt) {
    $dt->setTimeZone(new DateTimeZone('UTC'));
    echo $dt->format(DateTime::ATOM) . PHP_EOL;
}

function printUTC2(DateTimeImmutable $dt)
{
    $dt->setTimeZone(new DateTimeZone('UTC'));
    echo $dt->format(DateTime::ATOM) . PHP_EOL;
}

$dt = new DateTime('now');
printUTC1($dt); // 2013-06-28T05:58:49+00:00

$dt = new DateTimeImmutable('now');

printUTC2($dt); // 2013-06-28T13:58:49+08:00

过期与删除的特性

  • 不再支持Windows XP和 Windows Server 2003
  • 扩展mysql过期,需使用MySQLi或PDO_MySQL
  • preg_replace /e 修饰符过期
  • 删除Logo GUIDs
  • intl扩展的一些方法/函数过期IntlDateFormatter::setTimeZoneID()和datefmt_set_timezone_id()现在已经过期.可以分别使用IntlDateFormatter::setTimeZone()和datefmt_set_timezone().
  • mcrypt扩展的一些函数过期mcrypt_cbc(),mcrypt_cfb(),mcrypt_ecb(),mcrypt_ofb()等函数过期
via http://www.zrwm.com/?p=7536 ,有修改。

Linux:怎样在Ubuntu 13.04,12.10,12.04安装Qmmp 0.7.2

大家好,Ubuntu迷们!

你们好吗? 这篇文章的目标是介绍怎么在Ubuntu系统上安装Qmmp0.7.2应用程序。在Ubuntu上有许多的多媒体娱乐软件,Qmmp是其中一个,它是基于Qt语言的。Qmmp播放器依赖于Qt库文件,其用户界面类似于Winamp或者是Xmms。你喜欢Winamp的用户界面吗?或者是Xmms的界面?不是?好的,不要担心,还有可替换的用户界面。你可以看看这里

Qmmp 的实际稳定版本为0.7.2,已于8月26日发布。该程序非常强大并且很容易使用。它采用了MPEG V1/2的 1/2/3层解码器,支持多种输出格式,例如 MPEG1 2/3层,Ogg Vorbis,Ogg Opus,Native FLAC/Ogg FLAC,Musepack,WavePack,下面列出所有支持的输出格式。

  • MPEG1 2/3层
  • Ogg Vorbis
  • Ogg Opus
  • Native FLAC/Ogg FLAC
  • Musepack
  • WavePack
  • Tracker 模块 (mod, s3m, it, xm, etc)
  • ADTS AAC
  • CD Audio
  • WMA, Monkey’s Audio (以及ffmpeg库提供的其他格式)
  • PCM WAVE (以及 libsndfile 库提供的其他格式)
  • Midi
  • Chiptune formats (AY, GBS, GYM, HES, KSS, NSF, NSFE, SAP, SPC, VGM, VGZ, VTX)

和以前的版本相比,Qmmp0.7.2应用程序有许多修正和变化。API文件已修复,未实现的功能已经被删除,那你想知道从0.7.1版本所以的变化吗?

从0.7.1开始的变化有:

  • 修复队列更新的bug;
  • 修复轨道长度格式;
  • 修复API文档;
  • 修复了gcc编译器的警告;
  • 修复了内存泄露;
  • 修复wildmidi的配置路径;
  • 修复了播放列表自动保存的功能;
  • 修复了可能出现的段错误;
  • 修复了标题格式更新错误;
  • 去除未实现的功能;

现在开始安装Qmmp 0.7.2;打开新的终端,输入下列命令:

$sudo add-apt-repository ppa:forktov02/ppa

Linux:怎样在Ubuntu 13.04,12.10,12.04安装Qmmp 0.7.2
Linux:怎样在Ubuntu 13.04,12.10,12.04安装Qmmp 0.7.2
Linux:怎样在Ubuntu 13.04,12.10,12.04安装Qmmp 0.7.2
Linux:怎样在Ubuntu 13.04,12.10,12.04安装Qmmp 0.7.2

 

然后更新源列表,输入:

$sudo apt-get update

Linux:怎样在Ubuntu 13.04,12.10,12.04安装Qmmp 0.7.2
Linux:怎样在Ubuntu 13.04,12.10,12.04安装Qmmp 0.7.2

更新完后,我们下载qmpp,qmmp-plugin-pack,然后安装它们。

$sudo apt-get install qmmp qmmp-plugin-pack;

Linux:怎样在Ubuntu 13.04,12.10,12.04安装Qmmp 0.7.2
Linux:怎样在Ubuntu 13.04,12.10,12.04安装Qmmp 0.7.2

via:http://www.unixmen.com/install-qmmp-0-7-2-ubuntu-13-04-12-10-12-04/

本文由 LCTT 原创翻译,Linux中国 荣誉推出

译者:神之一手 校对:jasminepeng

来源:https://linux.cn/article-1968-1.html

Linux:Linux内核学习经验总结

开篇

学习内核,每个人都有自己的学习方法,仁者见仁智者见智。以下是我在学习过程中总结出来的东西,对自身来说,我认为比较有效率,拿出来跟大家交流一下。​

内核学习,一偏之见;疏漏难免,恳请指正。

为什么写这篇博客

刚开始学内核的时候,不要执着于一个方面,不要专注于一个子系统就一头扎到实际的代码行中去,因为这样的话,牵涉的面会很广,会碰到很多困难,容易产生挫败感,一个函数体中(假设刚开始的时候正在学习某个方面的某个具体的功能函数)很可能掺杂着其他各个子系统方面设计理念(多是大量相关的数据结构或者全局变量,用于支撑该子系统的管理工作)下相应的代码实现,这个时候看到这些东西,纷繁芜杂,是没有头绪而且很不理解的,会产生很多很多的疑问,(这个时候如果对这些疑问纠缠不清,刨根问底,那么事实上就是在学习当前子系统的过程中频繁的去涉足其他子系统,这时候注意力就分散了),而事实上等了解了各个子系统后再回头看这些东西的话,就简单多了,而且思路也会比较清晰。所以,要避免 “只见树木,不见森林”,不要急于深入到底层代码中去,不要过早研究底层代码。

我在大二的时候刚开始接触内核,就犯了这个错误,一头扎到内存管理里头,去看非常底层的实现代码,虽然也是建立在内存管理的设计思想的基础上,但是相对来说,比较孤立,因为此时并没有学习其它子系统,应该说无论是视野还是思想,都比较狭隘,所以代码中牵涉到的其它子系统的实现我都直接跳过了,这一点还算聪明,当然也是迫不得已的。

我的学习方法

刚开始,我认为主要的问题在于你知道不知道,而不是理解不理解,某个子系统的实现采用了某种策略、方法,而你在学习中需要做的就是知道有这么一回事儿,然后才是理解所描述的策略或者方法。

根据自己的学习经验,刚开始学习内核的时候,我认为要做的是在自己的脑海中建立起内核的大体框架,理解各个子系统的设计理念和构建思想,这些理念和思想会从宏观上呈献给你清晰的脉络,就像一个去除了枝枝叶叶的大树的主干,一目了然;当然,肯定还会涉及到具体的实现方法、函数,但是此时接触到的函数或者方法位于内核实现的较高的层次,是主(要)函数,已经了解到这些函数,针对的是哪些设计思想,实现了什么样的功能,达成了什么样的目的,混个脸熟的说法在这儿也是成立的。至于该主函数所调用的其它的辅助性函数就等同于枝枝叶叶了,不必太早就去深究。此时,也就初步建立起了内核子系统框架和代码实现之间的关联,关联其实很简单,比如一看到某个函数名字,就想起这个函数是针对哪个子系统的,实现了什么功能。

我认为此时要看的就是LKD3,这本书算是泛泛而谈,主要就是从概念,设计,大的实现方法上描述各个子系统,而对于具体的相关的函数实现的代码讲解很少涉及(对比于ULK3,此书主要就是关于具体函数代码的具体实现的深入分析,当然,你也可以看,但是过早看这本书,会感觉很痛苦,很枯燥无味,基本上都是函数的实现),很少,但不是没有,这就很好,满足我们当前的需求,还避免我们过早深入到实际的代码中去。而且本书在一些重要的点上还给出了写程序时的注意事项,算是指导性建议。主要的子系统包括:内存管理,进程管理和调度,系统调用,中断和异常,内核同步,时间和定时器管理,虚拟文件系统,块I/O层,设备和模块。(这里的先后顺序其实就是LKD3的目录的顺序)。

我学习的时候是三本书交叉着看的,先看LKD3,专于一个子系统,主要就是了解设计的原理和思想,当然也会碰到对一些主要函数的介绍,但大多就是该函数基于前面介绍的思想和原理完成了什么样的功能,该书并没有就函数本身的实现进行深入剖析。然后再看ULK3和PLKA上看同样的子系统,但是并不仔细分析底层具体函数的代码,只是粗略地、不求甚解地看,甚至不看。因为,有些时候,在其中一本书的某个点上,卡壳了,不是很理解了,在另外的书上你可能就碰到对同一个问题的不同角度的描述,说不准哪句话就能让你豁然开朗,如醍醐灌顶。我经常碰到这种情况。并不是说学习过程中对一些函数体的实现完全就忽略掉,只要自己想彻底了解其代码实现,没有谁会阻止你。我是在反复阅读过程中慢慢深入的。比如VFS中文件打开需要对路径进行分析,需要考虑的细节不少(.././之类的),但是其代码实现是很好理解的。再比如,CFS调度中根据shedule latency、队列中进程个数及其nice值(使用的是动态优先级)计算出分配给进程的时间片,没理由不看的,这个太重要了,而且也很有意思。

ULK3也会有设计原理与思想之类的概括性介绍,基本上都位于某个主题的开篇段落。但是更多的是对支持该原理和思想的主要函数实现的具体分析,同样在首段,一句话综述函数的功能,然后对函数的实现以1、2、3,或者a、b、c步骤的形式进行讲解。我只是有选择性的看,有时候对照着用source insight打开的源码,确认一下代码大体上确实是按书中所描述的步骤实现的,就当是增加感性认识。由于步骤中掺杂着各种针对不同实现目的安全性、有效性检查,如果不理解就先跳过。这并不妨碍你对函数体功能实现的整体把握。

PLKA介于LKD3和ULK3之间。我觉得PLKA的作者(看照片,真一德国帅小伙,技术如此了得)肯定看过ULK,无论他的本意还是有意,总之PLKA还是跟ULK有所不同,对函数的仔细讲解都做补充说明,去掉函数体中边边角角的情况,比如一些特殊情况的处理,有效性检查等,而不妨碍对整个函数体功能的理解,这些他都有所交代,做了声明;而且,就像LKD3一样,在某些点上也给出了指导性编程建议。作者们甚至对同一个主要函数的讲解的着重点都不一样。这样的话,对我们学习的人而言,有助于加深理解。另外,我认为很重要的一点就是PLKA针对的2.6.24的内核版本,而ULK是2.6.11,LKD3是2.6.34。在某些方面PLKA比较接近现代的实现。其实作者们之所以分别选择11或者24,都是因为在版本发行树中,这两个版本在某些方面都做了不小的变动,或者说是具有标志性的转折点(这些信息大多是在书中的引言部分介绍的,具体的细节我想不起来了)。

Intel V3,针对X86的CPU,本书自然是系统编程的权威。内核部分实现都可以在本书找到其根源。所以,在读以上三本书某个子系统的时候,不要忘记可以在V3中相应章节找到一些基础性支撑信息。

在读书过程中,会产生相当多的疑问,这一点是确信无疑的。 大到搞不明白一个设计思想,小到不理解某行代码的用途。各个方面,各种疑问,你完全可以把不理解的地方都记录下来(不过,我并没有这么做,没有把疑问全部记下来,只标记了很少一部分我认为很关键的几个问题),专门写到一张纸上,不对,一个本上,我确信会产生这么多的疑问,不然内核相关的论坛早就可以关闭了。其实,大部分的问题(其中很多问题都是你知道不知道有这么一回事的问题)都可以迎刃而解,只要你肯回头再看,书读百遍,其义自现。多看几遍,前前后后的联系明白个七七八八是没有问题的。我也这么做了,针对某些子系统也看了好几遍,切身体会。

当你按顺序学习这些子系统的时候,前面的章节很可能会引用后面的章节,就像PLKA的作者说的那样,完全没有向后引用是不可能的,他能做的只是尽量减少这种引用而又不损害你对当前问题的理解。不理解,没关系,跳过就行了。后面的章节同样会有向前章节的引用,不过这个问题就简单一些了 ,你可以再回头去看相应的介绍,当时你不太理解的东西,很可能这个时候就知道了它的设计的目的以及具体的应用。不求甚解只是暂时的。比如说,内核各个子系统之间的交互和引用在代码中的体现就是实现函数穿插调用,比如你在内存管理章节学习了的内存分配和释放的函数,而你是了解内存在先的,在学习驱动或者模块的时候就会碰到这些函数的调用,这样也就比较容易接受,不至于太过茫然;再比如,你了解了系统时间和定时器的管理,再回头看中断和异常中bottom half的调度实现,你对它的理解就会加深一层。

子系统进行管理工作需要大量的数据结构。子系统之间交互的一种方式就是各个子系统各自的主要数据结构通过指针成员相互引用。学习过程中,参考书上在讲解某个子系统的时候会对数据结构中主要成员的用途解释一下,但肯定不会覆盖全部(成员比较多的情况,例如task_struct),对其它子系统基于某个功能实现的引用可能解释了,也可能没做解释,还可能说这个变量在何处会做进一步说明。所以,不要纠结于一个不理解的点上,暂且放过,回头还可以看的。之间的联系可以在对各个子系统都有所了解之后再建立起来。其实,我仍然在强调先理解概念和框架的重要性。

等我们完成了建立框架这一步,就可以选择一个比较感兴趣的子系统,比如驱动、网络,或者文件系统之类的。这个时候你再去深入了解底层代码实现,相较于一开始就钻研代码,更容易一些,而且碰到了不解之处,或者忘记了某个方面的实现,此时你完全可以找到相应的子系统,因为你知道在哪去找,查漏补缺,不仅完成了对当前函数的钻研,而且可以回顾、温习以前的内容,融会贯通的时机就在这里了。

《深入理解linux虚拟内存》(2.4内核版本),LDD3,《深入理解linux网络技术内幕》,几乎每一个子系统都需要一本书的容量去讲解,所以说,刚开始学习不宜对某个模块太过深入,等对各个子系统都有所了解了,再有针对性的去学习一个特定的子系统。这时候对其它系统的援引都可以让我们不再感到茫然、复杂,不知所云。

比如,LDD3中的以下所列章节:构造和运行模块,并发和竞态,时间、延迟及延缓操作,分配内存,中断处理等,都属于驱动开发的支撑性子系统,虽说本书对这些子系统都专门开辟一个章节进行讲解,但是详细程度怎么能比得上PLKA,ULK3,LKD3这三本书,看完这三本书,你会发现读LDD3这些章节的时候简直跟喝白开水一样,太随意了,因为LDD3的讲解比之LKD3更粗略。打好了基础,PCI、USB、TTY驱动,块设备驱动,网卡驱动,需要了解和学习的东西就比较有针对性了。这些子系统就属于通用子系统,了解之后,基于这些子系统的子系统的开发—驱动(需进一步针对硬件特性)和网络(需进一步理解各种协议)—相对而言,其学习难度大大降低,学习进度大大加快,学习效率大大提升。说着容易做来难。达到这样一种效果的前提就是:必须得静下心来,认真读书,要看得进去,PLKA,ULK3厚得都跟砖头块儿一样,令人望之生畏,如果没有兴趣,没有热情,没有毅力,无论如何都是不行,因为需要时间,需要很长时间。我并不是说必须打好了基础才可以进行驱动开发,只是说打好了基础的情况下进行开发会更轻松,更有效率,而且自己对内核代码的驾驭能力会更强大。这只是我个人见解,我自己的学习方式,仅供参考。

语言

PLKA是个德国人用德语写的,后来翻译成英文,又从英文翻译成中文,我在网上书店里没有找到它的纸质英文版,所以就买了中文版的。ULK3和LKD3都是英文版的。大牛们写的书,遣词造句真的是简洁,易懂,看原版对我们学习计算机编程的程序员来说完全不成问题,最好原汁原味。如果一本书确实翻译地很好,我们当然可以看中文版的,用母语进行学习,理解速度和学习进度当然是很快的,不作他想。看英文的时候不要脑子里想着把他翻译成中文,没必要。

API感想

“比起知道你所用技术的重要性,成为某一个特别领域的专家是不重要的。知道某一个具体API调用一点好处都没有,当你需要他的时候只要查询下就好了。”这句话源于我看到的一篇翻译过来的博客。我想强调的就是,这句话针应用型编程再合适不过,但是内核API就不完全如此。

内核相当复杂,学习起来很不容易,但是当你学习到一定程度,你会发现,如果自己打算写内核代码,到最后要关注的仍然是API接口,只不过这些API绝大部分是跨平台的,满足可移植性。内核黑客基本上已经标准化、文档化了这些接口,你所要做的只是调用而已。当然,在使用的时候,最好对可移植性这一话题在内核中的编码约定烂熟于心,这样才会写出可移植性的代码。就像应用程序一样,可以使用开发商提供的动态库API,或者使用开源API。同样是调用API,不同点在于使用内核API要比使用应用API了解的东西要多出许多。

当你了解了操作系统的实现—这些实现可都是对应用程序的基础性支撑啊—你再去写应用程序的时候,应用程序中用到的多线程,定时器,同步锁机制等等等等,使用共享库API的时候,联系到操作系统,从而把对该API的文档描述同自己所了解到的这些方面在内核中的相应支撑性实现结合起来进行考虑,这会指导你选择使用哪一个API接口,选出效率最高的实现方式。对系统编程颇有了解的话,对应用编程不无益处,甚至可以说是大有好处。 

设计实现的本质,知道还是理解

操作系统是介于底层硬件和应用软件之间的接口,其各个子系统的实现很大程度上依赖于硬件特性。书上介绍这些子系统的设计和实现的时候,我们读过了,也就知道了,如果再深入考虑一下,为什么整体架构要按照这种方式组织,为什么局部函数要遵循这样的步骤处理,知其然,知其所以然,如果你知道了某个功能的实现是因为芯片就是这么设计的,CPU就是这么做的,那么你的疑问也就基本上到此为止了。再深究,就是芯片架构方面的设计与实现,对于程序员来讲,无论是系统还是应用程序员,足迹探究到这里,已经解决了很多疑问,因为我们的工作性质偏软,而这些东西实在是够硬。

比如,ULK3中讲解的中断和异常的实现,究其根源,那是因为Intel x86系列就是这么设计的,去看看Intel V3手册中相应章节介绍,都可以为ULK3中描述的代码实现方式找到注解。还有时间和定时器管理,同样可以在Intel V3 对APIC的介绍中获取足够的信息,操作系统就是依据这些硬件特性来实现软件方法定义的。

又是那句话,不是理解不理解的问题,而是知道不知道的问题。有时候,知道了,就理解了。在整个学习过程中,知道,理解,知道,理解,知道……,交叉反复。为什么开始和结尾都是知道,而理解只是中间步骤呢?世界上万事万物自有其规律,人类只是发现而已,实践是第一位的,实践就是知道的过程,实践产生经验,经验的总结就是理论,理论源于实践,理论才需要理解。我们学习内核,深入研究,搞来搞去,又回到了芯片上,芯片是物质的,芯片的功用基于自然界中物质本有的物理和电子特性。追本溯源,此之谓也。

动手写代码

纸上得来终觉浅,绝知此事要躬行。只看书是绝对不行的,一定要结合课本给出的编程建议自己敲代码。刚开始就以模块形式测试好了,或者自己编译一个开发版本的内核。一台机器的话,使用UML方式调试,内核控制路走到哪一步,单步调试看看程序执行过程,比书上的讲解更直观明了。一定要动手实际操作。

参考书

LDD3          Linux Device Driver 3rd
LKD3          Linux Kernel Development 3rd
ULK3          Understanding the Linux Kernel 3rd
PLKA          Professional Linux Kernel ArchitectureUML            User Mode Linux
Intel V3       Intel? 64 and IA-32 Architectures Software Developer’s Manual Volume 3 (3A, 3B & 3C): System Programming Guide
 
 
作者在写书的时候,都是以自己的理解组织内容,从自己的观点看待一个主题,关注点跟作者自身有很大的关系。出书的时间有先后,后来人针对同一个主题想要出书而又不落入窠臼,最好有自己的切入方式,从自己的角度讲解相关问题,这才值得出这本书,千篇一律是个掉价的行为,书就不值钱了。
   
尽信书不如无书。
 
        http://lwn.net/Articles/419855/ 此处是一篇关于LKD3的书评,指出了其中的错误,当你读完的时候,不妨去找找,看一下自己在其中所描述的地方有什么特别的印象。
        http://lwn.net/Articles/161190/此处是一篇对ULK3的介绍,我认为其中很关键的几句话就可以给本书定位:
    Many of the key control paths in the kernel are described, step by step;
     一步一步地讲述内核控制路径的实现。

     The level of detail sometimes makes it hard to get a sense for the big picture, but it does help somebody trying to figure out how a particular function works.

     对代码讲解的详细程度有时候很难让读者把握住它的主旨大意,但是确实有助于读者理解一个特定的函数到底是如何工作的。

     Indeed, that is perhaps the key feature which differentiates this book. It is very much a “how it works” book, designed to help people understand the code.

     事实上,这也正是本书与众不同的地方。更像一个“如何工作”的书,帮助读者理解代码实现。

     It presents kernel functions and data structures, steps the reader through them, but does not, for example, emphasize the rules for using them. UTLK is a study guide, not a programming manual.

     本书描述了内核函数和数据结构,引导读者穿行于其间,但是,并没有着重强调使用它们的法则。UTLK是一本学习指南,而不是编程手册。    

     这几句话对本书的描述非常到位。基于此,作为指导性原则,我们就可以很有效率地使用它了。

     看一本技术书籍,书中的序言部分绝对是首先应该翻阅的,其次就是目录。我发现在阅读过程中我会频繁的查看目录,甚至是喜欢看目录。

结尾

兴趣的力量是无穷的。兴趣能带来激情,如果工作可以和兴趣结合到一起,工作起来才会有热情,那么工作就不只是工作了,更是一种享受。Linux,我的兴趣,我的动力,我的方向,我的未来!

 

 

来源:https://linux.cn/article-2384-1.html

Linux:Linux 内核测试和调试(4)

自动测试工具

这里列出一些能满足不同需求的测试工具供你选择。本小节只是简单介绍个大概,并不提供详细操作指南。

AuToTest

AuToTest 是一个全自动测试框架,存在的主要目的就是测试 Linux 内核,当然也可以用来测试其他东西,比如测试一块新硬件是否能稳定工作。AuToTest 是开源软件,以 GPL 方式授权,运行于 server-client 架构(即 C/S 架构)。你可以通过配置 server 端来对运行了 client 端的系统执行初始化、运行与监测工作,也可以自己在目标系统上让 client 运行起来。另外你可以为这个测试框架添加测试用例,详情请参考AuToTest 白皮书

Linaro Automated Validation Architecture

LAVA 自动测试框架用于自动安装于运行测试。举个例子:你在 LAVA 里面只需运行几个命令就可以跑 LTP(LCTT:Linux Test Project,中文是 Linux 测试计划,SGI发起并由IBM负责维护,目的是为开源社区提供测试套件来验证Linux的可靠性、健壮性和稳定性)。通过 LAVA 命令可以自动为你安装 LTP 所需要的所有依赖包,下载源码、编译编码、将 LTP 安装到某个独立的地方,方便卸载 LTP 时能移除所有二进制文件。安装好 LTP 后,运行 LAVA 命令时添加 ‘ltp’ 选项就可以运行 LTP 测试任务了,它会将测试结果以文件方式保存下来,文件名包含测试名称、时间戳。这些测试结果可以留着供以后参考。这是个发现软件退化(如果软件退化了的话)的好方法。下面列出 LAVA 配合 LTP 使用的一些命令:

显示 LAVA 支持的测试列表:

lava-test list-tests

安装测试套件:

lava-test install ltp

运行测试:

lava-test run ltp

查看结果:

lava-test results show ltp-timestamp.0

卸载测试套件:

lava-test uninstall ltp

内核调试功能

Linux 内核本身包含很多调试功能,比如 kmemcheck 和 kmemleak。

Linux:Linux 内核测试和调试(4)
Linux:Linux 内核测试和调试(4)

kmemcheck

kmemcheck 是一个动态检查工具,可以检测出一些未被初始化的内存(LCTT:内核态使用这些内存可能会造成系统崩溃)并发出警告。它的功能与 Valgrind 类似,只是 Valgrind 运行在用户态,而 kmemchecke 运行在内核态。编译内核时加上 CONFIG_KMEMCHECK 选项打开 kmemcheck 调试功能。你可以阅读 Documentation/kmemcheck.txt 来学习如何配置使用这个功能,以及如何看懂调试结果。

kmemleak

kmemleak 通过类似于垃圾收集器的功能来检测内核是否有内存泄漏问题。而 kmemleak 与垃圾收集器的不同之处在于前者不会释放孤儿目标(LCTT:不会再被使用的、应该被释放而没被释放的内存区域),而是将它们打印到 /sys/kernel/debug/kmemleak 文件中。用户态的 Valgrind 也有一个类似的功能,使用 –leak-check 选项可以检测并报错内存泄漏问题,但并不释放这个孤儿内存。编译内核时使用 CONFIG_DEBUG_KMEMLEAK 选项打开 kmemcleak 调试功能。阅读 Documentation/kmemleak.txt 来学习怎么使用这个工具并读懂调试结果。

内核调试接口

Linux 内核通过配置选项、调试用的 API、接口和框架来支持动态或静态的调试。我们现在就好好学习学习这些牛逼的功能,从静态编译选项开始讲。

调试配置选项:静态编译

大部分 Linux 内核以及内核模块都包含调试选项,你只要在编译内核或内核模块的时候添加这个静态调试选项,程序运行时后就会产生调试信息,并记录在 dmesg 缓存中。

调试的 API

调试 API 的一个很好的例子是 DMA-debug,用来调试驱动是否错误使用了 DMA 提供的 API。它会跟踪每个设备的映射关系,检测程序有没有试图为一些根本不存在的映射执行“取消映射”操作,检测代码建立 DMA 映射后可能产生的“映射丢失”的错误。内核配置选项 CONFIG_HAVE_DMA_APT_DEBUG 和 CONFIG_DMA_API_DEBUG 可以为内核提供这个功能。其中,CONFIG_DMA_API_DEBUG 选项启用后,内核调用 DMA 的 API 的同时也会调用 Debug-dma 接口。举例来说,当一个驱动调用 dma_map_page() 函数来映射一个 DMA 缓存时,dma_map_page() 会调用debug_dma_map_page() 函数来跟踪这个缓存,直到驱动调用 dma_unmap_page() 来取消映射。详细内容请参考使用 DMA 调试 API 检测潜在的数据污染和内存泄漏问题

动态调试

动态调试功能就是你可以决定在程序运行过程中是否要 pr_debug(), dev_dbg(), print_hex_dump_debug(), print_hex_dump_bytes() 这些函数正常运行起来。什么意思?当程序运行过程中出现错误时,你可以指定程序打印有针对性的、详细的调试信息。这功能牛逼极了,我们不再需要为了添加调试代码定位一个问题,而重新编译安装内核。你可以指定 CONDIF_DYNAMIC_DEBUG 选项打开动态调试功能,然后通过 /sys/kernel/debug/dynamic_debug/control 接口指定要打印哪些调试日志。下面分别列出代码级别和模块级别打印日志的操作方法:

让 kernel/power/suspend.c 源码第340行的 pr_debug() 函数打印日志:

echo 'file suspend.c line 340 +p' > /sys/kernel/debug/dynamic_debug/control

让内核模块在加载过程中打开动态调试功能:

使用 modprobe 命令加在模块时加上 dyndbg=’plmft’ 选项。

让内核模块的动态调试功能在重启后依然有效:

编辑 /etc/modprobe.d/modname.conf 文件(没有这个文件就创建一个),添加 dyndbg=’plmft’ 选项。然而对于哪些通过 initramfs 加载的驱动来说,这个配置基本无效(LCTT:免费奉送点比较高级的知识哈。系统启动时,需要先让 initramfs 挂载一个虚拟的文件系统,然后再挂载启动盘上的真实文件系统。这个虚拟文件系统里面的文件是 initramfs 自己提供的,也就是说你在真实的文件系统下面配置了 /etc/modprobe.d/modname.conf 这个文件,initramfs 是压根不去理会的。站在内核驱动的角度看:如果内核驱动在 initramfs 过程中被加载到内核,这个驱动读取到的 /etc/modprobe.d/modname.conf 是 initramfs 提供的,而不是你编辑的那个。所以会有上述“写了配置文件后重启依然无效”的结论)。对于这种刁民,呃,刁驱动,我们需要修改 grub 配置文件,在 kernel 那一行添加 module.dyndbg=’plmft’ 参数,这样你的驱动就可以开机启动动态调试功能了。

想打印更详细的调试信息,可以使用 dynamic_debug.verbose=1 选项。参考 Documentation/dynamic-debug-howto.txt 文件获取更多信息。

设置追踪点

到目前为止,我们介绍了多种动态和静态调试方法。静态调试选项和静态调试钩子函数(比如 DMA Debug API)需要的编译过程打开或关闭,导致了一个难过的事实:需要重新编译安装内核。而动态编译功能省去了“重新编译”这件麻烦事,但是也有不足的地方,就是调试代码引入了条件变量,用于判断是否打印调试信息。这种方法可以让你在程序运行时决定是否打印日志,但需要执行额外的判断过程。“追踪点”代码只会在程序运行过程中使用“追踪点”功能才会被触发。也就是说,“追踪点”代码与上述说的两种方法都不一样。当用不到它时,它不会运行(LCTT:动态调试的话,代码每次都需要查看下变量,然后判断是否需要打印日志;而“追踪点”貌似利用某种触发机制,不需要每次都去查看变量)。当你需要用到它时,程序的代码会把“追踪点”代码包含进去。它不会添加任何条件变量来增加系统的运行负担。

详细信息请参考布置追踪代码的小技巧

“追踪点”的原理

追踪点使用“跳跃标签”,这是一种使用分支跳转的编码修正(code modification)技术。

当关闭追踪点的时候,其伪代码看起来时这样的:

[ code1 ]
nop
back:
[ code2 ]
return;
tracepoint:
[ tracepoint code ]
jmp back;

当打开追踪点的时候,其伪代码看起来时这样的:(注意追踪点代码出现的位置)

[ code1 ]
jmp tracepoint
back:
[ code2 ]
return;
tracepoint:
[ tracepoint code ]
jmp back;

(LCTT:咳咳,解释解释上面两段伪代码吧,能看懂的大神请忽略这段注释。不使用追踪点时,代码运行过程是:code1->code2->return结束;使用追踪点时,代码运行过程是:code1->跳到tracepoint code执行调试代码->跳回code2->return结束。两段代码的唯一区别就是第二行,前者为 nop(不做任何操作),后者为 jmp tracepoint (跳到调试代码)。)

Linux 电源管理子系统的测试

使用静态调试、动态调试和追踪调试技术,我们来跑一下磁盘的电源管理测试。当系统被挂起时,内核会为磁盘创建一个休眠镜像,使磁盘进入休眠模式,当系统重新被唤醒时,内核又利用这个休眠镜像重新唤醒磁盘。

设置挂起设备与唤醒设备需要的时间:

echo 1 > /sys/power/pm_print_times

以 reboot 模式挂起磁盘:

echo reboot > /sys/power/disk
echo disk > /sys/power/state

以 shutdown 模式挂起磁盘 —— 与 reboot 模式一样,只是重新唤醒磁盘的话还需要电源提供。

echo shutdown > /sys/power/disk
echo disk > /sys/power/state

以 platform 模式挂起磁盘 —— 能测试更多内容,比如 BIOS 挂起和唤醒,会涉及到 ACPI 功能。我们推荐你使用这种方式,把 BIOS 也拉下水陪你玩挂起和唤醒游戏。

echo platform > /sys/power/disk
echo disk > /sys/power/state

via:http://www.linuxjournal.com/content/linux-kernel-testing-and-debugging?page=0,3

译者:bazz2 校对:校对者ID

本文由 LCTT 原创翻译,Linux中国 荣誉推出

来源:https://linux.cn/article-3682-1.html

python中如何使用minidom读写xml格式的文件

python中使用minidom读写xml格式的文件是如何来实现的呢?下面的内容将会通过具体的实例来演示python中使用minidom读写xml格式的文件的实现方法及相关技巧:

本文实例讲述了Python使用minidom读写xml的方法。分享给大家供大家参考。具体分析如下:

一 python提供的xml支持

2种工业标准的xml解析方法-SAX和DOM。SAX(simple API for XML),是基于事件处理的,当XML文档顺序地读入时,每次遇到一个元素会触发相应的事件处理函数来处理。DOM(Document Object Model),通过构建一个树结构来表现整个xml文档,一旦树被构建,可以通过DOM提供了接口来遍历树和提取相应的数据。

python还提供了python独特的xml解析方法,相比于SAX和DOM更容易使用和更加快速,此方法为ElementTree。

python的xml模块为:

1)xml.dom.minidom

2)xml.elementtree

3)xml.sax + xml.dom

二 xml实例:(employees.xml)

1
2
3
4
5
6
7
8
9
10
11
<&#63;xml version="1.0" encoding="UTF-8" &#63;>
<employees>
 <employee>
  <name>l inux </name>
  <age> 30 </age>
 </employee>
 <employee>
  <name>windows </name>
  <age> 20 </age>
 </employee>
</employees>

三 使用xml.dom.minidom来读写xml

1)使用xml.dom.minidom来解析xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def TestMiniDom():
  from xml.dom import minidom
  doc = minidom.parse( "employees.xml" )
  # get root element: <employees/>
  root = doc.documentElement
  # get all children elements: <employee/> <employee/>
  employees = root.getElementsByTagName( "employee" )
  for employee in employees:
    print ( " ------------------------------------------- " )
    # element name : employee
    print (employee.nodeName)
    # element xml content : <employee><name>windows</name><age>20</age></employee>
    # basically equal to toprettyxml function
    print (employee.toxml())
    nameNode = employee.getElementsByTagName( "name" )[0]
    print (nameNode.childNodes)
    print (nameNode.nodeName +  ":"  + nameNode.childNodes[0].nodeValue)
    ageNode = employee.getElementsByTagName( "age" )[0]
    print (ageNode.childNodes)
    print (ageNode.nodeName +  ":"  + ageNode.childNodes[0].nodeValue)
    print ( " ------------------------------------------- " )
    for n in employee.childNodes:
      print (n)
TestMiniDom()

2)使用xml.dom.minidom来生成xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def CreateXml():
  import xml.dom.minidom
  impl = xml.dom.minidom.getDOMImplementation()
  dom = impl.createDocument(None, 'employees' , None)
  root = dom.documentElement
  employee = dom.createElement( 'employee' )
  root.appendChild(employee)
  nameE = dom.createElement( 'name' )
  nameT = dom.createTextNode( 'linux' )
  nameE.appendChild(nameT)
  employee.appendChild(nameE)
  ageE = dom.createElement( 'age' )
  ageT = dom.createTextNode( '30' )
  ageE.appendChild(ageT)
  employee.appendChild(ageE)
  f = open( 'employees2.xml' , 'w')
  dom.writexml(f, addindent = ' ' , newl = 'n' ,encoding = 'utf-8' )
  f.close()
CreateXml()

3)使用xml.dom.minidom需要注意的

*使用parse()或createDocument()返回的为DOM对象;

*使用DOM的documentElement属性可以获得Root Element;

*DOM为树形结构,包含许多的nodes,其中element是node的一种,可以包含子elements,textNode也是node的一种,是最终的子节点;

*每个node都有nodeName,nodeValue,nodeType属性,nodeValue是结点的值,只对textNode有效。对于textNode,想得到它的文本内容可以使用: .data属性。

*nodeType是结点的类型,现在有以下:

‘ATTRIBUTE_NODE”CDATA_SECTION_NODE”COMMENT_NODE”DOCUMENT_FRAGMENT_NODE’

‘DOCUMENT_NODE”DOCUMENT_TYPE_NODE”ELEMENT_NODE”ENTITY_NODE”ENTITY_REFERENCE_NODE’

‘NOTATION_NODE”PROCESSING_INSTRUCTION_NODE”TEXT_NODE’

*getElementsByTagName()可以根据名字来查找子elements;

*childNodes返回所有的子Nodes,其中所有的文本均为textNode,包含元素间的‘nr’和空格均为textNode;

*writexml() 时addindent=’ ‘表示子元素的缩进,newl=’n’表示元素间的换行,encoding=’utf-8’表示生成的xml的编码格式(<?xml version="1.0" encoding="utf-8"?>)。

python中使用minidom读写xml格式的文件就是这样,欢迎大家参考。。。。

Linux:使用HAProxy、PHP、Redis和MySQL支撑每周10亿请求

在公司的发展中,保证服务器的可扩展性对于扩大企业的市场需要具有重要作用,因此,这对架构师提出了一定的要求。Octivi联合创始人兼软件架构师Antoni Orfin将向你介绍一个非常简单的架构,使用HAProxy、PHP、Redis和MySQL就能支撑每周10亿请求。同时,你还能了解项目未来的横向扩展途径及常见的模式。

Linux:使用HAProxy、PHP、Redis和MySQL支撑每周10亿请求
Linux:使用HAProxy、PHP、Redis和MySQL支撑每周10亿请求

状态

  • 服务器
    • 3个应用程序节点
    • 2个MySQL+1个备份
    • 2个Redis
  • 应用程序
    • 应用程序每周处理10亿请求
    • 峰值700请求/秒的单Symfony2实例(平均工作日约550请求/秒)
    • 平均响应时间30毫秒
    • Varnish,每秒请求超过1.2万次(压力测试过程中获得)
  • 数据存储
    • Redis储存了1.6亿记录,数据体积大约100GB,同时它是我们的主要数据存储
    • MySQL储存了3亿记录,数据体积大约300GB,通常情况下它作为三级缓存层

平台

Linux:使用HAProxy、PHP、Redis和MySQL支撑每周10亿请求
Linux:使用HAProxy、PHP、Redis和MySQL支撑每周10亿请求
  • 监视:
    • Icinga
    • Collectd
  • 应用程序
    • HAProxy + Keepalived
    • Varnish
    • PHP(PHP-FPM)+ Symfony2 Framework
  • 数据存储
    • MySQL(主从配置),使用HAProxy做负载均衡
    • Redis (主从配置)

背景

大约1年前,一个朋友找到我并提出了一个苛刻的要求:它们是一个飞速发展的电子商务初创公司,而当时已经准备向国际发展。介于那个时候他们仍然是一个创业公司,初始解决方案必须符合所谓的成本效益,因此也就无法在服务器上投入更多的资金。遗留系统使用了标准的LAMP堆栈,因此他们拥有一个强力的PHP开发团队。如果必须引入新技术的话,那么这些技术必须足够简单,不会存在太多架构上的复杂性;那么,他们当下的技术团队就可以对应用进行长期的维护。

为了满足他们扩展到下一个市场的需求,架构师必须使用可扩展理念进行设计。首先,我们审视了他们的基础设施:

Linux:使用HAProxy、PHP、Redis和MySQL支撑每周10亿请求
Linux:使用HAProxy、PHP、Redis和MySQL支撑每周10亿请求
Linux:使用HAProxy、PHP、Redis和MySQL支撑每周10亿请求
Linux:使用HAProxy、PHP、Redis和MySQL支撑每周10亿请求

老系统使用了单模块化设计思路,底层是一些基于PHP的Web应用程序。这个初创公司有许多所谓的前端网站,它们大多都使用了独立的数据库,并共享了一些支撑业务逻辑的通用代码。毫不客气的说,长期维护这种应用程序绝对是一个噩梦:因为随着业务的发展,有些代码必须被重写,这样的话,修改某个网站将不可避免导致业务逻辑上的不一致,这样一来,他们不得不在所有Web应用程序上做相同的修改。

通常情况下,这该归结于项目管理问题,管理员必须对横跨多个代码库的那些代码负责。基于这个观点,整改第一步就是提取核心的业务关键功能,并将之拆分为独立的服务(这也是本文的一个重点部分),也就是所谓的面向服务架构,在整个系统内遵循“separation of concern”原则。每个服务只负责一个业务逻辑,同时也要明确更高等级的业务功能。举个形象的例子也就是,这个系统可能是个搜索引擎、一个销售系统等。

前端网站通过REST API与服务交互,响应则基于JSON格式。为了简单起见,我们没有选择SOAP,一个开发者比较无爱的协议,因为谁都不愿意解析一堆的XML。

提取一些不会经常处理的服务,比如身份验证和会话管理。这是非常必要的一个环节,因为它们的处理等级比较高。前端网站负责这个部分,只有它们可以识别用户。这样一来我们可以保持服务的足够简单,在处理扩展和代码相关问题时都具有巨大的优势,可谓各司其职,完美无缺。

带来的好处:

  • 独立子系统(服务)可以便捷的在不同团队中开发,开发者互不干涉,效率理所当然提升。
  • 身份验证和会话不会通过它们来管理,因此它们造成的扩展问题不翼而飞。
  • 业务逻辑被区分,不同的前端网站不会再存在功能冗余。
  • 显著地提高了服务的可用性。 

共生的缺点:

为系统管理员带来更大的工作量。鉴于服务都使用了独立的基础设施,这将给管理员带来更多需要关注的地方。

很难保持向后兼容。在一年的维护之后,API方法中发生了数不尽的变化。因此问题发生了,它们必将破坏向后兼容,因为每个网站的代码都可能发生变化,还可能存在许多技术人员同时修改一个网站的情况……然而,一年后,所有方法匹配的仍然是项目开始时建立的文档。

应用程序层

Linux:使用HAProxy、PHP、Redis和MySQL支撑每周10亿请求
Linux:使用HAProxy、PHP、Redis和MySQL支撑每周10亿请求

着眼请求工作流,第一层是应用程序。HAProxy负载均衡器、Varnish和Symfony2应用程序都在这一层。来自前端网站的请求首先会传递给HAProxy,随后负载均衡器将把他分给不同的节点。

应用程序节点配置

  • Xeon E5-1620@3.60GHz,64GB RAM,SATA
  • Varnish
  • Apache2
  • PHP 5.4.X(PHP-FPM),使用APC字节码缓存

我们购买了3个这样的服务器,N+1冗余配置的active-active模式,备份服务器同样处理请求。因为性能不是首要因素,我们为每个节点配置独立的Varnish以降低缓存hit,同时也避免了单点故障(SPOF)。在这个项目中,我们更重视可用性。因为一个前端网站服务器中使用了Apache 2,我们保留了这个堆栈。这样一来,管理员不会困扰于太多新加入的技术。

Symfony2应用程序

应用程序本身基于Symfony2建立,这是一个PHP全堆栈框架,提供了大量加速开发的组件。作为基于复杂框架的典型REST服务可能受到很多人质疑,这里为你细说:

  • 对 PHP/Symfony 开发者友好。客户端IT团队由PHP开发者组成,添加新技术将意味必须招聘新的开发者,因为业务系统必须做长时间的维护。
  • 清晰的项目结构。 PHP/Symfony虽然从来都不是必需品,但却是许多项目的默认选择。引入新的开发者将非常方便,因为对他们来说代码非常友好。
  • 许多现成的组件。遵循DRY思想……没有人愿意花力气去做重复的工作,我们也不例外。我们使用了大量的Symfony2 Console Component,这个框架非常有利于做CLI命令,以及应用程序性能分析(debug工具栏)、记录器等。

在选用Symfony2之前,我们做了大量的性能测试以保证应用程序可以支撑计划流量。我们制定了概念验证,并使用JMeter执行,我们得到了让人满意的结果——每秒700请求时响应时间可以控制在50毫秒。这些测试给了我们足够的信心,让我们坚信,即使Symfony2这样复杂的框架也可以得到理想的性能。

应用程序分析与监控

我们使用Symfony2工具来监视应用程序,在收集指定方法执行时间上表现的非常不错,特别是那些与第三方网络服务交互的操作。这样一来,我们可以发现架构中潜在的弱点,找出应用程序中最耗时的部分。

冗长的日志同样是不可缺少的一部分,我们使用PHP Monolog库把这些日志处理成优雅的log-lines,便于开发者和管理员理解。这里需要注意的是尽可能多地添加细节,越详细越好,我们使用了不同的日志等级:

  • Debug,可能会发生的事情。比如,请求信息在调用前会传送给一个外部Web服务;事情发生后从API调用响应。
  • Error,当错误发生时请求流并未被终止,比如第三方API的错误响应。
  • Critical,应用程序崩溃的瞬间。

因此,你可以清晰地了解Error和Critical信息。而在开发/测试环境中,Debug信息同样被记录。同时,日志被存储在不同的文件中,也就是Monolog库下的“channels”。系统中有一个主日志文件,记录了所有应用程序级错误,以及各个channel的短日志,从单独的文件中记录了来自各个channel的详细日志。

扩展性

扩展平台的应用程序层并不困难,HAProxy性能并不会在短时间耗尽,唯一需要考虑的就是如何冗余以避免单点故障。因此,当下需要做的只是添加下一个应用程序节点。

数据层

我们使用Redis和MySQL存储所有的数据,MySQL更多作为三级缓存层,而Redis则是系统的主要数据存储。

Redis

在系统设计时,我们基于以下几点来选择满足计划需求的数据库:

  • 在存储大量数据时不会影响性能,大约2.5亿记录
  • 通常情况下多是基于特定资源的简单GET请求,没有查找及复杂的SELECT操作
  • 在单请求时尽可能多的获得资源以降低延时

在经过一些调查后,我们决定使用Redis 

  • 大部分我们执行的操作都具有 O(1)或O(N)复杂性, N是需要检索键的数量,这意味着keyspace大小并不会影响性能。
  • 通常情况下会使用MGET命令行同时检索100个以上的键,这样可以尽可能的避免网络延时,而不是在循环中做多重GET操作。 

我们当下拥有两个Redis服务器,使用主从复制模式。这两个节点的配置相同,都是Xeon E5-2650v2@2.60GHz,128GB,SSD。内存限制被设置为100GB,通常情况下使用率都是100%。

Linux:使用HAProxy、PHP、Redis和MySQL支撑每周10亿请求
Linux:使用HAProxy、PHP、Redis和MySQL支撑每周10亿请求

在应用程序并没有耗尽单个Redis服务器的所有资源时,从节点主要作作备份使用,用以保证高有效性。如果主节点宕机,我们可以快速的将应用程序切换到从节点。在维护和服务器迁移时,复制同样被执行——转换一个服务器非常简单。

你可能会猜想当Redis资源被一直耗尽时的情景,所有的键都是持久化类型,大约占90% keyspace,剩余资源被全部被用于TTL过期缓存。当下,keyspace已经被分为两个部分:一个是TTL集(缓存),另一个则是用于持久化数据。感谢“volatile-lru”最大化内存设置的可行性,最不经常使用缓存键会被移除。如此一来,系统就可以一直保持单Redis实例同时执行两个操作——主存储和通用缓存。

使用这个模式必须一直监视“期满”键的数量:

db.redis1:6379> info keyspace
# Keyspace
db0:keys=16XXXXXXX,expires=11XXXXXX,avg_ttl=0

“期满”键数量越接近0情况越危险,这个时候管理员就需要考虑适当的分片或者是增加内存。

我们如何进行监控?这里使用Icinga check,仪表盘会显示数字是否会达到临界点,我们还使用了Redis来可视化“丢失键”的比率。

Linux:使用HAProxy、PHP、Redis和MySQL支撑每周10亿请求
Linux:使用HAProxy、PHP、Redis和MySQL支撑每周10亿请求

在一年后,我们已经爱上了Redis,它从未让我们失望,这一年系统从未发生任何宕机情况。

MySQL

在Redis之外,我们还使用了传统RDBMS——MySQL。但是区别于他人,我们通常使用它作为三级缓存层。我们使用MySQL存储一些不会经常使用对象以降低Redis的资源使用率,因此它们被放到了硬盘上。这里没有什么可说道的地方,我们只是尽可能地让其保持简单。我们使用了两个MySQL服务器,配置是Xeon E5-1620@3.60GHz,64GB RAM,SSD。两个服务器使用本地、异步的主-主复制。此外,我们使用一个单独的从节点作为备份。

MySQL的高可用性

在应用程序中,数据库永远是最难的瓶颈。当前,这里还不需要考虑横向扩展操作,我们多是纵向扩展Redis和MySQL服务器。当下这个策略还存在一定的发展空间,Redis运行在一个126GB内存的服务器上,扩展到256GB也并不困难。当然,这样的服务器也存在劣势,比如快照,又或是是简单的启动——Redis服务器启动需要很长的时间。

在纵向扩展失效后进行的必然是横向扩展,值得高兴的是,项目开始时我们就为数据准备了一个易于分片的结构:

在Redis中,我们为记录使用了4个“heavy”类型。基于数据类型,它们可以分片到4个服务器上。我们避免使用哈希分片,而是选择基于记录类型分片。这种情况下,我们仍然可以运行MGET,它始终在一种类型键上执行。

在MySQL上,结构化的表格非常易于向另一台服务器上迁移——同样基于记录类型(表格)。当然,一旦基于记录类型的分片不再奏效,我们将转移至哈希。

学到的知识 

  • 不要随便共享你的数据库给别人用。有一次,一个前端网站希望将会话处理切换到Redis,所以就直接连接了一个,它耗尽了Redis的缓存空间,以至于应用程序再也不能保存下一个缓存键了。所有的缓存应该在 MySQL 的结果集占用大量开销时才进行存储。
  • 日志越详细越好。如果log-lines中没有足够的信息,快速Debug问题定位将成为难点。如此一来,你不得不等待一个又一个问题发生,直到找到根结所在。
  • 架构中使用复杂的框架并不意味着低性能。许多人惊讶我们使用全堆栈框架来支撑如此流量应用程序,其秘诀在于更聪明的使用工具,否则即使是Node.js也可能变得很慢。选择一个提供良好开发环境的技术,没有人期望使用一堆不友好的工具,这将降低开发团队士气。

来源:http://www.csdn.net/article/2014-08-14/2821203

Linux:Libvirt 虚拟化库介绍

Libvirt 库是一种实现 Linux 虚拟化功能的 Linux® API,它支持各种虚拟机监控程序,包括 Xen 和 KVM,以及 QEMU 和用于其他操作系统的一些虚拟产品。本文主要探讨 libvirt 及其用途和架构。

Linux:Libvirt 虚拟化库介绍
Linux:Libvirt 虚拟化库介绍

讲到向外扩展计算(比如云计算),libvirt 可能是您从未听说过的最重要的库之一。libvirt 提供一种虚拟机监控程序不可知的 API 来安全管理运行于主机上的来宾操作系统。libvirt 本身不是一种工具, 它是一种可以建立工具来管理来宾操作系统的 API。libvirt 本身构建于一种抽象的概念之上。它为受支持的虚拟机监控程序实现的常用功能提供通用的 API。libvirt 起初是专门为 Xen 设计的一种管理 API,后来被扩展为可支持多个虚拟机监控程序。

基本架构

首先让我们从用例模型视角来展开对 libvirt 的讨论,然后深入探究其架构和用途。libvirt 以一组 API 的形式存在,旨在供管理应用程序使用(见图 1 )。libvirt 通过一种特定于虚拟机监控程序的机制与每个有效虚拟机监控程序进行通信,以完成 API 请求。文章后面我将探讨如何通过 QEMU 来实现该功能。

图 1. libvirt 比较和用例模型
Linux:Libvirt 虚拟化库介绍
Linux:Libvirt 虚拟化库介绍

图中还显示了 libvirt 所用术语对照。这些术语很重要,因为在对 API 命名时会用到它们。两个根本区别在于,libvirt 将物理主机称作节点,将来宾操作系统称作。这里需要注意的是,libvirt(及其应用程序)在宿主 Linux 操作系统(域 0)中运行。

控制方式

使用 libvirt,我们有两种不同的控制方式。第一种如 图 1 所示,其中管理应用程序和域位于同一节点上。 在本例中,管理应用程序通过 libvirt 工作,以控制本地域。当管理应用程序和域位于不同节点上时,便产生了另一种控制方式。在本例中需要进行远程通信(参见 图 2)。该模式使用一种运行于远程节点上、名为 libvirtd 的特殊守护进程。当在新节点上安装 libvirt 时该程序会自动启动,且可自动确定本地虚拟机监控程序并为其安装驱动程序(稍后讨论)。该管理应用程序通过一种通用协议从本地 libvirt 连接到远程 libvirtd。对于 QEMU,协议在 QEMU 监视器处结束。QEMU 包含一个监测控制台,它允许检查运行中的来宾操作系统并控制虚拟机(VM)各部分。

图 2. 使用 libvirtd 控制远程虚拟机监控程序
Linux:Libvirt 虚拟化库介绍
Linux:Libvirt 虚拟化库介绍

虚拟机监控程序支持

为支持各种虚拟机监控程序的可扩展性,libvirt 实施一种基于驱动程序的架构,该架构允许一种通用的 API 以通用方式为大量潜在的虚拟机监控程序提供服务。这意味着,一些虚拟机监控程序的某些专业功能在 API 中不可见。另外,有些虚拟机监控程序可能不能实施所有 API 功能,因而在特定驱动程序内被定义为不受支持。图 3 展示了 libvirt API 与相关驱动程序的层次结构。这里也需要注意,libvirtd 提供从远程应用程序访问本地域的方式。

图 3. 基于驱动程序的 libvirt 架构
Linux:Libvirt 虚拟化库介绍
Linux:Libvirt 虚拟化库介绍

在撰写此文时,libvirt 为表 1 所列的虚拟机监控程序实现了驱动程序。随着新的虚拟机监控程序在开源社区出现,其他驱动程序无疑也将可用。

表 1. libvirt 支持的虚拟机监控程序
虚拟机监控程序 描述
Xen 面向 IA-32,IA-64 和 PowerPC 970 架构的虚拟机监控程序
QEMU 面向各种架构的平台仿真器
Kernel-based Virtual Machine (KVM) Linux 平台仿真器
Linux Containers(LXC) 用于操作系统虚拟化的 Linux(轻量级)容器
OpenVZ 基于 Linux 内核的操作系统级虚拟化
VirtualBox x86 虚拟化虚拟机监控程序
User Mode Linux 面向各种架构的 Linux 平台仿真器
Test 面向伪虚拟机监控程序的测试驱动器
Storage 存储池驱动器(本地磁盘,网络磁盘,iSCSI 卷)

libvirt 和虚拟 shell

上面已经介绍了 libvirt 的一些架构,接下来看一下如何使用 libvirt 虚拟化 API 的一些示例。首先介绍一种名为 virsh(虚拟 shell)的应用程序,它构建于 libvirt 之上。该 shell 允许以交互(基于 shell)方式使用多个 libvirt 功能。在本节中,我使用 virsh 演示了一些 VM 操作。

第一步是要定义域配置文件(如下面的 清单 1 所示)。该代码指定了定义域所需的所有选项 — 从虚拟机监控程序(仿真器)到域使用的资源以及外围配置(比如网络)。注意,这只是个简单的配置,libvirt 真正支持的属性更加多样化。例如,您可以指定 BIOS 和主机引导程序,域要使用的资源,以及要用到的设备 — 从软盘和 CD-ROM 到 USB 和 PCI 设备。

域配置文件定义该 QEMU 域要使用的一些基本元数据,包括域名、最大内存、初始可用内存(当前)以及该域可用的虚拟处理器数量。您不需要自己分配 Universally Unique Idenifier (UUID),而是让 libvirt 分配。您需要为该平台定义要仿真的机器类型 — 在本例中是被完全虚拟化(hvm)的 686 处理器。您需要为域定义仿真器的位置(以备需要支持多个同类型仿真器时使用)和虚拟磁盘。这里注意要指明 VM,它是以 Virtual Machine Disk(VMDK)格式存在的 ReactOS 操作系统。最后,要指定默认网络设置,并使用面向图形的 Virtual Network Computing (VNC)。

清单 1. 域配置文件


  ReactOS-on-QEMU
  
  131072
  131072
  1
  
    hvm
  
  
    usr/bin/qemu
    
      
      
    
    
      
    
    
  

完成了域配置文件之后,现在开始使用 virsh 工具启动域。virsh 工具为要执行的特定动作采用命令参数。在启动新域时,使用 create 命令和域配置文件:

清单 2. 启动新域
mtj@mtj-desktop:~/libvtest$ virsh create react-qemu.xml
Connecting to uri: qemu:///system
Domain ReactOS-on-QEMU created from react-qemu.xml

mtj@mtj-desktop:~/libvtest$

这里要注意用于连接到域(qemu:///system)的 Universal Resource Indicator (URI)。该本地 URI 连接到本地 QEMU 驱动程序的系统模式守护进程上。要通过主机 shinchan 上的 Secure Shell (SSH) 协议连接到远程 QEMU 虚拟机监控程序,可以使用 URL qemu+ssh://shinchan/。

下一步,您可以使用 virsh 内的 list 命令列出给定主机上的活动域。这样做可以列出活动域,域 ID,以及它们的状态,如下所示:

清单 3. 列出活动域
mtj@mtj-desktop:~/libvtest$ virsh list
Connecting to uri: qemu:///system
 Id Name                 State
----------------------------------
  1 ReactOS-on-QEMU      running

mtj@mtj-desktop:~/libvtest$

注意,这里定义的名称是在域配置文件元数据中定义过的名称。可以看到,该域的域名是 1 且正在运行中。

您也可以使用 suspend 命令中止域。该命令可停止处于调度中的域,不过该域仍存在于内存中,可快速恢复运行。下面的例子展示了如何中止域,执行列表查看状态,然后重新启动域:

清单 4. 中止域,检查状态,并重新启动
mtj@mtj-desktop:~/libvtest$ virsh suspend 1
Connecting to uri: qemu:///system
Domain 1 suspended

mtj@mtj-desktop:~/libvtest$ virsh list
Connecting to uri: qemu:///system
 Id Name                 State
----------------------------------
  1 ReactOS-on-QEMU      paused

mtj@mtj-desktop:~/libvtest$ virsh resume 1
Connecting to uri: qemu:///system
Domain 1 resumed

mtj@mtj-desktop:~/libvtest$

virsh 工具也支持许多其他命令,比如保存域的命令(save),恢复已存域的命令(restore),重新启动域的命令(reboot),以及其他命令。您还可以从运行中的域(dumpxml)创建域配置文件。

到目前为止,我们已经启动并操作了域,但是如何连接域来查看当前活动域呢?这可以通过 VNC 实现。要创建表示特定域图形桌面的窗口,可以使用 VNC:

清单 5. 连接到域
mtj@mtj-desktop:~/libvtest$ xvnc4viewer 127.0.0.1 0

libvirt 和 Python

上一个例子说明了如何使用命令行工具 virsh 实现对域的控制。现在我们看一个使用 Python 来控制域的例子。Python 是受 libvirt 支持的脚本语言,它向 libvirt API 提供完全面向对象的接口。

在本例中,我研究了一些基本操作,与之前用 virsh 工具(list、suspend、resume 等)展示的操作类似。Python 示例脚本见 清单 6。在本例中,我们从导入 libvirt 模块开始。然后连接到本地 QEMU 虚拟机监控程序。从这里开始,重复可用的域 ID;对每个 ID 创建一个域对象,然后中止,继续,最后删除该域。

清单 6. 用于控制域的示例 Python 脚本(libvtest.py)
import libvirt

conn = libvirt.open('qemu:///system')

for id in conn.listDomainsID():

	dom = conn.lookupByID(id)

	print "Dom %s  State %s" % ( dom.name(), dom.info()[0] )

	dom.suspend()
	print "Dom %s  State %s (after suspend)" % ( dom.name(), dom.info()[0] )

	dom.resume()
	print "Dom %s  State %s (after resume)" % ( dom.name(), dom.info()[0] )

	dom.destroy()

虽然这只是个简单示例,我们仍然可以看到 libvirt 通过 Python 提供的强大功能。通过一个简单的脚本就能够重复所有本地 QEMU 域,发行有关域的信息,然后控制域。该脚本的结果如 清单 7 所示。

清单 7. 清单 6 中的 Python 脚本输出的结果
mtj@mtj-desktop:~/libvtest$ python libvtest.py
Dom ReactOS-on-QEMU  State 1
Dom ReactOS-on-QEMU  State 3 (after suspend)
Dom ReactOS-on-QEMU  State 1 (after resume)
mtj@mtj-desktop:~/libvtest$

API 概述

高级 libvirt API 可划分为 5 个 API 部分:虚拟机监控程序连接 API、域 API、网络 API、存储卷 API 以及存储池 API。

为给定虚拟机监控程序创建连接后会产生所有 libvirt 通信(例如,清单 6 中所示的 open 调用)。该连接为所有其他要使用的 API 提供路径。在 C API 中,该行为通过 virConnectOpen 调用(以及其他进行认证的调用)提供。这些函数的返回值是一个 virConnectPtr 对象,它代表到虚拟机监控程序的一个连接。该对象作为所有其他管理功能的基础,是对给定虚拟机监控程序进行并发 API 调用所必需的语句。重要的并发调用是 virConnectGetCapabilities 和 virNodeGetInfo,前者返回虚拟机监控程序和驱动程序的功能,后者获取有关节点的信息。该信息以 XML 文档的形式返回,这样通过解析便可了解可能发生的行为。

进入虚拟机监控程序后,便可以使用一组 API 调用函数重复使用该虚拟机监控程序上的各种资源。virConnectListDomains API 调用函数返回一列域标识符,它们代表该虚拟机监控程序上的活动域。

API 实现大量针对域的函数。要探究或管理域,首先需要一个 virDomainPtr 对象。您可通过多种方式获得该句柄(使用 ID、UUID 或域名)。继续来看重复域的例子,您可以使用该函数返回的索引表并调用 virDomainLookupByID 来获取域句柄。有了该域句柄,就可以执行很多操作,从探究域(virDomainGetUUID、virDomainGetInfo、virDomainGetXMLDesc、virDomainMemoryPeek)到控制域(virDomainCreate、virDomainSuspend、virDomainResume、virDomainDestroy 和 virDomainMigrate)。

您还可使用 API 管理并检查虚拟网络和存储资源。建立了 API 模型之后,需要一个 virNetworkPtr 对象来管理并检查虚拟网络,且需要一个virStoragePoolPtr(存储池)或 virStorageVolPtr(卷)对象来管理这些资源。

API 还支持一种事件机制,您可使用该机制注册为在特定事件(比如域的启动、中止、恢复或停止)发生时获得通知。

语言绑定

libvirt 库用 C (支持 C++)实现,且包含对 Python 的直接支持。不过它还支持大量语言绑定。目前已经对 Ruby、Java™ 语言,Perl 和 OCaml 实施了绑定。在从 C# 调用 libvirt 方面我们已做了大量工作。libvirt 支持最流行的系统编程语言(C 和 C++)、多种脚本语言、甚至一种统一的函数型语言(Objective caml)。因此,不管您侧重何种语言,libvirt 都会提供一种路径来帮助您控制域。

使用 libvirt 的应用程序

仅从本文已经展示的一小部分功能上便可看出 libvirt 提供的强大功能。且如您所愿,有大量应用程序正成功构建于 libvirt 之上。其中一个有趣的应用程序就是 virsh(这里所示),它是一种虚拟 shell。还有一种名为 virt-install 的应用程序,它可用于从多个操作系统发行版供应新域。virt-clone 可用于从另一个 VM 复制 VM(既包括操作系统复制也包括磁盘复制)。一些高级应用程序包括多用途桌面管理工具 virt-manager 和安全连接到 VM 图形控制台的轻量级工具 virt-viewer。

构建于 libvirt 之上的一种最重要的工具名为 oVirt。oVirt VM 管理应用程序旨在管理单个节点上的单个 VM 或多个主机上的大量 VM。除了可以简化大量主机和 VM 的管理之外,它还可用于跨平台和架构自动化集群,负载平衡和工作。

深入探究

从这篇简短的文章可以看出,libvirt 是一种用来构建应用程序的强大库,能够跨系统的大型网络在不同的虚拟机监控程序环境中管理域。鉴于云计算的日渐流行,libvirt 无疑也会随之发展,不断获得新的应用程序和用户。撰写本文时,libvirt 也仅有四年的发展史,因此在大规模可伸缩计算领域中相对较新。libvirt 将来肯定会有很大发展。

参考资料

学习

  • 查阅 libvirt Web 站点,了解 libvirt 最新信息并下载最新版本。您还会找到完整版的 API 参考手册,它详细介绍了核心界面和错误处理界面。
  • libvirt 支持大量虚拟机监控程序,包括:
  • 在 “虚拟 Linux”(developerWorks,2006 年 12 月)中,了解关于各种类型的虚拟化的更多信息。云计算依靠虚拟化来最大限度地使用可用服务器的资源。通过虚拟化,服务器可寄宿多个操作系统和应用程序集。
  • 在 “LXC:Linux 容器工具”(developerWorks,2009 年 2 月)中,您可了解更多有关专门为 LXC 构建管理工具的信息。这里会发现与 libvirt 管理方法的一些对应之处。

来源:http://www.ibm.com/developerworks/cn/linux/l-libvirt/

Linux:Web API设计方法论

 

设计Web API不止是URL、HTTP状态码、头信息和有效负载。设计的过程–基本上是为了你的API“观察和感受” — 这非常重要,并且值得你付出努力。本文简要概括了一种同时发挥HTTP和Web两者优势的API设计方法论。并且它不仅对HTTP有效。如果有时你还需要 通过WebSockets、XMPP、MQTT等实现同样的服务,大部分API设计的结果同样可用。可以让未来支持多种协议更容易实现和维护。

优秀的设计超越了URL、状态码、头信息和有效负载

一般来说, Web API设计指南的重点是通用的功能特性,比如URL设计,正确使用状态码、方法、头信息之类的HTTP功能特性,以及持有序列化的对象或对象图的有效负载 设计。这些都是重要的实现细节,但不太算得上API设计。并且正是API的设计–服务的基本功能特性的表达和描述方式–为Web API的成功和可用性做出了重要贡献。

一个优秀的设计过程或方法论定义了一组一致的、可重复的步骤集,可以在将一个服务器端服务组件输出为一个可访问的、有用的Web API时使用。那就是说,一个清晰的方法论可以由开发人员、设计师和软件架构师共享,以便在整个实现周期内帮助大家协同活动。一个成熟的方法论还可以随着 时间的发展,随着每个团队不断发现改善和精简过程的方式而得到精炼,却不会对实现细节产生不利的影响。实际上,当实现细节设计过程两者都有清晰的定义并相互分离时,实现细节的改变(比如采用哪个平台、OS、框架和UI样式)可以独立于设计过程。

API设计七步法

接下来我们要对Richardson和Amundsen合著的《REST风格的Web API》一书中所介绍的设计方法论做简要地概述。因为篇幅所限,我们不能深入探讨这一过程中的每一步骤,但这篇文章可以让你有个大概的认识。另外,读者可以用这篇概述作为指南,根据自己组织的技能和目标开发一个独有的Web API设计过程。

说明:是的,7步看起来有点儿多。实际上清单中有5个步骤属于设计,额外还有两个条目是实现和发布。最后这两个设计过程之外的步骤是为了提供一个从头到尾的体验。

你应该计划好根据需要重新迭代这些步骤。通过步骤2(绘制状态图)意识到在步骤1(列出所有组成部分)有更多工作要做。当你接近于写代码(步骤6) 时,可能会发现第5步(创建语义档案)中漏了一些东西。关键是用这个过程暴露尽可能多的细节,并愿意回退一步或者两步,把前面漏掉的补上。迭代是构建更加 完整的服务画面以及澄清如何将它暴露给客户端程序的关键。

步骤1 : 列出所有组成部分

第一步是列出客户端程序可能要从我们的服务中获取的,或要放到我们的服务中的所有数据片段。我们将这些称为语义描述符。语义是指它们处理数据在应用 程序中的含义,描述符是指它们描述了在应用程序自身中发生了什么。注意,这里的视点是客户端,不是服务器端。将API设计成客户端使用的东西很重要。

比如说,在一个简单的待办事项列表应用中,你可能会找到下面这些语义描述符:

  • id : 系统中每条记录的唯一标识符
  • title : 每个待办事项的标题
  • dateDue : 待办事项应该完成的日期
  • complete : 一个是/否标记,表明待办事项是否已经完成了。

在一个功能完备的应用程序中,可能还会有很多语义描述符,涉及待办事项的分类(工作、家庭、园艺等),用户信息(用于多用户的实现)等等。不过为了突出过程本身,我们会保持它的简单性。

步骤2 : 绘制状态图

下一步是根据建议的API绘制出状态图。图中的每个框都表示一种可能的表示–一个包含在步骤1中确定的一或多个语义描述符的文档。你可以用箭头表示从一个框到下一个的转变–从一个状态到下一个状态。这些转变是由协议请求触发的。

在每次变化中还不用急着指明用哪个协议方法。只要标明变化是安全的(比如HTTP GET),还是不安全/非幂等的(比如HTTP POST),或者不安全/幂等的(PUT)。

说明:幂等动作是指重复执行时不会有无法预料的副作用。比如HTTP PUT,因为规范说服务器应该用客户端传来的状态值替换目标资源的已有值,所以说它是幂等的。而 HTTP POST是非幂等的,因为规范指出提交的值应该是追加到已有资源集合上的,而不是替换。

在这个案例中,我们这个简单的待办事项服务的客户端应用程序可能需要访问可用条目的清单,能过滤这个清单,能查看单个条目,并且能将条目标记为已完 成。这些动作中很多都用状态值在客户端和服务器之间传递数据。比如add-item 动作允许客户端传递状态值title和dueDate。下面是一个说明那些动作的状态图。

Linux:Web API设计方法论
Linux:Web API设计方法论

这个状态图中展示的这些动作(也在下面列出来了)也是语义描述符– 它们描述了这个服务的语义动作

  • read-list
  • filter-list
  • read-item
  • create-item
  • mark-complete

在你做这个状态图的过程中,你可能会发现自己漏掉了客户端真正想要或需要的动作或数据项。这是退回到步骤1的机会,添加一些新的描述符,并/或者在步骤2中改进状态图。

在你重新迭代过这两步之后,你应该对客户端跟服务交互所需的所有数据点和动作有了好的认识和想法。

步骤 3 : 调和魔法字符串

下一步是调和服务接口中的所有“魔法字符串”。“魔法字符串” 全是描述符的名称–它们没有内在的含义,只是表示客户端跟你的服务通讯时将要访问的动作或数据元素。调和这些描述符名称的意思是指采用源自下面这些地方的,知名度更高的公共名称:

这些全是明确定义的、共享的名称库。当你服务接口使用来自这些源头的名称时,开发人员很可能之前见过并知道它们是什么意思。这可以提高API的可用性。

说明:尽管在服务接口上使用共享名称是个好主意,但在内部实现里可以不用(比如数据库里的数据域名称)。服务自身可以毫不困难地将公共接口名称映射为内部存储名称。

以待办事项服务为例,除了一个语义描述符- create-item,我能找到所有可接受的已有名称。为此我根据Web Linking RFC5988中的规则创建了一个具有唯一性的URI。在给接口描述符选择知名的名称时需要折中。它们极少能跟你的内部数据存储元素完美匹配,不过那没关系。

这里是我的结果:

经过名称调和,我的状态图变成了下面这样:

Linux:Web API设计方法论
Linux:Web API设计方法论

步骤 4 : 选一个媒体类型

API设计过程的下一步是选一个媒体类型,用来在客户端和服务器端之间传递消息。Web的特点之一是数据是通过统一的接口作为标准化文档传输的。选 择同时支持数据描述符(比如”identifier”、”status”等)和动作描述符(比如”search”、”edit”等)的媒体类型很重要。有相当多可用的格式。

在我写这篇文章时,一些顶尖的超媒体格式是 (排名不分先后):

  • 超文本标记语言 (HTML)
  • 超文本应用程序语言(HAL)
  • Collection+JSON (Cj)
  • Siren
  • JSON-API
  • 交换表达式的统一基础 (UBER)

让所选择的媒体类型适用于你的目标协议也很重要。大多数开发人员喜欢用HTTP 协议做服务接口。然而WebSocketsXMPPMQTTCoAP 也会用–特别是对于高速、短消息、端到端的实现。

在这个例子中,我会以HTML为消息格式,并采用HTTP协议。HTML有所有数据描述符所需的支持(

    用于列表,

  • 用于条目, 用于数据元素)。它也有足够的动作描述符支持 (用于安全链接,
    用于安全转变,

    用于非安全转变)。

    注意:在这个状态图中, 编辑动作是幂等的(比如HTTP PUT),并且HTML仍然没有对PUT的原生支持。在这个例子中,我会添加一个域来将HTMLPOST做成幂等的。

    好了,现在我可以基于那个状态图创建一些样例表示来“试试”这个接口了。对我们的例子而言,只有两个表示要渲染:“待办事项列表”和“待办事项条目”表示:

    1 :用HTML表示待办事项列表集合

    Linux:Web API设计方法论
    Linux:Web API设计方法论
    Linux:Web API设计方法论
    Linux:Web API设计方法论

    图2:用HTML表示待办事项条目

    Linux:Web API设计方法论
    Linux:Web API设计方法论

    记住,在你做状态图的表示样例时,可能会发现之前的步骤中有所遗漏(比如漏掉描述符,动作描述符中有幂等之类的变化等)。那也没关系。现在就是解决所有这些问题的时机– 在你把这个设计变成代码之前。

    等你对表示完全满意之后,在开始写代码之前还有一个步骤–创建语义档案。

    步骤 5 : 创建语义档案

    语义档案是一个文档,其中列出了设计中的所有描述符,包括对开发人员构建客户端和服务器端实现有帮助的所有细节。这个档案是一个实现指南,不是实现描述。这个差别很重要。

    服务描述符的格式

    服务描述文档格式已经出现了相当长一段时间了,并且当你想给已有的服务实现生产代码或文档时很方便。确实有很多种格式。

    在我写这篇文章时,顶级竞争者有:

    档案的格式

    现在只有几种档案格式。我们推荐下面两种:

    这两个都比较新。JSON-LD规范在2014年早期达成了W3C推荐状态。Hydra仍是一个非官方草案(本文写成时还是),有一个活跃的开发者社区。ALPS仍处于IETF的早期草案阶段。

    因为档案文档的理念是要描述一个问题空间的现实生活方面(不只是那一空间中的单一实现),所以其格式跟典型的描述格式十分不同:

    3 : ALPS格式的待办事项列表语义档案

    Linux:Web API设计方法论
    Linux:Web API设计方法论

    你会注意到,这个文档就像一个基本的词汇表,包含了待办事项服务接口中所有可能的数据值和动作–就是这个理念。同意遵循这个档案的服务可以自行决定它们的协议、消息格式甚至URL。同意接受这个档案的客户端将会构建为可以识别,如果合适的话,启用这个文档中的描述符。

    这种格式也很适合生成人类可读的文档,分析相似的档案,追踪哪个档案用得最广泛,甚至生成状态图。但那是另外一篇文章的课题了。

    现在你有完整的已调和名称的描述符清单,已标记的状态图,以及一个语义档案文档,可以开始准备编码实现样例服务器和客户端了。

    步骤 6 : 写代码

    到了这一步,你应该可以将设计文档(状态图和语义档案)交给服务器和客户端程序的开发人员了,让他们开始做具体的实现。

    HTTP服务器应该实现在第2步中创建的状态图,并且来自客户端的请求应该触发正确的状态转变。服务发送的每个表示都应该用第3步中选好的格式,并 且应该包含一个第4步中创建的指向一个档案的链接。响应中应该包含相应的超媒体控件,实现了在状态图中显示、并在档案文档中描述的动作。客户端和服务器端 开发人员在这时可以创建相对独立的实现,并用测试验证其是否遵守了状态图和档案。

    有了稳定的可运行代码,还有一步要做:发布。

    步骤 7 : 发布你的API

    Web API应该至少发布一个总能给客户端响应的URL — 即便是在遥远的将来。我将其称为“看板URL” –每个人都知道的。发布档案文档也是个好主意,服务的新实现可以在响应中链接它。你还可以发布人类可读的文档、教程等,以帮助开发人员理解和使用你的服 务。

    做好这个之后,你应该有了一个设计良好的、稳定的、可访问的服务运行起来了,随时可以用。

    总结

    本文讨论了为Web设计API的一组步骤。重点是让数据和动作描述正确,并以机器可读的方式记录它们,以便让人类开发人员即便不直接接触也能轻松为这个设计实现客户端和服务器端。

    这些步骤是:

    1. 列出所有组成部分 收集客户端跟服务交互所需的所有数据元素。
    2. 绘制状态图 记录服务提供的所有动作(状态变化)
    3. 调和魔法字符串 整理你的公开接口以符合(尽可能)知名的名称
    4. 选择媒体类型 评审消息格式,找到跟目标协议的服务转变最贴近的那个。
    5. 创建语义档案 编写一个档案文档,定义服务中用的所有描述符。
    6. 写代码 跟客户端和服务器端开发人员分享档案文档,并开始写代码测试跟档案/状态图的一致性,并在有必要时进行调整。
    7. 发布你的API 发布你的”看板URL”和档案文档,以便其他人可以用他们创建新的服务以及/或者客户端程序。

    在你的设计过程中,你可能会发现有遗漏的元素,需要重做某些步骤,以及要做一些折中的决定。这在设计过程中出现得越早越好。将来开发人员要求用新的格式和协议实现时,你还有可能用这个API设计。

    最后,这个方法论只是为Web API设计过程创建一种可靠、可重复、一致的设计过程的一种可能方式。在你做这个例子时,可能会发现插入一些额外的步骤,或者缩减一些会更好用,并且– 当然 — 消息格式和协议决策在不同案例中可能也会发生变化。

    希望这篇文章能给你一些启发,让你知道如何给自己的组织以及/或者团队创建一个最佳的API设计方法论。

    关于作者

    Web API设计方法论 Mike Amundsen 是Layer 7科技的首席API架构师,帮助人们为Web创建优秀的API。国际知名作者和讲师,Mike 在欧美四处旅行,就分布式网络架构、Web应用程序开发、云计算和其他题目提供咨询和演讲。他的名下有十多本书。

    来源:http://www.infoq.com/cn/articles/web-api-design-methodology