qileilove

blog已经转移至github,大家请访问 http://qaseven.github.io/

Selenium Grid 安装

  Selenium Grid
  Selenium Grid允许同时并行地、在不同的环境上运行多个测试任务。这里主要演示一下怎么使用Selenium Grid。
  准备
  1、需要两台机子
  2、两台机子分别安装好JDK环境
  3、两台机子需要从 http://code.google.com/p/selenium/downloads/list下载selenium-server-standalone-*.jar包
  开始:
  Grid需要一台机子做为主节点,然后其它机子做为子节点连接到这个主节点上来。所以首先要启动主节点。
  启动主节点
  选一台机子做为主节点。打开命令行,cd至selenium-server-standalone-*.jar包的目录下,然后用下面的命令启动主节点服务:
  java -jar selenium-server-standalone-2.24.1.jar -role hub
  默认启动默认端口为4444。如果要改这个端口,可以再上面的命令后面加上 -port XXXX。启动完后,你可以用浏览 器    打开 http://localhost:4444/grid/console 这个网址查看主节点的状态。
  启动完主节点之后,要做的就是启动子节点。
  启动子节点:
  先另一台机子做为子节点。同样打开命令行,cd至selenium-server-standalone-*.jar包的目录下,然后用下面的命令启动次节点服务:
  java -jar selenium-server-standalone-2.24.1.jar  -role  node  -hubhttp://192.168.4.124:4444/grid/register
  其中192.168.4.124为主节点机子的ip地址,可以使用ipconfig命令在命令行查看得到。上面命令默认启动5555端口,可使用-port 更改。
  启动完成连接到主节点后,可以在主节点机子上 ,http://localhost:4444/grid/console网址查看到这个子节点状态。使用同样的方法,可以链接其它的子节点。
  运行一个简单的例子:
  上面已经把grid弄成功了,现在我们用Grid来运行一个很简单的例子。代码如下:
import java.net.MalformedURLException;
import java.net.URL;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
public class GridTest {
/**
* @throws MalformedURLException
*/
public static void main(String[] args) throws MalformedURLException {
DesiredCapabilities test = DesiredCapabilities.firefox();
WebDriver dr = new RemoteWebDriver(new URL("http://192.168.4.137:5555/wd/hub"),test);
dr.get("http://www.baidu.com");
}
}
  在主节点机子上运行上面的代码,你可以在次节点机子上看到firefox浏览器被启动,然后打开了www.baidu.com这个网址。
  值得注意的是:
  WebDriver dr = new RemoteWebDriver(newURL("http://192.168.4.137:5555/wd/hub"),test);
  这一句中的192.168.4.137为次节点的ip地址。

posted @ 2014-03-28 11:14 顺其自然EVO 阅读(258) | 评论 (0)编辑 收藏

Android软件测试的日志文件抓取简介

1、log文件分类简介
  实时打印 的主要有: logcat main , logcat radio , logcat events , tcpdump ,还有高通平台的还会有QXDM 日志
  状态信息 的有: adb shell cat /proc/kmsg , adb shell dmesg , adb shell dumpstate , adb shell dumpsys , adb bugreport ,工程模式等
  2、LOG抓取详解
  l  实时打印
  adb logcat -b main -v time>app.log  打印应用程序的 log
  adb logcat -b radio -v time> radio.log 打印射频相关的 log , SIM STK 也会在里面, modem 相关的ATcommand 等,当然跟 QXDM 差的很远了。
  adb logcat -b events -v time  打印系统事件的日志,比如触屏事件。。。
  tcpdump 是很有用的,对于 TCP/IP 协议相关的都可以使用这个来抓, adb shell tcpdump -s 10000 -w /sdcard/capture.pcap ,比如抓 mms 下载的时候的 UA profile , browser 上网的时候,使用 proxy 的 APN 下载, streaming 的相关内容包括 UA profile 等。
  最后是高通平台的 QXDM ,不管是不是 Android ,只要使用高通芯片,都会对它很熟悉,当然了,不是高通的芯片就不用提它了。这个不多讲,内容丰富,射频,电话,上网, ... 凡是高通提供的解决方案,这个都可以抓。(QXDM 的 LOG抓取方法请参考 QPST、 QXDM的基本使用说明及作用 )
  l  状态信息
  o   bugreport (命令 adb bugreport>bugreport.log) 。里面包含有 dmesg , dumpstate 和 dumpsys 。
  o   dumpstate 是系统状态信息,里面比较全,包括手机当前的内存信息、 cpu 信息、 logcat 缓存, kernel 缓存等等。
  o     adb shell dumpsys 这个是关于系统 service 的内容都在这个里面,这个命令还有更详尽的用法,比如 adb shell dumpsys meminfo system 是查看 system 这个 process 的内存信息。
  o   kmsg 抓取
  adb shell cat /proc/kmsg > kmsg.txt ,打开后查 msm_kgsl 字段
  说明:用于检索用 printk 生成的内核消息。任何时刻只能有一个具有超级用户权限的进程可以读取这个文件。也可以用系统调用 syslog 检索这些消息。通常使用工具 dmesg 或守护进程 klogd 检索这些消息。 proc 是一个内存文件系统 , 每次读文件 kmsg 实际是内核内部的循环缓冲区 , 每读过后 , 循环缓冲区的东西就被认为已经处理过了 ( 也就是变成无效内容 ), 所以你再次读为空是很正常的 为什么会这样处理呢 , 循环缓冲区大小有限 , 内核又随时可能往里面写东西 , 所以这样处理很正常 . 你去查一下 /proc/kmsg 的信息有没有跟系统日志关联 , 如果有的话 ,你就可以读日志文件
  o   dmsg 抓取
  adb shell dmesg > dmesg.txt
  说明: dmesg 用来显示开机信息, kernel 会将开机信息存储在 ring buffer 中。您若是开机时来不及查看信息,可利用 dmesg 来查看。 dmesg 是 kernel 的 log ,凡是跟 kernel 相关的,比如 driver 出了问题(相机,蓝牙, usb ,启动,等等)开机信息亦保存在 /var/log 目录中,名称为 dmesg 的文件里。 more /var/log/dmesg
  o   工程模式下 log 的抓取
  对于 Apollo 手机请拨打 *#*#8888#*#* , 然后勾选相应的 LOG 。待测试结束后,通过 SD 卡导出 LOG 到PC.
  3、Log分析:
  Get Log from Android System
  adb bugreport > bugreport.txt
  copy bugreport to the current directory.
  bugreport 里面包含了各种 log 信息 , 大部分 log 也可以通过直接运行相关的程序来直接获得 .
  步骤如下 :
  1.adb shell 2. 进入相关工具程式的目录 3. 执行相关程式 4. 得到相关信息
  下面以输出进程信息为例 1.adb shell 2. 输入 ps -P 3. 可以看到相关进程信息
  Log Archive Analysis
  1.bugreport
  bugreport 记录 android 启动过程的 log, 以及启动后的系统状态 , 包括进程列表,内存信息, VM 信息等等到.

 2.bugreport 结构分析
  (1)dumpstate
  MEMORY INFO
  获取该 log: 读取文件 /proc/meminfo
  系统内存使用状态
  CPU INFO
  获取该 log: 执行 /system/bin/top -n 1 -d 1 -m 30 -t
  系统 CPU 使用状态
  PROCRANK
  获取该 log: 执行 /system/bin/procrank
  执行 /system/xbin/procrank 后输出的结果 , 查看一些内存使用状态
  VIRTUAL MEMORY STATS
  获取该 log: 读取文件 /proc/vmstat
  虚拟内存分配情况
  vmalloc 申请的内存则位于 vmalloc_start ~ vmalloc_end 之间,与物理地址没有简单的转换关系,虽然在逻辑上它们也是连续的,但是在物理上它们不要求连续。
  VMALLOC INFO
  获取该 log: 读取文件 /proc/vmallocinfo
  虚拟内存分配情况
  SLAB INFO
  获取该 log: 读取文件 /proc/slabinfo
  SLAB 是一种内存分配器 . 这里输出该分配器的一些信息
  ZONEINFO
  获取该 log: 读取文件 /proc/zoneinfo
  zone info
  SYSTEM LOG( 需要着重分析 )
  获取该 log: 执行 /system/bin/logcat -v time -d *:v
  会输出在程序中输出的 Log, 用于分析系统的当前状态
  VM TRACES
  获取该 log: 读取文件 /data/anr/traces.txt
  因为每个程序都是在各自的 VM 中运行的 , 这个 Log 是现实各自 VM 的一些 traces
  EVENT LOG TAGS
  获取该 log: 读取文件 /etc/event-log-tags
  EVENT LOG
  获取该 log: 执行 /system/bin/logcat -b events -v time -d *:v
  输出一些 Event 的 log
  RADIO LOG
  获取该 log: 执行 /system/bin/logcat -b radio -v time -d *:v
  显示一些无线设备的链接状态 , 如 GSM , PHONE,STK(Satellite Tool Kit)…
  NETWORK STATE
  获取该 log: 执行 /system/bin/netcfg ( 得到网络链接状态 )
  获取该 log: 读取文件 /proc/net/route ( 得到路由状态 )
  显示网络链接和路由
  SYSTEM PROPERTIES
  获取该 log: 参考代码实现
  显示一些系统属性 , 如 Version,Services,network…
  KERNEL LOG
  获取该 log: 执行 /system/bin/dmesg
  显示 Android 内核输出的 Log
  KERNEL WAKELOCKS
  获取该 log: 读取文件 /proc/wakelocks
  内核对一些程式和服务唤醒和休眠的一些记录
  KERNEL CPUFREQ
  (Linux kernel CPUfreq subsystem) Clock scaling allows you to change the clock speed of the CPUs on the fly.
  This is a nice method to save battery power, because the lower the clock speed is, the less power the CPU consumes.
  PROCESSES
  获取该 log: 执行 ps -P
  显示当前进程
  PROCESSES AND THREADS
  获取该 log: 执行 ps -t -p -P
  显示当前进程和线程
  LIBRANK
  获取该 log: 执行 /system/xbin/librank
  剔除不必要的 library
  BINDER FAILED TRANSACTION LOG
  获取该 log: 读取文件 /proc/binder/failed_transaction_log
  BINDER TRANSACTION LOG
  获取该 log: 读取文件 /proc/binder/transaction_log
  BINDER TRANSACTIONS
  获取该 log: 读取文件 /proc/binder/transactions
  BINDER STATS
  获取该 log: 读取文件 /proc/binder/stats
  BINDER PROCESS STATE
  获取该 log: 读取文件 /proc/binder/proc/*
  bind 相关的一些状态
  FILESYSTEMS
  获取该 log: 执行 /system/bin/df
  主要文件的一些容量使用状态 (cache,sqlite,dev…)
  PACKAGE SETTINGS
  获取该 log: 读取文件 /data/system/packages.xml
  系统中 package 的一些状态 ( 访问权限 , 路径 …) ,类似 Windows 里面的一些 lnk 文件吧 .
  PACKAGE UID ERRORS
  获取该 log: 读取文件 /data/system/uiderrors.txt
  错误信息
  KERNEL LAST KMSG LOG
  最新 kernel message log
  LAST RADIO LOG
  最新 radio log
  KERNEL PANIC CONSOLE LOG
  KERNEL PANIC THREADS LOG
  控制台 / 线程的一些错误信息 log
  BACKLIGHTS
  获取该 log: 获取 LCD brightness 读 /sys/class/leds/lcd-backlight/brightness
  获取该 log: 获取 Button brightness 读 /sys/class/leds/button-backlight/brightness
  获取该 log: 获取 Keyboard brightness 读 /sys/class/leds/keyboard-backlight/brightness
  获取该 log: 获取 ALS mode 读 /sys/class/leds/lcd-backlight/als
  获取该 log: 获取 LCD driver registers 读 /sys/class/leds/lcd-backlight/registers
  获取相关亮度的一些信息
  (2)build.prop
  VERSION INFO 输出下列信息
  当前时间
  当前内核版本 : 可以读取文件 (/proc/version) 获得
  显示当前命令 : 可以读取文件夹 (/proc/cmdline) 获得
  显示系统 build 的一些属性 : 可以读取文件 (/system/build.prop) 获得
  输出系统一些属性
  gsm.version.ril-impl
  gsm.version.baseband
  gsm.imei
  gsm.sim.operator.numeric
  gsm.operator.alpha

 (3)dumpsys
  执行 /system/bin/dumpsys 后可以获得这个 log.
  经常会发现该 log 输出不完整 , 因为代码里面要求该工具最多只执行 60ms, 可能会导致 log 无法完全输出来 .
  可以通过修改时间参数来保证 log 完全输出 .
  信息 :
  Currently running services
  DUMP OF SERVICE services-name(running)
  Log Code Analysis
  Site: .\frameworks\base\cmds\dumpstate\
  相关 Log 程序的代码可以从上面目录获取
  Log Analysis Experience
  分析步骤
  1. 查看一些版本信息
  确认问题的系统环境
  2. 查看 CPU/MEMORY 的使用状况
  看是否有内存耗尽 ,CPU 繁忙这样的背景情况出现 .
  3. 分析 traces
  因为 traces 是系统出错以后输出的一些线程堆栈信息 , 可以很快定位到问题出在哪里 .
  4. 分析 SYSTEM LOG
  系统 Log 详细输出各种 log, 可以找出相关 log 进行逐一分析
  实例分析
  下面分析我写的一个测试例子 , 在 OnCreate 做一个死循环 , 这样主线程会被锁住,在按下硬件的 Back 之后会出现 ANR 的错误 .
  在 traces 中发现该程序的堆栈信息如下 :
—– pid 20597 at 2010-03-15 01:29:53 —–
Cmd line: com.android.test
DALVIK THREADS:
"main" prio=5 tid=3 TIMED_WAIT
| group="main" sCount=1 dsCount=0 s=N obj=0x2aac6240 self=0xbda8
| sysTid=20597 nice=0 sched=0/0 cgrp=default handle=1877232296
at java.lang.VMThread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:1306)
at java.lang.Thread.sleep(Thread.java:1286)
at android.os.SystemClock.sleep(SystemClock.java:114)
at com.android.test.main.onCreate(main.java:20)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2459)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2512)
at android.app.ActivityThread.access$2200(ActivityThread.java:119)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1863)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4363)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
at dalvik.system.NativeStart.main(Native Method)
"Binder Thread #2" prio=5 tid=11 NATIVE
| group="main" sCount=1 dsCount=0 s=N obj=0x2fb7c260 self=0×143860
| sysTid=20601 nice=0 sched=0/0 cgrp=default handle=1211376
at dalvik.system.NativeStart.run(Native Method)
"Binder Thread #1" prio=5 tid=9 NATIVE
| group="main" sCount=1 dsCount=0 s=N obj=0x2fb7c1a0 self=0x14c980
| sysTid=20600 nice=0 sched=0/0 cgrp=default handle=1207920
at dalvik.system.NativeStart.run(Native Method)
"Signal Catcher" daemon prio=5 tid=7 RUNNABLE
| group="system" sCount=0 dsCount=0 s=N obj=0x2fb7a1e8 self=0x126cc0
| sysTid=20599 nice=0 sched=0/0 cgrp=default handle=1269048
at dalvik.system.NativeStart.run(Native Method)
"HeapWorker" daemon prio=5 tid=5 VMWAIT
| group="system" sCount=1 dsCount=0 s=N obj=0x2e31daf0 self=0x135c08
| sysTid=20598 nice=0 sched=0/0 cgrp=default handle=1268528
at dalvik.system.NativeStart.run(Native Method)
—– end 20597 —–
  该文件的堆栈结构从下往上进行分析
  (1) 栈底 at dalvik.system.NativeStart.run(Native Method)
  系统为当前的 task( 应用程式 ) 启动一个专用的虚拟机
  (2) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2459)
  Activity Services 是在后台负责管理 Activity, 它此时将测试例子的 Activity 启动起来了
  (3)at com.android.test.main.onCreate(main.java:20)
  启动测试程序
  (4) 栈顶 at java.lang.VMThread.sleep(Native Method)
  线程被 sleep 掉了 , 所以无法响应用户 , 出现 ANR 错误 .
  上面是对一个非常简单的问题的分析 .
  如果遇到比较复杂的问题还需要详细分析 SYSTEM LOG.
  1. 比如网络异常 , 要通过 SYSTEM LOG 里面输出的网络链接信息来判断网络状态
  2. 数据传输 , 网络链接等耗时的操作需要分析 SYSTEM LOG 里面 ActivityManager 的响应时间
  3…

posted @ 2014-03-28 11:12 顺其自然EVO 阅读(203) | 评论 (0)编辑 收藏

如何一步一步从 QA 到 EP

  两三年以前,和友人谈到 QA(软件质量保证) 这个行业,还有 QA 这个团队的未来,就有了一丝忧虑。而现在,终于有机会实践一下自己之前的想法,在这里分享给大家。
  从我有限的从业经验到现在,经历了很多次软件开发模式的变化,这些变化,或因为跟风,或因为有切实的问题要解决,总之始终处于各种不同的尝试的路上。QA 团队从最早的强调流程,到后来强调开发技术,搞自动化测试,再后来又开始做敏捷和持续集成,这条发展的路上,对自己的要求不断变高的同时,也伴随着一个组织和团队发展的魔咒。
  组织发展魔咒
  这个发展的魔咒更像是一个循环,可能开始于任何一个环节。
  例如,公司负责技术的高层,没来由的认为,测试和质量保证并不重要。这个判断会慢慢渗透到技术团队的各个角落,导致测试工程师,乃至测试团队的其他角色,例如SQA,未来发展的空间会被压缩,而压缩发展空间的结果就是留不住人、招不到人。一方面相关工作的经验技能要求越来越高,一方面可见的天花板又摆在那里。于是整个 QA 团队都成了别人眼中的 「低技术」团队,不论真的低技术还是别人以为的低技术,这种印象都很要命,为了摆脱这种印象,大家需要做点东西来证明自己,于是各种自动化测试框架、平台、系统,纷纷出现,殊不知此时,QA 团队和整个公司的价值已经慢慢的不一致了,自己关起门跟自己玩,成了普遍现象之后,在公司高层看来,他会觉得自己的 「QA 团队并不重要」的判断被证明了,因为没有任何明确的证据表明,QA 团队与公司愿景和计划之间的直接联系。
  可怕么?在很多软件研发组织中,这是现实存在的循环。
  说起来我们的实践,确实打破了这个循环,说起来好笑,我们解救 QA 团队的方式,就是彻底取消这个团队。但是反过来讲,只有杀死「QA 团队」,才能真正的解放「QA 工程师们」,真正解放整个软件研发过程。
  一些基本的价值观
  这个事情,就要从一些最基本的价值观说起。
  比如,人总要对自己做的事负责。当然做了漂亮的事情,谁都希望头上有光环,但是做了丑事,也要能忍受得了羞辱。之为 「吃自己的狗食」,而老式的软件开发分段流程,等于鼓励上游做的错事,下游来擦屁股,于是上游颐指气使,下游低三下四,这种颐指气使和低三下四还能传导,于是的于是,最下游的一个环节,就是公认的受气包了。暂不说效率和质量,从最基本的做事方法的角度,似乎也有些欠妥。我们这一条怎么落地呢,就是改组研发团队,建立 Owner 制度。一个项目的 Owner,就像一个项目的 CEO,大事小情都要关注,从产品到开发,从测试到运维,总之一个项目的成败,都需要 Owner 来操心,项目外的人,都是他的资源。相应的,项目也变得和平台无关,而与特性有关,每个项目组都会涉及到几个平台的设计、开发工作。
  还比如,给质量一个明确的标准。做质量工作或者测试的人,都会有强迫症,总觉得哪里不对劲,还得狠狠的回归一遍,又一遍。可其实大家都知道质量是没有个确定的标准说好还是坏的,那怎么确定质量呢?我们称作 「质量体现在造成实际的影响上」,也就是说,一个严重的问题,如果没造成影响,或很轻微,那就不严重。而一个轻微问题,如果影响面很广,例如有 1000 万用户都看见了,那就不轻微。
  又比如,交付一个完美产品还是建立一个快速召回的机制?我们确实真的想每时每刻都能交付一个完美无暇的产品,但那不可能。特别是在互联网行业,跟传统的电信、医疗、航空航天的产品迭代有天壤之别,一个完美产品用一年做出来,市场可能早就变了天了。但不完美就有质量风险和代价,为了平衡这一点,我们必须建立一个快速召回缺陷产品的机制,甚至能让用户在发现缺陷之前,就用上了新版本。
  有了这几条价值观,我们就大概知道开发过程改进的方向,以及做事情的原则了。那我们做了什么呢?我们组建了 EP 团队。
  EP 是什么
  说到这里,EP 这个词才第一次出现,这个词的内涵之丰富,可能需要仔细说说。
  我最早看到 EP 这个词,是在当时还是 Google EP 团队成员的 James Wittaker 写的那一个有名的 「How Google Test」的系列博客中,内容我就不转述了,很多人都读过。
  EP 是 Engineering Productivity 的缩写,工程生产力的意思,这个团队,就是给整个大技术团队,甚至整个公司提高工作效率的。通俗直白的说,就是一个工具团队。因为工欲善其事、必先利其器,不要小看工具团队,某些程度上来讲,一个产品随着市场的变化可能很快凋亡,而一个好的工程工具,生命力要强得多,比如,开发语言其实就是最基本的工程工具了。那么,对一个公司,或者说交付团队来讲,怎么衡量工程生产力的高低呢?这个衡量方式其实就决定了「EP团队」的工作方向。我们自己定义的工程生产力从低到高的定义是这样的:1)质量,这是最基础的指标,什么都不行,也要保证质量过关,否则一个产品连生存的可能都没有。2)同等质量水平下,追求速度。质量过关了,就要看迭代速度了,你比竞争对手快,你就能活下来。3)同等质量和速度下,工程师的幸福感。如果质量也过关了,速度也快,但是大家过得很苦,天天加班,重复劳动,看不到未来,这也不行。幸福是什么?对我们来说,就是不被反复的简单问题所困扰,该自动的都自动,自动不是说一定快,但是一定省心,这里的幸福就是省心,有精力去关注更多的有意义的事情,而不是每天处理简单重复的问题。4)同等质量和速度,也有幸福感,再看成长。工程师们有没有感受到成长?不断的解决问题或开发产品,感受到的是重复劳动还是成长?其实前三点都做到了,第四点一定是有的。

 EP 做什么
  我们回头说 EP 团队,EP 团队也有自己的人生理想,那就是一个三部曲:替、教、独立。
  第一个是替的阶段,其实就是比较老式的开发过程,我替你测试、替你上线、替你运维。
  这个阶段,完全不符合我们「吃狗食」那一条价值观,按照狗食法则,一个人自己设计开发编码,当然要自己测试,自己写的代码 bug 多要一遍遍回归,这个苦果自己不吃谁来吃?但没办法,大多数程序员在如何测试自己的程序方面没有受过什么训练,为了尽快发布产品,只能替,但这个替的时间要越短越好,尽快进入下一个阶段,教。
  第二个是教,就是教技术团队的其他成员,如何测试自己的程序,如何构造环境、构造数据,如何部署和运维自己的产品。这里的自己做,并不是回到蛮荒时期,例如创业初期只有一个程序员的时候,他当然是自己开发自己测试自己部署,但我们到了第二阶段的自己做,是自己规范的做,通过我们提供的相对完善和规范的工具做。我们就处于这个阶段的初期。
  第三个阶段是独立,独立是说 EP 团队从一个替人做事的下游团队,到一个教人做事的教练团队,真正进化为一个提供技术服务的产品团队。这个产品团队的产品,大多数应该是以一个标准化的、健壮的服务的形式,而不是人力资源的形式,提供给其他团队的。当然这是我们的理想,能否达到或者是否切合实际,还需要时间来观察。
  EP 团队和整个技术团队的关系
  从这三个阶段的描述中,多多少少都提到一些 EP 团队和整个技术团队或整个公司的关系,那这个关系是什么呢?
  前面提过,我们不希望是个下游替人收尾的团队,我们也有「吃狗食」这样的原则,所以我想到一个比喻,来说明 EP 团队和其他技术团队的关系。
  我们都知道有一个行业叫做汽车制造业,他们遵循一定的规范和标准,还能巧妙的将创意和标准结合在一起,制造出一些工业奇迹,这些汽车被卖给各式各样的人,汽车企业并不关心他们的产品用在什么地方,不过他们又发明了一种叫做 4S 店的东西,用来给那些工业奇迹定期维护、修理、还可以收集市场反馈以便改进产品。
  还有另一个行业叫做物流业,他们的目的就是将货物从一个地方运到另一个地方,这种空间的转移,就是他们为客户创造的价值。在这过程中,他们会利用到汽车制造业产出的汽车,但他们不太关心汽车本身的标准、设计细节,他们只是使用,他们关心的是使用成本、维修方便。出问题,他们会找 4S 店。
  这两个行业之间的交集是汽车,汽车制造业的价值是制造出好的汽车,物流业的价值是货物的到达,汽车制造业不关心你的货物的目的地,物流业不关心他的汽车的制造工艺。但汽车制造业会很关心你怎么用这个汽车,以及积极的帮助你保养,而物流业也会很关心这个车费不费油,好不好开。
  说到这里,你可能已经看明白 EP 团队和其他技术团队的关系了:EP 团队就像汽车制造业,提供高效、低耗的工具;产品技术团队就像物流业,使用工具,快速前进,创造用户价值。他们之间互相依赖,却又彼此独立。
  EP 都有谁
  了解了 EP 和周围团队的关系之后,来看看我们的 EP 团队的角色和成员。
  我们的 EP 团队,大致分成如下几个角色(而实际上的工作是混合的,之所以要分开成角色,主要是从招聘的角度出发):
  SED,Software Engineer in DevOps。顾名思义,这个角色首先是个软件开发工程师,其次,面向的领域是 DevOps,DevOps 的概念我们就不必多讲了,在实际工作中,SED 工程师是个真正的多面手,他们可能今天在开发一个 Linux server 的自动化上线和回滚的工具,明天就要设计或优化 CDN 的部署,后天又要解决一个 Windows 平台编译加速问题,还有还有一个自动性能 benchmark 工具等着他来开发。这个角色目前我们只有两位,而且这个角色的工程师是最难招聘的,因为新人,或者很小的公司出来的人,很少有受过系统的训练或有比较先进的软件工程思想,而从大公司出来的人,已经被大公司条块分割的工作方式同化,一般只擅长一个领域,而对跨界的或者不懂,或者没兴趣。所以这个岗位的工程师,都是有成熟公司工作经验的 Geek 型的人。
  SA,System Admin。系统工程师,和很多公司的运维工程师很想像,实际上我们现在的状态,做的事情也和大多数公司的运维工程师一样,处理监控,优化服务部署等等,但不一样的是我们的目标是将绝大多数应用层面的运维工作交还给开发团队,所以我们在不断的将监控系统改造为友好的,自助的,也不断的将各位上线部署类的工作做成自动的,现在已经有了很多成果,我们的 SA 主要精力可以放在系统以及更底层的部分了。
  TE,Testing Engineer。测试工程师,其实这个称呼有点名不符实,我们的唯一一位测试工程师,主要的工作其实是发布和迭代控制,要保证整个交付团队的迭代节奏,例如在代码上拉发布分支、触发发布事件、监控数据等等工作,这个工作要求非常精确,又很繁琐,因此和 SED 工程师有非常多的交互,他们负责将这个过程自动化。这里插入介绍一下我们的发布过程,可能大家会更理解为什么还有个「发布工程师」:
  我们有三个发布 Channel:Beta、RC、Release,作用各有不同。例如 Beta Channel,主要用于一些新特性的提前发布,这里面可能会多少有点缺陷,所以一定要控制人数,并且是那些喜欢尝鲜的用户,他们会用的比较彻底。而 Beta Channel,可能每天都有版本更新,会有一些用户喜欢跟着 Beta 版。而这些新的特性如果用户反馈不错,并且没有什么严重的问题,就会进入最近一次 RC(Release Candidate),这个量就很大了,大概能占到我们每日活跃用户的十分之一到五分之一,这里面的功能在没有意外的话,就是正式发布的功能了,需要注意的是,不是每个 Beta 都会变成 RC。而 RC 在发布几天之后,如果一切正常,就会切换为 Release,Release Channel 一般会在一天之内,让绝大多数活跃用户升级完毕,这个时候,如果程序有 bug,影响就非常大了。
  Venders,外包测试团队。我们有大约六七个人的外包测试团队(on-site),主要负责我们主要产品的人工验收测试。我们对外包测试团队的工作方式也有一个设想,就是一个项目刚开始的时候,外包测试团队应当是先上很多人,然后随着 SED 的介入,让自动化程度加强,慢慢人少下来,直到下一个新项目开始。但这个设想在国内想实现,却没那么容易,主要有几个原因:1)国内的外包测试的工程师,通常是技术和经验都比较初级的人来做,外包测试成了一个门槛低天花板也低的行业,技术和经验缺乏,导致进入新项目以后没办法非常快的上手,而有经验有能力的人,很快就会脱离外包行业;2)外包测试的公司,人才储备不足,很少有人力资源池,都是有需求,现从市场上招,或从竞争对手那里挖,有的人都没见过,就派到客户那边来面试,这也导致了没办法几个月就撤下来,因为他没办法跟候选人签合同。这两个客观原因,我们也比较无奈,所以我们的外包测试团队基本上还是长期 on-site。
  UOE,user operation engineer。用户运营工程师,这个岗位很多人不太容易理解,一般用户运营人员都是跟内容啊、用户打交道的,就像贴吧管理员就是典型的用户运营人员,那为什么要有个运营工程师呢?这个我们是跟硅谷的 Dropbox 学习的。因为在日常工作中,我们发现有想当一部分用户的反馈,不论是新功能的需求还是缺陷,都是技术性很强的,如果你能做到第一时间和用户做深入的,技术含量较高的沟通,从解决问题的成功率上会高很多,而如果你反馈一个技术问题,总是过了几天才有技术人员跟你联系的话,你可能配合排查问题的愿望会小很多。基于这个思路,我们增加了这个角色,同时他们还负责一些运营过程中使用的工具和平台类的研发。可能会有人问这个角色为什么会在 EP 团队?其实仔细分析一下用户运营的工作,会发现他们处理的对象是一个个用户提交的 ticket,这非常像 test case,不同之处是一个是用户事后提交,一个是事先设计,分别保证了优先级和完备性,因此结合起来,对提高质量是非常有益的事情。
  EP 团队的工作方式与面临的挑战
  上面这几个角色,就组成了我们的 EP 团队,这样的一个团队,这样的一个能力构成,就有了一些鲜明的特点,例如:
  1)没人管的事情我们管,支持所有团队。公司内部虽然分成了很多个团队,但是很多技术问题是找不到人负责的,例如,一个简单的内部数据统计脚本,或者一个发布内容到 CDN 的 CMS,等等。这些事情基本都会由 EP 团队接过来。
  2)做事情没有计划。这个特点可能很多人会觉得匪夷所思,甚至不能接受,但实际上这跟 EP 团队的工作有关系,比如汽车 4S 店,有多少车祸的汽车要修理,多少人为损坏的车要修理,怎么做计划?实际上是遇山开道、遇水搭桥。外部的市场的变化、内部的技术人员的变化,都会有不断的瓶颈出现,而 EP 就要快速发现并解决这些瓶颈,直到发现下一个瓶颈,这个过程没办法有详尽的长期的计划。而替代的是使用目标管理的方式,我们公司内部所有团队都会用一种叫做 OKR(Objective and Key Result)的方式来做管理,简单的说,就是设定目标,然后评估完成度。EP 团队的目标大致有两个方向,一个我们叫做 「Smoothly & Fast」,就是让一切跑通做顺的能力,还有一个就是「实现自助」,能让其他团队的成员,不管是技术还是非技术背景,都能自己通过我们的工具完成任务。
  这些特点看起来很不错,但是实际上带来的问题也非常多,例如:
  没有成就感。因为所有人都是你的需求方,这个感觉实在是不太好,另一个角度讲,很多研发工程师会觉得开发一个对外的产品比较有成就感,对内的总觉得没意思。这个问题要解决,其实就要靠所谓的「工程师文化」来解决,国内长期以来在职业化上有一些不好的习惯,其实能发明工具的人都是大师,开发语言就是工具,操作系统也是工具,真正的牛人,都在做各式各样的工具。能帮助别人成功的人,是最成功的。
  还有,就是脱离实际。很多人做工具,怎么炫怎么做,流行什么做什么,要么就大而全,这还是好的,更多的时候是想的大而全,半年做不出来。整个公司的价值取向是一致的,特别是小公司,容不得这种炫技一般的工作方式。所以我有一句话,叫做「自 high 无价值」,什么叫「自 high」?就是自己跟自己玩,玩的很高兴。
  还有一个问题,就是招聘困难。这个在 SED 的工作职责部分提过,就不展开了。因为招聘困难,我们就要考虑怎么培养这样的人才,所以我们有一个方法论,叫做「要改进,先体验」,因为很多 EP 的成员是要改进工作过程的,但是怎么改,不是所有人都能搞定,这依赖于大量的经验积累,对经验不足的人,很简单,就是让他去做。要提高研发效率,找到痛点,那就先去做一个月研发;要去改进测试过程,提高效率,就去做一个月测试。一个技术和思维方式都很不错,只是经验少的人,经过一个月的体验,能提出非常多的、而其他人已经麻木了的改进点,并推动实施。
  再有,依赖整个团队的成熟度。不是说有了 EP 这样一个团队,整个公司的效率和工作模式就会有大幅度提升,因为一个汽车再好,你开的方向不对,也到不了目的地。现实中存在着非常多缺乏责任感的 Owner,很多人觉得,我写完代码编译通过了,丢给测试组就行了,没我的事了,这样的想法大有人在,所以从成立 EP 团队,到整个公司的生产力真正被提高,中间不只是提供工具这么简单,还有一系列的指导和训练的工作。
  Why we can & why you can
  最后,关于我们为什么能做这个事情,我们也有一些总结:
  1)创业团队。创业团队就是短小精悍,精力集中,没有太多无谓的纷扰,快速交付产品快速迭代是主要的工作方式。
  2)从第一天开始坚持自由和责任的工程文化。从创始人开始,很坚持这种工程文化,有什么样的 leader 就有什么样的团队,所以大家接收和拥抱 EP 的工作模式,也非常快。
  虽然上述这两条很多公司没有,但不代表做不成这个事情,在我看来,只要具备下面几条,想做成 EP 的工作,就并不难。
  1)互联网行业。互联网行业有一个非常好的,区别于以往其他行业的特点,就是你的产品在物理上是自己控制的,提供的只是服务,这非常有利于快速迭代,因为传统行业不可能做到。
  2)快速变化的业务模式。这不是说我们自己要快速变化,而是业务模式和市场不断变化,来推着我们前进。只有业务模式的快速发展,才对生产力有不断更高的要求。
  3)有改变的决心。这个说起来有点虚了,但也很重要,因为 EP 这样的工作模式会有阵痛,例如团队的重组、转型,都会影响到一部分人的利益,特别是团队的管理者,而这些中高层管理者,也确实有能力阻止变革。但坦白的说,很多时候你不主动改变,到了客观环境推动你不得不变的时候,到最后就成了被淘汰的人了。我还有一句话,叫做「组织结构决定工作模式」,意思是说,很多工作模式的出现,是因为组织结构的需要。反过来说,在你的组织里很多很好的工作模式推动不下去,或者效果很差,你就要看看你的组织结构是不是有问题。比如两个本来应该紧密合作的团队,一直合作不好,互相鄙视,你想简化流程,最后流程越做越多,大家都在垒墙,那你就要看看两个团队共同的老板,是不是级别太高了。
  4)对团队成员的高标准。前面我提过,我们 EP 团队的大部分是 Geek 型的人,Geek 这个词在英语里是一种很高的评价。只有一个技术和经验都非常丰富的人,才能胜任 EP 这样的工作,所以要坚持不懈的雇佣一流的人才,人不够,可以抓大放小,但绝不能有二流、三流的人在团队里。


posted @ 2014-03-28 11:06 顺其自然EVO 阅读(928) | 评论 (0)编辑 收藏

Flask-Testing 单元测试

准备
  1. 安装pip
  2. 安装Flask
  3. 安装Flask-Testing (ver:>=0.4.1)
  测试程序
#coding=utf8
from flask import Flask,jsonify
from flask.ext.testing import TestCase
import unittest
app = Flask(__name__)
@app.route("/ajax/")
def some_json():
return jsonify(success=False)
class TestViews(TestCase):
def create_app(self):
app.config['TESTING'] = True
return app
def test_some_json(self):
response = self.client.get("/ajax/")
'''''
判断还回的JSON对像是不是{'success':True}
'''
self.assertEquals(response.json, dict(success=True))
if  __name__ =='__main__':
unittest.main()
  执行测试结果:
python tests.py

posted @ 2014-03-28 11:02 顺其自然EVO 阅读(249) | 评论 (0)编辑 收藏

Linux学习—fork用法

 linux当中可以使用fork函数创建一个新进程
  #include <unistd.h>
  pid_t fork(void);
  返回值:子进程中返回0,父进程中返回子进程ID,出错返回-1
  在fork调用之后,子进程获得父进程数据空间、堆和栈的副本,但是并不共享这些存储空间部分。
  fork的一个特性是父进程的所有打开文件描述符都被复制到子进程中。父、子进程的每个相同的打开描述符共享一个文件表项。
  这种共享文件的方式使父、子进程对同一文件使用了一个文件偏移量。
  fork有两种用法:
  (1)一个父进程希望复制自己,使父、子进程同时执行不同的代码段。例如,父进程等待客户端的服务请求,然后fork一个子进程处理这个请求,自己则继续等待下一个服务请求。
  (2)一个进程要执行一个不同的操作。fork一个子进程之后立马调用exec
  fork的特殊应用:fork两次可以避免僵死进程,(僵死进程是指一个已经终止,但是其父进程尚未对其进行善后处理的进程),父进程先fork一个子进程,子进程继续fork一个孙子进程,然后就直接退出。这样,父进程就可以很快的wait到子进程,释放其资源,不需要阻塞,继续自己的操作;而孙子进程交由了init进程托管,执行自己的操作而不用担心了。
  ps:write函数不带缓冲,标准I/O库是带缓冲的,如果标准输出练到终端设备,则它是行缓冲的,否则它是全缓冲的

posted @ 2014-03-27 17:07 顺其自然EVO 阅读(275) | 评论 (0)编辑 收藏

数据库中的事务、存储过程和触发器的简单使用

 什么是事务(Transaction)
  指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)--也就是由多个sql语句组成,必须作为一个整体执行
  这些sql语句作为一个整体一起向系统提交,要么都执行、要么都不执行
  语法步骤:
  开始事务:BEGIN TRANSACTION
  事务提交:COMMIT TRANSACTION
  事务回滚:ROLLBACK TRANSACTION
  判断某条语句执行是否出错:
  全局变量@@ERROR;
  @@ERROR只能判断当前一条T-SQL语句执行是否有错,为了判断事务中所有T-SQL语句是否有错,我们需要对错误进行累计;
  例:SET @errorSum=@errorSum+@@error
  存储过程---就像数据库中运行方法(函数)
  和C#里的方法一样,由存储过程名/存储过程参数组成/可以有返回结果。
  前面学的if else/while/变量 等,都可以在存储过程中使用
  优点:
  执行速度更快
  允许模块化程序设计
  提高系统安全性
  减少网络流通量
  系统存储过程
  由系统定义,存放在master数据库中
  名称以“sp_”开头或”xp_”开头
  自定义存储过程
  由用户在自己的数据库中创建的存储过程
  系统存储过程
  说明
  sp_databases
  列出服务器上的所有数据库。
  sp_helpdb
  报告有关指定数据库或所有数据库的信息
  sp_renamedb
  更改数据库的名称
  sp_tables
  返回当前环境下可查询的对象的列表
  sp_columns
  回某个表列的信息
  sp_help
  查看某个表的所有信息
  sp_helpconstraint
  查看某个表的约束
  sp_helpindex
  查看某个表的索引
  sp_stored_procedures
  列出当前环境中的所有存储过程。  sp_password
  添加或修改登录帐户的密码。
  sp_helptext
  显示默认值、未加密的存储过程、用户定义的存储过程、触发器或视图的实际文本。
  定义存储过程的语法
  CREATE  PROC[EDURE]  存储过程名
  @参数   数据类型 = 默认值 OUTPUT,
  @参数n  数据类型 = 默认值 OUTPUT
  AS
  SQL语句
  参数说明:
  参数可选
  参数分为输入参数、输出参数
  输入参数允许有默认值
  EXEC  过程名  [参数]
  触发器是一种特殊类型的存储过程,它不同于前面介绍过的一般的存储过程。
  一般的存储过程通过存储过程名称被直接调用,而触发器主要是通过事件进行触发而被执行。
  触发器是一个功能强大的工具,在表中数据发生变化时自动强制执行。触发器可以用于SQL Server约束、默认值和规则的完整性检查,还可以完成难以用普通约束实现的复杂功能。
  那究竟何为触发器?在SQL Server里面也就是对某一个表的一定的操作,触发某种条件,从而执行的一段程序。触发器是一个特殊的存储过程。
  常见的触发器有三种:分别应用于Insert , Update , Delete 事件
  常用语法
CREATE TRIGGER triggerName ON Table
for UPDATE|INSERT|DELETE
AS
begin
end
  触发器-更新
CREATE TRIGGER testForFun ON dbo.Category
for UPDATE
AS
begin
select * from book
end
update Category set c_name = 'Android2' where c_id=3
  触发器-删除
CREATE TRIGGER testForDel ON dbo.Category
for delete
AS
begin
select * from book
end
delete Category set c_name = 'Android2' where c_id=3

posted @ 2014-03-27 17:06 顺其自然EVO 阅读(467) | 评论 (0)编辑 收藏

Selenium实例:Python登录WebQQ

 selenium范例:python登录webqq
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.keys import Keys
import time
from pprint import pprint
username="username"
passwd="password"
class WebQQ:
def __init__(self):
self.browser = webdriver.Firefox() # Get local session of firefox
self.browser.set_window_position(800,0)
self.browser.get("http://web.qq.com") # Load page
while True:
try:
elem = self.browser.find_element_by_id("alloy_icon_app_50_3") # Find the qq button
elem.click()
print "click qq button"
break
except NoSuchElementException:
print "element not loaded yet"
time.sleep(0.1) # Let the page load, will be added to the API
def login(self):
self.browser.switch_to_frame("ifram_login")
# Find the loginState option
elem = self.browser.find_element_by_id("loginState")
elem.click()
print "click loginState"
# choose hidden state
elem = self.browser.find_element_by_id("loginStatePanel")
elems = elem.find_elements_by_class_name("statePanel_li")
elems[0].click()
print "choose online state"
# login
elem = self.browser.find_element_by_id("u") # user name
elem.send_keys(username)
elem = self.browser.find_element_by_id("p") # passwd
elem.send_keys(passwd)
elem = self.browser.find_element_by_id("login_button") # login button
elem.click()
def main():
webqq = WebQQ()
webqq.login()
if __name__ == "__main__":
main()

posted @ 2014-03-27 17:04 顺其自然EVO 阅读(717) | 评论 (0)编辑 收藏

Windows 7上QTP11破解方法

 QTP11破解方法:
  1.准备文件
  注册机mgn-mqt82.exe
  2.安装QTP11
  3.运行注册机mgn-mqt82.exe
  如果运行mgn-mqt82.exe 没有反应-,请注意关掉暂时关掉杀毒软件
  根据路径“C:\Program Files\Common Files\Mercury Interactive\License Manager\lservrc”
  以上方法是网上9.2 的破解方法, 以下是11的破解方法,同时适用QTP10.0,只不过以下方法破解10.0可以无时间限制,而11只能适用15天或者30天 。如果11的License到期了,可以使用此方法再次重新破解,不需要重装,即可再次使用15-30天。
  1.找到lservrc文件,将它剪切至桌面,或者是别的文件夹,注意一定是剪切。
  2.运行QTP 11
  或者
  4.点击Install License,选择Seat license,因为QTP10和QTP11都采用了两种license ,单机版的和服务器并发的,
  我们破解的是单机许可。
  5.点击下一步
  用记事本打开前面注册机生产的lservrc文件,会看到有4个许可密钥,复制任意一条密钥,从开头至#号结束,
  粘贴至窗口的文本框中,点击下一步,如果提示该密钥已经安装,何以选择另外一条安装,如果仍然提示,
  请注意检查第三步lservrc文件是否移走了,
  点击下一步
  6.安装完成,启动QTP
  插件破解方法:
  1.net插件安装
  将下载下来的插件进行解压缩到目录qtp92-net-addin中,进入该目录,点击setup.exe,直接默认安装,到最后,把“立即注册”取消,安装完毕。重新启动qtp,会发现插件已经存在,但是显示灰色,不可选,状态为no license。
  2.破解.net插件
  (1)进入注册表,将HKEY_LOCAL_MACHINE\SOFTWARE\Mercury Interactive备份
  (2)找到HKEY_LOCAL_MACHINE\SOFTWARE\Mercury Interactive\QuickTest Professional\Add-In Manager\WPF,将名称为“默认”的键值修改为“QTCoreAddin”,再将“ProgID”的键值修改为 “Mercury.AddinBaseObj”
  (3)找到HKEY_LOCAL_MACHINE\SOFTWARE\Mercury Interactive\QuickTest Professional\Add-In Manager\WinForms,将将名称为“默认”的键值修改为“QTCoreAddin”,再将“ProgID”的键值修改为 “Mercury.AddinBaseObj”
  (4)关闭注册表,重启软件,发现.net和wpf插件的状态变为built in状态,经过验证,可以正常使用
  3.破解java插件
  (1)进入注册表,将HKEY_LOCAL_MACHINE\SOFTWARE\Mercury Interactive备份
  (2)找到HKEY_LOCAL_MACHINE\SOFTWARE\Mercury Interactive\QuickTest Professional\Add-In Manager\java,将名称为“默认”的键值修改为“QTCoreAddin”,再将“ProgID”的键值修改为 “Mercury.AddinBaseObj”
  (3)关闭注册表,重启软件,发现java插件的状态变为built in状态,经过验证,可以正常使用
  4.破解webservice插件
  (1)进入注册表,将HKEY_LOCAL_MACHINE\SOFTWARE\Mercury Interactive备份
  (2)找到HKEY_LOCAL_MACHINE\SOFTWARE\Mercury Interactive\QuickTest Professional\Add-In Manager\webservice,将名称为“默认”的键值修改为“QTCoreAddin”,再将“ProgID”的键值修改为 “Mercury.AddinBaseObj”
  (3)关闭注册表,重启软件,发现webservice插件的状态变为built in状态,经过验证,可以正常使用
  QTP11 30天试用期到后的破解新方法
  1. 找到 C:\ProgramData\SafeNet Sentinel 目录,更名或者删除.2. 找到 QTP11 安装目录下bin子目录,如 C:\Program Files (x86)\HP\QuickTest Professional\bin,执行.(win7以管理员权限执行.) 此方法win7 64bit 可行. 以上两点无法破解,个人用的是win 7 家庭普通版,64bit的,但运行instdemo.exe 后,C:\ProgramData\SafeNet Sentinel重新生成Sentinel RMS Development Kit,重启qtp 11还是提示还有30天后过期~!

posted @ 2014-03-27 16:56 顺其自然EVO 阅读(339) | 评论 (0)编辑 收藏

Android游戏开发cocos-2d中精灵的动作测试

  安装好cocos-2d之后,创建一个好一个Android项目,在项目中复制下载好的Cocos-2d包中的cocos2d-master\cocos2d-android\libs中的所有文件,全部粘贴到Android项目中的libs目录下,右击libs中的cocos2d-android.jar,然后builder path操作。这样就创建好了游戏项目。
  以下是一个入门的小程序:
  MainActivity.java代码:
import org.cocos2d.layers.CCScene;
import org.cocos2d.nodes.CCDirector;
import org.cocos2d.opengl.CCGLSurfaceView;
import android.os.Bundle;
import android.app.Activity;
public class MainActivity extends Activity {
//Cocos2d引擎将会把图形绘制到View对象上
private CCGLSurfaceView view = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
view = new CCGLSurfaceView(this);
setContentView(view);
//得到CCDirector对象
CCDirector director = CCDirector.sharedDirector();
//设置游戏的相关属性
//设置当前游戏程序中所使用的view对象
director.attachInView(view);
//设置是否显示FPS值
director.setDisplayFPS(true);
//设置游戏渲染一帧所需要的时间
director.setAnimationInterval(1/30);
//生成一个游戏场景对象
CCScene scene = CCScene.node();
//生成布景对象
GameLayer gameLayer = new GameLayer();
//将布景层对象添加到游戏场景中去
scene.addChild(gameLayer);
//运行游戏场景
director.runWithScene(scene);
}
}
  GameLayer.java代码:
import org.cocos2d.actions.interval.CCBlink;
import org.cocos2d.layers.CCLayer;
import org.cocos2d.nodes.CCSprite;
public class GameLayer extends CCLayer{
//声明一个精灵对象
CCSprite player;
public GameLayer(){
//创建一个精灵对象
player = CCSprite.sprite("player.png");
//将精灵对象添加到布景层当中
this.addChild(player);
//精灵对象位置
player.setPosition(300,300);
//测试闪烁
CCBlink blink = CCBlink.action(3, 60);//第一个参数是时间,第二个参数闪烁的次数
player.runAction(blink);
// //测试缩放指定倍数
// CCScaleTo caleTo = CCScaleTo.action(3, 5, 5);//第一个参数是时间,第二、三个参数分别表示在x轴和Y轴上的缩放倍数
//
// player.runAction(caleTo);
// //测试旋转
// CCRotateTo rotateTo = CCRotateTo.action(3, 180);//第一个参数是旋转所需的时间,
// //第二个是旋转的度数,当度数<=180时,顺时针旋转;>180时,逆时针旋转
// player.runAction(rotateTo);
//
// //测试移动精灵
// CGPoint  point = CGPoint.ccp(500,500);//最后的位置
// CCMoveTo moveTo = CCMoveTo.action(3, point);//第一个参数是精灵移动的时间
// player.runAction(moveTo);
//
// 测试进项旋转
//1.生成动作对象
// CCFlipX flipx = CCFlipX.action(true);
// //2.使用精灵对象去执行动作对象
// player.runAction(flipx);
//
// 测试精灵隐藏
//1.生成动作对象
// CCHide hide = CCHide.action();
// //2.执行
// player.runAction(hide);
}
}

posted @ 2014-03-27 16:55 顺其自然EVO 阅读(430) | 评论 (0)编辑 收藏

面向系统测试的一种ganglia指标扩展的方法

 ganlia 和 nagios 等工具,是业界优秀的监控告警工具;这种工具主要是面向运维的,也可以用来进行性能稳定性的测试
  面对分布式系统测试,耗时都比较长,往往一台机器安装多套系统,影响监控指标的准确性。
  下面是一种进行进程级别监控的方n法,可以通过扩展,集群的监控力度;同时将监控脚本加入告警,防止脚本异常退出(Nagios扩展另文描述)
  GEngin.py:总体的引擎,根据conf下配置文件的配置项,轮询监控指标,调用gmetric广播出去
  conf:目录中保存metrix配置文件,配置参数指标
  flag:目录中仅保存一个flag文件,文件名就是任务名,监控指标将根据任务名分离,便于汇总统计对比
  log: 目录中记录GEngin的log及每个指标收取脚本的log
  pid: GEngin的pid 为告警脚本使用
  script: 指标收集的具体的脚本
  cat conf/metrix.cfg:
YARN|ResourceManager|cpu|ResourceManager_cpu.py|ResourceManager_cpu.txt|int16|Percent|
YARN|ResourceManager|mem|ResourceManager_mem.py|ResourceManager_mem.txt|int16|Percent|
YARN|ResourceManager|lsof|ResourceManager_lsof.py|ResourceManager_lsof.txt|int16|Number|
  ls flag/:
  yarntestD001.flag
  ll log/:
-rw-r--r-- 1 yarn users     168 Mar 19 20:02 yarntestD001_YARNResourceManagercputdw-10-16-19-91.txt
-rw-r--r-- 1 yarn users     168 Mar 19 20:02 yarntestD001_YARNResourceManagerlsoftdw-10-16-19-91.txt
-rw-r--r-- 1 yarn users     168 Mar 19 20:02 yarntestD001_YARNResourceManagermemtdw-10-16-19-91.txt
  ll script/:
-rw-r--r-- 1 yarn users  882 Feb 28 17:20 ResourceManager_cpu.py
-rw-r--r-- 1 yarn users 1093 Feb 28 17:45 ResourceManager_lsof.py
-rw-r--r-- 1 yarn users  882 Feb 28 17:18 ResourceManager_mem.py
  cat script/SAMPLE.py:
#!/usr/bin/env python
# coding=gbk
import sys
import os
import datetime
import time
def CheckInput():
"Check Input parameters , they should be a pysql file."
if len(sys.argv) < 2 :
print "Usage: " + sys.argv[0] + " FileNamePrefix "
sys.exit()
if __name__== '__main__':
CheckInput() # check parameter and asign PyFileName
## result file log to directory of LOG
LogFile = open("log/"+sys.argv[1],'a')
res = "29"
## Interface to Gmetrix ,must be value:Value
print "value:"+res
ntime = str(time.strftime("%Y-%m-%d %X",time.localtime()))
LogFile.write(ntime+" "+res+"\n")
LogFile.close()

  cat GEngin.py :
#!/usr/bin/env python
# coding=gbk
import sys
import os
import random
import datetime
import time
from time import sleep
def CheckInput():
"Check Input parameters , they should be a pysql file."
print "Usage : python ./" + sys.argv[0]
if not os.path.exists("conf/metrix.cfg"):
print "Error : config file conf/metrix.cfg does not exsits ! "
sys.exit()
## kill previous proc For restart
if os.path.exists("pid/pid.txt"):
pfile = open("pid/pid.txt",'r')
for p in pfile:
pid = p.strip()
os.system("kill -9 "+pid)
pfile.close()
os.system("rm pid/pid.txt")
pfile = open("pid/pid.txt",'a')
pid = os.getpid()
pfile.write(str(pid))
pfile.close()
if __name__== '__main__':
CheckInput() # check parameter and asign PyFileName
LogFile = open("log/"+sys.argv[0]+".log",'a')
# File Prefix of logs
filePre="noTask"
for fi in os.listdir("flag"):
if fi.endswith(".flag"):
filePre=fi.split('.')[0].strip()
# host name for gmetrix
host=""
f = os.popen("hostname")
for res in f:
if res.startswith("tdw"):
host=res.strip()
LogFile.write("******** Start task "+filePre+" monitoring *******\n")
# Main Loop untile flag is null
while True:
if len(os.listdir("flag")) < 1 or len(os.listdir("flag")) > 1:
sleep(10)
LogFile.write("Finish previous take "+filePre+"  .... No task ,Main loop .....\n")
LogFile.flush()
continue
if len(os.listdir("flag")) == 1 and not os.path.exists("flag/"+filePre+".flag"):
LogFile.write("Finish previous take "+filePre+".....\n")
for fi in os.listdir("flag"):
if fi.endswith(".flag"):
filePre=fi.split('.')[0].strip()
LogFile.write("***** Start New Task "+filePre+" monitoring *******\n")
# Deal with config metrix one by one
insFile = open("conf/metrix.cfg",'r')
for line in insFile:
mGroup,mName,mItem,mShell,mFile,mUnit,mWeiht,nouse = line.split('|');
outPutFile = filePre+"_"+mGroup+mName+mItem+host+".txt"
value = ""
if mShell.endswith(".py"):
f = os.popen("python script/"+mShell+" "+outPutFile)
for res in f:
if res.startswith("value:"):
value=res.split(':')[1].strip()
else:
value="0"
f.close()
if mShell.endswith(".sh"):
f = os.popen("script/"+mShell+" "+outPutFile)
for res in f:
if res.startswith("value:"):
value=res.split(':')[1].strip()
else:
value="0"
f.close()
cmd = "gmetric -n "+mGroup+"_"+mName+"_"+mItem+" -v "+value+" -t "+mUnit+" -u "+mWeiht+" -S "+host+":"+host
print cmd
f = os.popen(cmd)
ntime = str(time.strftime("%Y-%m-%d %X",time.localtime()))
LogFile.write(ntime+" "+cmd+"\n")
insFile.close()
LogFile.flush()
if len(os.listdir("flag")) == 1 and os.path.exists("flag/"+filePre+".flag"):
sleep(8)
LogFile.close()
  Ganglia 中显示的监控指标:
  将运行的GEngin.py脚本加入监控,防止进程异常退出

posted @ 2014-03-27 16:53 顺其自然EVO 阅读(288) | 评论 (0)编辑 收藏

仅列出标题
共394页: First 上一页 133 134 135 136 137 138 139 140 141 下一页 Last 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜