HSF:High-speed Service Framework

主要做了以下方面:服务方式的远程调用;软负载体系;服务可用性保障

spring-hessian

序列化方式一:Hessian 是由 caucho 提供的一个基于 binary-RPC 实现的远程通讯 library 。

   1 、是基于什么协议实现的 ?

            基于 Binary-RPC 协议实现。

   2 、怎么发起请求 ?

            需通过 Hessian 本身提供的 API 来发起请求。

   3 、怎么将请求转化为符合协议的格式的 ?

            Hessian 通过其自定义的串行化机制将请求信息进行序列化,产生二进制流。

   4 、使用什么传输协议传输 ?

            Hessian 基于 Http 协议进行传输。

   5 、响应端基于什么机制来接收请求 ?

            响应端根据 Hessian 提供的 API 来接收请求。

   6 、怎么将流还原为传输格式的 ?

            Hessian 根据其私有的串行化机制来将请求信息进行反序列化,传递给使用者时已是相应的请求信息对象了。

   7 、处理完毕后怎么回应 ?

            处理完毕后直接返回, hessian 将结果对象进行序列化,传输至调用端。

 

序列化方式二: Protocol buffer,Protocol buffers are a flexible, efficient, automated mechanism for serializing structured data – think XML, but smaller, faster, and simpler. You define how you want your data to be structured once, then you can use special generated source code to easily write and read your structured data to and from a variety of data streams and using a variety of languages. You can even update your data structure without breaking deployed programs that are compiled against the "old" format.

 

 

远程调用:同步、单向异步、Future异步、Callback异步、可靠异步

 

负载均衡 : failover,随机寻址,第七层路由,路由规则,权重规则

 

failover(故障转移):In computing , failover is the capability to switch over automatically to a redundant or standbycomputer server , system , or network upon the failure or abnormal termination of the previously activeapplication ,[ 1] server, system, or network. Failover happens without human intervention and generally without warning, unlike switchover .

 

随机寻址:

负载均衡策略

选择合适的负载均衡策略,使多个设备能很好的共同完成任务,消除或避免现有网络负载分布不均、数据流量拥挤反应时间长的瓶颈。在各负载均衡方式中,针对不同的应用需求,在 OSI 参考模型的第二、三、四、七层的负载均衡都有相应的负载均衡策略。 
负载均衡策略的优劣及其实现的难易程度有两个关键因素:一、负载均衡算法,二、对网络系统状况的检测方式和能力。 
轮循均衡( Round Robin ): 每一次来自网络的请求轮流分配给内部中的服务器,从 1 至 N 然后重新开始。此种均衡算法适合于服务器组中的所有服务器都有相同的软硬件配置并且平均服务请求相对均衡的情况。  
权重轮循均衡( Weighted Round Robin ): 根据服务器的不同处理能力,给每个服务器分配不同的权值,使其能够接受相应权值数的服务请求。例如:服务器 A 的权值被设计成 1 , B 的权值是 3 , C 的权值是 6 ,则服务器 A 、 B、 C 将分别接受到 10% 、 30 %、 60 %的服务请求。此种均衡算法能确保高性能的服务器得到更多的使用率,避免低性能的服务器负载过重。  
随机均衡( Random ): 把来自网络的请求随机分配给内部中的多个服务器。  
权重随机均衡( Weighted Random ): 此种均衡算法类似于权重轮循算法,不过在处理请求分担时是个随机选择的过程。  
响应速度均衡( Response Time ): 负载均衡设备对内部各服务器发出一个探测请求(例如 Ping ),然后根据内部中各服务器对探测请求的最快响应时间来决定哪一台服务器来响应客户端的服务请求。此种均衡算法能较好的反映服务器的当前运行状态,但这最快响应时间仅仅指的是负载均衡设备与服务器间的最快响应时间,而不是客户端与服务器间的最快响应时间。 
最少连接数均衡( Least Connection ): 客 户端的每一次请求服务在服务器停留的时间可能会有较大的差异,随着工作时间加长,如果采用简单的轮循或随机均衡算法,每一台服务器上的连接进程可能会产生 极大的不同,并没有达到真正的负载均衡。最少连接数均衡算法对内部中需负载的每一台服务器都有一个数据记录,记录当前该服务器正在处理的连接数量,当有新 的服务连接请求时,将把当前请求分配给连接数最少的服务器,使均衡更加符合实际情况,负载更加均衡。此种均衡算法适合长时处理的请求服务,如 FTP 。 
处理能力均衡: 此种均衡算法将把服务请求分配给内部中处理负荷(根据服务器 CPU 型号、 CPU 数量、内存大小及当前连接数等换算而成)最轻的服务器,由于考虑到了内部服务器的处理能力及当前网络运行状况,所以此种均衡算法相对来说更加精确,尤其适合运用到第七层(应用层)负载均衡的情况下。 
DNS 
响应均衡( Flash DNS ): 在 Internet 上,无论是 HTTP 、 FTP 或是其它的服务请求,客户端一般都是通过域名解析来找到服务器确切的 IP 地址的。在此均衡算法下,分处在不同地理位置的负载均衡设备收到同一个客户端的域名解析请求,并在同一时间内把此域名解析成各自相对应服务器的 IP 地址(即与此负载均衡设备在同一位地理位置的服务器的 IP 地址)并返回给客户端,则客户端将以最先收到的域名解析 IP 地址来继续请求服务,而忽略其它的 IP 地址响应。在种均衡策略适合应用在全局负载均衡的情况下,对本地负载均衡是没有意义的。 

 

HSF 框架:OSGI,动态部署;Core+Plugins 模式;动态线程池

 

 

动态线程池:Jini中的TaskManager

 

服务方式的远程调用:远程通信框架Mina,消息中间件Notify,序列化协议Hessian;跨语言协议XFire

Equinox

 

Mina通信

 

心跳机制

 

分享到:  
posted @ 2014-12-01 19:42 小马歌 阅读(371) | 评论 (0)编辑 收藏
 
     摘要: pt-table-checksum测试库一致的情况下./pt-table-checksum h='127.0.0.1',u='root',p='dh123',P=5331 -d db1 --recursion-method=processlist --no-check-binlog-format --replicate=test.checksumsSELECT db, tbl,lower_boun...  阅读全文
posted @ 2014-11-21 16:08 小马歌 阅读(1470) | 评论 (0)编辑 收藏
 


以前statement复制下做表结构变更时,一般是一台一台的从库依次去做,最后做主库。

今天在一台从库上进行表结构变更时却遇到一个复制报错,
Last_Errno: 1677
Last_Error: Column 7 of table ‘user_0.user_00′ cannot be converted from type ‘varchar(10)’ to type ‘varbinary(30)’

原变更语句为
alter table user_00 modify `column7` varbinary(30) NOT NULL DEFAULT ”;

原表中此字段类型为
`column7` varbinary(10) NOT NULL DEFAULT ”

s 命令显示此从库为5.5格式,并且复制是row格式。

官网查询后,发现这并不是一个bug,

http://bugs.mysql.com/bug.php?id=59424

在5.5的row格式复制中,有参数
slave_type_conversions来控制复制中主从结构不一致的处理

取值见下表:

默认为”,即不支持主从字段类型不一致,
其它3种类型为:
all_lossy 支持有损转换,如int–>tinyint
all_non_lossy 支持无损转换,如char(20)–>varchar(25)
all_lossy,all_non_lossy 支持所有转换

此时手工在从库上执行:
stop slave;
set global slave_type_conversions=ALL_LOSSY;
start slave;

复制恢复正常

posted @ 2014-11-21 14:19 小马歌 阅读(2510) | 评论 (0)编辑 收藏
 


客户端在测试服务器上上传大的图片,报“413 Request Entity Too Large”的错误,从网上搜索解决办法如下:

解决方法:打开nginx主配置文件nginx.conf,找到http{}段,添加
client_max_body_size 20m;
Centos下 vi /usr/local/nginx/conf/nginx.conf

重启NGINX
kill -HUP `cat /usr/local/nginx/nginx.pid `
恢复正常

posted @ 2014-11-14 14:08 小马歌 阅读(410) | 评论 (0)编辑 收藏
 
本文由 ImportNew - xiafei 翻译自 anturis。欢迎加入翻译小组。转载请参见文章末尾的要求。

简介

Java虚拟机(JVM)是Java应用的运行环境,从一般意义上来讲,JVM是通过规范来定义的一个虚拟的计算机,被设计用来解释执行从Java源码编译而来的字节码。更通俗地说,JVM是指对这个规范的具体实现。这种实现基于严格的指令集和全面的内存模型。另外,JVM也通常被形容为对软件运行时环境的实现。通常JVM实现主要指的是HotSpot。

JVM规范保证任何的实现都能够以同样的方式解释执行字节码。其实现可以多样化,包括进程、独立的Java操作系统或者直接执行字节码的处理器芯片。我们了解最多的JVM是作为软件实现,运行在流行的操作系统平台上(包括Windows、OS X、Linux和Solaris等)。

JVM的结构允许对一个Java应用进行更细微的控制。这些应用运行在沙箱(Sandbox)环境中。确保在没有恰当的许可时,无法访问到本地文件系统、处理器和网络连接。远程执行时,代码还需要进行证书认证。

除了解释执行Java字节码,大多数的JVM实现还包含一个JIT(just-in-time 即时)编译器,用于为常用的方法生成机器码。机器码使用的是CPU的本地语言,相比字节码有着更快的运行速度。

虽然理解JVM不是开发或运行Java程序的必要条件,但是如果多了解一些JVM知识,那么就有机会避免很多性能上的问题。理解了JVM,实际上这些问题会变得简单明了。

体系结构

JVM规范定义了一系列子系统以及它们的外部行为。JVM主要有以下子系统:

  • Class Loader 类加载器。 用于读入Java源代码并将类加载到数据区。
  • Execution Engine 执行引擎。 执行来自数据区的指令。

数据区使用的是底层操作系统分配给JVM的内存。

类加载器(Class Loader)

JVM在下面几种不同的层面使用不同的类加载器:

  • bootstrap class loader(引导类加载器):是其他类加载器的父类,它用于加载Java核心库,并且是唯一一个用本地代码编写的类加载器。
  • extension class loader(扩展类加载器):是bootstrap class loader加载器的子类,用于加载扩展库。
  • system class loader(系统类加载器):是extension class loader加载器的子类,用于加载在classpath中的应用程序的类文件。
  • user-defined class loader(用户定义的类加载器):是系统类加载器或其他用户定义的类加载器的子类。

当一个类加载器收到一个加载类的请求,首先它会检查缓存,确认该类是否已经被加载,然后把请求代理给它的父类。如果父类没能成功的加载类,那么子类就会自己去尝试加载该类。子类可检查父类加载器的缓存,但父类不能看到子类所加载的类。之所类加载体系会这样设计,是认为一个子类不应该重复加载已经被父类加载过的类。

执行引擎(Execution Engine)

执行引擎一个接一个地执行被加载到数据区的字节码。为了保证字节码指令对于机器来说是可读的,执行引擎使用下面两个方法:

  • 解释执行:执行引擎把它遇到的每一条指令解释为机器语言。
  • 即时编译:如果一条指令经常被使用,执行引擎会把它编译为本地代码并存储在缓存中。这样,所有和这个方法相关的代码都会直接执行,从而避免重复解释。

尽管即时编译比解释执行要占用更多的时间,但是对于需要使用成千上万次的方法,只需要处理一次。相比每次都解释执行,以本地代码的方式运行会节约很多执行时间。

JVM规范中并不规定一定要使用即时编译。即时编译也不是用于提高JVM性能的唯一的手段。规范仅仅规定了每条字节码对应的本地代码,至于执行引擎如何实现这一对应过程的,完全由JVM的具体实现来决定。

内存模型(Memory Model)

Java内存模型建立在自动内存管理的概念之上。当一个对象不再被一个应用所引用,垃圾回收器就会回收它,从而释放相应的内存。这一点和其他很多需要自行释放内存的语言有很大不同。

JVM从底层操作系统中分配内存,并将它们分为以下几个区域:

  • 堆空间(Heap Space):这是共享的内存区域,用于存储可以被垃圾回收器回收的对象。
  • 方法区(Method Area):这块区域以前被称作“永生代”(permanent generation),用于存储被加载的类。这块区域最近被JVM取消了。现在,被加载的类作为元数据加载到底层操作系统的本地内存区。
  • 本地区(Native Area):这个区域用于存储基本类型的引用和变量。

一个有效的管理内存方法是把对空间划分为不同代,这样垃圾回收器就不用扫描整个堆区。大多数的对象的生命周期都很段短暂,那些生命周期较长的对象往往直到应用退出才需要被清除。

当一个Java应用创建了一个对象,这个对象是被存储到“初生池”(eden pool)。一旦初生池存储满了,就会在新生代触发一次minor gc(小范围的垃圾回收)。首先,垃圾回收器会标记出那些“死对象”(不再被应用所引用的对象),同时延长所有保留对象的生命周期(这个生命周期长度是用数字来描述,代表了期所经历过的垃圾回收的次数)。然后,垃圾回收器会回收这些死对象,并把剩余的活着的对象移动到“幸存池”(survivor pool),从而清空初生池。

当一个对象存活达到一定的周期后,它就会被移动到堆中的老生代:“终身代”(tenured pool)。最后,当终身代被填满时,就会触发一次full gc或major gc(完全的垃圾回收),以清理终身代。

(译者注:一般我们把初生池和幸存池所在的区域合并成为新生代,把终身代所在的区域成为老生代。对应的,在新生代上产生的gc称为minor gc,在老生代上产生的gc称为full gc。希望这样大家在其他地方看到对应的术语时能更好理解)

当垃圾回收(gc)执行的时候,所有应用线程都要被停止,系统产生一次暂停。minor gc非常频繁,所以被优化的能够快速的回收死对象,是新生代的内存的主要的回收方式。major gc运行起来就相对慢得多,因为要扫描非常多的活着的对象。垃圾回收器本身也有多种实现,有些垃圾回收器在一定情况下能更快的执行major gc。

堆的大小是动态的,只有堆需要扩张的时候才会从内存中分配。当堆被填满时,JVM会重新给堆分配更多的内存,直到达到堆大小的上限,这种重新分配同样会导致应用的短暂停止。

线程

JVM是运行在一个独立的进程中的,但它可以并发执行多个线程,每个线程都运行自己的方法,这是Java必备的一个部分。以即时消息客户端这样一个应用为例,它至少运行两个线程。一个线程用于等待用户输入,另一个检查服务端是否有新的消息传输。再以服务端应用为例,有时一个请求可能要涉及多个线程并发执行,所以需要多线程来处理请求。

在JVM的进程中,所有的线程共享内存和其他可用的资源。每一个JVM进程在进入点(main方法)处都要启动一个主线程,其他线程都从主线程启动,成为执行过程中的一个独立部分。线程可以再不同的处理器上并行执行,同样也可以共享一个处理器,线程调度器负责处理多个线程共享一个处理器的情况。

很多应用(特别是服务端应用)会处理很多任务,需要并行运行。这些任务中有些是非常重要的,需要实时执行的。而另外一些是后台任务,可以在CPU空闲时执行。任务是在不同的线程中运行的。举例子来说,服务端可能有一些低优先级的线程,它们会根据一些数据来计算统计信息。同时也会启动一些高优先级的进程用于处理传入的数据,响应对这些统计信息的请求。这里可能有很多的源数据,很多来自客户端的数据请求,每个请求都会使服务端短暂的停止后台计算的线程以响应这个请求。所以,你必须监控在运行的线程数目并且保证有足够的CPU时间来执行必要的计算。

(译者注:这一段在原文中是在性能优化的章节,译者认为这可能是作者的不小心,似乎放在线程的章节更合适。)

性能优化

JVM的性能取决于其配置是否与应用的功能相匹配。尽管垃圾回收器和内存回收进程是自动管理内存的,但是你必须掌管它们的频率。通常来说,你的应用可使用的内存越多,那么这些会导致应用暂停的内存管理进程需要起作用的就越少。

如果垃圾回收发生的频率比你想的要多很多,那么可以在启动JVM的时候为其配置更大的最大堆大小值。堆被填满的时间越久,就越能降低垃圾回收发生的频率。最大堆大小值可以在启动JVM的时候,用-Xmx参数来设定。默认的最大堆大小是被设置为可用的操作系统内存的四分之一,或者最小1GB。

如果问题出在经常重新分配内存,那么你可以把初始化堆大小设置为和最大堆大小一样。这就意味着JVM永远不需要为堆重新分配内存。但这样做就会失去动态堆大小适配的优化,堆的大小从一开始就被固定下来。配置初始化对大小是在启动JVM,用-Xms来设定。默认初始化堆大小会被设定为操作系统可用的物理内存的六十四分之一,或者设置一个最小值。这个值是根据不同的平台来确定的。

如果你清楚是哪种垃圾回收(minor gc或major gc)导致了性能问题,可以在不改变整个堆大小的情况下设定新生代和老生代的大小比例。对于需要产生大量临时对象的应用,需要增大新生代的比例(当然,后果是减小了老生代的大小)。对于长生命周期对象较多的应用,则需增大老生代的比例(自然需要减少新生代的大小)。以下几种方法可以用来设定新生代和老生代的大小:

  • 在启动JVM时,使用-XX:NewRatio参数来具体指定新生代和老生代的大小比例。比如,如果想让老生代的大小是新生代的五倍,则设置参数为-XX:NewRatio=5,默认这个参数设定为2(即老生代占用堆空间的三分之二,新生代占用三分之一)。
  • 在启动JVM时,直接使用-Xmn参数设定初始化和最大新生代大小,那么堆中的剩余大小即是老生代的大小。
  • 在启动JVM时,直接使用-XX:NewSize-XX:MaxNewSize参数设定初始化和最大新生代大小,那么堆中的剩余大小即是老生代的大小。

每一个线程都有一个栈,用于保存函数调用、返回地址等等,这些栈有着对应的内存分配。如果线程过多,就会导致OutOfMemory错误。即使你有足够的空间的堆来存放对象,你的应用也可能会因为创建一个新的线程而崩溃。这种情况下,需要考虑限制线程中的栈大小的最大值。线程栈大小可以在JVM启动的时候,通过-Xss参数来设置,默认这个值被设定为320KB至1024KB之间,这和平台相关。

性能监控

当开发或运行一个Java应用的时候,对JVM的性能进行监控是很重要的。配置JVM不是一次配置就万事大吉的,特别是你要应对的是Java服务器应用的情况。你必须持续的检查堆内存和非堆内存的分配和使用情况,线程数的创建情况和内存中加载的类的数据情况等。这些都是核心参数。

使用Anturis控制台,你可以为任何的硬件组件上运行的JVM配置监控(例如,在一台电脑上运行的一个Tomcat网页服务器)。

JVM监控可以使用以下衡量标准:

  • 总内存使用情况(MB):即JVM使用的总内存。如果JVM使用了所有可用内存,这项指标可以衡量底层操作系统的整体性能。
  • 堆内存使用(MB):即JVM为运行的Java应用所使用的对象分配的所有内存。不使用的对象通常会被垃圾回收器从堆中移除。所以,如果这个指数增大,表示你的应用没有把不使用的对象移除或者你需要更好的配置垃圾回收器的参数。
  • 非堆内存的使用(MB):即为方法区和代码缓存分配的所有内存。方法区是用于存储被加载的类的引用,如果这些引用没有被适当的清理,永生代池会在每次应用被重新部署的时候都会增大,导致非堆的内存泄露。这个指标也可能指示了线程创建的泄露。
  • 池内总内存(MB):即JVM所分配的所有变量内存池的内存和(即除了代码缓存区外的所有内存和)。这个指标能够让你明确你的应用在JVM过载前所能使用的总内存。
  • 线程:即所有有效线程数。举个例子,在Tomcat服务器中每个请求都是一个独立的线程来处理,所以这个衡量指标可以表示当前有多少个请求数,是否影响到了后台低权限的线程的运行。
  • 类:即所有被加载的类的总数。如果你的应用动态的创建很多类,这可能是服务器内存泄露的一个原因。

原文链接: anturis 翻译: ImportNew.com xiafei
译文链接: http://www.importnew.com/13556.html
转载请保留原文出处、译者和译文链接。]



相关文章

posted @ 2014-11-11 18:30 小马歌 阅读(158) | 评论 (0)编辑 收藏
 

akullpp 在 Github 上发起维护的一个 Java 资源列表,内容包括:构建工具、数据库、框架、模板、安全、代码分析、日志、第三方库、书籍、Twitter、Java站点等等。

包括:

Awesome XXX 系列:

Github干货系列:C++资源集合

Github干货系列:Go 语言资源集合

Github干货系列:PHP 资源集合

Github干货系列:Python 资源集合

Github干货系列:系统管理员免费资源集合

Github干货系列:Web性能优化

Github干货系列:React 资源集合

阅读原文 »

› RSS订阅Java话题,关注Java技术的最新动态和优秀文章!

posted @ 2014-11-11 18:30 小马歌 阅读(727) | 评论 (0)编辑 收藏
 

流程:把线程dump出来,然后分析

1:Threaddump的方法:

  • kill -3 pid
  • jstack -l pid
  • jvisualvm中来thread dump

2:找到导致cpu高的线程
top -H -p pid
  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                             
 4691 root      19   0  448m 132m  13m S  0.0  7.5   0:00.01 java                                                                
 4692 root      19   0  448m 132m  13m S  0.0  7.5   0:00.39 java                                                                
 4698 root      15   0  448m 132m  13m S  0.0  7.5   0:14.14 java                                                                
 4699 root      15   0  448m 132m  13m S  0.0  7.5   0:00.43 java                                                                
 4700 root      15   0  448m 132m  13m S  0.0  7.5   0:00.63 java                                                                
 4701 root      18   0  448m 132m  13m S  0.0  7.5   0:00.00 java                                                               

 对应的线程id是十进制的,需要转换为十六进制的在threaddump文件中才可以看到

3:十进制转十六进制
-bash-3.2# python
>>> print hex(4739)
0×1283

4:找到对应的线程
vi threaddump.log
查找:/0×1283
找到对应的线程,把相关的方法找出来,可以精确到代码的行号,自己修改相关源码来fix bug。

posted @ 2014-11-11 13:33 小马歌 阅读(201) | 评论 (0)编辑 收藏
 
     摘要: 假如我们对面向对象的思维已经C语言都很熟悉的话,对于我们学习Objective-C将会非常有用。假如我们对C语言还不熟悉的话,那我们需要学习一下C语言。AD:2014WOT全球软件技术峰会北京站 课程视频发布2010年11月编程语言排行榜和2月编程语言排行榜讲的都是Objective-C。Objective-C是Mac软件开发领域最主要的开发语言,假如我们对面向对象的思维已经C语言都很熟悉的话,对...  阅读全文
posted @ 2014-11-04 11:24 小马歌 阅读(269) | 评论 (0)编辑 收藏
 
构建iOS持续集成平台
 
作者 刘先宁,火龙果软件    发布于 2013-11-11
 

(一)——自动化构建和依赖管理

2000年Matin Fowler发表文章Continuous Integration【1】;2007年,Paul Duvall, Steve Matyas和 Andrew Glover合著的《Continuous Integration:Improving Software Quality and Reducing Risk》 【2】出版发行,该书获得了2008年的图灵大奖。持续集成理念经过10多年的发展,已经成为了业界的标准。在Java, Ruby的世界已经诞生了非常成熟的持续集成工具和实践,而对于iOS领域来说,因为技术本身相对比较年轻和苹果与生俱来的封闭思想,在持续集成方面的发展相对滞后一些,但是,随着越来越多的iOS开发者的涌入,以及各个互联网巨头加大对iOS开发的投入,诞生了一大批非常好用的持续集成工具和服务,本文的目的就是介绍一下如何有效的利用这些类库,服务快速构建一个iOS开发环境下的持续集成平台。

自动化构建

在MartinFowler的文章[1]中关于自动化的构建定义如下:

Anyone should be able to bring in a virgin machine, check the sources 
out of the repository, issue a single command, and have a running
system on their machine.

因此,自动化构建的的首要前提是有一个支持自动化构建的命令行工具,可以让开发人员可以通过一个简单的命令运行当前项目。

命令行工具

自动化构建的命令行工具比持续集成的概念要诞生得早很多,几十年前,Unix世界就已经有了Make,而Java世界有Ant,Maven,以及当前最流行的Gradle,.Net世界则有Nant和MSBuild。作为以GUI和命令行操作结合的完美性著称的苹果公司来说,当然也不会忘记为自己的封闭的iOS系统提供开发环境下命令行编译工具:xcodebuild【3】

xcodebuild

在介绍xcodebuild之前,需要先弄清楚一些在XCode环境下的一些概念【4】:

Workspace:简单来说,Workspace就是一个容器,在该容器中可以存放多个你创建的Xcode Project, 以及其他的项目中需要使用到的文件。使用Workspace的好处有,1),扩展项目的可视域,即可以在多个项目之间跳转,重构,一个项目可以使用另一个项目的输出。Workspace会负责各个Project之间提供各种相互依赖的关系;2),多个项目之间共享Build目录。

Project:指一个项目,该项目会负责管理生成一个或者多个软件产品的全部文件和配置,一个Project可以包含多个Target。

Target:一个Target是指在一个Project中构建的一个产品,它包含了构建该产品的所有文件,以及如何构建该产品的配置。

Scheme:一个定义好构建过程的Target成为一个Scheme。可在Scheme中定义的Target的构建过程有

Build/Run/Test/Profile/Analyze/Archive

BuildSetting:配置产品的Build设置,比方说,使用哪个Architectures?使用哪个版本的SDK?。在Xcode Project中,有Project级别的Build Setting,也有Target级别的Build Setting。Build一个产品时一定是针对某个Target的,因此,XCode中总是优先选择Target的Build Setting,如果Target没有配置,则会使用Project的Build Setting。

弄清楚上面的这些概念之后,xcodebuild就很好理解了,官网上对其作用的描述如下:

xcodebuild builds one or more targets contained in an Xcode 
project, or builds a scheme contained in an Xcode workspace or
Xcode project.

xcodebuild就是用了构建产品的命令行工具,其用法可以归结为3个部分:

1.可构建的对象

2.构建行为

3.一些其他的辅助命令

可以构建的对象有,默认情况下会运行project下的第一个target:

1.workspace:必须和“-scheme”一起使用,构建该workspace下的一个scheme。

2.project:当根目录下有多个Project的时候,必须使用“-project”指定project,然后会运行

3.target:构建某个Target

4.scheme:和“-workspace”一起使用,指定构建的scheme。

5.……

构建行为包括:

1.clean:清除build目录下的

2.build: 构建

3.test: 测试某个scheme,必须和"-scheme"一起使用

4.archive:打包,必须和“-scheme”一起使用

5.……

辅助命令包括:

1.-sdk:指定构建使用的SDK

2.-list:列出当前项目下所有的Target和scheme。

3.-version:版本信息

4.…...

关于xcodebuild更多详细的命令行请参见:https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/xcodebuild.1.html

下图是使用XcodeBuild运行一个scheme的build的结果:

了解了xcodebuild的用法之后,接下来分析一下xcodebuild的主要缺陷:

1.从上图直接可以得到的感觉,其脚本输出的可读性极差,

2.只能要么完整的运行一个target或者scheme,要么全部不运行。不能指定运行Target中特定的测试。

3.最令人发指的是,XCode 4中的xcodebuild居然不支持iOSUnitTest的Target【5】,当我尝试运行一个iOS App的测试target时,得到如下的错误:

对于上面提到的缺陷,Facebook给出了他们的解决方案:xctool【6】

xctool

xctool在 其主页直接表明了其目的:

xctool is a replacement for Apple's xcodebuild that makes it easier  to build and
test iOS and Mac products. It's especially helpful for continuous integration.

其作用是替代xcodebuild,目的是让构建和测试更加容易,更好的支持持续集成。从个人感受来看,它的确成功取代了xcodebuild。但是xctool说到底只是对xcodebuild的一个封装,只是提供了更加丰富的build指令,因此,使用xctool的前提是xcodebuild已经存在,且能正常工作。

安装

xctool的安装非常简单,只需要clone xctool的repository到项目根目录就可以使用, 如果你的机器上安装有Homebrew,可以通过“brew install xctool”命令直接安装。(注意:使用xctool前一定要首先确认xcodebuild已安装且能正确工作)。

用法

关于xctool的用法就更加人性化了,几乎可以重用所有的xcodebuild的指令,配置。只需要注意一下几点:

1.xctool不支持target构建,只能使用scheme构建。

2.支持“-only”指令运行指定的测试。

3.支持多种格式的build报告。

例子:

path/to/xctool.sh 
-workspaceYourWorkspace.xcworkspace
-schemeYourScheme
test -only SomeTestTarget:SomeTestClass/testSomeMethod

下图是我使用xctool运行test的效果:

常见问题:

No architectures to compile for (ONLY_ACTIVE_ARCH=YES, active arch=x86_64, VALID_ARCHS=armv7 armv7s).

解决方法:到Project Setting中,把"Build Active Architecture Only"设置为NO

Code Sign error: A valid provisioning profile matching the application's Identifier 'dk.muncken.MyApp' could not be found

解决方法:通过“-sdkiphonesimulator”指定SDK,从而能够使用符合iOS约定的application Identifier。

依赖管理

选定了命令行工具之后, 接下来可以考虑下依赖管理的问题了。我到现在还记得几年前,刚从Ant转到使用Maven的那种爽快的感觉。后来,进入Ruby的世界,其与生俱来的Gem管理系统,也让其依赖管理变得极其简单。 对于iOS平台来说,在做项目时,经常需要使用到各种各样的第三方Framework,这同样需要一个爽快的依赖管理系统,不然的话,各位可以想象一下重复的下载Framework文件,拖入各个Target的Build Phase的Link Binary With Libraries中的场景。这种重复的劳动对于“懒惰”的程序员来说,是很难接受的,于是,活跃的社区开发者们提供了这样的一个工具:Cocoapods【7】

Cocoapods开始于2011年8月12日,经过2年多的发展,现在已经超过2500次提交,并且持续保持活跃更新,目前已成为iOS领域最流行的第三方依赖管理工具。从技术层面来说,其是一个Ruby Gem,从功能层面来说,其是一个iOS平台下的依赖管理工具,为iOS项目开发提供了类似于在Ruby世界使用Gem的依赖管理体验。

安装

前面提到cocoapods本质上是一个Ruby Gem,因此,其使用前提首先是Ruby开发环境。庆幸的是,Mac下都自带Ruby。这样,只需要简单的2条命令,就可以把cocoapods安装好:

$ [sudo] gem install cocoapods
$ pod setup

用法

cocoapods的使用方式和使用Ruby Gem非常相似,首先需要在项目根目录下创建文件Podfile,在Podfile中,开发人员只需要按照规则配置好如下内容就好:

1.项目支持的平台,版本(iOS/OSX)

2.每个target的第三方依赖

例子:

platform :ios, '6.0'
inhibit_all_warnings!
xcodeproj `MyProject`
pod 'ObjectiveSugar', '~> 0.5'
target :test do
pod 'OCMock', '~> 2.0.1'
end
post_install do |installer|
installer.project.targets.each do |target|
puts "#{target.name}"
end
end

修改好配置文件之后,只需要简单的使用“pod install”即可安装好所有的依赖,执行该命令之后,在项目跟目录下会出现“.xcworkspace”和“Pods”两个目录:

接下来,开发者需要使用xcworkspace打开项目而不是使用xcodeproject,打开项目之后,在项目目录下除了自己的project以外,还可以看到一个叫做Pods的项目,该项目会为每一个依赖创建一个target:

在Podfile中,还可以指定依赖专属于某个Target,

target :CocoaPodsTest do
pod 'OCMock', '~> 2.0.1'
pod 'OCHamcrest'
end

如果你记不清楚某个依赖库的名称,可以使用“pod search <name>”模糊搜索依赖库中的相似库,另外,如果你想使用的库在cocoapods的中央库中找不到,那么,你可以考虑为开源社区做做贡献,把你觉得好用的库添加到中央库中,Cocoapods的官网上有具体的步骤【8】

原理

CocoaPods的原理思想基本上来自于Jonah Williams的博客“Using Open Source Static Libraries in Xcode 4”【9】,当使用“pod install”安装文件时,cocoapods做了如下这些事:

1.创建或者更新当前的workspace

2.创建一个新的项目来存放静态库

3.把静态库会编译生成的libpods.a文件配置到target的build phase的link with libraries中

4.在依赖项目中创建*.xcconfig文件,指定在编译时的一些参数和依赖

5.添加一个新的名为“Copy Pods Resource”的Build Phase,该build phase会使用"${SRCROOT}/Pods/Pods-CocoaPodsTest-resources.sh"把Pods下的资源文件拷贝到app bundle下。

注意事项

当使用xctool作为命令行工具构建项目时,使用cocoapods管理依赖时,需要做一些额外的配置:

1.编辑Scheme,把pods静态库项目作为显式的依赖添加到项目的build中,

2.把pods依赖项目拖动到本来的项目之上,表示先编译pods静态库项目,再编译自己的项目。

(二)——测试框架

测试框架

有了自动化构建和依赖管理之后,开发者可以很轻松的在命令行构建整个项目,但是,作为持续集成平台来说,最重要的还是测试,持续集成最大的好处在于能够尽早发现问题,降低解决问题的成本。而发现问题的手段主要就是测试。在Martin Fowler的Test Pyramid【10】一文中论述了测试金子塔的概念,测试金字塔的概念来自Mike Cohn,在他的书Succeeding With Agile中有详细描述:测试金字塔最底层是单元测试,然后是业务逻辑测试,如果更细化一点的话,可以分为把完整的测试策略分为如下的层级:

作为持续集成平台,能自动化的测试层级越多,平台就能产生越大的价值。

Unit Test

目前,在iOS领域, 最流行的Unit测试框架有2个:OCUnit【11】和GHunit【12】,这两个框架各有其优缺点:

 优点
缺点
OCUnit与Xcode无缝集成, 快捷键,Scheme配置都非常方便1. 只能一次运行整个测试,不能灵活的运行某个测试集; 2.测试结果输出的可读性不好,不容易找到失败的测试
GHUnit1.自带GUI,测试结果清晰可见;2.可以灵活的运行指定的测试;3.开源项目1.需开发者安装,配置略显复杂;2. 对命令行运行测试的支持不是很好,
   

OCUnit的运行结果会通过弹窗直接告诉开发者,运行的细节信息则会打印在Xcode的输出窗口中:

GHUnit的运行结果则全部显示在自己的应用界面中,开发者可以在应用中查看所有的信息,以及做运行测试等各种各样的操作。

关于如何使用OCUnit和GHUnit, InfoQ上有高嘉峻的文章《iOS开发中的单元测试》(http://www.infoq.com/cn/articles/ios-unit-test-1)有详细的介绍,我就不再这儿重复叙述了。

如果单从单元测试框架来看,个人更喜欢GHUnit测试结果的可读性和运行测试的灵活性,但是,随着Facebook的xctool的发布,OCUnit华丽丽的逆袭了,因为xctool帮助OCUnit把运行测试的灵活性和测试结果的可读性这两块短板给补齐了,再加上其和Xcode的集成优势以及通过命令行运行的便捷性,让其成为持续集成平台的Unit测试框架的首选。

在Java程序员的心中,Junit和Hamcrest永远是一体的,Hamcrest为junit提供了非常丰富的断言机制,极大的增强了测试的可读性。越来越活跃的iOS开发社区,当然不会让Object-C的世界缺失这样一个优秀的框架,于是OCHamcrest【13】诞生了。

在测试项目中使用OCHamcrest非常简单,尤其是使用了cocoapods管理依赖的项目。只需要在Podfile文件中加上:

target :<TestTargetName> do
...
pod 'OCHamcrest'
end

然后,运行“pod install”命令安装Hamcrest到测试Target,安装好之后,为了在测试类中使用OCHamcrest的断言。还需要在测试类的头文件中加入如下代码:

#define HC_SHORTHAND
#import<OCHamcrest/OCHamcrest.h>

开发者可以把这段代码加入<TestTargetName>-prefix.pch中,这样所有的测试类就都可以使用OCHamcrest的断言了。在前面提到的高嘉峻的文章中的第二部分更加详细的讲解了OCHamcrest的断言,以及其和另一个断言框架Expecta的对比,感兴趣的同学可以跳过去看看。

Component Test & Integration Test

在开发手机应用时,总难免会和其他的系统集成,尤其当开发的应用是某个系统的手机客户端时,这样就涉及到很多第三方API的集成点需要测试,在成熟的Java世界中,诞生了EasyMock,Mockito,moco等针对这种集成点的测试工具。同样的,活跃的社区力量正一步一步的在让Object-C世界成熟,OCMock【14】诞生。

OCMock

有了cocoaPods,新加框架变得非常容易,基本上就是“哪里没有加哪里”的节奏,添加OCMock框架,只需要在Podfile文件中加上:

target :<TestTargetName> do
...
pod 'OCMock', '~> 2.0.1'
end

然后,运行“pod install”命令安装OCMock到测试Target,同样的,需要把OCmock的头文件添加<TestTargetName>-prefix.pch文件中

#import<OCMock/OCMock.h>

下面是一个简单的使用OCMock的例子,更多的用法请参考OCMock的官网:http://ocmock.org/features/:

- (void)testSimpeMockPass{
idmockObject = [OCMockObjectmockForClass:NSString.class];
[[[mockObject stub] andReturn:@"test"] lowercaseString];

NSString * returnValue = [mockObjectlowercaseString];
assertThat(returnValue, equalTo(@"test"));
}

moco

moco【15】以其对系统集成点测试的贡献荣获了2013年的“Duke's Choice Award”奖,虽然其以Java写成,主要的生态系统也是围绕Java打造,但是,其Standalone模式可以非常方便的构造一个开发者期望的服务器,这对于Mobile领域的集成点测试来说,本就是一个非常好的Mock服务器的工具。Moco有如下的特点:

易于配置,使用:一个Json配置文件,然后“java -jar moco-runner--standalone.jar -p 8080 ***.json”就可以启动一个Mock服务器。该服务器的所有行为都在配置文件里。如果你想添加,修改服务器行为,只需要修改一下配置文件,然后重新启动该服务器就行了。

配置文件可读性好,使用Json格式的配置文件,对绝大多数开发者来说都可以很容易理解。

支持模拟客户端需要的所有http操作,moco实现了针对请求Content、URI、Query Parameter、Http Method、Header、Xpath的模拟。对响应的格式支持有Content、Status Code、Header、URL、甚至支持Sequence请求,即根据对同一请求的调用次数返回不同的结果。

完全开源,代码不多也比较易懂,如果没有覆盖到我们的场景,完全可以在该项目基础上实现一个自己的Mock服务器 。

熊节在infoQ上发表的《企业系统集成点测试策略》【16】一文中,详细的论述了在企业系统中,moco对测试系统集成点的 帮助。这些论点在Mobile开发领域同样适用,因此合理的使用moco可以帮助iOS开发者更加容易的构建一个稳固的持续集成平台。

System Test

对于iOS的系统(UI)测试来说,比较知名的工具有UIAutomation【17】和FonMonkey【18】。

UIAutomation

UIAutomation是随着iOS SDK 4.0引入,帮助开发者在真实设备和模拟器上执行自动化的UI测试。其本质上是一个Javascript的类库,通过 界面上的标签和值的访问性来获得UI元素,完成相应的交互操作,从而达到测试的目的,类似于Web世界的Selenium。

通过上面的描述,可以得知,使用UIAutomation做测试时,开发者必须掌握两件事:

1.如何找到界面上的一个UI元素

2.如何指定针对一个UI元素的操作

在UIAutomation中,界面就是由一堆UI元素构建的层级结构,所有UI元素都继承对象UIAElement ,该对象提供了每个UI元素必须具备的一些属性:

1.name

2.value

3.elements

4.parent

5.…

而整个界面的层级结构如下:

Target(设备级别的UI,用于支持晃动,屏幕方向变动等操作)
Application(设备上的应用,比方说Status Bar,keyboard等)
Main window(应用的界面,比方说导航条)
View(界面下的View,比方说UITableView)
Element(View下的一个元素)
Child element(元素下的一个子元素)

下面是一个访问到Child element的例子:

UIATarget.localTarget().HamcrestDemo().tableViews()[0].cells()[0].elements()

开发者还可以通过“UIATarget.localTarget().logElementTree()”在控制台打印出该target下所有的的elements。

找到UI元素之后,开发者可以基于该UI元素做期望的操作,UIAutomation作为原生的UI测试框架,基本上支持iOS上的所有UI元素和操作,比方说:

1.点击按钮,例: ***.buttons[“add”].tap()

2.输入文本, 例:***.textfields[0].setValue(“new”)

3.滚动屏幕,例:***.scrollToElementWithPredicate(“name begin with ’test’”)

4.……

关于使用UIAutomation做UI测试,推荐大家一定要看一下2010的WWDC的Session 306:Automating User Interface Testing with Instruments【19】。 另外,这儿还有一篇很好的博客,详细的讲解了如何使用UIAutomation做UI自动化测试:http://blog.manbolo.com/2012/04/08/ios-automated-tests-with-uiautomation

Apple通过Instruments为UIAutomation测试用例的命令行运行提供了支持,这样就为UIAutomation和CI服务器的集成提供了便利。开发者可以通过如下的步骤在命令行中运行UIAutomation测试脚本

指定目标设备,构建被测应用,该应用会被安装到指定的DSTROOT目录下

xcodebuild
-project "/Users/twer/Documents/xcodeworkspace/AudioDemo/AudioDemo.xcodeproj"
-schemeAudioDemo
-sdk iphonesimulator6.1
-configuration Release SYMROOT="/Users/twer/Documents/xcodeworkspace/
AudioDemo/build" DSTROOT="/Users/twer/Documents/xcodeworkspace/AudioDemo/
build" TARGETED_DEVICE_FAMILY="1"
install

启动Instruments,基于第一步生成的应用运行UIAutomation测试

instruments
-t "/Applications/Xcode.app/Contents/Applications/Instruments.app/
Contents/PlugIns/AutomationInstrument.bundle/Contents/Resources/
Automation.tracetemplate" "/Users/twer/Documents/xcodeworkspace/AudioDemo
/build/Applications/TestExample.app"
-e UIASCRIPT <absolute_path_to_the_test_file>

为了更好的展示测试效果以及与CI服务器集成,活跃的社区开发者们还尝试把UIAutomation和Jasmine集成: https://github.com/shaune/jasmine-ios-acceptance-tests

UIAutomation因其原生支持,并且通过和Instruments的绝佳配合,开发者可以非常方便的使用录制操作自动生成测试脚本,赢得了很多开发者的支持,但是因苹果公司的基因,其系统非常封闭,导致开发者难以扩展,于是活跃的社区开发者们开始制造自己的轮子,Fone Monkey就是其中的一个优秀成果。

Fone Monkey

Fone Monkey是由Gorilla Logic 公司创建并维护的一个iOS自动化测试工具,其功能和UIAutomation差不多,但是由于其开源特性,极大的解放了活跃开发者的生产力,开发者可以很容易的根据自身需要扩展其功能。

Fone Monkey的安装虽然简单,但是比UIAutomation的原生支持来说,也算是一大劣势了,具体的安装过程可以参考:http://www.gorillalogic.com/fonemonkey-ios/fonemonkey-setup-guide/add-fonemonkey-your-xcode-project

Fone Monkey的使用方式主要就是录制/回放,也可以把录制好的测试用例保存为脚本以其他方式运行。安装好Fone Monkey启动测试以后,应用界面会有点变化:

开发者通过点击Record按钮录制操作,点击Play按钮播放之前录制的操作,点击More按钮可以添加一些针对元素的验证, 这样就形成了一个测试用例。

在Fone Monkey中录制的测试用例可以保存为3种格式,以支持多种运行方式:

1.scriptName.fm:用于支持在Fone Monkey的窗口中运行测试

2.scriptName.m:用于和Xcode的OCUnit test 集成,可以以OCUnit的测试用例的形式运行UI测试,这就让UI具备了命令行运行和与CI集成的能力。

3.scriptName.js:UIAutomation的格式,用于支持在Instruments中,以UIAutomation的形式运行测试。

(三)——CI服务器与自动化部署

CI服务器

写到这儿,对于iOS开发者来说,需要准备好:

一个比较容易获取的源代码仓库(包含源代码)
一套自动化构建脚本
一系列围绕构建的可执行测试
接下来就需要一个CI服务器来根据源代码的变更触发构建,监控测试结果。目前,业界比较流行的,支持iOS构建的CI服务器有Travis CI和Jenkins

Travis CI

Travis CI【20】是一个免费的云服务平台,主要功能就是为开源社区提供免费的CI服务,对于商业用户可以使用Travis Pro版本,其基本上支持所有目前主流的语言,Object-C自然也在其中。但是,Travis CI只支持github极大的限制了其应用场景。但是也由于其只支持github,其把和github的集成做到了极致的平滑,易用,因此,对于本就把github作为代码托管平台的项目来说,Travis CI可以做为第一选择。

使用Travis CI只需要简单的2步即可为你托管在github的项目增加一个CI服务器

第一步:在项目的根目录下创建travis CI配置文件“.travis.yml”,在配置文件指定语言,环境要求,构建脚本等

language: objective-c
before_install:
- brew update
- brew install xctool
script: xctool -project LighterViewControllerDemo.xcodeproj -scheme
LighterViewControllerDemo -sdkiphonesimulator test

第二步:使用github账号登陆Travis CI,在账户的repositories开启该项目的自动构建功能,该设置会在github上该项目repository中开启对Travis CI的Service Hook

下图就是我的一个iOS项目在Travis CI的构建记录:

除此之外,Travis CI还非常贴心的提供了一个指示灯,可以让开发者在自己的repository展示构建状态。使用指示灯的方法很简单,只需要在repository的README.md中添加下面这行代码就行了。

[![Build Status](https://travis-ci.org/xianlinbox/iOSCIDemo.png)](https://travis-ci.
org/xianlinbox/iOSCIDemo)

效果如下:

Jenkins

Jenkins【21】经过多年的发展,其活跃的社区和丰富的插件让其成为了业界最受欢迎的CI服务器。通过使用Xcode插件,可以非常方便在Jenkins中运行iOS项目的构建脚本。

安装

Jenkins的安装非常简单,尤其是在Mac环境下使用Homebrew安装,只需要简单的使用“brew install jenkins”,即可成功安装,这个安装过程做的事情非常简单,就是把对应版本的jenkins.war文件下载到对应的目录“/usr/local/Cellar/jenkins/1.524/libexec/”。因此,如果没有Homebrew,可以直接到官网下载安装文件,自己安装。

配置安装完之后,只需要使用“java -jar */jenkins.war”即可启动Jenkins,开发者可以自己创建一个bash alias简化输入,在用户目录下的.bashrc文件中添加如下代码

aliasjenkins='java -jar /Applications/Jenkins/jenkins.war'

启动Jenkins之后,可以通过浏览器访问http://localhost:8080查看Jenkins界面。

然后,开发者可以到Manage Plugins界面,安装需要的插件:Xcode Plugin,Git Plugin,Github Plugin(我使用git做源代码管理),Cocoapods Plugin。安装好插件之后,需要重启Jenkins,一切就绪之后,开始配置自己的iOS构建过程。

在Jenkins中配置一个iOS Job的步骤如下:

1.新建Job,Job类型选择“Build a free-style software project”。

2.配置代码仓库,

3.点击“Add build step”添加构建步骤,如果已安装Xcode插件,则可以在Step类型中看到Xcode选项:

选择Xcode,可以看到Xcode构建step的所有配置选项:

4.点击“Add build step”添加测试步骤,选择“Execute shell”选项,然后,添加脚本,执行测试并生成期望的Report, 可以重复本步骤添加“Acceptaince Test”等构建步骤。

path-to/xctool.sh 
-workspaceAudioDemo.xcworkspace -scheme AudioDemo -sdkiphonesimulator
-reporter junit:test-reports/junit-report.xml clean test

5.点击“Add post-build action”添加一个新的步骤,选择“publish Junit test result report”,把测试报告展示在Jenkins中。

配置好之后,可以点击Build Now运行Job,下图是在Jenkins中运行iOS构建 Job的结果图:

开发者可以点击每次的构建,查看具体的构建信息及测试结果:

常见问题

启动Jenkins提示运行“java”命令需要X11支持,

解决方法:这是因为在OS X Mountain Lion系统中不再内置X11,当需要使用X11时,可通过安装XQuartz project得到支持,具体信息:http://support.apple.com/kb/HT5293

自动化部署

这儿的想谈的“部署”不是传统意义上的直接部署到产品环境的部署,而是指如何把最新版本的应用快速的部署到测试用户的机器上以收集反馈,或者做一些探索性的测试。

在我写第一个iOS应用的时候,我想把应用安装到多个机器上测试的时候,需要非常繁琐的步骤:

1.需要申请到苹果开发者账号,获得开发者证书。

2.需要在苹果的开发者网站上注册我想使用的设备。

3.使用开发者证书打包应用,使用Ad-HOC部署模式,生成ipa文件。

4.通过ipa文件把应用安装到iTunes上。

5.通过iTunes把应用同步到多台测试机器上。

如果是测试机器在多个地理位置的时候,还需要把ipa文件发送到对应的地点,每个地点都需要重复的做第4,5步。 这样一个繁琐,且低效的过程让开发者非常痛苦,直到TestFlight的出现。

TestFlight

TestFlight【22】就是一个专门解决上面提到的痛点的云服务方案,它可以帮助开发者:

1.轻松采集测试用户的UDID和iOS 版本、硬件版本,并发送给开发者。

2.实时反馈应用是否成功安装到测试机器

3.轻松部署最新版本应用到测试用机上。

4.开发者可以灵活选择部署哪个版本到哪部分测试机器上。

使用使用Test Flight服务非常简单,只需要到Test Flight注册一个账号。然后把链接发送给测试设备,测试设备只要打开该链接,并授权给Test Flight,在Test Flight的设备中心就可以看到这些设备。

而测试设备上,则会多一个Test Flight的应用。

当应用有了新的测试包之后,只需要将IPA上传到TestFlight网站,然后勾选合适的测试用户或者合适的设备,点击Update & Notify。

TestFlight会向对应的测试设备发送更新通知,测试用户只需在测试设备上打开TestFlight应用,点击Install,TestFlight就会自动将新版本的IPA文件安装到测试设备上。

另外,TestFlight还提供了Upload API,这样就等于提供了和CI服务器集成的能力。其Upload API非常简单,就是一个简单的,带有指定参数的HTTP POST请求,具体细节可参考官网:https://www.testflightapp.com/api/doc/。

在 CI服务器中,开发者可以在构建过程中,添加步骤通过upload API把通过测试的ipa文件自动上传到TestFlight上,从而达到持续部署的目的。而像Jenkins这样有丰富插件机制的CI服务器, 活跃的社区开发者们早为其开发了十分便于使用的TestFlight的插件。

在Jenkins中使用TestFlight插件也非常简单,安装好插件,重启Jenkins,然后在项目的构建过程配置页面的Post-build Actions中,点击add post-build action,可以看到Upload to Testflight选项:

选择之后,可以在页面上看到TestFlight的配置项:

为了增强安全性,该插件把Token的设置移到了Jenkins的Global Setting界面:

配置好Token Pair之后,在在TestFlight的配置项 上选择相应的pair即可,设置想要的参数,保存即可。

你如果不喜欢使用插件或者说使用的其它CI服务器的话,可以通过添加一个Execute shell步骤,直接通过代码上传构建好的ipa文件:

curl http://testflightapp.com/api/builds.json      
-F file=@testflightapp.ipa
-F dsym=@testflightapp.app.dSYM.zip
-F api_token='your_api_token'
-F team_token='your_team_token'
-F notes='This build was uploaded via the upload API'
-F notify=True
-F distribution_lists='Internal, QA'

结语

《持续集成》一书中引用了Javaranch.com的创始人Kathy Sierra的一句话:

There's a big difference between saying, "Eat an apple a day" and actually eating the apple

正所谓知易行难,您几乎很难听到开发者说:“持续集成毫无价值”,但是,构建一个持续集成平台并非易事,希望本文中介绍的iOS应用的构建过程,以及在构建过程的各个阶段可以使用的一些优秀的类库,服务,能够让iOS开发者们在想搭建一个持续集成平台时有所参考,从而能够更加坚定,且容易的为自己的项目搭建一个持续集成平台。

posted @ 2014-10-29 16:52 小马歌 阅读(353) | 评论 (0)编辑 收藏
 

控制了一门语言的构建系统(build system),就控制了这门语言的命运,以及它的生态和社区。

Objective-C 用很短的时间,取得了非常大的变化。在短短几年间,这门语言从 NeXT 的“废墟”中走出,成为颇具影响力的一门语言。开源社区对于 Objective-C 有着巨大的贡献,其中一个很重要的方面是:开发工具。

例如 CocoaPods,它充分证明了科技和社区结合的威力。两年间,有超过 2700 个开源的库或框架被添加进去,你只需要简单的命令行 - pod install,就可以轻松管理第三方库。

开源社区对于 iOS 和 Mac OS X 开发的各个方面都有着巨大贡献,第三方库管理只是其中一个,其他方面还包括自动配置和分发报告BUG文档检索等。

但是本周,我们聚焦于这样一个工具:它重新定义了构建过程,是新一代开发集成的基础——xctool。

xctool 是 Fred Potter 的开源项目,来自于他在 Facebook 工作中创建的自动构建系统。xctool 用于替换 Xcode.app 中的 xcodebuild

在 Xcode 中点击“Build & Run”,所有的 project、build target 和 scheme settings 都被传到 xcodebuild,xcodebuild 调用构建的命令行,然后执行 .ipa 文件运行在真机或模拟器上。

这个过程我们只能祈祷它顺利了,因为 Xcode 将自己的构建系统封闭起来,我们从外部很难访问和控制。当我们试图通过 Terminal.app 和 xcodebuild 交互的时候,会发现真的很难用。

与其使用上个时代的工具,狂打断点去遍历所有错误,不如让 xctool 来告诉你什么是现代的解决方案。

美学和风格

xctool 给人的第一印象是它那漂亮、彩色的输出样式。

我们自己作为苹果硬件和软件的用户,对设计绝对不会轻视。xctool 也是。 它把构建过程的每一步都组织的很整洁,通过 ANSI 标准色 和 Unicode 符号输出易懂、易读的报告。

但是 xctool 的美丽不仅是外在,build 的过程中可以生成用其他工具可读的报告:

  xctool -reporter plain:output.txt build

报告类型:

  • pretty:(默认)输出样式使用 ANSI 标准色和 Unicode 符号
  • plain:类似 pretty,但没有 ANSI 标准色和 Unicode 符号
  • phabricator:将构建/测试的结果输出为 JSON 数组,可导入到 Phabricator 这个 code-review 工具
  • junit:生成兼容 JUnit/xUnit 的 XML 格式的测试结果
  • json-stream:输出构建/测试的事件流 ( JSON 字典格式),一行一个(如图)
  • json-compilation-database:输出 JSON Compilation Database,可用于基于 Clang Tooling 的工具,如 OCLint

集成构建系统

相对于 xcodebuild,xctool 另一个重大改进是可以和 Xcode.app 一样运行测试(xcodebuild 并不能分辨哪些 target 是 test target,从而单独运行它们)。

单单这个原因,对于 Objective-C 社区的持续集成测试这个领域,xctool 就有非常大的意义。

Travis CI

Travis CI 为开源项目提供免费的持续集成服务(商业项目收费),支持 Objective-C。它可以在你每次提交到 GitHub 时自动运行你的测试代码,如果最新的提交导致构建失败它会通知你。

在你的 Objective-C 项目中添加 Travis CI,你需要创建账号绑定 GitHub,然后在你的 repo 中添加 .travis.yml 文件:

.travis.yml

language: objective-c before_install:     - brew update     - brew install xctool script: xctool -workspace MyApp.xcworkspace -scheme MyApp test

OCLint

OCLint 是一个静态代码分析器,可以检测 Objective-C 代码(同时支持 C 和 C++),发现常见的问题:例如 if/else/try/catch/finally 声明出错、未被使用的本地实例和参数、过度复杂(有效代码行数 和 循环复杂度太高)、冗余代码、代码异味、以及其他不好的代码。

还记得 xctool 的 json-compilation-database 格式吧,这种格式可以被 OCLint 直接读取,进而进行神奇的静态分析。

在我写这篇文章的时候,离构建系统集成被广泛接受还有很长的路,但我的希望是,既然已经开始, 大家就应该齐心协力,让这个有前途的工具变得更加的强大。

这就像一座城市,随着人口的增长,基础设施就需要改变。通过各种各样的方式,通过本地政府、新兴组织,或者其他。总之,环境需要改变,以适应人口的增长。

Objective-C 已经并且还将随着 iOS 设备的流行而快速进化。为更多新进开发者提供必要的基础设施是社区的责任,当然这离不开与苹果的协作(有时是反对或抗议)。我们在这方面做得如何,决定了我们是否真正理解并传达了作为专业开发者应当扮演的角色,以及应当的承担责任。

我们是该成为平庸的业余者,还是该成为改善技术的关键角色?

xctool,和社区的其他工具一样,为这门语言的未来和生态,提供了希望和灵感。让我们继续创造这样强大的工具,创造我们为之自豪的开发体验。

注:本文译自 NSHipster

posted @ 2014-10-29 16:38 小马歌 阅读(1651) | 评论 (0)编辑 收藏
仅列出标题
共95页: First 上一页 15 16 17 18 19 20 21 22 23 下一页 Last