<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    qileilove

    blog已經(jīng)轉(zhuǎn)移至github,大家請(qǐng)?jiān)L問 http://qaseven.github.io/

    如何測(cè)試Nginx的高性能

    簡(jiǎn)介
      Nginx ("engine x") 是一個(gè)高性能的HTTP和反向代理服務(wù)器,也是一個(gè)IMAP/POP3/SMTP代理服務(wù)器;
      作為一款輕量級(jí)的Web服務(wù)器,具有占有內(nèi)存少,并發(fā)能力強(qiáng)等優(yōu)勢(shì),是高連接并發(fā)場(chǎng)景下Apache的不錯(cuò)的替代品;
      本篇主要介紹Nginx作為Web服務(wù)器時(shí),相對(duì)于Apache的性能優(yōu)勢(shì);
      下一篇將會(huì)介紹Nginx作為方向代理服務(wù)器的實(shí)現(xiàn);
      重要特點(diǎn)
      非阻塞:數(shù)據(jù)復(fù)制時(shí),磁盤I/O的第一階段是非阻塞的;
      事件驅(qū)動(dòng):通信機(jī)制采用epoll模型,支持更大的并發(fā)連接;
      master/worker結(jié)構(gòu):一個(gè)master進(jìn)程,生成一個(gè)或多個(gè)worker進(jìn)程;
      基礎(chǔ)架構(gòu)
      Nginx如何實(shí)現(xiàn)高并發(fā):
      I/O模型采用異步非阻塞的事件驅(qū)動(dòng)機(jī)制,由進(jìn)程循環(huán)處理多個(gè)準(zhǔn)備好的事件,如epoll機(jī)制;
      Nginx與Apache對(duì)高并發(fā)處理上的區(qū)別:
      對(duì)于Apache,每個(gè)請(qǐng)求都會(huì)獨(dú)占一個(gè)工作線程,當(dāng)并發(fā)量增大時(shí),也會(huì)產(chǎn)生大量的工作線程,導(dǎo)致內(nèi)存占用急劇上升,同時(shí)線程的上下文切換也會(huì)導(dǎo)致CPU開銷增大,導(dǎo)致在高并發(fā)場(chǎng)景下性能下降嚴(yán)重;
      對(duì)于Nginx,一個(gè)worker進(jìn)程只有一個(gè)主線程,通過事件驅(qū)動(dòng)機(jī)制,實(shí)現(xiàn)循環(huán)處理多個(gè)準(zhǔn)備好的事件,從而實(shí)現(xiàn)輕量級(jí)和高并發(fā);
      部署配置
      安裝
    yum -y groupinstall “Development tools”
    yum -y groupinstall “Server Platform Development”
    yum install gcc openssl-devel pcre-devel zlib-devel
    groupadd -r nginx
    useradd -r -g nginx -s /sbin/nologin -M nginx
    tar xf nginx-1.4.7.tar.gz
    cd nginx-1.4.7
    mkdir -pv /var/tmp/nginx
    ./configure \
    --prefix=/usr \
    --sbin-path=/usr/sbin/nginx \
    --conf-path=/etc/nginx/nginx.conf \
    --error-log-path=/var/log/nginx/error.log \
    --http-log-path=/var/log/nginx/access.log \
    --pid-path=/var/run/nginx/nginx.pid  \
    --lock-path=/var/lock/nginx.lock \
    --user=nginx \
    --group=nginx \
    --with-http_ssl_module \
    --with-http_flv_module \
    --with-http_stub_status_module \
    --with-http_gzip_static_module \
    --http-client-body-temp-path=/var/tmp/nginx/client/ \
    --http-proxy-temp-path=/var/tmp/nginx/proxy/ \
    --http-fastcgi-temp-path=/var/tmp/nginx/fcgi/ \
    --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi \
    --http-scgi-temp-path=/var/tmp/nginx/scgi \
    --with-pcre
    make && make install
    配置:
    vi /etc/init.d/nginx # 配置服務(wù)腳本
    #!/bin/sh
    #
    # nginx - this script starts and stops the nginx daemon
    #
    # chkconfig:   - 85 15
    # description:  Nginx is an HTTP(S) server, HTTP(S) reverse \
    #               proxy and IMAP/POP3 proxy server
    # processname: nginx
    # config:      /etc/nginx/nginx.conf
    # config:      /etc/sysconfig/nginx
    # pidfile:     /var/run/nginx.pid
    # Source function library.
    . /etc/rc.d/init.d/functions
    # Source networking configuration.
    . /etc/sysconfig/network
    # Check that networking is up.
    [ "$NETWORKING" = "no" ] && exit 0
    nginx="/usr/sbin/nginx"
    prog=$(basename $nginx)
    NGINX_CONF_FILE="/etc/nginx/nginx.conf"
    [ -f /etc/sysconfig/nginx ] && . /etc/sysconfig/nginx
    lockfile=/var/lock/subsys/nginx
    make_dirs() {
    # make required directories
    user=`nginx -V 2>&1 | grep "configure arguments:" | sed 's/[^*]*--user=\([^ ]*\).*/\1/g' -`
    options=`$nginx -V 2>&1 | grep 'configure arguments:'`
    for opt in $options; do
    if [ `echo $opt | grep '.*-temp-path'` ]; then
    value=`echo $opt | cut -d "=" -f 2`
    if [ ! -d "$value" ]; then
    # echo "creating" $value
    mkdir -p $value && chown -R $user $value
    fi
    fi
    done
    }
    start() {
    [ -x $nginx ] || exit 5
    [ -f $NGINX_CONF_FILE ] || exit 6
    make_dirs
    echo -n $"Starting $prog: "
    daemon $nginx -c $NGINX_CONF_FILE
    retval=$?
    echo
    [ $retval -eq 0 ] && touch $lockfile
    return $retval
    }
    stop() {
    echo -n $"Stopping $prog: "
    killproc $prog -QUIT
    retval=$?
    echo
    [ $retval -eq 0 ] && rm -f $lockfile
    return $retval
    }
    restart() {
    configtest || return $?
    stop
    sleep 1
    start
    }
    reload() {
    configtest || return $?
    echo -n $"Reloading $prog: "
    killproc $nginx -HUP
    RETVAL=$?
    echo
    }
    force_reload() {
    restart
    }
    configtest() {
    $nginx -t -c $NGINX_CONF_FILE
    }
    rh_status() {
    status $prog
    }
    rh_status_q() {
    rh_status >/dev/null 2>&1
    }
    case "$1" in
    start)
    rh_status_q && exit 0
    $1
    ;;
    stop)
    rh_status_q || exit 0
    $1
    ;;
    restart|configtest)
    $1
    ;;
    reload)
    rh_status_q || exit 7
    $1
    ;;
    force-reload)
    force_reload
    ;;
    status)
    rh_status
    ;;
    condrestart|try-restart)
    rh_status_q || exit 0
    ;;
    *)
    echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}"
    exit 2
    esac
    chmod +x /etc/init.d/nginx # 復(fù)***務(wù)腳本執(zhí)行權(quán)限
    vi /etc/nginx/nginx.conf # 編輯主配置文件
    worker_processes  2;
    error_log  /var/log/nginx/nginx.error.log;
    pid        /var/run/nginx.pid;
    events {
    worker_connections  1024;
    }
    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"';
    sendfile        on;
    keepalive_timeout  65;
    server {
    listen       80;
    server_name  xxrenzhe.lnmmp.com;
    access_log  /var/log/nginx/nginx.access.log  main;
    location / {
    root   /www/lnmmp.com;
    index  index.php index.html index.htm;
    }
    error_page  404              /404.html;
    error_page  500 502 503 504  /50x.html;
    location = /50x.html {
    root   /www/lnmmp.com;
    }
    location ~ \.php$ {
    root           /www/lnmmp.com;
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include        fastcgi_params;
    }
    }
    }
    vi /etc/nginx/fastcgi_params # 編輯fastcgi參數(shù)文件
    fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
    fastcgi_param  SERVER_SOFTWARE    nginx;
    fastcgi_param  QUERY_STRING       $query_string;
    fastcgi_param  REQUEST_METHOD     $request_method;
    fastcgi_param  CONTENT_TYPE       $content_type;
    fastcgi_param  CONTENT_LENGTH     $content_length;
    fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
    fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
    fastcgi_param  REQUEST_URI        $request_uri;
    fastcgi_param  DOCUMENT_URI       $document_uri;
    fastcgi_param  DOCUMENT_ROOT      $document_root;
    fastcgi_param  SERVER_PROTOCOL    $server_protocol;
    fastcgi_param  REMOTE_ADDR        $remote_addr;
    fastcgi_param  REMOTE_PORT        $remote_port;
    fastcgi_param  SERVER_ADDR        $server_addr;
    fastcgi_param  SERVER_PORT        $server_port;
    fastcgi_param  SERVER_NAME        $server_name;
      啟動(dòng)服務(wù):
      service nginx configtest # 服務(wù)啟動(dòng)前先驗(yàn)證配置文件是否正確
      service nginx start
      ps -ef |grep nginx # 檢查nginx進(jìn)程,尤其是worker進(jìn)程是否與worker_processes值一致
      ss -antupl |grep 80 # 檢查服務(wù)端口是否啟動(dòng)
      性能測(cè)試
      測(cè)試說明
      每次測(cè)試都進(jìn)行3次,最后數(shù)據(jù)取平均值;
      對(duì)比測(cè)試中的Apache采用event的MPM機(jī)制,最大化提高Apache的并發(fā)性能;
      每次測(cè)試后,都需重新啟動(dòng)服務(wù)(httpd或nginx),以防止多次測(cè)試數(shù)據(jù)不準(zhǔn);
      測(cè)試工具:webbench
      優(yōu)點(diǎn):比ab能更好的模擬并發(fā)請(qǐng)求,最大支持模擬30000并發(fā)連接;
     測(cè)試方法
    # 安裝wenbench
    wget http://blog.s135.com/soft/linux/webbench/webbench-1.5.tar.gz
    tar xf webbench-1.5.tar.gz
    cd webbench-1.5
    make && make install
    # 測(cè)試
    webbench -c 100 -t 30 http://172.16.25.112/nginx.html # 測(cè)試靜態(tài)文件訪問
    webbench -c 20 -t 30 http://172.16.25.112/test_mem.php # 測(cè)試動(dòng)態(tài)文件訪問
      測(cè)試數(shù)據(jù)
      分析趨勢(shì)圖
      靜態(tài)文件訪問趨勢(shì)圖
      動(dòng)態(tài)文件訪問趨勢(shì)圖
      
      總結(jié)
      綜合上面測(cè)試得出的趨勢(shì)圖可以看出:
      靜態(tài)文件測(cè)試時(shí),低并發(fā)(200以下)情況下,Nginx和Apach的處理能力相當(dāng)(2000pages/sec左右),當(dāng)并發(fā)數(shù)超過200后,則 Apache的處理能力開始下降,而Nginx保持穩(wěn)定;同時(shí)隨著并發(fā)量的增大,Apache令人詬病的內(nèi)存占用和負(fù)載開始急劇上升,與此同 時(shí),Nginx在內(nèi)存占用和負(fù)載方面的略微提升則可以忽略不計(jì)了;
      動(dòng)態(tài)文件測(cè)試時(shí),低并發(fā) (100以下)情況下,Nginx和Apache的處理能力相當(dāng)(650pages/sec左右),但Nginx的內(nèi)存占用和負(fù)載峰值只有Apache的 50%左右;在高并發(fā)情況下(100以上),Apach的動(dòng)態(tài)處理能力開始下滑,當(dāng)并發(fā)達(dá)到500時(shí),開始出現(xiàn)失敗的請(qǐng)求,說明此時(shí)已達(dá)到的Apache 的處理上限了,而反觀Nginx,雖然處理動(dòng)態(tài)請(qǐng)求會(huì)消耗更多的內(nèi)存,但其處理能力隨著并發(fā)量的上升而上升,即使并發(fā)1000動(dòng)態(tài)請(qǐng)求,也未達(dá)到其處理能 力上限;
      故不管是在靜態(tài)文件請(qǐng)求還是動(dòng)態(tài)文件請(qǐng)求方面,Nginx的性能都是強(qiáng)勢(shì)優(yōu)于Apache的;雖然可以通過系統(tǒng)調(diào)優(yōu)的方式提高Apache的處理性能,但和Nginx相比,還是不足以打動(dòng)技術(shù)狂熱份子的吧,哈哈!

    posted @ 2014-05-23 10:04 順其自然EVO 閱讀(2376) | 評(píng)論 (0)編輯 收藏

    Linux下設(shè)置環(huán)境變量各配置文件的區(qū)別

     /etc/profile:此文件為系統(tǒng)的每個(gè)用戶設(shè)置環(huán)境信息,當(dāng)用戶第一次登錄時(shí),該文件被執(zhí)行.
      并從/etc/profile.d目錄的配置文件中搜集shell的設(shè)置.
      /etc/bashrc:為每一個(gè)運(yùn)行bash shell的用戶執(zhí)行此文件.當(dāng)bash shell被打開時(shí),該文件被讀取.
      ~/.bash_profile:每個(gè)用戶都可使用該文件輸入專用于自己使用的shell信息,當(dāng)用戶登錄時(shí),該
      文件僅僅執(zhí)行一次!默認(rèn)情況下,他設(shè)置一些環(huán)境變量,執(zhí)行用戶的.bashrc文件.
      ~/.bashrc:該文件包含專用于你的bash shell的bash信息,當(dāng)?shù)卿洉r(shí)以及每次打開新的shell時(shí),該
      該文件被讀取.
      ~/.bash_logout:當(dāng)每次退出系統(tǒng)(退出bash shell)時(shí),執(zhí)行該文件.
      另外,/etc/profile中設(shè)定的變量(全局)的可以作用于任何用戶,而~/.bashrc等中設(shè)定的變量(局部)只能繼承/etc/profile中的變量,他們是"父子"關(guān)系.
      ~/.bash_profile 是交互式、login 方式進(jìn)入 bash 運(yùn)行的
      ~/.bashrc 是交互式 non-login 方式進(jìn)入 bash 運(yùn)行的
      通常二者設(shè)置大致相同,所以通常前者會(huì)調(diào)用后者。

    posted @ 2014-05-22 10:21 順其自然EVO 閱讀(194) | 評(píng)論 (0)編輯 收藏

    Derby數(shù)據(jù)庫(kù)的安裝配置及使用

    Derby數(shù)據(jù)庫(kù)是一個(gè)純用Java實(shí)現(xiàn)的內(nèi)存數(shù)據(jù)庫(kù),屬于Apache的一個(gè)開源項(xiàng)目。由于是用Java實(shí)現(xiàn)的,所以可以在任何平臺(tái)上運(yùn)行;另外一個(gè)特點(diǎn)是體積小,免安裝,只需要幾個(gè)小jar包就可以運(yùn)行了。下面說下其安裝及配置
      安裝
      1).從apache下載Derby數(shù)據(jù)庫(kù)(如db-derby-10.10.1.1-bin.zip)并解壓到任意目錄(如:D:\Derby\db-derby-10.10.1.1-bin)。
      2).配置環(huán)境變量DERBY_HOME=D:\Derby\db-derby-10.10.1.1-bin
      并添加到path和classpath環(huán)境變量(%DERBY_HOME%\bin;%DERBY_HOME%\lib\derbyrun.jar)
      3).測(cè)試數(shù)據(jù)庫(kù)安裝 C:\>sysinfo
    ------------------ Java Information ------------------
    Java Version:    1.7.0_40
    Java Vendor:     Oracle Corporation
    Java home:       C:\Program Files\Java\jdk1.7.0_40\jre
    Java classpath:  D:\Derby\db-derby-10.10.1.1-bin\bin;D:\Derby\db-derby-10.10.1.1-bin\lib\derbyrun.jar;
    OS name:         Windows 7
    OS architecture: amd64
    OS version:      6.1
    Java user name:  qqqqq
    Java user home:  D:\userdata\qqq
    Java user dir:   C:\
    java.specification.name: Java Platform API Specification
    java.specification.version: 1.7
    java.runtime.version: 1.7.0_40-b43
    --------- Derby Information --------
    [D:\Derby\db-derby-10.10.1.1-bin\lib\derby.jar] 10.10.1.1 - (1458268)
    [D:\Derby\db-derby-10.10.1.1-bin\lib\derbytools.jar] 10.10.1.1 - (1458268)
    [D:\Derby\db-derby-10.10.1.1-bin\lib\derbynet.jar] 10.10.1.1 - (1458268)
    [D:\Derby\db-derby-10.10.1.1-bin\lib\derbyclient.jar] 10.10.1.1 - (1458268)
      連接
      C:\>ij
      ij 版本 10.10
      ij> CONNECT 'jdbc:derby:D:\Project\derbyDB\testdb;create=true';(如果數(shù)據(jù)庫(kù)testdb不存在,則創(chuàng)建改數(shù)據(jù)庫(kù))
      ij> CONNECT 'jdbc:derby:D:\Project\derbyDB\testdb;';           (連接testdb數(shù)據(jù)庫(kù))
      ij(CONNECTION1)> CREATE TABLE FIRSTTABLE(ID INT PRIMARY KEY,NAME VARCHAR(12));(創(chuàng)建表)
      已插入/更新/刪除 0 行
      ij(CONNECTION1)> INSERT INTO FIRSTTABLE VALUES(10,'TEN'),(20,'TWENTY'),(30,'THIRTY');(插入數(shù)據(jù))已插入/更新/刪除 3 行
      ij(CONNECTION1)> SELECT * FROM FIRSTTABLE;
      ID |NAME
      ------------------------
      10 |TEN
      20 |TWENTY
      30 |THIRTY
      已選擇 3 行
      ij(CONNECTION1)>exit;(退出)
      說明
      1. sysinfo工具用于顯示Java環(huán)境信息和Derby的版本信息。
      2. ij工具來進(jìn)行數(shù)據(jù)庫(kù)交互,執(zhí)行SQL腳本,如查詢、增刪改、創(chuàng)建表等
      例子
      下面是個(gè)完整的例子,如何程序中操作JavaDB
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.Statement;
    import java.util.Properties;
    public class TestDerby {
    public static void main(String[] args) {
    try {
    Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance();
    System.out.println("Load the embedded driver");
    Connection conn = null;
    Properties props = new Properties();
    props.put("user", "user1"); props.put("password", "user1");
    conn=DriverManager.getConnection("jdbc:derby:C:\\Project\\derbyDB\\testdb;");
    System.out.println("create and connect to testdb");
    Statement s = conn.createStatement();
    ResultSet rs = s.executeQuery("SELECT * FROM FIRSTTABLE");
    System.out.println("name\t\tscore");
    while(rs.next()) {
    StringBuilder builder = new StringBuilder(rs.getString(1));
    builder.append("\t");
    builder.append(rs.getInt(1));
    System.out.println(builder.toString());
    }
    } catch (InstantiationException e) {
    e.printStackTrace();
    } catch (IllegalAccessException e) {
    e.printStackTrace();
    } catch (ClassNotFoundException e) {
    e.printStackTrace();
    }catch (Exception e) {
    e.printStackTrace();
    }
    }
    }

    posted @ 2014-05-22 10:18 順其自然EVO 閱讀(2645) | 評(píng)論 (0)編輯 收藏

    Ant構(gòu)建Java項(xiàng)目之第2篇

     實(shí)例1:實(shí)現(xiàn)自定義Ant任務(wù)
      其中FileSorter文件的源碼如下:
    package com.ant.test02;
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.ListIterator;
    import org.apache.tools.ant.BuildException;
    import org.apache.tools.ant.Task;
    public class FileSorter extends Task {
    private File srcFile;
    private File destFile;
    public File getSrcFile() {
    return srcFile;
    }
    public void setSrcFile(File srcFile) {
    this.srcFile = srcFile;
    }
    public File getDestFile() {
    return destFile;
    }
    public void setDestFile(File destFile) {
    this.destFile = destFile;
    }
    @Override
    public void execute() throws BuildException {
    try {
    BufferedReader fromFile = new BufferedReader(
    new FileReader(srcFile));
    BufferedWriter toFile = new BufferedWriter(new FileWriter(destFile));
    List<String> list = new ArrayList<String>();
    String line = fromFile.readLine();
    while (line != null) {
    list.add(line);
    line = fromFile.readLine();
    }
    Collections.sort(list);
    for (ListIterator<String> li = list.listIterator(); li.hasNext();) {
    String str = li.next();
    toFile.write(str);
    toFile.newLine();
    }
    fromFile.close();
    toFile.close();
    } catch (Exception ex) {
    ex.printStackTrace();
    }
    }
    }
    實(shí)現(xiàn)一個(gè)文件排序的功能。
      build.xml文件源碼如下:
    <?xml  version="1.0"?>
    <project name="tasks" default="main">
    <property name="build.dir" location="build" />
    <target name="init">
    <mkdir dir="${build.dir}" />
    </target>
    <target name="compile" depends="init">
    <javac srcdir="src" destdir="${build.dir}" />
    </target>
    <target name="simpletask" depends="compile">
    <taskdef name="simpletask" classname="com.ant.test02.FileSorter" classpath="${build.dir}" />
    <simpletask srcFile="input.txt" destFile="output.txt"/>
    </target>
    <target name="clean">
    <delete dir="${build.dir}" />
    </target>
    <target name="main" depends="simpletask" />
    </project>
      input.txt中輸入需要排序的內(nèi)容后,運(yùn)行build.xml。查看output.txt,發(fā)現(xiàn)內(nèi)容進(jìn)行了排序。
      實(shí)例2:模式匹配
      可以對(duì)目錄執(zhí)行模式匹配。例如,模式src*/*.java將匹配帶src前綴的任何目錄中的所有Java文件。
      還有另一種模式結(jié)構(gòu):**,它匹配任意數(shù)量的目錄。例如,模式**/*.java將匹配當(dāng)前目錄結(jié)構(gòu)下的所有Java文件。
    <copy todir=”archive”>
    <fileset dir=”src”>
    <include name=”*.java”/>
    </fileset>
    </copy>
      fileset默認(rèn)情況下包含指定src目錄下的所有文件,因此為了僅選擇Java文件,我們對(duì)模式使用一個(gè)include元素。類似地,我們可以對(duì)另一個(gè)模式添加一個(gè)exclude元素,從而潛在地排除include指定的匹配項(xiàng)。甚至可以指定多個(gè)include和exclude元素;這樣將得到一組文件和目錄,它們包含include模式的所有匹配項(xiàng)的并集,但排除了exclude模式的所有匹配項(xiàng)。
    <?xml  version="1.0"?>
    <project name="tasks" default="main">
    <property name="build.dir" location="build" />
    <target name="main" >
    <copy todir="${build.dir}/dest_dir">
    <!-- 只拷貝build目錄下的文件,其子目錄下的文件不做拷貝 -->
    <fileset dir="${build.dir}">
    <include name="*.class"/>
    <exclude name="*.war"/>
    </fileset>
    </copy>
    </target>
    </project>
     實(shí)例3:可執(zhí)行Jar文件的生成
      在com.ant.test03包下新建一個(gè)Testjar.java文件,源碼如下:
    package com.ant.test03;
    import java.awt.Color;
    import java.awt.Dimension;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    public class TestJar {
    public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.setSize(new Dimension(200, 300));
    frame.setBackground(new Color(200, 200, 200));
    frame.setAlwaysOnTop(true);
    frame.getContentPane().add(new JButton("Test Ant"));
    frame.setVisible(true);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    }
      build.xml文件源碼如下:
    <?xml version="1.0" encoding="utf-8"?>
    <project name="myAntProject" default="dist" basedir=".">
    <property name="compile" value="compile" />
    <property name="dist" value="dist" />
    <target name="preprocess" >
    <mkdir dir="${compile}" />
    <mkdir dir="${dist}" />
    </target>
    <target name="myCompile" depends="preprocess">
    <javac srcdir="src" destdir="${compile}">
    </javac>
    </target>
    <target name="dist" depends="myCompile">
    <jar destfile="${dist}/package.jar" basedir="${compile}">
    <manifest>
    <attribute name="Built-By" value="${user.name}" />
    <attribute name="Main-Class" value="com.ant.test03.TestJar" />
    </manifest>
    </jar>
    </target>
    </project>
      運(yùn)行后刷新MyEclipse項(xiàng)目,就可以看到在dist文件夾下生成的package.jar文件,雙擊可以運(yùn)行。
    相關(guān)文章:

    posted @ 2014-05-22 10:16 順其自然EVO 閱讀(193) | 評(píng)論 (0)編輯 收藏

    使用Jmeter錄制web腳本

      1。web性能測(cè)試以及web http請(qǐng)求基本原理。
      再介紹錄制jmeter腳本之前,我們先談一下web性能測(cè)試。web就是調(diào)用http/https接口, 其實(shí)沒有是什么復(fù)雜度可言。只是我們必須清楚,對(duì)于一個(gè)網(wǎng)站說,一個(gè)頁(yè)面并通常不是只有一個(gè)http請(qǐng)求。如果需要測(cè)試一個(gè)rich web page,必須了解到這一點(diǎn)(測(cè)試http接口不在此類,雖然也是http協(xié)議,但是以http接口對(duì)外服務(wù))。
      例如這樣一個(gè)頁(yè)面(下面是html代碼)
      如果向服務(wù)器請(qǐng)求上面的一個(gè)頁(yè)面,則除了本身頁(yè)面的請(qǐng)求本身,還有一個(gè)css和img的資源。這樣打開這樣一個(gè)頁(yè)面,本需要有三個(gè)http請(qǐng)求。
      1. http1 -> get the web page
      2. the web page contain two resources
      3. then request  http2 and http3 to  get the css file and image respecitively.
      所以這樣就是發(fā)起了3個(gè)http請(qǐng)求,才完整的打開了一個(gè)頁(yè)面。瀏覽器實(shí)際是這么做的,但瀏覽器作了很多優(yōu)化。 (比如多線程下載資源,緩存圖片,css等資源)。說道這里,不得不佩服loadrunner的強(qiáng)大。他可以盡量模擬瀏覽器的這些行為,來保證測(cè)試的結(jié)果準(zhǔn)確性。但這些參數(shù)都可以動(dòng)態(tài)調(diào)整的。
      2。實(shí)際錄制腳本
      好,那么我們現(xiàn)在就來實(shí)際錄制腳本吧,并且體驗(yàn)一下真正一個(gè)頁(yè)面的實(shí)際http請(qǐng)求過程。這里我們只談http,https不在本文所講范圍內(nèi)。
      錄制腳本目前有2種方法,我們先介紹jmeter proxy方法。另一個(gè)是badboy錄制的腳本轉(zhuǎn)化為jmeter腳本,直接想了解badboy,請(qǐng)直接訪問
      http://www.badboysoftware.biz/docs/jmeter.htm
      jmeterproxy 來了。
      1)啟動(dòng)jmeter,這個(gè)不多說了。
      2)選擇測(cè)試計(jì)劃,右鍵添加線程組(thread group)
      3)選擇這個(gè)線程組,右鍵添加config element -〉 http默認(rèn)請(qǐng)求
      4)在http默認(rèn)請(qǐng)求單元,填入server name 為jakarta.apache.org. 這個(gè)是我們要錄制的頁(yè)面。其他地方不填
      5)然后選擇剛才那個(gè)縣城組,右鍵加入一個(gè)錄制控制器。位于Add>Logic Controllers -> Recording Controller
      6) 選擇WorkBench,右鍵加入 Non-Test Elements -> Http proxy server
      7)在http proxy server里, 的patterns to include 里,寫入.*\.html  這個(gè)是正則表達(dá)式,意思是錄制所有的html為后綴名的頁(yè)面。 那么如果你要錄制后綴名為jsp或者do的,則寫入.*\.jsp  和 .*\.do 分別。
      8)對(duì)于url怕tterns to exclude的地方,是寫入不想被錄制的一些資源文件url。比如圖片等。 這些配置,視測(cè)試的具體場(chǎng)景而定。比如是否要測(cè)試靜態(tài)圖片等。
      9)為了調(diào)試錄制的情況,我們選擇http proxy server ,添加一個(gè)察看結(jié)果樹監(jiān)聽器。這個(gè)以前我們?cè)?jīng)用過。
    10)回到http proxy server然后,選擇開始啟動(dòng)。這樣proxy server 就啟動(dòng)了。
      11)這樣我們打開一個(gè)瀏覽器,將瀏覽器的代理設(shè)置為jmeter proxy server 的ip和端口號(hào)。 因?yàn)閜roxy server 就在本地,所以就寫localhost 和端口即可。
      12)設(shè)置好代理后,用瀏覽器訪問http://jakarta.apache.org/jmeter/index.html
      13)隨便點(diǎn)擊一些連接,然后回到j(luò)meter 的窗口,你查看結(jié)果樹就看到了錄制的http請(qǐng)求了。
      14)這樣的腳本,我們就可以直接用來運(yùn)行了。

    posted @ 2014-05-22 10:12 順其自然EVO 閱讀(3335) | 評(píng)論 (0)編輯 收藏

    驗(yàn)證集成數(shù)據(jù)的策略

    在一個(gè)數(shù)據(jù)不斷增長(zhǎng)的世界,軟件測(cè)試涉及不同的數(shù)據(jù)驗(yàn)證程序,這些程序是傳統(tǒng)質(zhì)量保證方法的一個(gè)組成部分。本文旨在為非數(shù)據(jù)人員提供數(shù)據(jù)驗(yàn)證工具,同時(shí)也為回歸測(cè)試和功能測(cè)試提供更多自動(dòng)且迅速的工具和測(cè)試策略。
      本文中,我將詳細(xì)介紹數(shù)據(jù)驗(yàn)證的兩種方法:自下而上法和自上而下法。這兩種方法在不同的應(yīng)用程序和測(cè)試策略中已實(shí)現(xiàn)的數(shù)據(jù)中所起的作用是不一樣的。
      自下而上方法中,應(yīng)用程序是代理器,而數(shù)據(jù)是反應(yīng)器。我們通常使用此方法對(duì)新功能進(jìn)行功能測(cè)試或把它與自上而下結(jié)合方法以覆蓋大量代碼。
      另一方面自上而下法中,數(shù)據(jù)只在畫面里,我們根據(jù)外部知識(shí)判斷關(guān)于數(shù)據(jù)模式內(nèi)關(guān)系的猜想是真還是假。
      后一種方法使我們能夠設(shè)計(jì)出成本低但功能強(qiáng)大的回歸測(cè)試集以及根本原因分析法。

      介紹
      數(shù)據(jù)無所不在。它已不再是某些較高等級(jí)的應(yīng)用程序的附帶品,而被視為是有價(jià)值的、脆弱的。在信息論中,數(shù)據(jù)是信息抽象化即知識(shí)的最低水平。這意味著我們要有一個(gè)專家把行業(yè)知識(shí)原則又稱產(chǎn)品需求和產(chǎn)品規(guī)格轉(zhuǎn)化為一個(gè)一致的數(shù)據(jù)模式。
      這實(shí)際上解決了這一行中一直以來的一個(gè)困境:產(chǎn)品經(jīng)理和產(chǎn)品架構(gòu)師對(duì)產(chǎn)品規(guī)格的見解不一。
      事實(shí)上,數(shù)據(jù)的既定邏輯結(jié)構(gòu)及評(píng)估它的明確結(jié)果可以把一個(gè)MRD / PRD轉(zhuǎn)化成一個(gè)比以往更容易的測(cè)試計(jì)劃
      但是,數(shù)據(jù)質(zhì)量保證技術(shù)通常很昂貴且需要分析、BI工具和專業(yè)知識(shí)。
      本文不看數(shù)據(jù)驗(yàn)證本身,而是建議用一個(gè)簡(jiǎn)單的、可重復(fù)的、不怎么需要資源和外部或技術(shù)支持的、且基于數(shù)據(jù)驗(yàn)證的測(cè)試策略去替代標(biāo)準(zhǔn)質(zhì)量保證程序。
      讓我們考慮考慮一個(gè)在線購(gòu)物市場(chǎng)的簡(jiǎn)單案例研究:實(shí)現(xiàn)B2C交易并對(duì)測(cè)試這樣一個(gè)應(yīng)用程序的設(shè)計(jì)和選項(xiàng)進(jìn)行檢查。

      自下而上的數(shù)據(jù)驗(yàn)證法
      直接設(shè)計(jì)包括應(yīng)用程序和DB。
      此方法中,我們有一個(gè)傳統(tǒng)的STP ,當(dāng)自動(dòng)使用簡(jiǎn)單的宏命令時(shí),可用作一個(gè)自定義的和高度有效的低成本測(cè)試工具。
      為簡(jiǎn)單起見,我們假設(shè)在我們的例子中,基本的測(cè)試用例:



      

      需要注意的是,我們還沒有對(duì)這個(gè)流程進(jìn)行任何驗(yàn)證。
      我們不會(huì)把這個(gè)簡(jiǎn)單直接的流程擴(kuò)展為一個(gè)完整的測(cè)試過程:“生成用戶> 0 $交易”。
      這個(gè)程序?qū)⑹褂煤?jiǎn)單的Web宏命令調(diào)整發(fā)送給應(yīng)用程序的涵蓋購(gòu)物車、用戶資料等所有可能方案的需求。驗(yàn)證在最后進(jìn)行以防止斷裂或使用昂貴的基礎(chǔ)設(shè)施。
      這個(gè)例子演示了數(shù)據(jù)流的另一個(gè)強(qiáng)大的優(yōu)點(diǎn)。數(shù)據(jù)是事件流的結(jié)果。這意味著:例如,如果用戶還沒有注冊(cè)賬戶(根據(jù)這個(gè)具體的例子),那我們就不指望在DB中找到交易。這可以被用于負(fù)面測(cè)試以擴(kuò)大代碼覆蓋。
      回到我們的例子中,另一個(gè)過程可能是“用戶退款”(在這里,例如,我們想用負(fù)數(shù)金額重復(fù)(a))等等。 驗(yàn)證應(yīng)該在最后用任一電子表格(Excel,Zoho)或是像MySQL Workbench的免費(fèi)SQL工具通過點(diǎn)擊完成。從這個(gè)意義上來說Excel非常方便,它不需要專業(yè)知識(shí),并具有不受規(guī)范限制的比較工具。關(guān)于這個(gè)我將在下一節(jié)做簡(jiǎn)要探討。

      總結(jié)一下這種方法:
      1. 做小測(cè)試用例。
      2. 把它們一起放入10 TC過程。
      3. 把過程一起放入一個(gè)測(cè)試集。
      4. 最后進(jìn)行驗(yàn)證。

      自上而下的數(shù)據(jù)驗(yàn)證法
      這種方法與我們所知道的經(jīng)典的STP設(shè)計(jì)完全不同。它對(duì)功能測(cè)試和根本原因分析都有用。它可以被視作是我們所知道的黑盒測(cè)試,并在兩個(gè)重要概念上不同于以往的設(shè)計(jì):
      1. 數(shù)據(jù)只在圖像中。因此,我們通常會(huì)把這個(gè)用作測(cè)試周期的第二層,用于bug和問題的根本原因分析。
      2. 如果先前的方法里我們?cè)噲D把測(cè)試計(jì)劃打破成一個(gè)個(gè)小流程,那么在這里,我們依靠產(chǎn)品規(guī)格、領(lǐng)域知識(shí)和很多常識(shí),創(chuàng)造性地創(chuàng)建系統(tǒng)的不同用例之間的依賴關(guān)系。例如,對(duì)比之前的設(shè)計(jì)中的退款模塊,讓我們回憶一下我們的交易模塊,。
      在那種設(shè)計(jì)中,我們不得不考慮一個(gè)交易,一個(gè)我們不得不事先用我們應(yīng)用于退款和驗(yàn)證的宏命令建立的交易。
      現(xiàn)在的方法中,我們將使用電子表格/ SQL來獲取DB中的所有退款行,將它們連到它們的父事務(wù),并根據(jù)產(chǎn)品規(guī)格(金額,原因等)驗(yàn)證不同的數(shù)據(jù)字段。這種方法是很強(qiáng)大的,往往能揭示產(chǎn)品需求和架構(gòu)中的問題。
      對(duì)于負(fù)面測(cè)試,我們有一個(gè)穩(wěn)贏策略。例如,發(fā)現(xiàn)一筆交易里用戶沒有注冊(cè)網(wǎng)站(例如,用戶的電子郵件未被記錄)。根本原因分析中,我們能夠直接從DB生成一個(gè)執(zhí)行流–即什么導(dǎo)致失敗,并使用     簡(jiǎn)單的排序、篩選和條件格式作為Excel不受規(guī)范限制的分析工具去評(píng)估他們的IF-AND-OR-NOT依賴關(guān)系。
      這種方法對(duì)于使用Excel數(shù)據(jù)驗(yàn)證功能來執(zhí)行簡(jiǎn)單的驗(yàn)證(如無前/后間隔,減少/增加/恰好n位場(chǎng)/十進(jìn)制值溢出,日期范圍限制等)的簡(jiǎn)單現(xiàn)場(chǎng)驗(yàn)證來說也是很經(jīng)典的。

      自動(dòng)化與回歸
      正如前面提到的,數(shù)據(jù)驗(yàn)證對(duì)自動(dòng)化而言很容易。
      對(duì)于迅速回歸,我們會(huì)選擇第二種方法,即根據(jù)一個(gè)預(yù)定義的基線驗(yàn)證DB數(shù)據(jù)。
      例如對(duì)于我們的購(gòu)物網(wǎng)站,我們將有一組預(yù)定義的會(huì)產(chǎn)生一個(gè)數(shù)據(jù)集的一系列操作。
      我們可以輸出DB到電子表格或上傳基線到MySQL并點(diǎn)擊/查詢以便比較兩個(gè)表格。
      Excel的VLOOKUP在帶來兩個(gè)表間的差異上表現(xiàn)出色,SQL通過采用兩表間簡(jiǎn)單的左連接點(diǎn)帶來差異。

      總結(jié)
      如前所述,數(shù)據(jù)驗(yàn)證在取代傳統(tǒng)的功能測(cè)試和根本原因分析中非常有效。可以用簡(jiǎn)單,低成本的方法通過把它的繼承邏輯結(jié)構(gòu)擴(kuò)展到被明確標(biāo)示為通過/不通過的功能測(cè)試用例來進(jìn)行數(shù)據(jù)驗(yàn)證。使用數(shù)據(jù)驗(yàn)證而非傳統(tǒng)功能測(cè)試可以通過把產(chǎn)品規(guī)格轉(zhuǎn)化為明確清晰的數(shù)據(jù)架構(gòu)內(nèi)的邏輯關(guān)系使編譯出更好的測(cè)試用例更容易。

    posted @ 2014-05-22 10:12 順其自然EVO 閱讀(188) | 評(píng)論 (0)編輯 收藏

    QTP腳本—測(cè)試參數(shù)限制

     以前一直覺得自己沒有寫代碼的資質(zhì),太急于求成,以為一天就能寫好幾個(gè)功能,幾千行代碼,于是就沒耐心了,沒心情學(xué)下去了....但是最近發(fā)現(xiàn)其實(shí)寫代碼是一個(gè)漫長(zhǎng)的過程,都是在修修改改中成長(zhǎng)起來的。于是今天試著慢慢用QTP測(cè)下參數(shù)限制,雖然代碼量不多,其實(shí)也算不上編程,O(∩_∩)O哈哈~但也是個(gè)慢慢積累的過程。
      首先,我有一段登陸系統(tǒng)的測(cè)試模塊,可以把它設(shè)為可重用的,并且參數(shù)化必要的信息,比如登陸用戶名密碼等等,這些就不細(xì)說了。可以參見《QTP自動(dòng)化測(cè)試實(shí)踐》8.3節(jié) Action測(cè)試輸入的參數(shù)化,調(diào)用過程見上一篇關(guān)于action的文章
      現(xiàn)在我要測(cè)試參數(shù)的限制:
      第一步,必須要在當(dāng)前項(xiàng)目下新建一個(gè)action,步驟如下:
      選擇Insert|Call to New Action:
      然后在彈出框中填寫新建action的name和description,可不可重用,以及新action的位置,這里我的參數(shù)檢查功能是在登陸模塊之后,所以選擇第一個(gè)At the end of the test
      第二步,既然2個(gè)action都是測(cè)試同一個(gè)軟件,可以重用它們的對(duì)象庫(kù)respositories,操作步驟是:
      先保存login這個(gè)action的對(duì)象庫(kù),后綴是“.tsr",然后選擇Resources|Associate Respositories,選定剛剛保存的對(duì)象庫(kù)文件,然后下面的Available Action選擇login模塊,右邊的Associated Action選擇Test_Parameters模塊:
      于是,在測(cè)試參數(shù)的模塊中就可以直接使用login的對(duì)象庫(kù)了。 第三步,因?yàn)闇y(cè)試參數(shù)是一個(gè)繁雜的過程,有很多種組合方式,而且每個(gè)參數(shù)輸入框都要求輸入一遍,但是好在參數(shù)輸入框的規(guī)則都是一樣的,比如不能輸入符號(hào),字母,負(fù)數(shù),小數(shù),空格等等,除此之外還有范圍限制,于是我就采用數(shù)據(jù)驅(qū)動(dòng)測(cè)試的方法來做這個(gè)腳本。先寫好輸入?yún)?shù)的過程:
    Dialog("App(1.0.1.0)").WinEdit("MINS").Set ”1“
    Dialog("App(1.0.1.0)").WinEdit("MINX").Set ”1“
    Dialog("App(1.0.1.0)").WinEdit("MAXS").Set ”2“
    Dialog("App(1.0.1.0)").WinEdit("MAXX").Set ”2“
    Dialog("App(1.0.1.0)").WinEdit("IR").Set ”1“
    Dialog("App(1.0.1.0)").WinEdit("OR").Set ”2“
      然后根據(jù)數(shù)據(jù)驅(qū)動(dòng)測(cè)試的步驟設(shè)置參數(shù)根據(jù)table中的值來輸入,詳情參見我博客《QTP:數(shù)據(jù)驅(qū)動(dòng)測(cè)試》,Expert View的顯示如下:
    Dialog("App(1.0.1.0)").WinEdit("MINS").Set DataTable("minS", dtLocalSheet)
    Dialog("App(1.0.1.0)").WinEdit("MINX").Set DataTable("minX", dtLocalSheet)
    Dialog("App(1.0.1.0)").WinEdit("MAXS").Set DataTable("maxS", dtLocalSheet)
    Dialog("App(1.0.1.0)").WinEdit("MAXX").Set DataTable("maxX", dtLocalSheet)
    Dialog("App(1.0.1.0)").WinEdit("IR").Set DataTable("IR", dtLocalSheet)
    Dialog("App(1.0.1.0)").WinEdit("OR").Set DataTable("OR", dtLocalSheet)<br>Dialog("App(1.0.1.0)").WinButton("應(yīng)用參數(shù)").Click    ' 點(diǎn)擊開始應(yīng)用參數(shù)
      我設(shè)置了2種參數(shù)范圍邊界的數(shù)據(jù),9種服務(wù)端會(huì)拒絕應(yīng)用的參數(shù)組合,30種客戶端限制的參數(shù)類型組合。
      第四步,對(duì)測(cè)試結(jié)果進(jìn)行判斷并顯示在QTP生成的測(cè)試報(bào)告中:
      開始我是這么寫的:
    If   Dialog("App(1.0.1.0)").Dialog("提示").Exist(3) Then
    Dialog("App(1.0.1.0)").Dialog("提示").WinButton("應(yīng)用參數(shù)成功-確定").Click    ' 應(yīng)用成功
    reporter.ReportEvent micDone,"yes","前2個(gè):可以應(yīng)用成功"
    else
    reporter.ReportEvent micFail, "yes"," 前2個(gè):服務(wù)端拒絕應(yīng)用"
    End If
    If Dialog("App(1.0.1.0)").Dialog("錯(cuò)誤").Exist(3) Then
    Dialog("App(1.0.1.0)").Dialog("錯(cuò)誤").WinButton("服務(wù)器不支持該參數(shù)-確定").Click
    reporter.ReportEvent micDone,"server no","中間9個(gè):服務(wù)端拒絕應(yīng)用"
    else
    reporter.ReportEvent micFail,"server no", "中間9個(gè):服務(wù)端居然應(yīng)用了"
    End If
    If  Dialog("App(1.0.1.0)").Dialog("警告").Exist(3) Then
    Dialog("App(1.0.1.0)").Dialog("警告").WinButton("客戶端不支持該參數(shù)-確定").Click
    reporter.ReportEvent micDone,"client no","后30個(gè):DTC拒絕應(yīng)用"
    else
    reporter.ReportEvent micFail,"client no", "后30個(gè):DTC居然應(yīng)用了"
    End If
      運(yùn)行之后發(fā)現(xiàn),每一行參數(shù)的結(jié)果都有2個(gè)fail,因?yàn)槲以O(shè)置的三個(gè)主if是并列關(guān)系!符合其中一種情況之后,另外2種情況都會(huì)失敗。
      于是,我再寫成這樣的:
    If   Dialog("App(1.0.1.0)").Dialog("提示").Exist(3) Then
    Dialog("App(1.0.1.0)").Dialog("提示").WinButton("應(yīng)用參數(shù)成功-確定").Click    ' 應(yīng)用成功
    reporter.ReportEvent micDone,"yes","前2個(gè):可以應(yīng)用成功"
    elseif Dialog("App(1.0.1.0)").Dialog("錯(cuò)誤").Exist(3) Then
    Dialog("App(1.0.1.0)").Dialog("錯(cuò)誤").WinButton("服務(wù)器不支持該參數(shù)-確定").Click
    reporter.ReportEvent micDone,"server no","中間9個(gè):服務(wù)端拒絕應(yīng)用"
    elseif Dialog("App(1.0.1.0)").Dialog("警告").Exist(3) Then
    Dialog("App(1.0.1.0)").Dialog("警告").WinButton("客戶端不支持該參數(shù)-確定").Click
    reporter.ReportEvent micDone,"client no","后30個(gè):客戶端拒絕應(yīng)用"
    else
    reporter.ReportEvent micFail, "fail"," 結(jié)果跟預(yù)期不一致"
    End If
      現(xiàn)在如果全部測(cè)試通過,不會(huì)出現(xiàn)fail的情況,但是需要在測(cè)試報(bào)告中一層層點(diǎn)開,查看對(duì)于每個(gè)測(cè)試數(shù)據(jù)行的測(cè)試結(jié)果是不是符合以下描述:
      1.前2個(gè):可以應(yīng)用成功
      2.中間9個(gè):服務(wù)端拒絕應(yīng)用
      3.后30個(gè):客戶端拒絕應(yīng)用
      雖然上面的腳本避免了重復(fù)輸入41種數(shù)據(jù),但是后期的結(jié)果查看還是一個(gè)艱辛的過程,不知道QTP有沒有把測(cè)試人員要求的結(jié)果描述統(tǒng)一到一個(gè)頁(yè)面來的功能呢,有待挖掘。

    posted @ 2014-05-22 10:11 順其自然EVO 閱讀(376) | 評(píng)論 (0)編輯 收藏

    對(duì)redis中單元測(cè)試框架的簡(jiǎn)單修改

     最近在看redis的sds,發(fā)現(xiàn)原來redis提供了一個(gè)簡(jiǎn)單的測(cè)試框架,覺得挺有新意的。之前寫代碼,都是單獨(dú)寫所有的單元測(cè)試代碼,感覺很麻煩,這個(gè)還不錯(cuò),感覺修改一下會(huì)更加方便使用。
      測(cè)試宏定義如下:
    /* This is a really minimal testing framework for C.
    *
    * Example:
    *
    * test_cond("Check if 1 == 1", 1==1)
    * test_cond("Check if 5 > 10", 5 > 10)
    * test_report()
    *
    * ----------------------------------------------------------------------------
    *
    * Copyright (c) 2010-2012, Salvatore Sanfilippo <antirez at gmail dot com>
    * All rights reserved.
    *
    * Redistribution and use in source and binary forms, with or without
    * modification, are permitted provided that the following conditions are met:
    *
    *   * Redistributions of source code must retain the above copyright notice,
    *     this list of conditions and the following disclaimer.
    *   * Redistributions in binary form must reproduce the above copyright
    *     notice, this list of conditions and the following disclaimer in the
    *     documentation and/or other materials provided with the distribution.
    *   * Neither the name of Redis nor the names of its contributors may be used
    *     to endorse or promote products derived from this software without
    *     specific prior written permission.
    *
    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    * POSSIBILITY OF SUCH DAMAGE.
    */
    #ifndef __TESTHELP_H
    #define __TESTHELP_H
    int __failed_tests = 0;
    int __test_num = 0;
    #define test_cond(descr,_c) do { \
    __test_num++; printf("%d - %s: ", __test_num, descr); \
    if(_c) printf("PASSED\n"); \
    else {printf("FAILED\n"); __failed_tests++;\
    printf("************************ WARNING ***************************\n\
    [*failed*] We have failed tests here : %s :%d\n",__FILE__, __LINE__);\
    printf("are you continue[y]:");\
    if(tolower(getchar())=='n'){\
    test_report();\
    printf("************************************************************\n");\
    exit(1);}\
    else{\
    printf("************************************************************\n");\
    }} \
    } while(0);
    #define test_report() do { \
    printf("------------------------------------------------------------\n");\
    printf("%d tests, %d passed, %d failed\n", __test_num, \
    __test_num-__failed_tests, __failed_tests);\
    printf("------------------------------------------------------------\n");\
    } while(0);
    #endif


      寫一個(gè)簡(jiǎn)單的測(cè)試來展現(xiàn)一下:
    #include<stdio.h>
    #include<stdlib.h>
    #include"testhelp.h"
    int fun1(int a,int b){
    return a+b;
    }
    int fun2(int a,int b){
    return a-b;
    }
    int fun3(int a,int b){
    return a*b;
    }
    int fun4(int a,int b){
    return a/b;
    }
    int main(){
    test_cond("jiafa", fun1(1,2)==3);
    test_cond("jianfa",fun2(2,1)==2);
    test_cond("chengfa",fun3(2,1)==2);
    test_cond("chufa",fun4(2,1)==2);
    test_report();
    }
      可以得到如下的測(cè)試結(jié)果:
      哈哈,其實(shí)也沒有加進(jìn)去什么,只是在出現(xiàn)錯(cuò)誤的時(shí)候,出現(xiàn)錯(cuò)誤出現(xiàn)在哪里的提示,以及選擇是否要繼續(xù)測(cè)試。這樣的話更有利于跟蹤錯(cuò)誤的位置,及時(shí)修改錯(cuò)誤。

    posted @ 2014-05-22 10:09 順其自然EVO 閱讀(276) | 評(píng)論 (0)編輯 收藏

    數(shù)據(jù)庫(kù)關(guān)系模式規(guī)范化

      在教學(xué)中,大多實(shí)例都是主鍵由一列構(gòu)成,所以也可以簡(jiǎn)單地說主屬性與主鍵沒有什么區(qū)別。
      第三范式的定義:如果關(guān)系模式R中的所有非主屬性對(duì)任何候選關(guān)鍵字都不存在傳遞依賴,則稱關(guān)系R是屬于第三范式的。記作R 3NF。
      如:學(xué)生關(guān)系模式S1(學(xué)號(hào),姓名,系號(hào),系名,系地址)
      (學(xué)號(hào))為關(guān)鍵字,因是單屬性關(guān)鍵字,不存在部份依賴問題,應(yīng)屬于第二范式。但因?yàn)椋簩W(xué)號(hào)—>系號(hào),系號(hào)—\>學(xué)號(hào),系號(hào)—>系地址,因此:學(xué)號(hào)—>系地址 是通過傳遞依賴實(shí)現(xiàn)的。即候選關(guān)鍵字“學(xué)號(hào)”不直接函數(shù)決定于非主屬性“系地址”。所以此關(guān)系不屬于第三范式。應(yīng)將其分解為:
      SC(學(xué)號(hào),姓名,系號(hào))
      D(系號(hào),系名,系地址)
      C(課程號(hào),課程名,學(xué)分)
      S(學(xué)號(hào),課程號(hào),成績(jī))
      設(shè)計(jì)原則:“一事一地”,即一個(gè)關(guān)系反映一個(gè)實(shí)體或一個(gè)聯(lián)系,不應(yīng)把幾樣?xùn)|西混合放在一起。基本關(guān)系模式切忌“大而全”,在若干個(gè)基本關(guān)系模式組成的關(guān)系模型上,根據(jù)需要可以通過自然聯(lián)接導(dǎo)出所需要的關(guān)系。
      BCNF的定義:如果一個(gè)關(guān)系R中的所有屬性都不傳遞依賴于R的任何候選關(guān)鍵字,或者說關(guān)系R中的每個(gè)決定因數(shù)都是候選關(guān)鍵字時(shí),則稱關(guān)系R屬于BCNF范式,記作R BCNF。
      一個(gè)滿足BCNF的關(guān)系模式有
      1.所有非主屬性對(duì)每一個(gè)碼都是完全函數(shù)依賴。
      2.所有的主屬性對(duì)每一個(gè)不包含它的碼,也是完全函數(shù)依賴。
      3.沒有任何屬性完全函數(shù)依賴于非碼的任何一組屬性。
      由于RBCNF,按定義排除了任何屬性對(duì)碼的傳遞依賴與部分依賴,所以R3NF。但是若R3NF,則R未必屬于BCNF。
      下面用幾個(gè)例子說明屬于3NF的關(guān)系模式有的屬于BCNF,但有的不屬于BCNF。
      詳細(xì)信息...
      例l 關(guān)系模式SJP(S,J,P)中,S是學(xué)生,J表示課程,P表示名次。每一個(gè)學(xué)生選修每門課程的成績(jī)有一定的名次,每門課程中每一名次只有一個(gè)學(xué)生(即沒有并列名次)。由語(yǔ)義可得到下面的函數(shù)依賴:
      (S,J)→P ,(J,P)→S
      所以(S,J)與(J,P)都可以作為候選碼。這兩個(gè)碼各由兩個(gè)屬性組成,而且它們是相交的。這個(gè)關(guān)系模式中顯然沒有屬性對(duì)碼傳遞依賴或部分依賴。所以SJP3NF,而且除(S,J)與(J,P)以外沒有其它決定因素,所以SJPBCNF。
      例2 關(guān)系模式STJ(S,T,J)中,S表示學(xué)生,T表示教師,J表示課程。每一教師只教一門課。每門課有若干教師,某一學(xué)生選定某門課,就對(duì)應(yīng)一個(gè)固定的教師。由語(yǔ)義可得到如下的函數(shù)依賴。
      (S,J)→T;(S,T)→J;T→J。
      這里(S,J),(S,T)都是候選碼。
      STJ是3NF,因?yàn)闆]有任何非主屬性對(duì)碼傳遞依賴或部分依賴。但STJ不是BCNF關(guān)系,因?yàn)門是決定因素,而T不包含碼。
      3NF的“不徹底”性表現(xiàn)在可能存在主屬性對(duì)碼的部分依賴和傳遞依賴。非BCNF的關(guān)系模式也可以通過分解成為BCNF。例如STJ可分解為ST(S,T)與TJ(T,J),它們都是BCNF。
      一個(gè)模式中的關(guān)系模式如果都屬于BCNF,那么在函數(shù)依賴范疇內(nèi),它已實(shí)現(xiàn)了徹底的分離,已消除了插入和刪除的異常。
      關(guān)系模式規(guī)范化小結(jié)
      目的:規(guī)范化的目的是使結(jié)構(gòu)合理,消除存儲(chǔ)異常,使數(shù)據(jù)冗余盡量小,便于插入、刪除和更新。
      原則:遵從概念單一化“一事一地”的原則,即一個(gè)關(guān)系模式描述一個(gè)實(shí)體或?qū)嶓w間的一種聯(lián)系。規(guī)范的實(shí)質(zhì)就是概念單一化。
      方法:將關(guān)系模式投影分解成兩個(gè)或兩個(gè)以上的關(guān)系模式。
      要求:分解后的關(guān)系模式集合應(yīng)當(dāng)與原關(guān)系模式“等價(jià)”,即經(jīng)過自然聯(lián)接可以恢復(fù)原關(guān)系而不丟失信息,并保持屬性間合理的聯(lián)系。

    posted @ 2014-05-21 10:10 順其自然EVO 閱讀(527) | 評(píng)論 (0)編輯 收藏

    Win7 64位下安裝Selenium

     例子是基于python的,selenium是做啥的自己看吧。
      網(wǎng)上找了下,貌似有些已經(jīng)過時(shí)了,重新弄了下,記錄過程。
      0.安裝python
      (略)
      我的python版本是2.7.5
      1.安裝easy_install
      只能使用http://peak.telecommunity.com/dist/ez_setup.py進(jìn)行安裝,將文件下載下來后直接python運(yùn)行即可;
      2.安裝pip
      在c:\Python27\Scripts目錄(目錄可能不是這個(gè),取決于你的python安裝目錄)下可以找到安裝的easy_install,執(zhí)行easy_install.exe pip即可,自動(dòng)下載安裝。
      3.安裝selenium
      在c:\Python27\Scripts目錄(目錄可能不是這個(gè),取決于你的python安裝目錄)下可以找到安裝的pip,執(zhí)行pip install selenium即可,自動(dòng)下載安裝。
      4.下載chrome的webdriver
      對(duì)應(yīng)的下載目錄都在這里了 http://chromedriver.storage.googleapis.com/index.html ,自己尋找合適的版本下載即可,然后32位的也可以在64位的系統(tǒng)上跑,但是需要注意的是對(duì)應(yīng)的webdriver會(huì)需要chrome的版本,所以如果下載的比較新的webdriver版本,請(qǐng)更新自己的chrome版本。
      下載后將對(duì)應(yīng)的exe的目錄加入到環(huán)境變量,我是直接拷貝到c:\Python27目錄下了,這樣最方便。。。
      5.enjoy your time
      這里仍然來一個(gè)簡(jiǎn)單的例子。
    # -*- coding:utf-8 -*-
    from selenium import webdriver
    browser = webdriver.Chrome()
    browser.get("http://www.soso.com")
    print browser.title
    browser.close()
      打開chrome,打開soso的主頁(yè)(不要問我為啥選擇這個(gè)頁(yè)面,緬懷下過去),輸出頁(yè)面的title,關(guān)閉瀏覽器。
      OK,搞定收工,后續(xù)再繼續(xù)研究api。

    posted @ 2014-05-21 10:09 順其自然EVO 閱讀(2881) | 評(píng)論 (0)編輯 收藏

    僅列出標(biāo)題
    共394頁(yè): First 上一頁(yè) 108 109 110 111 112 113 114 115 116 下一頁(yè) Last 
    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    導(dǎo)航

    統(tǒng)計(jì)

    常用鏈接

    留言簿(55)

    隨筆分類

    隨筆檔案

    文章分類

    文章檔案

    搜索

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

    主站蜘蛛池模板: 亚洲一区中文字幕在线观看| 精品免费tv久久久久久久 | 免费又黄又爽又猛的毛片| 亚洲国产精品综合久久网各| 久久福利青草精品资源站免费| 亚洲人成网站观看在线播放| 亚洲av成人一区二区三区观看在线 | 在线播放亚洲第一字幕| 香蕉97碰碰视频免费| 四虎在线播放免费永久视频| 亚洲熟女乱色一区二区三区| 性短视频在线观看免费不卡流畅| 亚洲经典在线观看| 99在线免费观看视频| 亚洲精品国产成人99久久| 免费成人在线视频观看| 人人狠狠综合久久亚洲88| 中文在线免费观看| 亚洲欧洲成人精品香蕉网| 中国性猛交xxxxx免费看| 亚洲人成伊人成综合网久久久| 亚洲一区二区三区免费| 亚洲中文字幕无码永久在线| 亚欧洲精品在线视频免费观看| 久久激情亚洲精品无码?V| 无码AV动漫精品一区二区免费 | 成人人免费夜夜视频观看| 亚洲最大的成人网| 好男人看视频免费2019中文| 成人区精品一区二区不卡亚洲| 成人无码区免费视频观看 | 最近最新的免费中文字幕| 一区二区亚洲精品精华液| 搡女人免费视频大全| 亚洲无码一区二区三区| 国产精品国产午夜免费福利看| 亚洲AV无码专区国产乱码不卡| 国产男女猛烈无遮挡免费视频 | 亚洲精品国产精品| 国产免费69成人精品视频| 黄色一级视频免费|