不应该是家,窝更适合一点

发布新日志

  • oracle服务器导表

    2012-05-10 09:22:56

    现在在青蛙上导出90.22表时,会报错。
    目前通过服务器导出
    192.168.90.22 数据库导出表
    1.oracle帐号进入90.22
    2. 进入bin目录cd /u01/app/oracle/product/11.1.0/db_1/bin/
    3.运行导出命令exp bap2/1@ORCL file=/home/bap2.dmp 
  • Linux mysql 安装配置

    2012-04-11 14:37:58

    安装文档:
    http://cache.baidu.com/c?m=9f65cb4a8c8507ed4fece763104c8f25195697634b9c8d1568d4e419cf790801013db2e570630d5a88993b2d16d84f4b9afa2178475c6bf6d896c80882ace52f38fb50237a17913618c46aaedc4c22d657964d99a50eedcde74595b9a2a5c82427dd24006d8bf69c290a03ca18aa526ebdb2dc1555091fe4b67739b54e7758c06801f519eee1427907f6e1dc2c0c8175c7616080df37a74e12a265a1086b2653a13ca679573140945856ff534d75&p=aa79c64ad5c006f61abd9b7d0e10cb14&user=baidu&fm=sc&query=liunx+mysql%2Erpm+%B0%B2%D7%B0%C4%BF%C2%BC&qid=cfac62f50117cb50&p1=2

    中文乱码:
    http://cache.baidu.com/c?m=9f65cb4a8c8507ed4fece763105392230e54f732668c8c423e83cd13cf2311061b3da7ec67634b4298d27c105df80f41b9b07029665d77f686c98349dcbe852858c97d722f4292044f934fa0940d64827e875a98b86fe3ad863084dfa5c4ae5244cb23120b80e7fd5a1765ca788764&p=836fc20291904eab08e2977e0e5ecc&user=baidu&fm=sc&query=mysql+%D6%D0%CE%C4%C2%D2%C2%EB&qid=d449b4be007fdc0d&p1=9
  • 谈谈关于内存的一些心得体会【转qa.taobao】

    2012-04-02 22:34:17

    最近的项目中发现一些内存泄漏问题,问题也很不好排查,因此自己也动手开发了一个CPU和内存监控工具,同时也学习了很多的相关的内存相关的知识,在此作以分享。

     一些奇怪的现象

    现象1以旺旺为例,打开旺旺主窗口的时候,内存占用大勇50多M,点击右上角的关闭按钮隐藏旺旺到系统托盘图标以后,旺旺占用的内存迅速降低到20多M。
    解释:将程序隐藏到系统托盘后,尽管程序本身可能并没有释放内存的操作,但系统会进行资源优化释放一些不必要的资源。这部分优化过程是系统主动进行的。

    现象2再次显示旺旺主窗口,内存占用明显上升但并没有达到刚刚开始启动旺旺的高度。
    解释:程序在启动的时候需要动态编译并加载一些资源,有很多资源是只有启动的时候才会加载。有些程序为了节约资源,采用了一种投机取巧的方式,在程序启动完成后,先最小化程序,再还原,内存占用会明显减少。

    现象3同样的程序,在XP下运行内存占用为20多M,在WIN7 下内存占用怎么10M都不到,难道WIN7系统需要的内存很少么?
    解释:其实程序在XP下和WIN7下的内存占用是差不多的,唯一的区别是在任务管理器中的显示内容不一样。细心的人可能会发现XP下内存默认显示的是工作集的大小,而在WIN7下默认显示的是专用工作集的大小,关于工作集和专用工作集的介绍可以查看本文后面的名词解释。 

    目前测试内存泄漏并没有发现好的办法,主要通过观察程序占用的内存有无明显的上升,一般来说上升了应该存在一定的内存泄漏,但是如果内存没有上升是否意味一定没有内存泄漏呢,答案是否定的。程序员可以通过一些手段来掩盖内存泄漏的事实。举两个简单的例子:
    1,设置程序的最大工作集来调整内存占用。
    2,定期将物理内存的占用转移到虚拟内存。
    这样尽管物理内存并没有显著的上升,但仍然有可能存在内存泄漏。
    因此在发现程序有可能存在内存泄漏的时候,不光要看物理内存的变化,同时也要关注到虚拟内存的变化。 

    以上是自己对内存的一些新的认识,关于内存泄漏相关的知识网上有很多,这里就不多赘述了。

    一些名词解释:

    PID(进程控制符):
    英文全称为Process Identifier,就是各进程的身份标识,程序一运行系统就会自动分配给进程一个独一无二的PID。进程中止后PID被系统回收,可能会被继续分配给新运行的程序。

    工作集:
    所谓工作集是指进程已映射的物理内存部分(即这些内存块全在物理内存中,并且 CPU 可以直接访问),还有一部分不在工作集中的虚拟内存则可能在转换列表中(CPU 不能通过虚地址访问,需要 Windows 映射之后才能访问),还有一部分则在磁盘上的页面文件里。工作集在进程运行时会被 Windows 自动调整,频繁访问的页面(4KB 的块)会留在内存中,而不频繁访问的页面在内存紧张时会被从工作集中移出,暂时保存在内存中的“转换列表”中,或者进一步换出到页面文件中。当应用程序再次访问某一页面时,操作系统会将它重新加回工作集中。

     专用工作集:
             工作集的子集,它专门描述了某个进程正在使用且无法与其他进程共享的内存数量。

    共享工作集:
             工作集的子集,它专门描述了某个进程可与其他进程共享的内存数量。

    从上面的介绍可以得出:工作集=专用工作集+共享工作集

     硬错误
    内存错误的一种,与之对应的是软错误!硬错误/秒(分钟也可以):指处理器处理错误页的综合速率。用错误页数/秒来计算。当处理器请求一个不在其工作集(在物理内存中的空间)内的代码或数据时出现的页错误。这个计数器包括硬错误(那些需要磁盘访问的)和软错误(在物理内存的其它地方找到的错误页)。

     提交
    就是该进程或程序当前使用的内存数。它可以大于工作集(物理内存),这时系统会生成虚拟内存来解决物理内存不足的问题;当然也可以小于物理内存,这样的话就不用虚拟内存了

     虚拟内存
             当内存耗尽时,电脑就会自动调用硬盘来充当内存,以缓解内存的紧张。这部分用来充当内存使用的硬盘就叫做虚拟内存。因为硬盘的访问速度远远小于物理内存,因此虚拟内存设置过高将影响到程序的执行效率。

  • Mysql 安装配置

    2012-03-27 13:23:00

    安装参照下面的文档即可
    http://wenku.baidu.com/view/d0bd1022aaea998fcc220e64.html
  • 【Linux】vim的用法

    2012-02-08 16:45:11

        在linux下写ns代码以及tcl测试脚本,观察trace文件等都少不了用vim。vim是一个高效的浏览工具。有很多使用技巧,在 此,转载一个用法简介。

    =========================================


    先稍为介绍一下 vim. vi 是 unix/linux 下极为普遍的一种文本编辑器, 大部分机器上都
    有. vi 有各种变种, 在不同的机器上常用不同的变种软件. 其中 vim 比较好用也用得比较
    广泛. vim 是 Vi IMproved 的缩写, 表示更好的 vi. 我个人觉得它是非常好的编辑器(为
    了避免 Emacs 粉丝挑眼, 就不说最好了). 没用过的也建议试试看, 当然 vim 对编写文本
    文件很方便好用, 比如编写程序, html文档等等, 却不能用来编写 word 文档.

    关于 vim 的安装, 基本使用方法等网络上能搜出许多, 就不在这里罗嗦了, 要是对 vim 有
    兴趣, 那就看看这里(中文文档): http://vcd.gro.clinux.org/

    本 文就说些其中比较有用, 比较常用的命令, 若能熟练运用这些命令, 那么会发现编辑文件
    很舒服.

    说明:
    以下的例子中 xxx 表示在命令模式下输入 xxx 并回车
    以下的例子中 :xxx 表示在扩展模式下输入 xxx 并回车
    小括号中的命令表示相关命 令.
    在编辑模式或可视模式下输入的命令会另外注明.

     

     

    1. 查找

      /xxx(?xxx)      表示在整篇文档中搜索匹配xxx的字符串, / 表示向下查找, ? 表示
                      向上查找.其中xxx可以是正规表达式,关于正规式就不多说了.
                      一般来说是区分大小写的, 要想不区分大小写, 那得先输入
                      :set ignorecase
                      查找到以后, 再输入 n 查找下一个匹配处, 输入 N 反方向查找.

      *(#)            当光标停留在某个单词上时, 输入这条命令表示查找与该单词匹配的
                      下(上)一个单词. 同样, 再输入 n 查找下一个匹配处, 输入 N 反方
                      向查找.

      g*(g#)          此命令与上条命令相似, 只不过它不完全匹配光标所在处的单词, 而
                      是匹配包含该单词的所有字符串.

      gd              本命令查找与光标所在单词相匹配的单词, 并将光标停留在文档的非
                      注释段中第一次出现这个单词的地方.

      %               本命令查找与光标所在处相匹配的反括号, 包括 () [] {}

      f(F)x           本命令表示在光标所在行进行查找, 查找光标右(左)方第一个x字符.
                      找到后:
                      输入 ; 表示继续往下找
                      输入 , 表示反方向查找

     

     

    2. 快速移动光标
       在 vi 中, 移动光标和编辑是两件事, 正因为区分开来, 所以可以很方便的进行光标定
    位和编辑. 因此能更快一点移动光标是很有用的.

      w(e)            移动光标到下一个单词.
      b               移动光标到上一个单词.

      0               移动光标到本行最开头.
      ^               移动光标到本行最开头的字符处.
      $               移动光标到本行结尾处.

      H               移动光标到屏幕的首行.
      M               移动光标到屏幕的中间一行.
      L               移动光标到屏幕的尾行.
      gg              移动光标到文档首行.
      G               移动光标到文档尾行.
      c-f             (即 ctrl 键与 f 键一同按下) 本命令即 page down.
      c-b             (即 ctrl 键与 b 键一同按下, 后同) 本命令即 page up.

      ''              此命令相当有用, 它移动光标到上一个标记处, 比如用 gd, * 等查
                      找到某个单词后, 再输入此命令则回到上次停留的位置.

      '.              此命令相当好使, 它移动光标到上一次的修改行.

      `.              此命令相当强大, 它移动光标到上一次的修改点.

     

     

     

    3. 拷贝, 删除与粘贴
       在 vi 中 y 表示拷贝, d 表示删除, p 表示粘贴. 其中拷贝与删除是与光标移动命令
    结合的, 看几个例子就能够明白了.

      yw              表示拷贝从当前光标到光标所在单词结尾的内容.
      dw              表示删除从当前光标到光标所在单词结尾的内容.
      y0              表示拷贝从当前光标到光标所在行首的内容.
      d0              表示删除从当前光标到光标所在行首的内容.
      y$              表示拷贝从当前光标到光标所在行尾的内容.
      d$              表示删除从当前光标到光标所在行尾的内容.
      yfa             表示拷贝从当前光标到光标后面的第一个a字符之间的内容.
      dfa             表示删除从当前光标到光标后面的第一个a字符之间的内容.

      特殊地:
      yy              表示拷贝光标所在行.
      dd              表示删除光标所在行.
      D               表示删除从当前光标到光标所在行尾的内容.

      关于拷贝, 删除和粘贴的复杂用法与寄存器有关, 可以自行查询.

     

     

     

    4. 数字与命令
       在 vi 中数字与命令结合往往表示重复进行此命令, 若在扩展模式的开头出现则表示行
    号定位. 如:

      5fx             表示查找光标后第 5 个 x 字符.

      5w(e)           移动光标到下五个单词.

      5yy             表示拷贝光标以下 5 行.
      5dd             表示删除光标以下 5 行.

      y2fa            表示拷贝从当前光标到光标后面的第二个a字符之间的内容.

      :12,24y         表示拷贝第12行到第24行之间的内容.
      :12,y           表示拷贝第12行到光标所在行之间的内容.
      :,24y           表示拷贝光标所在行到第24行之间的内容. 删除类似.

     

     

     

    5. 快速输入字符
       在 vi 中, 不要求你输入每一个字符, 可以有很多种方法快速输入一些字符.
       使用 linux/unix 的同学一定有一个经验, 在命令行下输入命令时敲入头几个字符再按
    TAB 系统就会自动将剩下的字符补齐, 假如有多个匹配则会打印出来. 这就是著名的命令
    补齐(其实windows中也有文件名补齐功能). vi 中有许多的字符串补齐命令, 非常方便.

      c-p(c-n)        在编辑模式中, 输入几个字符后再输入此命令则 vi 开始向上(下)搜
                      索开头与其匹配的单词并补齐, 不断输入此命令则循环查找. 此命令
                      会在所有在这个 vim 程序中打开的文件中进行匹配.

      c-x-l           在编辑模式中, 此命令快速补齐整行内容, 但是仅在本窗口中出现的
                      文档中进行匹配.

      c-x-f           在编辑模式中, 这个命令表示补齐文件名. 如输入:
                      /usr/local/tom 后再输入此命令则它会自动匹配出:
                      /usr/local/tomcat/

      abbr            即缩写. 这是一个宏操作, 可以在编辑模式中用一个缩写代替另一个
                      字符串. 比如编写java文件的常常输入 System.out.println, 这很
                      是麻烦, 所以应该用缩写来减少敲字. 可以这么做:
                      :abbr sprt System.out.println
                      以后在输入sprt后再输入其他非字母符号, 它就会自动扩展为System.
                      out.println

     

     

     

    6. 替换
       替换是 vi 的强项, 因为可以用正规表达式来匹配字符串.以下提供几个例子.

      :s/aa/bb/g      将光标所在行出现的所有包含 aa 的字符串中的 aa 替换为 bb
      :s/\<aa\>/bb/g  将光标所在行出现的所有 aa 替换为 bb, 仅替换 aa 这个单词
      :%s/aa/bb/g     将文档中出现的所有包含 aa 的字符串中的 aa 替换为 bb
      :12,23s/aa/bb/g 将从12行到23行中出现的所有包含 aa 的字符串中的 aa 替换为 bb
      :12,23s/^/#/    将从12行到23行的行首加入 # 字符
      :%s= *$==       将所有行尾多余的空格删除
      :g/^\s*$/d       将所有不包含字符(空格也不包含)的空行删除.

     

     

     

    7. 多文件编辑
       在一个 vim 程序中打开很多文件进行编辑是挺方便的.

      :sp(:vsp) 文件名    vim 将分割出一个横(纵)向窗口, 并在该窗口中打开新文件.
                          从 vim6.0 开始, 文件名可以是一个目录的名称, 这样, vim 会
                          把该目录打开并显示文件列表, 在文件名上按回车则在本窗口打
                          开该文件, 若输入 O 则在新窗口中打开该文件, 输入 ? 可以看
                          到帮助信息.

      :e 文件名           vim 将在原窗口中打开新的文件, 若旧文件编辑过, 会要求保存.

      c-w-w               vim 分割了好几个窗口怎么办? 输入此命令可以将光标循环定位
                          到各个窗口之中.

      :ls                 此命令查看本 vim 程序已经打开了多少个文件, 在屏幕的最下方
                          会显示出如下数据:
                          1   %a      "usevim.html"         行 162
                          2   #       "xxxxxx.html"         行 0

                          其中:
                          1               表示打开的文件序号, 这个序号很有用处.
                          %a              表示文件代号, % 表示当前编辑的文件,
                                          # 表示上次编辑的文件
                          "usevim.html"   表示文件名.
                          行 162          表示光标位置.

      :b 序号(代号)       此命令将指定序号(代号)的文件在本窗口打开, 其中的序号(代号)
                          就是用 :ls 命令看到的.

      :set diff           此命令用于比较两个文件, 可以用
                          :vsp filename
                          命令打开另一个文件, 然后在每个文件窗口中输入此命令,就能看
                          到效果了.

     

     

     

    8. 宏替换
       vi 不仅可以用 abbr 来替换文字, 也可以进行命令的宏定义. 有些命令输起来很费劲,
    因此我把它们定义到 <F1>-<F12> 上, 这样就很方便了.这些配置可以预先写到 ~/.vimrc
    (windows 下为 $VIM/_vimrc) 中, 写进去的时候不用写前面的冒号.

      :nmap <F2> :nohls<cr>              取消被搜索字串的高亮
      :nmap <F9> <C-W>w                  命令模式下转移光标到不同窗口
      :imap <F9> <ESC><F9>               输入模式下运行<F9>
      :nmap <F12> :%s= *$==<cr>          删除所有行尾多余的空格.
      :imap <F12> <ESC><F12>             同上

      :java 中: (注, 这里为什么说 java 中, 因为以下定义对其他文件格式不起作用, 下文
                会说到如何实现这一点)
      :nmap <F3> :comp javac<CR>:mak -d . %<CR>
           此命令用 javac 编译 java 文件, 它会自动将光标定位到出错点. 不过这需要定
    义一个 javac.vim 文件在 $VIM/compiler 下, 在 javac.vim 里面只有两行字:
              setlocal makeprg=javac
              setlocal errorformat=%A%f:%l:\ %m,%-Z%p^,%-C%.%#

      :nmap <F4> :comp ant<CR>:mak<CR>
           此命令用 ant 编译 java 文件, 它会自动将光标定位到出错点. 一般来说, 安装
           vim 后已经有了compiler/ant.vim文件, 因此这个命令可以直接使用. 但是需要
           在当前目录下有 build.xml 文件, 当然还必须安装 ant 才行.

      :nmap <F5> :cl<CR>                 此命令用于查看所有的编译错误.
      :imap <F5> <ESC><F5>

      :nmap <F6> :cc<CR>                 此命令用于查看当前的编译错误.
      :imap <F6> <ESC><F6>

      :nmap <F7> :cn<CR>                 此命令用于跳到下一个出错位置.
      :imap <F7> <ESC><F7>

      :nmap <F8> :cp<CR>                 此命令用于跳到上一个出错位置.
      :imap <F8> <ESC><F8>

      :nmap <F11> :JavaBrowser<cr>
           此命令用于在窗口左部分割出一个新窗口, 里面的内容是 java 的资源树, 包括
           本文件中出现的类, 类的成员变量及成员方法, 就好像 JCreator 表现的那样.
           在这个窗口中输入 ? 会看到帮助. 嘿嘿, 很好用, 不过需要 ctags 支持.
      :imap <F11> <ESC><F11>

     

     

     

    9. TAB
       TAB 就是制表符, 单独拿出来做一节是因为这个东西确实很有用.

       <<                  输入此命令则光标所在行向左移动一个 tab.
       >>                  输入此命令则光标所在行向右移动一个 tab.
       5>>                 输入此命令则光标后 5 行向右移动一个 tab.
       :12,24>             此命令将12行到14行的数据都向右移动一个 tab.
       :12,24>>            此命令将12行到14行的数据都向右移动两个 tab.

       那么如何定义 tab 的大小呢? 有人愿意使用 8 个空格位, 有人用4个, 有的用2个.
       有的人希望 tab 完全用空格代替, 也有的人希望 tab 就是 tab. 没关系, vim 能
       帮助你.以下的设置一般也都先写入配置文件中, 免得老敲.

       :set shiftwidth=4   设置自动缩进 4 个空格, 当然要设自动缩进先.
       :set sts=4          即设置 softtabstop 为 4. 输入 tab 后就跳了 4 格.
       :set tabstop=4      实际的 tab 即为 4 个空格, 而不是缺省的 8 个.
       :set expandtab      在输入 tab 后, vim 用恰当的空格来填充这个 tab.

     

     

     

    10. autocmd
        这个命令十分的强大, 可以用这个命令实现对不同的文件格式应用不同的配置; 可以
    在新建文件时自动添加上版权声明等等. 这些命令一般定义在 ~/.vimrc 这样的配置文件
    里面. 由于他很强大, 所以我不能给出很具体的说明, 只能举几个例子, 详细的请看帮助.

      :autocmd!               删除所有之前的自动命令.
      autocmd FileType        java  source ~/.vim/files/java.vim
      autocmd FileType        java  source ~/.vim/files/jcommenter.vim
        以上两条命令让我在打开 java 文件时才应用后面提到的两个配置文件.
      autocmd BufNewFile      *.java  0r ~/.vim/files/skeletons/java.skel
        以上这条命令让我在新建 java 文件时自动加入 java.skel 文件的内容.
      autocmd BufNewFile      *.java  normal gnp
        以上这条命令让我在新建 java 文件时自动运行 gnp 命令, 这个命令进行一些特殊化
        处理, 比如将新 java 文件中的 __date__ 替换成今天的日期什么的.

     

     

     

    11. 常用脚本
        在 vim.sf.net 你可以发现很多脚本(script), 这些脚本常常有让你意想不到的作用.
    我常用的有:

        jcommenter.vim        自动加入 javadoc 风格的注释.
        JBrowser.vim          类资源浏览. C, C++ 等可以用 Tlist

        还有许多有用的, 比如 checkstyle.vim 可以检验你的编程风格, jad.vim 可以直接
        反编译 .class 文件等等.

     

     

     

    12. 常用配置
        在~/.vimrc 配置文件中你常常需要一些个性化配置. 比如上面写的一些宏定义, 一些
    autocmd 定义等等. 比如:

        set suffixes=.bak,~,.o,.h,.info,.swp,.aux,.bbl,.blg,.dvi,.lof,.log,.lot,.ps,.toc
            这样在vim中打开文件时, 按 tab 键补齐文件名时它会忽略上述文件.

        set nu          显示行号
        set ai          设置自动缩进
        map Y y$        让 Y 和 D 一样, 要不然 Y 的本意和 yy 一样.

     

     

     

    13. 其他
        还有许多有意思的命令, 记录在这里免得忘记.

        .                                                                     重复上次编辑命令.
        :g/^/exec "s/^/".strpart(line(".")." ", 0, 4)     在行首插入行号。
        :runtime! syntax/2html.vim                            转换 txt 成 html, 会按照你的颜色配置来转换

  • nginx集群[配置参考贴]

    2012-02-06 10:27:34

        今天搭建了个集群,基于nginx服务,下面吧参考帖子转载,以备后后用:
    nginx集群

    http://hi.baidu.com/dianhui/blog/item/2f5e6ecb26f5e619bf09e62a.html

    Nginx 简介

    Nginx ("engine x") 是一个高性能的 HTTP 和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 代理服务器。 Nginx 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,它已经在该站点运行超过两年半了。 Igor 将源代码以类 BSD 许可证的形式发布。尽管还是测试版,但是,Nginx 已经因为它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名了。

    根据最新一期(08 年 6 月份)的 NetCraft 调查报告显示,已经有超过两百万的主机使用了 Nginx,这个数字超过了另外一个轻量级的 HTTP 服务器 lighttpd, 排名第四,并且发展迅速。下面是这份报告的前几名的报表:

    产品网站数
    Apache84,309,103
    IIS60,987,087
    Google GFE10,465,178
    Unknown4,903,174
    nginx2,125,160
    Oversee1,953,848
    lighttpd1,532,952

    关于这期调查报告的更详细信息请看下面链接:

    http://survey.netcraft.com/Reports/200806/

    下图是最近几个月使用 Nginx 和 lighttpd 的网站数比较


    图 1. 最近几个月使用 Nginx 和 lighttpd 的网站数比较
    图 1. 最近几个月使用 Nginx 和 lighttpd 的网站数比较

    使用 Nginx 前必须了解的事项

    1. 目前官方 Nginx 并不支持 Windows,您只能在包括 Linux、UNIX、BSD 系统下安装和使用;
    2. Nginx 本身只是一个 HTTP 和反向代理服务器,它无法像 Apache 一样通过安装各种模块来支持不同的页面脚本,例如 PHP、CGI 等;
    3. Nginx 支持简单的负载均衡和容错;
    4. 支持作为基本 HTTP 服务器的功能,例如日志、压缩、Byte ranges、Chunked responses、SSL、虚拟主机等等,应有尽有。

    在 Linux 下安装 Nginx

    为了确保能在 Nginx 中使用正则表达式进行更灵活的配置,安装之前需要确定系统是否安装有 PCRE(Perl Compatible Regular Expressions)包。您可以到 ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/ 下载最新的 PCRE 源码包,使用下面命令下载编译和安装 PCRE 包:

    # wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-7.7.tar.gz
    # tar zxvf pcre-7.7.tar.gz
    # cd pcre-7.7
    # ./configure
    # make
    # make install

    接下来安装 Nginx,Nginx 一般有两个版本,分别是稳定版和开发版,您可以根据您的目的来选择这两个版本的其中一个,下面是把 Nginx 安装到 /opt/nginx 目录下的详细步骤:

    # wget http://sysoev.ru/nginx/nginx-0.6.31.tar.gz
    # tar zxvf nginx-0.6.31.tar.gz
    # cd nginx-0.6.31
    # ./configure --with-http_stub_status_module –prefix=/opt/nginx
    # make
    # make install

    其中参数 --with-http_stub_status_module 是为了启用 nginx 的 NginxStatus 功能,用来监控 Nginx 的当前状态。

    安装成功后 /opt/nginx 目录下有四个子目录分别是:conf、html、logs、sbin 。其中 Nginx 的配置文件存放于 conf/nginx.conf,Nginx 只有一个程序文件位于 sbin 目录下的 nginx 文件。确保系统的 80 端口没被其他程序占用,运行 sbin/nginx 命令来启动 Nginx,打开浏览器访问此机器的 IP,如果浏览器出现 Welcome to nginx! 则表示 Nginx 已经安装并运行成功。

    常用的 Nginx 参数和控制

    程序运行参数

    Nginx 安装后只有一个程序文件,本身并不提供各种管理程序,它是使用参数和系统信号机制对 Nginx 进程本身进行控制的。 Nginx 的参数包括有如下几个:

    -c <path_to_config>:使用指定的配置文件而不是 conf 目录下的 nginx.conf 。

    -t:测试配置文件是否正确,在运行时需要重新加载配置的时候,此命令非常重要,用来检测所修改的配置文件是否有语法错误。

    -v:显示 nginx 版本号。

    -V:显示 nginx 的版本号以及编译环境信息以及编译时的参数。

    例如我们要测试某个配置文件是否书写正确,我们可以使用以下命令

    sbin/nginx – t – c conf/nginx2.conf

    通过信号对 Nginx 进行控制

    Nginx 支持下表中的信号:

    信号名作用描述
    TERM, INT快速关闭程序,中止当前正在处理的请求
    QUIT处理完当前请求后,关闭程序
    HUP重新加载配置,并开启新的工作进程,关闭就的进程,此操作不会中断请求
    USR1重新打开日志文件,用于切换日志,例如每天生成一个新的日志文件
    USR2平滑升级可执行程序
    WINCH从容关闭工作进程

    有两种方式来通过这些信号去控制 Nginx,第一是通过 logs 目录下的 nginx.pid 查看当前运行的 Nginx 的进程 ID,通过 kill – XXX <pid> 来控制 Nginx,其中 XXX 就是上表中列出的信号名。如果您的系统中只有一个 Nginx 进程,那您也可以通过 killall 命令来完成,例如运行 killall – s HUP nginx 来让 Nginx 重新加载配置。

    配置 Nginx

    先来看一个实际的配置文件:

    user  nobody;# 工作进程的属主
     worker_processes  4;# 工作进程数,一般与 CPU 核数等同
    
     #error_log  logs/error.log; 
     #error_log  logs/error.log  notice; 
     #error_log  logs/error.log  info; 
    
     #pid        logs/nginx.pid; 
    
     events { 
        use epoll;#Linux 下性能最好的 event 模式
        worker_connections  2048;# 每个工作进程允许最大的同时连接数
     } 
    
     http { 
        include       mime.types; 
        default_type  application/octet-stream; 
    
        #log_format  main  '$remote_addr - $remote_user [$time_local] $request ' 
        #                  '"$status" $body_bytes_sent "$http_referer" ' 
        #                  '"$http_user_agent" "$http_x_forwarded_for"'; 
    
        #access_log  off; 
        access_log  logs/access.log;# 日志文件名
    
        sendfile        on; 
        #tcp_nopush     on; 
        tcp_nodelay     on; 
    
        keepalive_timeout  65; 
    
        include   gzip.conf; 
        
        # 集群中的所有后台服务器的配置信息
        upstream tomcats { 
      server 192.168.0.11:8080 weight=10; 
      server 192.168.0.11:8081 weight=10; 
      server 192.168.0.12:8080 weight=10; 
      server 192.168.0.12:8081 weight=10; 
      server 192.168.0.13:8080 weight=10; 
      server 192.168.0.13:8081 weight=10; 
        } 
    
        server { 
            listen       80;#HTTP 的端口
            server_name  localhost; 
    
            charset utf-8; 
    
            #access_log  logs/host.access.log  main; 
    
      location ~ ^/NginxStatus/ { 
         stub_status on; #Nginx 状态监控配置
         access_log off; 
      } 
    
      location ~ ^/(WEB-INF)/ { 
         deny all; 
      } 
     
    
      location ~ \.(htm|html|asp|php|gif|jpg|jpeg|png|bmp|ico|rar|css|js|
      zip|java|jar|txt|flv|swf|mid|doc|ppt|xls|pdf|txt|mp3|wma)$ { 
                 root /opt/webapp; 
         expires 24h; 
            } 
    
            location / { 
         proxy_pass http://tomcats;# 反向代理
         include proxy.conf; 
            } 
    
            error_page 404 /html/404.html; 
    
            # redirect server error pages to the static page /50x.html 
            # 
      error_page 502 503 /html/502.html; 
            error_page 500 504 /50x.html; 
            location = /50x.html { 
                root   html; 
            } 
        } 
     }

    Nginx 监控

    上面是一个实际网站的配置实例,其中灰色文字为配置说明。上述配置中,首先我们定义了一个 location ~ ^/NginxStatus/,这样通过 http://localhost/NginxStatus/ 就可以监控到 Nginx 的运行信息,显示的内容如下:

    Active connections: 70 
    server accepts handled requests
     14553819 14553819 19239266 
    Reading: 0 Writing: 3 Waiting: 67

    NginxStatus 显示的内容意思如下:

    • active connections – 当前 Nginx 正处理的活动连接数。
    • server accepts handled requests -- 总共处理了 14553819 个连接 , 成功创建 14553819 次握手 ( 证明中间没有失败的 ), 总共处理了 19239266 个请求 ( 平均每次握手处理了 1.3 个数据请求 )。
    • reading -- nginx 读取到客户端的 Header 信息数。
    • writing -- nginx 返回给客户端的 Header 信息数。
    • waiting -- 开启 keep-alive 的情况下,这个值等于 active - (reading + writing),意思就是 Nginx 已经处理完正在等候下一次请求指令的驻留连接。

    静态文件处理

    通过正则表达式,我们可让 Nginx 识别出各种静态文件,例如 images 路径下的所有请求可以写为:

    location ~ ^/images/ {
        root /opt/webapp/images;
    }

    而下面的配置则定义了几种文件类型的请求处理方式。

    location ~ \.(htm|html|gif|jpg|jpeg|png|bmp|ico|css|js|txt)$ {
        root /opt/webapp;
        expires 24h;
    }

    对于例如图片、静态 HTML 文件、js 脚本文件和 css 样式文件等,我们希望 Nginx 直接处理并返回给浏览器,这样可以大大的加快网页浏览时的速度。因此对于这类文件我们需要通过 root 指令来指定文件的存放路径,同时因为这类文件并不常修改,通过 expires 指令来控制其在浏览器的缓存,以减少不必要的请求。 expires 指令可以控制 HTTP 应答中的“ Expires ”和“ Cache-Control ”的头标(起到控制页面缓存的作用)。您可以使用例如以下的格式来书写 Expires:

    expires 1 January, 1970, 00:00:01 GMT;
    expires 60s;
    expires 30m;
    expires 24h;
    expires 1d;
    expires max;
    expires off;

    动态页面请求处理

    Nginx 本身并不支持现在流行的 JSP、ASP、PHP、PERL 等动态页面,但是它可以通过反向代理将请求发送到后端的服务器,例如 Tomcat、Apache、IIS 等来完成动态页面的请求处理。前面的配置示例中,我们首先定义了由 Nginx 直接处理的一些静态文件请求后,其他所有的请求通过 proxy_pass 指令传送给后端的服务器(在上述例子中是 Tomcat)。最简单的 proxy_pass 用法如下:

    location / {
        proxy_pass        http://localhost:8080;
        proxy_set_header  X-Real-IP  $remote_addr;
    }

    这里我们没有使用到集群,而是将请求直接送到运行在 8080 端口的 Tomcat 服务上来完成类似 JSP 和 Servlet 的请求处理。

    当页面的访问量非常大的时候,往往需要多个应用服务器来共同承担动态页面的执行操作,这时我们就需要使用集群的架构。 Nginx 通过 upstream 指令来定义一个服务器的集群,最前面那个完整的例子中我们定义了一个名为 tomcats 的集群,这个集群中包括了三台服务器共 6 个 Tomcat 服务。而 proxy_pass 指令的写法变成了:

    location / {
        proxy_pass        http://tomcats;
        proxy_set_header  X-Real-IP  $remote_addr;
    }

    在 Nginx 的集群配置中,Nginx 使用最简单的平均分配规则给集群中的每个节点分配请求。一旦某个节点失效时,或者重新起效时,Nginx 都会非常及时的处理状态的变化,以保证不会影响到用户的访问。

    总结

    尽管整个程序包只有五百多 K,但麻雀虽小、五脏俱全。 Nginx 官方提供的各种功能模块应有尽有,结合这些模块可以完整各种各样的配置要求,例如:压缩、防盗链、集群、FastCGI、流媒体服务器、Memcached 支持、URL 重写等等,更关键的是 Nginx 拥有 Apache 和其他 HTTP 服务器无法比拟的高性能。您甚至可以在不改变原有网站的架构上,通过在前端引入 Nginx 来提升网站的访问速度。

    本文只是简单介绍了 Nginx 的安装以及常见的基本的配置和使用,更多关于 Nginx 的信息请阅读文章后面的参考资源。在这里要非常感谢我的朋友——陈磊(chanix@msn.com),他一直在做 Nginx 的中文 WIKI(http://wiki.codemongers.com/NginxChs),同时也是他介绍给我这么好的一款软件。

    如果您的网站是运行在 Linux 下,如果您并没有使用一些非常复杂的而且确定 Nginx 无法完成的功能,那您应该试试 Nginx 。

  • 拜年贴

    2012-01-24 09:36:46

        大年初二拜年喽,水生哥哥祝大家在新的一年里万事如意,心想事成,事业顺利,合家欢乐!!
  • Linux 硬件信息查看的命令

    2011-12-29 11:34:19

    Linux

    内存: cat /proc/meminfo
    cpu:  cat /proc/cpuinfo
    硬盘: df -h -T(no device的设备看不到)
          fdisk -l可以在redhat9.0中看; redhat企业版不行.
    还有几个命令可以看很多硬件设备的信息.
    lspci -v
    dmidecode 
    dmesg


    Solaris
    网卡速度:  dmesg |grep link 
    CPU频率:   psrinfo -v                    或者mpstat
    cpu\内存等:prtdiag -v (大部分信息全部列出)  需要先进入/usr/platform/sun4u/sbin
    硬盘:      iostat -E
  • 测试申请提交操作手册

    2011-12-21 13:46:56

    测试申请提交手册

    一、 

    针对目前测试申请提交过程中存在:

    Ø 装包错误率高达90%以上;

    Ø 文件遗漏,错误问题时有发生;

    Ø 提交周期比较长;

    Ø 驳回频繁;

    Ø 总体质量不高。

    等若干问题,经过跟部分开发人员,项目经理和开发经理讨论,确定由测试这边制订一份《测试申请提交手册》。

    二、 

    通过本操作手册的完善和补充,达到:

    Ø 降低测试装包错误率,努力控制在0%

    Ø 避免文件,存储过程等遗漏;

    Ø 变更及时通知;

    Ø 提高工作效率和补丁包总体质量。

    三、 

    1checklist约束

    基于之前checklist基础,持续完善checklist检查项内容,下面是最新修改的完善的内容:

    ===============================================================
    --开发人员确认--
                           检查要求                                      检查结果             
    -----------------------------------------------------------------------------------------------------------------------------


    1必须在装包环境通过装包验证                      |                    ??                
    2、必填项、唯一性等检测没问题                      |                    ??                
    3、字符长度、类型、名称符合设计,并与数据库表一致  |                    ??                
    4、页面查看源文件不报错                            |                    ??                  5、删除、启用、停用时提示是否正确                  |                    ??                
    6、所有的显示列表、PT页面必须提供导出             |                    ??                
    7、物品、客户、供应商、人员、部门选择必须提供助记码|                    ??                
    9、数字、日期、电话、邮箱等符合约定格式            |                    ??                

    10特殊的操作和配置等是否在测试申请中注明         |                    ??                

    11......                                            |                   ??                

    -----------------------------------------------------------------------------------------------------------------------------

    --项目负责人/设计师确认--
                           检查要求                                      检查结果             1、功能、业务流程符合设计,无报错                  |                    ??                
    2、数据回填等控制点是否完整                        |                    ??                
    3、数据表字段是否符合设计                          |                    ??                4、提示信息是否准确                                |                    ??                5、打包是否符合规范                                |                    ??                

    -----------------------------------------------------------------------------------------------------------------------------

    --UI设计师确认--
                           检查要求                                      检查结果 

    ===============================================================

    2、常见错误总结

    目前归纳记录下的装包错误也就是以下几种,通过记录平日常见错误,以避免类似的错误再犯。

    错误1文件、存储过程漏打入包中

    解决:a、装包环境装包  b、自己或者设计师检查确认 c、装包后功能验证 d...

    错误2存储过程带前缀:比如,solu201."Base_StatHotorder"

    解决:a、自己或者设计师检查确认 b、平日养成习惯  c.....

    错误3装包不成功

    解决:a一般是资源文件,result,存储过程安装不成功,通过查看数据库和菜单可以确认

    错误4装包乱码

    解决:a反复编码转换即可  b、找有经验的人,比如:king.bugDT等 c....

    错误5patch.xml文件写错

    解决:a参照已测试通过的patch文件  b、通过装包确认.

    错误6所有文件不是一次提交上来的,可能第一次装包没有问题,后续做了修改,导致出现问题

    解决:a尽量做到一次提交,避免提交后做大量修改或变更  b、最后的内容也要通过装包确认. C....

    错误7环境因素,环境,数据不一致不同造成的

    解决:a装包尽量在接近正式环境下验证  b、如果有该因素,最好在测试申请中提出来. C....

    错误8同一个包,包含多个人的内容

    解决:a推选包负责人  b、借鉴XGSCRUM小组的做法(目前他们做的很好). C....

    3、建议和要求

    1、目前部分审批流程只是走一个形式,建议相关审批人员起到检查和督促作用。

    Ps:关于测试申请走流程,希望开发人员发表自己的看法

    2、前期没有任何参与或了解的情况下接到测试申请。这样的测试申请测试组按规定是可以拒测

    3、提交测试时附属文档必须都是最新的

    4、每次文件,内容变更在bugfree中标注

    5、驳回的测试申请请重新走严格审批流程

    6、及时完善checklist内容

    7、为了便于查找和补充完善既定的规范标准、手册,建议建立专门svn目录(比如,《测试申请提交手册》,《bug解决处理手册》,《日常问题解决大全》等),所有新员工必须当作必修内容学习

    8、对新入职员工做集中培训


  • 真正的换位思考:我做测试人员的一天【zt】

    2011-12-21 13:32:39

    真正的换位思考:我做测试人员的一天

    字体:        | 上一篇 下一篇 | 打印  | 我要投稿  | 推荐标签: 软件测试 软件开发

      昨天一个偶然的机会,临时充当了下软件测试的角色,体会很深!也让我对软件测试人员有了更多的了解和理解,并对自己(开发者)也提高了要求。

      开发、测试不分家,心在一起,劲往一处用就可以大大改变产品的质量。这个道理大家谁都明白,但是我们是做到了60分,还是90分呢?这是值得我们深思的!

      情景1:

      我拿到一个产品,让我测测,我首先根据自己的理解来测试这个产品,保证链接的正确,数据不敢保证,随着测试的逐渐深入,我发现,有些功能根本不知道是做什么用的。

      感想:一个产品如果没有需求文档,就算有使用文档,那又有何意义呢?

      情景2:

      我找到了需求文档,对里面的内容进行查看,多多少少还是帮助了我理解了这个产品的功能,但是好多页面展现,无从考究!例如一些展现的关系图,是根据什么绘出来的?箭头代表什么?等等等等!总之给我的感觉就是功能依照使用手册会用了,但是不知道是用来做什么的!顿时你会想到,这些都是什么做什么用的?为什么文档没有写清楚?难道还要我逐条写下来,依依的问?

      感想:情景2做到了60分,因为有了需求文档,但是这份需求文档,有跟没有差别不大,主要是解决了有、无问题。对于根本性的问题还是没有解决。

      这时候想想我们自己做开发的时候,是不是以为这个很简单,我们组内都清楚,就没有写在需求里呢?现在想想,开发觉得自己明白的东西,往往别人是不明白的!看来以后写文档的时候,我们需要转换一个角度来写这个文档。逐渐达到70分!

      情景3:

      找到开发问了这个功能,开发简述一遍,是简述哦!大体知道这个功能是干啥的了,也能看到展现,但是展现的对错与否,谁又知道呢?

      感想:测试点很重要,我们的展现对错,是要考虑测试人员对这个系统的理解,如果我们能够在文档中,写清楚测试点,那么测试人员就会更有依据来判断展现的内容是否正确了,如果做到这点,我相信应该可以打80分了!

      情景4:

      随着时间的推移,我烦躁了起来,感觉能用,不知道为什么用,每个功能都可用,但是如何串联在一起呢?看了产品的SPD,使用手册,没有对各个模块串联着用的一个概览,这让我和恼火,我当时心里只有一个想法,这咋测?没有需求评审吗?双方之前没沟通吗?

      后来得知沟通过,需求是因为项目多,开发没时间,只是简略的说做了什么,没有说做到什么程度,什么效果!

      感想:看来对需求的沟通事值得我们深思的,开会了、沟通了,依然没有解决问题。所以我觉得只要是会议,一定要双方达成一致,且一定要形成结论,不然那就是扯淡!话糙理不糙!就是这么个道理!

      本以为这一天的时间是浪费了。其实不然,通过这一天的实际换位,真正的达到了换位思考的效果,理解了测试人员的苦衷,也了解了一些作为测试人员最需要的是什么样的文档,需要的是开发怎么的配合。同时自己身为开发人员的我,觉得在今后的开发中,一定要落实文档,积极配合测试人员!当然开发人员也有苦衷!但是我们需要先从自身找原因,解决掉自身问题,也许很多难题会迎刃而解也说不定呢?

  • 无线破解

    2011-11-22 10:49:10

    http://wenku.baidu.com/view/af4ca52d7375a417866f8f7f.html
  • JMeter技巧集锦

    2011-09-21 11:24:01

    收藏些介绍JMeter使用知识的文章

    1.JMeter技巧集锦

    http://www.javaworld.com/javaworld/jw-07-2005/jw-0711-jmeter.html

    (网上该篇的中文译文)

    2.JMeter高级技巧

    http://www.informit.com/guides/content.aspx?g=java&seqNum=520

    3.使用 JMeter 完成常用的压力测试

    http://www.ibm.com/developerworks/cn/opensource/os-pressiontest/index.html?ca=drs-
  • 运用系统思考,走上改善之路

    2011-08-29 10:06:23

    为什么敏捷实施,或是任何一点的过程改进都步履维艰?即使是十几人的团队中,也会出现“写自动化测试”──“不写自动化测试”──“写自动化测试”──“不写自动化测试”这种循环往复的过程?

    除了人们常常总结的“敏捷实施模式”,或是“敏捷失败经验分享”这样的具体话题之外,是不是还有一些存在于思维模式中的更加根本性的因素,阻碍了我们对系统全景的认知,从而导致改革先行者的黯然退场?

    本文将通过两个案例来讲述如何使用系统思考,从全局掌握我们所处的复杂环境,做到既见树木,又见森林。

    案例1:舍本逐末

    有一个测试团队的负责人找到我们说,我觉得现在的自动化测试问题很大,执行时间长,也不稳定,有的时候是测试写错了,也要花很长时间修。我打算组织一批人,重新设计一下测试代码的架构,把常用的底层功能封装成设计良好的API

    我的同事说,好啊,你们打算怎么做呢?

    他说,我也还没想好,所以想过来商量一下。我希望这个东西做成以后,能够让不会写程序的QA们都能用它来写自动化测试脚本了,他们现在就是这样,又要做测试,又要学着写程序,我觉得太辛苦了。能让他们不用学编程就能写测试脚本就好了。

    ……要不我们先看一下现在的代码,了解一下都有什么问题,然后再讨论?我们内心有点小小的纠结。

    好吧,我来给你们开通访问权限,找人给你们讲代码。他很爽快的答应了。

    打开代码之后我们就忍不住风中凌乱了,满屏都是重复的代码片段,让人一阵阵的眩晕。两天之内,我们仅仅用了提取方法这一个重构手法,就删掉了 1200行代码。期间还发现,不知道谁在调试的时候把一处代码从等待3秒改成了等待10秒忘了改回来,于是其他人再复制粘贴的时候,就全变成了等待10 秒。

    于是事情就明朗化了。

    依赖于一小拨人重新设计代码结构,提炼API,确实会在短期内使问题得到缓解。但使用这些API的人依然是那些不懂得如何编程的QA,他们依然会使 用复制粘贴来解决问题。再好的架构、再优秀的设计,最终还是会淹没在大量重复的代码中,犹如黄金深埋浮沙之下。而且如果问题表象得到暂时解决,人们就会缺 少动力从根本上提升QA的编码能力,随着设计一点一点腐化,就又需要精兵强将充当救火队员。如此不断反复,直到有一天又回到重新设计乃至重写的老路上来。

    这是个典型的舍本逐末的模型。

    分析

    在上面的场景中,对于测试代码质量低劣这个问题有两种解决方案,一种是精兵强将解决,另一种是测试人员自己解决,每一种方案都会削弱代码质量不断下滑的趋势,从而让系统处于一种平衡状态。如下图所示:

    图中的“S”表示同向连接,即箭头起点所示变量的增长会导致另一方变量的增长;“O”表示反向连接,即箭头起点变量的增长会导致另一方的减少。天平表示调节环路,该回路会趋于平衡稳定。

    但是,由精兵强将出马可以让问题症状迅速得到缓解,提升测试人员的编码能力则需要长期的辅导训练,不可能一蹴而就,所以图中下方的调节环路实际上是有时间滞延存在的:

    与此同时,由精兵强将解决问题会减少测试人员锻炼的机会,从而削弱测试人员的编码能力,进一步使人们不倾向于让测试人员自己解决问题,又转过头来增强了对于精兵强将的依赖。所以还要在图中增加另外一条回路:

    滚雪球表示增强环路,该回路会使得回路上的所有节点持续增强

    这幅图的全貌便构成了舍本逐末的模型。彼得·圣吉在《第五项修炼》中对此解释到:

    上面的环路代表快速见效的症状解,它迅速解决问题症状,但只是暂时的。下面的环路包含了时间延滞,它代表较根本的解决方案,但其效果要较长的时间才 会显现出来。然而它可能是惟一持久见效的方式。有时候舍本逐末的结构中,会多出一个由症状解所带来的副作用所形成的增强环路。发生这样的情形时,副作用常 使问题更难以解决。

    人们或者是因为没有找到问题的根源,或者是因为时间延滞的存在,倾向于采取一种简单易行又可以立竿见影的方案,这便是症状解了。但是症状消除以后, 问题就不再令人重视,从而丧失了从根本上解决问题的能力。而问题依然深藏,等到它有一天再度浮上水面时,症状就会更重,更难解决。

    这是一条不断衰减的增强环路。在回路上每走一步,情况就会更恶劣一分。

    面对舍本逐末,通常的解决方案有两条:

    必须要认识到症状解只是短期止痛的手段,切不能形成依赖;

    在症状得到缓解之后,要继续加强对根本解的重视。

    但见招拆招总是相对容易一些的,更关键的地方是,如何才能识别出舍本逐末这样的模型?当我们采取某些理所当然的对策却得到了不合理的结果,有什么办法可以帮助我们分析问题根源,找到解决方案?

    要认清问题的本质,就必须要认识到,我们所处的是各个因素之间紧密连接相互影响的复杂系统,当前采取的行动,会从多方面对这个系统产生影响,而这些影响之间或推波助澜,或相互牵制,从而导致相同的行动在长期和短期来看具有不同的结果。

    比如,当代码中发现bug的时候,很多人的常见反应都是调试──定位──解决这样的思路。从眼前来看,发现bug立即修复是可以最快见效的手 段,但却丧失了将测试进行完善的机会,相当于是安全网上明明出现了漏洞却听之任之。等到以后因为需求变更等原因影响了这块代码的时候,就再也无法通过执行 测试来得到快速而完整的反馈了。

    丹尼斯·舍伍德在《系统思考》中说,

    如果你希望了解一个系统,并进而能够预测它的行为,那么,就非常有必要将系统作为一个整体来研究。将系统各部分割裂开来研究,很可能破坏系统内部的连接,从而破坏系统本身。

    如果你希望影响或控制系统的行为,你必须将系统作为一个整体来采取行动。在某些地方采取行动并希望其他地方不受影响的想法注定要失败──这也就是连接的意义所在。

    为了帮助人们更好的从整体上研究复杂系统的行为,丹尼斯在书中提供了一套完整的工具──系统循环图。

    系统循环图中共有三个基本要素:

    增强环路。在增强环路中共有偶数个O型连接,增强环路上的各个节点会呈现指数增长或指数衰退。

    调节环路。在调节环路上共有奇数个O型连接,调节环路上的各个节点会趋于平衡状态。它会消化掉外界的影响力,使改变难以发生。

    时间滞延。时间滞延是一个不容忽视的影响力,由于滞延的存在,人们常常会发现某个行为在短期内没有产生预想的结果,从而加大投入力度,当行为的后果出现在眼前时,却已经矫枉过正。

    下面我将通过另一个真实的案例来讲述系统循环图的应用。

    案例2:历史不断重演

    一天中午,我忽然听到有人说,我们又开始讨论要不要放弃自动化功能测试了。

    咦?你为什么要说又呢?我忍不住问道。

    因为我们半年前已经讨论过一次,当时得出的结论是不再写自动化测试了。后来不记得是什么原因了,又用Cucumber来写,最近发现每次上线还是要做大量的手工测试,这些自动化测试又要浪费很多时间来修,所以我们准备讨论一下到底还要不要继续写。

    当天下午,我参加了这个团队的讨论,终于弄清楚了事情的来龙去脉:

    大概是一年前,为了减少手工测试的成本,团队决定一步步把上线时需要手工回归的测试用例转换成自动化,同时决定每个story做完以后都要加入自动化测试。研究了几天webdriver以后,就开始了自动化测试的尝试。

    但麻烦很快就出现了。第一,开发人员用Java代码写的测试,QA不好理解,也不是很清楚哪些场景被测试覆盖到,哪些没有,所以无法信赖测试结果; 第二,测试跟开发共用一套数据库,数据总是受到污染,因此造成测试失败,浪费大量定位和修复的时间。而数据库是由国外的客户控制的,催促了很长时间也没能 给测试分离出一套专用的数据库来。

    测试红啊红,修啊修,后来一算时间,在自动化测试上投入了120多人天,却依然得不到一套稳定执行的测试用例,不但没办法保证交付质量,还让每个人心力交瘁。于是毅然决然的停掉了。

    过了两三个月,客户终于准备好了一套专门用来跑功能测试的数据库,开发团队也对行为驱动开发有了一定的认识。于是又开始写自动化测试,这次用了CucumberQA写场景,Dev写实现。

    写了大概有100多个场景的测试,又有人开始质疑这一切:第一,整套系统实在是太庞大复杂了,写到现在为止,连1/4都没覆盖到,所以上线还是手工 回归。我们到底要花多大的精力才能把所有的场景都自动化起来,这些投入是不是值得?第二,测试环境还是不稳定,比如本地数据跟CI用的数据不一致,比如 Tomcat里面部署的应用常常启动不起来,等等。每个问题都要消耗大量的人力。这些浪费能不能平衡自动化测试到最后能够带来的收益?

    但团队中还有其他人对自动化测试持有乐观态度,认为问题总是可以解决的,只要坚持不懈就能够看到长期的回报。于是就有了这次讨论。

    分析

    绘制系统循环图的第二条法则是:从有趣的地方开始。在这个案例的场景下,开发团队最关心的是该不该写自动化测试,它对交付能力会带来什么影响,于是我们选择自动化测试的数量作为起始点。

    接下来是第三条法则──“询问它将驱动什么,以及它的驱动力是什么’”

    我们首先可以想到的是,自动化测试的数量增加,会缩短发现Bug的周期,但是这个作用是需要测试数量积累到一定程度才会发挥出来的。它同时还会增加测试的开发和维护成本,增加测试执行时间,缩短手工测试周期。见下图:

    手工测试周期的缩短会带来交付周期的缩短,提升产品收益,从而使人们更倾向于编写自动化测试。于是在图中就出现了一个增强环路:

    而测试的开发维护成本增加,会导致开发进度放缓,从而削弱收益,于是在图的下方出现了调节环路,这条调节环路的存在,会阻碍人们在自动化测试上的投入。

    与此类推,我们同样在图中可以发现其他的增强环路与调节环路:

    而在这四条回路之外,还会有其他因素对这个结构造成影响:

    画出系统循环图以后,就可以结合团队的状况进行整体分析:

    首先回到质疑的声音上来,有人说,整套系统实在是太庞大复杂了,写到现在为止,连1/4都没覆盖到,所以上线还是手工回归,这里反映出的正是从 自动化测试数量手工测试时间这条线上的时间滞延的效果。在前文中提到过,时间滞延在反馈环路中会造成矫枉过正,这里是它的第二个作用──给人带 来挫败感。它会导致某个行为在短期内看不到任何效果,当滞延的时间过长时,会令人失望乃至放弃努力。消除时间滞延可以对系统起到卓有成效的改善。在这个案 例中,我们可以通过推动手动测试用例向自动化测试的转化来缩短滞延。

    然而,当时间滞延的作用被削弱以后,还有另外的问题等着去解决。下面再来看看这支团队从写自动化测试不写自动化测试的变化过程中发生了什么。

    在刚开始写自动化测试的时候,团队主要的感受是QA少了手工测试的时间,质量多了保障,所以增强环路发挥了作用,每个story完成以后,开发人员 都会为所对应的场景写几个测试。但当测试数量增加到一定程度,调节环路的反馈力量开始占据主导地位──测试时间变长、维护成本增加。而且测试数量越多,带 来的问题就越大,最后便有了第一次的选择:放弃自动化测试。

    这正是《第五项修炼》中描述的成长上限模型:

    增强环路导致成长。成长总会碰到各种限制与瓶颈,然而大多数的成长之所以停止,却不是因为达到了真正的极限。这是由于,增强环路固然产生快速的成长。却常在不知不觉中,触动一个抑制成长的调节环路开始运作,而使成长减缓、停顿,甚或下滑。

    ……

    当改善的速度慢下来,你会更加努力地去改善。但渐渐的,你愈是用力推动你所熟悉的做法,调节环路的反作用愈是强烈,使你的努力愈是徒劳无功。到了最后,最常有的反应是放弃他们原来的目标。

    所以,当我们观察到有增强环路与调节环路遭遇的情况出现时,更为有效的解决方案应该到调节环路上寻找。在上面的系统循环图中,测试环境稳定性、 开发人员技能可以限制测试的开发维护成本硬件数量与质量可以限制测试运行时间,我们可以通过控制这些在调节环路之外起作用的因素,削弱 调节环路的影响,让增强环路的成长继续开始。

    小结

    结构决定行为。增强环路总是产生指数级的上升或衰减;调节环路总是倾向于使整个环路趋于平衡状态;各种环路的相互影响,就会产生舍本逐末成长上限或是饮鸩止渴等基本模型。

    然而不幸的是,参与系统的各个部分,常常见树不见林,只能针对眼中见到的局部信息,做出局部的最佳决策。但每个人的局部最佳决策,并不能构成整个系统的全局最佳决策。因为系统中有反馈、有延迟,因和果在时空上并不紧紧相连,显而易见的解往往无效。

    在纷乱芜杂的因果关系中,使用系统思考可以帮助我们理清问题的脉络,认识系统全貌,从而进一步寻找有效的杠杆解──比如案例2中的限制调节环路;而不是固囿于局部优化,把环状的因果关系割裂成线段,看不到当前采取的行动会在未来反作用于自身,因此导致各种系统问题的出现。

    系统循环图是一个很强大的工具,但一个人的视野总是有限的,用头脑风暴的方式往往可以得到更全面的认知。案例2的图形就是小组讨论的结果。只是这种方式需要引导者时时注意控制讨论的边界,不要偏离重心。

    本文通过两个实际案例对系统思考的基本概念进行了描述,并讲解了如何使用系统循环图对一个复杂系统进行分析,对这方面知识感兴趣的读者可以通过《系统思考》和《第五项修炼》这两本书进行深入研究,学会用这套工具来指导持续改进的步伐。


  • @ManyToOne 

    2011-08-02 13:11:35

    @ManyToOne注解的这端,是多端

    1.在注释@ManyToOne(cascade=CascadeType.REFRESH,optional=true)中将属性optional设置为true,这可以使得即使外键为空时仍可以向表中添加数据。

    2.假设Person和Book是一对多的关系,其中Person的成员变量为:

    private Integer personId;

    private String name;

    private Short age;

    private List<Book> bookList=new ArrayList<Book>();

    对应在MySql中表的结构为:Person(personId,name,age),不必有bookList一项,在getBookList上方通过注释: @OneToMany(mappedBy="person",cascade=CascadeType.ALL)

    @OrderBy(value="bookId ASC")

    与Book关系形成一对多的关系。Book的成员变量是:

    private int bookId;

    private String title;

    private double price;

    private Person person;

    对应在MySql中表的结构为:Book(bookId,title,price,personId),注意要有Person表的主键personId,这样在插入记录时才不会产生异常。在getPerson上方有注释:

    @ManyToOne(cascade=CascadeType.REFRESH,optional=true)

    @JoinColumn(name="personId")

    与@OneToMany形成呼应。

    在EJB3.0 规范中 多对一与一对多的双向关系, 多对一(就是@ManyToOne注解的这端,是多端哦不要搞混了)这端总是双向关联端的主题(owner)端, 而一对多端的关联注解为 @OneToMany(mappedBy=" " )其值是:多对一端的属性

    demo:

    被动方:其实也就是一方 或者说(OneToMany方)

    @Entity
    public class Customer extends AbstractEntity {

    private String name;

    @OneToMany(mappedBy="customer",cascade=CascadeType.ALL)
    private Set<Order> orders;

    public void addOrder(Order order){
    if(orders == null){
    rders = new HashSet<Order>();
    }
    orders.add(order);
    }

    public String getName() {
    return name;
    }
    public void setName(String name) {
    this.name = name;
    }
    public Set<Order> getOrders() {
    return orders;
    }
    public void setOrders(Set<Order> orders) {
    this.orders = orders;
    }

    }

    主动方:1.关系的维护方2.ManyToOne方3.多方

    @Entity
    @Table(name="orders")
    public class Order extends AbstractEntity {

    private String name;

    @ManyToOne(cascade=CascadeType.ALL)
    private Customer customer;

    public Customer getCustomer() {
    return customer;
    }

    public void setCustomer(Customer customer) {
    this.customer = customer;
    }

    public String getName() {
    return name;
    }

    public void setName(String name) {
    this.name = name;
    }
    }

    以上是实体

    下面是测试用列哦

    public void testCRUD() {
    // 第一种情况: 调用的被动方的Dao 绑定主动方关系,但主动方没有绑定被动方
    Customer entity = new Customer();
    entity.setName("customer1");
    Order rder = new Order();
    order.setName("order1");
    entity.addOrder(order);
    entity = customerDao.create(entity); // 这种情况下 orders.customer_id == null
    //控制台的信息
    //Hibernate: insert into Customer (name, id) values (?, ?)
    //这里的customer_id 为null
    //Hibernate: insert into orders (name, customer_id, id) values (?, ?, ?)
    System.out.println("entity id = " + entity.getId());
    System.out.println("order id = "+ order.getId());
    System.out.println("1111********************** over");

    // 第二种情况: 调用的被动方的Dao 绑定主动方关系,并且主动方也绑定被动方
    entity = new Customer();
    entity.setName("customer2");
    rder = new Order();
    order.setName("order2");
    entity.addOrder(order);
    order.setCustomer(entity); //这里进行双向关联
    entity = customerDao.create(entity);
    //
    //Hibernate: insert into Customer (name, id) values (?, ?)
    //这里的customer_id 有值哦
    //Hibernate: insert into orders (name, customer_id, id) values (?, ?, ?)
    System.out.println("entity id = " + entity.getId());
    System.out.println("order id = "+ order.getId());
    System.out.println("2222********************** over");
    // 第三种情况: 调用的主动方的Dao 绑定被动方关系,但是被东方不绑定主动方
    entity = new Customer();
    entity.setName("customer3");
    rder = new Order();
    order.setName("order3");
    order.setCustomer(entity); //绑定被动方
    orderDao.create(order);
    //Hibernate: insert into Customer (name, id) values (?, ?)
    //Hibernate: insert into orders (name, customer_id, id) values (?, ?, ?)
    System.out.println("entity id = " + entity.getId());
    System.out.println("order id = "+ order.getId());
    System.out.println("3333********************** over");

    // 第四种情况: 调用的主动方的Dao 绑定被动方关系,并且被东方也绑定主动方
    entity = new Customer();
    entity.setName("customer4");
    rder = new Order();
    order.setName("order4");
    order.setCustomer(entity); //绑定被动方
    orderDao.create(order);
    System.out.println("entity id = " + entity.getId());
    System.out.println("order id = "+ order.getId());
    System.out.println("4444********************** over");
    //Hibernate: insert into Customer (name, id) values (?, ?)
    //Hibernate: insert into orders (name, customer_id, id) values (?, ?, ?)

    //总结:经测验二三四种方法结果都是一样都能持久化到数据库,并且关系也建立好了
    // 也就说只要主动方绑定了被动方关系就维护好了
    }

    *****************************************做级联删除测试************************************************************

    public void testCascade(){

    //1. 做个级联删除吧 测试删除主动方是否删除关联方
    // 这里会级联删除主动方的关联对象,以及该关联对象(被动方)所关联的所有主动方都会被级联删除
    orderDao.delete("order_id_1");
    //Hibernate: delete from orders where id=?
    // Hibernate: delete from orders where id=?
    // Hibernate: delete from orders where id=?
    // Hibernate: delete from orders where id=?
    // Hibernate: delete from orders where id=?
    // Hibernate: delete from Customer where id=?
    assertNull( orderDao.findById("orderDao"));

    //2. 做个级联删除吧 测试删除被动方是否删除关联方
    //删除该被动方,以及所关联的所有主动方(维护关系方or多方)与上面的调用主动方的结果一样
    //Hibernate: delete from orders where id=?
    // Hibernate: delete from orders where id=?
    // Hibernate: delete from orders where id=?
    // Hibernate: delete from orders where id=?
    // Hibernate: delete from orders where id=?
    // Hibernate: delete from Customer where id=?
    customerDao.delete("2");
    assertNull( customerDao.findById("2"));

    }

  • selenium 2和watir webdriver中如何在启动浏览器时保留cookie【转】

    2011-07-25 08:54:19

    默认情况下使用selenium或者waitr webdriver启动浏览器,浏览器中保存的cookie是不可访问的。

    为什么要保留浏览器中的cookie呢?这是因为有些站点登陆时候有验证码或动态密码验证。一般情况下selenium或watir webdriver是无法获取动态密码或验证码自动完成登陆的。

    那么使用动态密码或验证码的网站我们就无法自动化测试了么?答案是不一定,因为有些网站登陆后会在cookie中写入当前用户的登录态,于是只要 登陆一次该站点,那么下次访问的时候就不需要再登陆了。因此我们需要让浏览器为我们保存cookie以使得我们可以不需要登陆站点就能进行自动化测试。

    但是selenium或watir webdriver默认启动的时候是没有加载已存在cookie的。其实这个问题的解决方法非常简单。

    Ruby代码  收藏代码
    1. Watir::Browser.new(:firefox, {:profile => 'default'}) # watir webdriver  
    2.   
    3. Selenium::WebDriver.for(:firefox:profile => 'default'#selenium 2  


    在启动浏览器的时候加上default profile就能解决这个问题了。至于原理就不做过多解释了,有兴趣的同学可以自己研究代码领悟。
  • 开源应用架构之​Selenium WebDriver(上)【转】

    2011-07-22 09:16:34

    前不久,InfoQ向大家推荐了几本有关软件架构的新书,引起了国内读者的广泛兴趣。​其中一本是《开源应用架构(The Architecture of Open Source Applications)》, 来自知名开源项目的各位作者对软件的设计进行了说明。通过对这些成功的系统架构进行概览,让软件工程师可以彻底了解最佳实践和陷阱。InfoQ中文站响应 读者的需求,整理了该书有关知名开源软件架构的精彩内容,供国内开发社区借鉴。本期介绍的是著名浏览器自动化工具Selenium WebDriver的软件架构,第一部分主要分享了Selenium WebDriver的演变历史和架构观点。

    Selenium是一个浏览器自动化工具,通常用来编写​Web应用 的端到端测试。浏览器自动化工具​准确执行你所期望的行为:自动化浏览器的某个控件,从而可以自动重复执行任务。这听起来像是一个很容易解决的问题,但是 正如我们即将看到的那样,其实Selenium成功的背后凝聚了大量的工作。

    介绍Selenium WebDriver软件架构的技术专家是​来自Google的Simon Stewart,他是Selenium的核心贡献者和Selenium WebDriver的创建者。

    Simon Stewart首先谈起了Selenium的组成部分:

    在介绍Selenium架构之前,最好先了解一下该项目的各个相关组成部分是如何结合在一起的。从较高的层次看,Selenium由三种工具组成。 第一个工具Selenium IDE,是Firefox的扩展插件,支持用户录制和回访测试。录制/回访模式​存在局限性,对许多用户来说并不适合,因此第二个工具—— Selenium WebDriver提供了各种语言环境的API来支持更多控制权和编写符合标准软件开发实践的应用程序。最后一个工具——Selenium Grid帮助工程师使用Selenium API控制分布在一系列机器上的浏览器实例,支持并发运行更多测试。在项目内部,它们分别被称为“IDE”、“WebDriver”和“Grid”。 

    ​追根溯源,Selenium和WebDriver最初是两个独立的项目,Simon Stewart​解释了发展的历史:

    Jason Huggins在2004年发起了Selenium项目,当时他在ThoughtWorks公司开发内部的时间和费用(Time and Expenses)系统,该应用使用了大量的JavaScript。虽然Internet Explorer在当时是主流浏览器,但是ThoughtWorks还使用一些其他浏览器(特别是Mozilla系列),当员工在自己的浏览器中无法正常 运行T&E系统时就会提交bug报告。当时的开源测试工具要么关注单一浏览器(通常是IE),要么是模拟浏览器(如HttpUnit)。购买商业 工具授权的成本会耗尽这个小型内部项目的有限预算,所以它们都不是可行的测试选项。​

    在自动化困难的情况下,通常会依靠手动测试。​当开发团队规模很小或者构建发布非常频繁时,这种方式不太适用。同时,让人手动执行原本可以自动化的脚本也是一种对人力的浪费。沉闷重复的任务越无聊,人们工作就会越慢而且比机器犯更多错误。手动测试也不是一种选择。​  

    幸运的是,所有被测试的浏览器都支持Javascript。Jason和他所在的团队有理由采用Javascript编写一种测试工具来验证应用的行为。他们受到FIT​(Framework for Integrated Test) 的启发,使用基于表格的语法替代了原始的Javascript​,这种做法支持那些编程经验有限的人在HTML文件中使用关键字驱动的方式来编写测试。该 工具,最初称为“Selenium”,后来称为“Selenium Core”,在2004年基于Apache 2授权发布。​

    Selenium的表格格式类似于FIT的​ActionFixture。表格的每一行分为三列。第一列给出了要执行的命令名称,第二列通常包含元 素标记符,第三列包含一个可选值。例如,如下格式表示了如何在名称为“q”的元素中输入字符串“Selenium WebDriver”:

    type   name=q   Selenium WebDriver

    因为Selenium过去使用纯JavaScript编写,它的最初设计要求开发人员把准备测试的应用和Selenium Core、测试脚本部署到同一台服务器上以避免触犯浏览器的安全规则和JavaScript沙箱​策略。在实际开发中,这种要求并不总是可行。更糟的是, 虽然开发人员的IDE能够帮助他们快速处理代码和浏览庞大的代码库,但是没有针对HTML的相关工具。​人们很快意识到维护一个中等规模的测试集是笨拙而 痛苦的过程。​

    为了解决这个问题和其他问题,我们编写了HTTP代理,这样所有的HTTP请求都会被Selenium截获。使用代理可以绕过“同源”规则(浏览器 不支持Javascript调用任何当前页面所在服务器以外的其他任何东西)的许多限制​,从而缓解了首要弱点。这种设计使得采用多种语言编写 Selenium绑定成为可能:它们只需把HTTP请求发送到特定URL。连接方法基于Selenium Core的表格语法​​严格建模,称之为“Selenese”​。因为语言绑定在远程控制浏览器,所以该工具称为“Selenium Remote Control”或者“Selenium RC”。

    就在Selenium处于开发阶段的同时,另一款浏览器自动化框架WebDriver也正在ThoughtWorks公司的酝酿之中。 WebDriver的最初代码在2007年初发布。WebDriver项目的初衷是把端对端测试与底层测试工具隔离开。通常情况下,这种隔离手段通过适配 器(Adapter)模式完成。WebDriver正是来源于该方法在许多项目上的不断实践应用,最初是HtmlUnit的封装,工具发布后很快开始支持 Internet Explorer和Firefox。

    在WebDriver最初发布时,与Selenium RC存在显著差异,尽管它们都属于浏览器自动化的API工具。对于用户来说,最明显的区别在于Selenium RC提供基于字典的API,所有方法都在一个类中开放,而WebDriver的API更面向对象。此外,WebDriver仅支持Java,而 Selenium RC提供广泛的语言支持。技术差异也很明显:Selenium Core(RC的基础)基本上是JavaScript应用,运行在浏览器的安全沙箱之内。WebDriver则尝试原生绑定到浏览器中,绕开了浏览器的安 全模型,代价就是框架自身的开发投入显著增加。​

    在2009年8月,两个项目宣布合并,Selenium WebDriver就是合并的成果。目前,WebDriver支持的语言绑定包括Java、C#、Python和Ruby。它支持Chrome、 Firefox、Opera和Android、iPhone浏览器。​此外,还有其他关联项目,不在同一源代码库中维护,但是和主项目(Selenium WebDriver)紧密合作,例如提供Perl绑定支持、BlackBerry浏览器支持,以及“无头”WebKit——用于持续集成的测试其无法正常 显示的情况。最初的Selenium RC机制仍然维持,帮助WebDriver在浏览器不受支持的情况下提供支持。​

    Simon Stewart在介绍Selenium WebDriver软件架构之前,谈到了架构和项目开发的重要主题。​概括如下:

    • 保持成本低廉。
    • 模拟用户。
    • 证明dirver运行良好......
    • ......但是你无需了解一切细节
    • 降低巴士因素(bus factor)。
    • ​偏爱JavaScript实现。
    • 所有方法调用都是RPC调用。
    • 我们是开源项目。

    具体来说:

    保持成本低廉​

    在Y平台上支持X浏览器本质上是一种昂贵的​提议,无论是从开发还是维护角度考虑。如果我们能够找到办法维持产品的高品质而又不违背太多其他原则,那么就值得采纳。这种思想最明显的体现在我们尽可能得使用JavaScript编程,你稍后会看到。​

    模拟用户
    ​WebDriver的设计目的是为了准确模拟用户与Web应用交互的方式。模拟用户输入的常用办法是利用Javascript合并和触发一系列事件(如 果真实用户执行相同交互操作,应用会处理同样的事件)。“合成事件”(synthesized events)方法在面对不同浏览器、有时相同浏览器的不同版本时存在不少困难,触发的事件和相关值略有不同。为了不让问题复杂化,大多数浏览器处于安全 原因不允许用户通过这种方式与表单元素(如文件输入元素)交互。

    WebDriver总是尽可能的使用在操作系统层面​触发事件的方法。​因为这些“原生事件”不是由浏览器生成,所以这种方法​避免了​合成事件导 致的安全限制,同时,因为它们是特定于具体操作系统的,​一旦在某个平台上的浏览器运行良好,在另一种浏览器上重用代码相对容易些。困难的是,这种方法​ 必须满足两点才可行:WebDriver与浏览器紧密绑定,同时开发团队在无需浏览器窗口获得焦点的情况下发送原生事件(因为Selenium测试运行时 间较长,最好能支持同时在机器上执行其他任务)。目前,原生事件可用于Linux、Windows平台,不包括Mac OS X。​

    不管WebDriver如何模拟用户输入,我们都在努力尽可能地模仿用户行为。​RC刚好相反,它提供的API层次远低于用户操作。​

    证明Driver运行良好​

    想让事情十全十美(motherhood and apple pie)​可能过于理想化了,但是我相信编写无法运行的代码是没有意义的。证明driver(指的是WebDriver API的特定实现。例如,Firefox和Internet Explorer各有自己的driver实现)在 Selenium项目中运行良好的办法是我们拥有一套广泛的自动化测试用例。这些通常是“集成测试”,需要编译代码并使用浏览器与Web服务器交互,但是 我们尽可能地编写“单元测试”,它不像集成测试,无需完全重新编译即可运行。目前,大约有500个集成测试和250个单元测试,涵盖所有浏览器。​我们在 修补缺陷和编写新代码时会增加测试,我们的重点正转移到编写更多的​单元测试。​

    并非所有​测试都要运行于每一个浏览器上。其中一些测试用于某些浏览器不支持的特定功能,或者在不同浏览器上处理方式不同的功能。例如,某些测试用 于新的HTML5功能,这些功能并非所有浏览器都支持。尽管如此,每一个主流的浏览器都进行了充分的测试。可以想象,在不同平台上针对每种浏览器运行超过 500个测试是一种极大的挑战,我们一直在努力面对。​

    你无需了解一切细节​​

    很少有开发人员精通各种语言和技术。因此,我们的架构需要帮助开发人员把他们的才华用于最擅长的地方,而无需处理不适合他们的代码片段。​

    降低巴士因素​

    在软件开发领域存在一种(非正式的)​概念,称为“巴士因素”。​它指的是关键开发人员的数量​,如果这些人遇到不幸——被大巴撞伤而离开项目,那 么项目就无法继续进行。​像浏览器自动化这样复杂的技术特别能够证明巴士因素的重要性,因此我们的许多架构决策都希望能够尽可能提高关键开发人员的数量。 ​

    偏爱Javascript实现

    WebDiver在没有其他方式控制浏览器的情况下会使用纯Javascript驱动浏览器。这意味着我们添加的所有API都应该倾向于偏爱 Javascript实现。举一个具体的例子,HTML5引入了LocalStorage,这是在客户端存储结构化数据的API。它通常在浏览器中使用 SQLite实现。比较自然的实现方式是使用类似JDBC的技术为底层的数据存储提供数据库连接。最终,我们决定采用底层Javascript实现的 API,因为通常数据库访问API与Javascript实现不太兼容。

    所有方法调用都是RPC调用​

    WebDriver控制运行在其它进程中的浏览器。一个很容易忽视的事实是,这意味着所有API调用都是RPC调用,因此框架的性能在于网络延迟 上。在正常操作中,​这未必明显——大多数操作系统优化了到本机(localhost)的路由——但是随着浏览器和测试代码之间的网络延迟增加,对于 API设计者和使用者来说,原本高效的调用会恶化。​

    ​这种情况给API的设计带来了一定压力。功能粗糙的较大规模的API可能会通过合并多个调用帮助减低延迟,但是这需要掌握平衡,时刻保持API的 可读性和易用性。例如,有时候需要检测某个元素是不是对最终用户可见。我们不仅需要考虑各种CSS属性(可能需要通过查看父元素来推断),也应该检查元素 的尺寸。最低限度情况下,API应该分别执行​所有这些检测。WebDriver把这些功能都合并到一个方法isDisplayed中。​

    这是开源项目​

    虽然严格意义上说,这不是一种架构观点,但还是要强调Selenium是一个开源项目。上面提到的所有观点联系在一起,表达的意思是:​我们希望尽 可能的帮助新的开发人员易于参与项目。降低参与门槛的措施包括尽可能使所需知识浅显、使用的语言种类较少、依赖自动化测试验证。

    最初该项目被划分成一系列模块,每一个模块​代表了一种特定的浏览器,其他的模块是通用代码和支持代码。每一个绑定的代码树保存在这些模块下面。这 种方法对于类似Java和C#​的语言来说非常有用,但是对于Ruby和Python的开发人员来说很痛苦。这种情况直接导致了有限的参与者,只有一小部 分人参与Python和Ruby的绑定工作。为了解决这个问题,在2010年的十月和十一月,项目源代码被重新组织,Ruby和Python代码存放在每 种语言的独立顶级文件夹中。这种方式符合开源开发人员的期望,立刻吸引了社区的广泛参与。​ 

    本文的第二部分会详细介绍Selenium WebDriver的架构设计和实现,感兴趣的读者可以阅读本书的在线版本

  • 测试积累【1】

    2011-06-28 21:31:44

  • 接口测试之写在最前面的话

    2011-05-30 20:50:34

        接触接口测试的日子不太长,期间也接触了一些相关的东西,怕时间长了会忘记,开个系列帖子,记录一下。
        说实话,到目前为止还不是能百分之百区别接口测试和单元测试,亦或是白盒测试,这里只是把我自己做过的一些事情记下来,如果以后某一天看到这个帖子,能说一句,这个也太简单吧,那也算小有所成。
        这里没有技术新的内容所在,仅仅是开个头,希望看到的同学们不要拍我哈。
        期待ing,并希望所有测试的同学们共同成长,一起进步!

    下一篇:接口测试之初探
  • linux下 快速搭建appach +tomcat集群

    2011-03-10 18:50:34

        测试环境快速搭建集群,主要操作步骤:
        搭建的集群是apache+tomcat(tomcat1、tomcat2)
    1、安装apache 、 tomcat 过程略

    2、tomcat配置
     修改tomcat1--server.xml>
        找到  <Engine name="Catalina" defaultHost="localhost" >修改为:
         
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">
     
     同理,修改 修改tomcat2--server.xml>

        找到  <Engine name="Catalina" defaultHost="localhost" >修改为:
         
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">

      1)此处注意tomcat端口冲突问题:
      2)jvmRoute="××××" 参数配置:这是实现负载均衡的关键一步,通过修改Route属性,使得apache把tomcat关联起来

    3、apache配置
      在conf/extra/httpd-vhosts.conf中增加虚拟机,示例代码如下:
    <VirtualHost *:80>
          ServerAdmin admin@bit.com
         ProxyRequests Off

         Proxypass /server-status !
         Proxypass /balancer-manager !
         Proxypass / balancer://proxy/ stickysession=JSESSIONID
         <Proxy balancer://proxy>
             Order Deny,Allow
             Allow from all
             BalancerMember http://10.10.90.98:8085  loadfactor=1 disablereuse=On route=tomcatc1
             BalancerMember http://10.10.90.98:8086  loadfactor=3 disablereuse=On route=tomcat2
         </Proxy>
    </VirtualHost>
      其中, loadfactor参数设置负载比例:



    4、后台监控 
    我们如何知道某个节点负载多少,响应时间多久,服务是否正常呢?Apache提供了负载均衡监控平台:http://localhost/balancer-manager 。但是,这个服务默认是不存在。我们需要配置:
    1. <Location /balancer-manager>  
    2.         SetHandler balancer-manager  
    3.         Order Deny,Allow  
    4.         #Deny from all  
    5.         Allow from localhost ip6-localhost  
    6. </Location>  

    5、session绑定
    配置参数:
     stickySession=JSESSIONID

    说明:
        每次请求都按照负载均衡配置的节点次序依次请求到不同的Tomcat上。尤其是当我们通过jvmRoute 和route 做了绑定之后,信息更加准确。但是,仔细观察,每次请求的SessionID都是不一样! 对于纯Web应用,尤其是依靠SessionID区分唯一用户的应用,这将是一场噩梦——解决了服务器压力均衡问题,却带来了SessionID不唯一问题!这就需要SessionID绑定,或者说叫做“会话复制”。 
        
    stickySession 粘性会话,根据这一属性,浏览器将通过cookie绑定SeesionID。如果这个时候再次访问http://localhost/zlex ,你会发现,页面不会来回跳转了! 

    sticky是什么?

    引用
    sticky模式 
    利用负载均衡器的sticky模式的方式把所有同一session的请求都发送到相同的Tomcat节点。这样不同用户的请求就被平均分配到集群 中各个tomcat节点上,实现负载均衡的能力。这样做的缺点是没有灾难恢复的能力。一旦一个节点发生故障,这个节点上所有的session信息全部丢 失; 
    同一用户同一session只和一个webServer交互,一旦这个webserver发生故障,本次session将丢失,用户不能继续使用 !
    6、session 复制
        Session复制,主要是指集群环境下,多台应用服务器之间同步Session,确保Session保持一致,且Session中的内容保持一致,对外透明——看起来就像是一台应用服务器! 
    如果其中一台服务器发生故障,根据负载均衡的原理,Apache会遍历寻找可用节点,分发请求。与此同时,当前用户Session不能发生数据丢失,其余各节点服务器应保证用户Session数据同步。
       
     
  • 数据库查询优化原则

    2011-02-21 08:58:13

    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。

      2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:

    select id from t where num is null

      可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:

    select id from t where num=0

      3.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。

      4.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:

    select id from t where num=10 or num=20

      可以这样查询:

    select id from t where num=10   
    union all   
    select id from t where num=20

      5.in 和 not in 也要慎用,否则会导致全表扫描,如:

    select id from t where num in(1,2,3)

      对于连续的数值,能用 between 就不要用 in 了:

      select id from t where num between 1 and 3

      6.下面的查询也将导致全表扫描:

    select id from t where name like '%abc%'

      若要提高效率,可以考虑全文检索。

      7.如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:

    select id from t wherenum=@num

      可以改为强制查询使用索引:

    select id from t with(index(索引名)) wherenum=@num

      8.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:

    select id from t where num/2=100

      应改为:

    select id from t where num=100*2

      9.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:

    select id from t where substring(name,1,3)='abc'--name以abc开头的id   
    select id from t where datediff(day,createdate,'2005-11-30')=0--‘2005-11-30’生成的id

      应改为:

    select id from t where name like 'abc%'   
    select id from t where createdate>='2005-11-30' and createdate<'2005-12-1'

    10.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

    11.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。

    12.不要写一些没有意义的查询,如需要生成一个空表结构:

    select col1,col2 into #t from t where 1=0

      这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:

    create table #t(...)

    13.很多时候用 exists 代替 in 是一个好的选择:

    select num from a where num in(select num from b)

      用下面的语句替换:

    select num from a where exists(select 1 from b where num=a.num)

    14.并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。

    15.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。

    16.应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。

    17.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

    18.尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。

    19.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。

    20.尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。

    21.避免频繁创建和删除临时表,以减少系统表资源的消耗。

    22.临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。

    23.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。

    24.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。

    25.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。

    26.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。

    27.与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。

    28.在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。

    29.尽量避免大事务操作,提高系统并发能力。

    30.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

962/5<12345>
Open Toolbar