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

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

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

    qileilove

    blog已經轉移至github,大家請訪問 http://qaseven.github.io/

    操作系統:進程管理和IO控制

     一、進程管理
      進程管理包括進程控制,進程調度,進程同步與通信,死鎖控制四個內容。
      (一)進程控制
      進程是操作系統中運行的基本單位,包括程序段,數據段和進程控制段。操作系統通過進程控制塊(PCB)管理進程。每一個PCB唯一標示一個進程。它存儲進程的PID,UID,當前狀態等信息,以及進程執行某一時刻的寄存器值,并且指向進程的數據段和程序段。OS把所有PCB鏈接為一個鏈表。
      進程在剛剛被創建時出于new狀態。OS負責申請一塊存儲空間作為該進程的PCB,在其中填上進程的信息,標示為ready,鏈接到PCB隊列和就緒隊列中,此時進程進入"就緒"態。進程調度程序在未來某一時刻將其分配給處理機執行,該進程出于"執行"態,OS將根據PCB中的內容初始化處理機的各個寄存器值,之后進入用戶態執行;進程請求I/O或系統調用時將放棄處理機,進入"活動阻塞"態,進程調度程序將當前處理機各寄存器值壓棧(存入PCB中,或存入核心棧),將該進程PCB運行隊列中移入阻塞隊列;當系統調用或I/O完成時被喚醒再次進入活動就緒態,即PCB由阻塞隊列鏈入就緒隊列(操作系統如何實現喚醒進程??)。如果在系統調用完成之前,進程被換到外存,相應內存空間被釋放,則進程進入"靜止阻塞態",這里中級調度程序將和磁盤驅動程序一起,把相應內容拷貝到對換空間,同時釋放內存;系統調用完成時若仍在外存中而為未被換入,則出于"靜止就緒"態,之后被換入內存可直接進入執行態,或動態就緒態。進程執行完畢后進入"僵死"態,系統將釋放內存空間,回收所有資源。
      UNIX系統中提供的系統調用中,fork產生一個與父進程完全相同的子進程,在子進程的地址空間中運行;spawn則從文件中裝入一個文件作為子進程,在其地址空間運行;exec則從文件中裝一個進程到當前進程地址空間,覆蓋當前進程執行。
      (二)進程調度
      這里說的進程調度主要指低級調度,即由調度程序負責完成的,在內存和處理機之間的調度。在文件和內存之間的調度成為高級調度,又叫作業調度,在內存和對換空間的調度稱為中級調度,又稱對換。低級調度算法主要有:先來先服務FIFO;.短作業優先SJF;.時間片輪轉Round Robin;靜態優先級調度;多級隊列反饋輪轉調度。UNIX系統中采用的動態優先級+多級隊列反饋輪轉調度。具體內容可參見前面的文章
      (三)進程通信
      進程通信有直接通信和間接通信,直接通信是兩進程直接交換數據或發送信息,間接通信則是把信息發送到一個中間實體中。根據同步方式,可分為:1.發送進程阻塞,接受進程不阻塞;2.發送進程不阻塞,接受進程阻塞;3.發送進程和接收進程都阻塞。
      進程通信也分為高級通信和低級通信。
      高級通信通常傳輸數據量較大,包括:消息機制,共享存儲區,管道。
      1、消息機制:
      在消息機制里值得注意的是消息緩沖隊列方式。發送進程首先申請一塊緩沖區,把消息內容和消息首部填入其中,之后調用send系統調用。send將把該進程的消息內容拷貝到消息緩沖隊列中;接受進程首先申請接受緩沖區,之后調用receive系統調用,OS負責把消息緩沖隊列中的消息拷貝到該進程空間的緩沖區中。
      2、管道通信:
      相當于一個隊列形式的一個進程在管道尾寫,另一個進程在管道頭取,管道分為無名管道和有名管道。無名管道是用pipe函數創建的,只能用于子進程之間的通信,有名管道用mkfifo函數創建用于任意兩個進程之間通信,對管道的操作相當于對文件的操作比如open函數打開管道close函數關閉管道等。
      低級通信包括同步和互斥兩種,通過信號量實現,主要針對臨界區問題。臨界區的互斥訪問要遵循4個原則:空閑讓進,忙則等待,有限等待,讓權等待。主要信號量分為0/1信號量,整型信號量,記錄型信號量,AND型信號量和信號量集。經典同步問題有生產者-消費者問題,讀者-寫者問題,哲學家進餐問題。
      信號量:信號量是操作系統提供的管理公有資源的手段,即PV操作。對于獨享設備有驅動程序做PV操作。
      p操作的過程是:s=s-1;if(s<0){進入等待隊列,自己阻塞進程}
      v操作的過程是:s=s+1;if(s<0){從等待隊列取一個進程;取出的進程進入就緒隊列,當前進程該干嘛干嘛}
      pv原語不能次序錯誤,而且必須成對出現。信號量的定義是semaphore mutex;經典同步問題有生產者-消費者問題;讀者-寫者問題;哲學家進餐問題。
      (四)死鎖控制
      由于進程競爭資源或推進順序非法,可能會造成進程死鎖。死鎖有四個必要條件:互斥訪問,請求保持,非剝奪,環路等待。打破其中任何一個都可以不出現死鎖。對于死鎖的處理有三類方法:
      1.死鎖預防:主要是打破死鎖必要條件中的一個。打破請求保持條件,則要求進程在執行之前一次性請求到所有資源才可以執行;打破非剝奪條件,要求進程執行過程中因為缺少資源無法執行時,剝奪所有資源,將其阻塞;打破環路等待條件,則給資源編號,要求進程請求資源的順序依照編號由高到低進行。
      2.死鎖避免:指Dijkstra的銀行家算法;
      3.死鎖檢測消除:指在發生死鎖時檢測資源分配圖中是否有子環,然后將一個或多個進程掛起,消除死鎖;檢測算法是NP完全問題,CPU代價較大。
      二、輸入輸出(I/O)管理
      (一)   I/O管理概述
      1.  I/O管理功能
      (1)   動態的紀錄各種設備的狀態
      (2)   設備分配與回收
      (3)   實施設備驅動和中段處理的工作
      2.  I/O應用接口
      (1)  設備和設備控制器的接口:設備和cpu之間不是直接通信的而是夾著一個設備控制器,設備與設備控制器是靠三根線相連的,數據信號線,控制信號線和狀態信號線,數據信號線用于在設備和設備控制器之間傳送數據信號,控制信號線傳送由設備控制器向I/O設備發送控制信號,狀態信號線用于傳送設備當前狀態的信號。
      (2)  設備控制器:控制一個或多個I/O設備,其基本功能有接收和識別(cpu發的)命令,數據交換(與cpu或與設備數據交換),標示和報告設備的狀態(給cpu發)地址識別,數據緩沖,差錯控制。
      (3)  設備控制器由三部分組成:設備控制器與處理器的接口(由數據線連接DMR和控制狀態寄存器,控制線,和地址線組成),設備控制器與設備的接口(多個設備接口,每個設備接口由數據控制和狀態三種信號),I/O邏輯(當cpu啟動一個設備時,將啟動命令發給I/O邏輯同時通過地址線給I/O邏輯由它進行譯碼。。譯出命令后對所選設備進行控制。所以地址線和控制線是直接跟I/O邏輯相連的。

      3.  I/O通道
      I/O通道是特殊的處理機。它具有執行I/O指令的能力,并通過執行通道程序來控制I/O操作,它的指令單一主要與I/O操作相關的指令,通道沒有自己的內存,它和CPU共享內存。通道又分為字節多路通道,數組選擇通道,和數組多路通道。
      (1)  數組選擇通道:又稱告訴通道,在物理上可以連接多個設備,但某一段時間內通道只能選擇一個設備進行工作
      (2)  數組多路通道:當某設備進行數據傳送時,通道只為該設備服務,當設備在執行尋址等控制性動作時,通道掛起該設備的通道程序,去為其他設備服務。
      (3)  字節多路通道:用于大量低速設備,與設備之間數據傳送的基本單位是字節,為一個設備傳送一個字節后,又可以為另個設備傳送一個字節。數組多路通道傳輸的基本單位是塊。而且一次只能有一個設備在傳輸數據。
      4.  I/O控制方式
      (1)  程序I/O方式:忙等方式。
      (2)  中段驅動I/O方式:當某進程要啟動某個I/O設備工作時,便由cpu向相應的設備控制器發出一條I/O命令,然后立即返回繼續執行原來的任務,此時,CPU和 I/O設備并行操作。以字節為單位進行I/O。
      (3)  DMA I/O方式:直接存儲器訪問方式數據傳輸的單位是塊,數據之間在設備和內存中進行交換,僅塊傳輸的開始和結束時才需要CPU干預。(替代了設備控制 器)。DMA控制器中有四類寄存器:命令寄存器(存cpu發的控制命令或設備的狀態),內存地址寄存器,數據寄存器(緩沖數據作用),數據計數器(存本次要讀的字節數)。
      (4)  I/O通道控制方式:通道是通過執行通道程序,并與設備控制器共同實現對I/O設備的控制的。通道指令格式為命令
      1)操作碼:規定了指令所執行的操作。
      2)內存地址:標明讀操作和寫操作時的內存首址
      3)計數:表示本條占領所要讀或寫數據的字節數
      4)通道程序結束位P:用于表示通道程序是否結束
      5)記錄結束標志R。
      三、設備管理
      設備管理包括:緩沖管理,設備分配和設備處理三個內容。
      進程I/O一共有四種方式:程序查詢方式,程序中斷方式,DMA方式,通道控制方式。通道處理器也是一個處理機,但是與CPU相比,它指令較為單一,沒有獨立的存儲空間(使用內存空間)。通道分為字節多路通道,數組多路通道和數組選擇通道。
      (一)緩沖管理
      緩沖管理包括單緩沖,雙緩沖,循環緩沖和緩沖池。這里需要注意的是緩沖池。它包括輸入隊列,輸出隊列和空閑隊列,有四種工作方式:
      1.收容輸入方式:進程請求向緩沖區輸入,從空閑緩沖隊列中申請一個緩沖區輸入,將之鏈接在輸入隊列尾;
      2.提取輸入方式:進程要從緩沖區中提取輸入,則從輸入隊列尾部緩沖區中拷貝信息,同時釋放空間,將之重新鏈入空閑隊列中;
      3.收入輸出方式:進程要想緩沖區輸出,從空閑緩沖區隊列中申請一個緩沖區輸出,將之鏈接在輸出隊列尾;
      4.提取輸出方式:進程要想緩沖區提取輸出,則從輸出隊列尾部緩沖區中拷貝信息,同時釋放空間,將之重新鏈入空閑隊列中。
      (二)設備分配
      操作系統為實現設備分配需要配備的數據結構有:設備使用表,控制器使用表,通道使用表,系統設備表。利用設備獨立性的思想分配設備。所謂設備獨立性,是指使用邏輯設備名稱來請求某種設備,而系統通過某種名稱轉換方式將其轉換成物理設備名稱進行分配。實現設備獨立性需要再驅動程序之上再增加一層設備獨立性軟件,負責實現名稱和地址的轉換,這種轉換信息使用邏輯設備表LUT實現,每一個表項存儲邏輯設備名,物理設備名,驅動程序入口地址。使用設備獨立性方法實現設備分配,具有靈活性和易于設備重定向。
      SPOOLing是設備分配的一個重要例子。它實現了獨占設備的共享。SPOOLing系統包括輸入井,輸出井,輸入緩沖,輸出緩沖,輸入進程和輸出進程。進程在請求獨占設備輸出時,OS要求它先把要輸出的內容輸入到輸入緩沖,之后有輸入進程將其輸入到輸入井中,此時如果處理機要處理這些數據,則從輸入井中取數據;獨占設備要輸出時,由輸出進程將輸出井中的信息送至輸出緩沖,由輸出設備輸出。這種方式實現了獨占設備的共享,如網絡打印機。
      (三)設備處理
      設備處理指的是一系列驅動程序。它們是OS與外設的接口,把OS進程發來的抽象的要求轉化為對硬件的具體的控制命令,如設置命令控制字,讀取設備狀態信息,啟動設備等。它們對操作系統提供統一的接口,方便用戶調用。

    posted @ 2014-06-20 13:24 順其自然EVO 閱讀(672) | 評論 (0)編輯 收藏

    Linux下LVM的配置詳解

     LVM是Logical Volume Manager(邏輯卷管理器)的簡寫,它為主機提供了更高層次的磁盤存儲管理能力。LVM可以幫助系統管理員為應用與用戶方便地分配存儲空間。在LVM管理下的邏輯卷可以按需改變大小或添加移除。另外,LVM可以為所管理的邏輯卷提供定制的命名標識。因此,使用LVM主要是方便了對存儲系統的管理,增加了系統的擴展性。
      一、準備lvm環境
      1.硬盤的準備
      添加了一塊硬盤/dev/hdb。
      準備了三個分區,方案如下:容量為100M,僅為了實驗準備。
      /dev/hdb1
      /dev/hdb2
      /dev/hdb3
      2.轉換分區類型為lvm卷
      fdisk /dev/hdb
      t轉換為lvm卷類型
      Device Boot      Start    End      Blocks  Id  System
      /dev/hdb1        1         208      98248+  8e  Linux LVM
      /dev/hdb2        209      416      98280   8e  Linux LVM
      /dev/hdb3        417      624      98280   8e  Linux LVM
      然后w保存并且
      #partprobe       /*使用磁盤分區生效*/
      二、lvm創建過程
      1.從硬盤驅動器分區中創建物理卷(physical volumes-PV)。
      2.從物理卷中創建卷組(volume groups-VG)
      3.從卷組中創建邏輯卷(logical volumes-LV),并分派邏輯卷掛載點,其中只有邏輯卷才可以寫數據。
      lvm的最大的特點就是可以動態的調整分區的大小,并且可以隨著分區容量的增長而增加磁盤空間的容量。
      LVM配置與創建
      三、LVM的物理卷PV
      1.相關命令
      pvcreate  創建PV
      pvscan    掃描PV
      pvdisplay 顯示PV
      pvremove  刪除PV
      partprobe
      2.創建物理卷
      如果以上容量不夠,可以再添加其它分區到物理卷中。
    [root@redhat ~]# pvcreate /dev/hdb1 /dev/hdb2
    Physical volume “/dev/hdb1″ successfully created
    Physical volume “/dev/hdb2″ successfully created
    [root@redhat ~]# pvscan
    PV /dev/hdb1         lvm2 [95.95 MB]
    PV /dev/hdb2         lvm2 [95.98 MB]
    Total: 2 [191.92 MB] / in use: 0 [0   ] / in no VG: 2 [191.92 MB]
    [root@redhat ~]# pvdisplay
    — NEW Physical volume —
    PV Name               /dev/hdb1
    VG Name
    PV Size               95.95 MB
    Allocatable           NO
    PE Size (KByte)       0
    Total PE              0
    Free PE               0
    Allocated PE          0
    PV UUID               2Ni0Tx-oeSy-zGUP-t7KG-Fh22-0BUi-iyPhhQ
    — NEW Physical volume —
    PV Name               /dev/hdb2
    VG Name
    PV Size               95.98 MB
    Allocatable           NO
    PE Size (KByte)       0
    Total PE                  0
    Free PE                  0
    Allocated PE           0
    PV UUID               2XLXfY-V3L2-Mtsl-79U4-ovuJ-YaQf-YV9qHs
    四、創建LVM的卷組VG
      1.相關命令
      vgcreate   創建VG
      vgscan     掃描VG
      vgdispaly
      vgextend
      vgreduce
      vgchange
      vgremove
      2.創建邏輯卷VG
    [root@redhat ~]# vgcreate vg0 /dev/hdb1 /dev/hdb2
    Volume group “vg0″ successfully created
    [root@redhat ~]# vgscan
    Reading all physical volumes.  This may take a while…
    Found volume group “vg0″ using metadata type lvm2
    [root@redhat ~]# vgdisplay
    — Volume group —
    VG Name               vg0
    System ID
    Format                lvm2
    Metadata Areas        2
    Metadata Sequence No  1
    VG Access             read/write
    VG Status             resizable
    MAX LV                0
    Cur LV                0
    Open LV               0
    Max PV                0
    Cur PV                2
    Act PV                2
    VG Size               184.00 MB
    PE Size               4.00 MB   /*分配的塊的大小默認為4M*/
    Total PE              46
    Alloc PE / Size       0 / 0
    Free  PE / Size       46 / 184.00 MB
    VG UUID               kL5CGk-5Odk-r3PK-9q0A-s94h-OHv4-BojBnH增加VG容量到1TB的方法:
    vgcreate -s 16M vg0 /dev/hdb1 /dev/hdb2
      3.刪除與添加邏輯卷
      [root@redhat ~]# vgreduce vg0 /dev/hdb2
      Removed “/dev/hdb2″ from volume group “vg0″
      [root@redhat ~]# vgextend vg0 /dev/hdb2
      Volume group “vg0″ successfully extended
      五、創建LVM的邏輯卷LV
      1.相關命令
      lvcreate
      lvscan
      lvdisplay
      lvextend
      lvreduce
      lvremove
      lvresize
      2.創建邏輯卷LV
    [root@redhat ~]# lvcreate -L 184M -n data vg0
    Logical volume “data” created
    [root@redhat ~]# lvscan
    ACTIVE     ‘/dev/vg0/data’ [184.00 MB] inherit
    [root@redhat ~]# lvdisplay
    — Logical volume —
    LV Name                /dev/vg0/data
    VG Name                vg0
    LV UUID                HNKO5d-yRre-qVnP-ZT8D-fXir-XTeM-r6WjDX
    LV Write Access        read/write
    LV Status              available
    # open                 0
    LV Size                184.00 MB
    Current LE             46
    Segments               2
    Allocation             inherit
    Read ahead sectors     0
    Block device           253:0
      六、掛載LVM的邏輯卷LV
      lv的格式化:
      mkfs.ext3 /dev/vg0/data
      mdkir /mnt/lvm
      mount /dev/vg0/data /mnt/lvm
      [root@redhat ~]# ls /mnt/lvm
      lost+found
      [root@redhat ~]# df -T
      文件系統      類型     1K-塊        已用     可用 已用% 掛載點
      /dev/hda3     ext3     7625092   2219460   5012040  31% /
      /dev/hda1     ext3      101086     10006     85861  11% /boot
      tmpfs        tmpfs      150108         0    150108   0% /dev/shm
      /dev/mapper/vg0-data
      ext3      182469      5664    167385   4% /mnt/lvm
      七、LVM的容量調整
      LVM的容量調整可以在多個環節進行調整,比如:可以在物理卷上,VG上,以及LV上,都可以進行容量的擴展,這也是LVM它的一個優勢所在。
      1.添加物理卷
      首先應卸載在使用過程中的LV,然后必須保證該磁盤的類型是lvm類型,才能添加進來。
    [root@redhat ~]# umount /dev/vg0/data
    [root@redhat ~]# pvcreate /dev/hdb3
    Physical volume “/dev/hdb3″ successfully created
    [root@redhat ~]# pvscan
    PV /dev/hdb1   VG vg0   lvm2 [92.00 MB / 0    free]
    PV /dev/hdb2   VG vg0   lvm2 [92.00 MB / 0    free]
    PV /dev/hdb3            lvm2 [95.98 MB]
    Total: 3 [279.98 MB] / in use: 2 [184.00 MB] / in no VG: 1 [95.98 MB]
      2.添加VG的容量
      把上面新添加的LVM磁盤加入到vg0卷組中。
    [root@redhat ~]# vgextend vg0 /dev/hdb3
    Volume group “vg0″ successfully extended
    [root@redhat ~]# vgdisplay
    — Volume group —
    VG Name               vg0
    System ID
    Format                lvm2
    Metadata Areas        3
    Metadata Sequence No  5
    VG Access             read/write
    VG Status             resizable
    MAX LV                0
    Cur LV                1
    Open LV               0
    Max PV                0
    Cur PV                3
    Act PV                3
    VG Size               276.00 MB
    PE Size               4.00 MB
    Total PE              69
    Alloc PE / Size       46 / 184.00 MB
    Free  PE / Size       23 / 92.00 MB
    VG UUID               kL5CGk-5Odk-r3PK-9q0A-s94h-OHv4-BojBnH
      3.添加入LV中VG增珈的容量
      把新加入LVM磁盤的容量加入LV中。
      [root@redhat ~]# lvextend -L +92M /dev/vg0/data
      Extending logical volume data to 276.00 MB
      Logical volume data successfully resized
      [root@redhat ~]# lvscan
      ACTIVE      ‘/dev/vg0/data’ [276.00 MB] inherit
      [root@redhat ~]# resize2fs -f /dev/vg0/data
      resize2fs 1.39 (29-May-2006)
      Resizing the filesystem on /dev/vg0/data to 282624 (1k) blocks.
      The filesystem on /dev/vg0/data is now 282624 blocks long.
      如果不做這一步的話,在實現掛載的時候,發現LV的容量沒有真正的加入進LV卷中,因為相關信息寫入到了磁盤超級塊中。
      4.掛載使用
      [root@redhat ~]# mount /dev/vg0/data /mnt/lvm
      [root@redhat ~]# df
      文件系統               1K-塊        已用     可用 已用% 掛載點
      /dev/hda3              7625092   2219468   5012032  31% /
      /dev/hda1               101086     10006     85861  11% /boot
      tmpfs                   150108         0    150108   0% /dev/shm
      /dev/mapper/vg0-data    273569      6168    256097   3% /mnt/lvm
      LVM的卸載
      八、LVM的卸載方法
      如果不想使用LVM的話,可以卸載它, 卸載的方法與分區的刪除方法類似,就是最后創建的最先刪除。順序如下:
      先刪除LV
      再刪除VG
      最后PV
      以前的LVM的分區應用fdisk轉換成其它類型的文件系統,當普通分區使用。
      九、LVM的卸載過程
      1.umount取消掛載
    [root@redhat ~]# df
    文件系統               1K-塊        已用     可用 已用% 掛載點
    /dev/hda3              7625092   2219468   5012032  31% /
    /dev/hda1               101086     10006     85861  11% /boot
    tmpfs                   150108         0    150108   0% /dev/shm
    /dev/mapper/vg0-data    273569      6168    256097   3% /mnt/lvm
    [root@redhat ~]# umount /mnt/lvm
      2.刪除LV邏輯卷
      [root@redhat ~]# lvremove /dev/vg0/data
      Do you really want to remove active logical volume “data”? [y/n]: y
      Logical volume “data” successfully removed
      3.刪除VG卷組
      [root@redhat ~]# vgchange -a n vg0
      0 logical volume(s) in volume group “vg0″ now active
      說明:把vg0轉換成休眠狀態,實驗中這一步可以不用。
      [root@redhat ~]# vgremove vg0
      Volume group “vg0″ successfully removed
      4.刪除PV
    [root@redhat ~]# pvscan 查看pv的情況
    PV /dev/hdb1         lvm2 [95.95 MB]
    PV /dev/hdb2         lvm2 [95.98 MB]
    PV /dev/hdb3         lvm2 [95.98 MB]
    Total: 3 [287.90 MB] / in use: 0 [0   ] / in no VG: 3 [287.90 MB]
    [root@redhat ~]# pvremove /dev/hdb1 /dev/hdb2 /dev/hdb3
    Attempt to close device ‘/dev/cdrom’ which is not open.
    Labels on physical volume “/dev/hdb1″ successfully wiped
    Labels on physical volume “/dev/hdb2″ successfully wiped
    Labels on physical volume “/dev/hdb3″ successfully wiped
      5.最后就是用fdisk修改磁盤的類型了。

    posted @ 2014-06-20 13:23 順其自然EVO 閱讀(3005) | 評論 (0)編輯 收藏

    Mybatis通用操作數據庫方法類總結

     在項目中用到myBatis作為orm框架,與spring結合,通常的做法是寫一個通用的數據庫操作類,包括對數據庫的增、刪、改、查操作。
      具體的實現類如下:
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.ResultSetMetaData;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import org.apache.log4j.Logger;
    import org.mybatis.spring.SqlSessionTemplate;
    import org.mybatis.spring.SqlSessionUtils;
    import org.railway.com.trainplan.entity.QueryResult;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Repository;
    import org.springframework.util.Assert;
    /**
    * 基礎框架的數據訪問層抽象實現類myBatis orm框架,
    * 所有模塊的數據訪問層實現類均繼承該類。<br>
    *
    * JDK版本:JDK1.6
    * @version 1.0
    */
    @Repository
    public class BaseDao {
    private static final Logger log = Logger.getLogger(BaseDao.class);
    @Autowired
    public SqlSessionTemplate sqlSession;
    /**
    * 獲取數據庫連接對象
    * @return
    */
    private Connection getConnection(){
    Connection connection = SqlSessionUtils.getSqlSession(
    sqlSession.getSqlSessionFactory(), sqlSession.getExecutorType(),
    sqlSession.getPersistenceExceptionTranslator()).getConnection();
    return connection;
    }
    //=================================以下代碼為myBatis實現的常用方法(后綴加BySql)===========================//
    /**
    * 往庫表中插入記錄
    * @param statementId 調用myBatis的mapper文件的聲明段名,
    * 規則名:mapper的namespace+"." + 該mapper文件某片段的id
    * @param value 要操作的對象
    * @return 插入成功的記錄數
    */
    public int insertBySql(String statementId, Object value) {
    return sqlSession.insert(statementId, value);
    }
    /**
    * 刪除庫表中的記錄(可批量刪除),返回刪除成功的記錄數。
    *
    * @param statementId 調用myBatis的mapper文件的聲明段名,
    * 規則名:mapper的namespace+"." + 該mapper文件某片段的id
    * @param value  刪除條件值
    * @return 刪除成功的記錄數
    */
    public int deleteBySql(String statementId, Object value) {
    return sqlSession.delete(statementId, value);
    }
    /**
    * 更新庫表中的記錄(可批量更新),返回更新成功的記錄數。
    *
    * @param statementId 調用myBatis的mapper文件的聲明段名,
    * 規則名:mapper的namespace+"." + 該mapper文件某片段的id
    * @param value       更新條件值值
    * @return 更新成功的記錄數
    */
    public int updateBySql(String statementId, Object value) {
    return sqlSession.update(statementId, value);
    }
    /**
    * 查詢符合條件的記錄,生成List返回。
    *
    * @param statementId 調用myBatis的mapper文件的聲明段名,
    * 規則名:mapper的namespace+"." + 該mapper文件某片段的id
    * @param value       查詢條件值
    * @return list       找到的記錄
    */
    public List selectListBySql(String statementId, Object value) {
    List list = sqlSession.selectList(statementId, value);
    //數據庫含有空格的字段值(char類型),裝載到myBatis生成的持久對象屬性中,是否自動去掉屬性值左右兩邊的空格
    //如果需要去掉空格,則使用如下方式
    //list = Config.getPropertyBool("po.propertyvalue.trimable", true) ? BeanUtil.trim(list) : list
    return list;
    }
    /**
    * 查詢單個符合條件的記錄,生成Object返回</br>
    * @param statementId 調用myBatis的mapper文件的聲明段名,
    * 規則名:mapper的namespace+"." + 該mapper文件某片段的id
    * @param parameter   查詢條件值
    * @return list 找到的記錄
    */
    public Object selectOneBySql(String statementId, Object parameter) {
    Object bean = sqlSession.selectOne(statementId, parameter);
    //數據庫含有空格的字段值(char類型),裝載到myBatis生成的持久對象屬性中,是否自動去掉屬性值左右兩邊的空格
    //如果需要去掉空格,則使用如下方式
    //bean = Config.getPropertyBool("po.propertyvalue.trimable", true) ? BeanUtil.trim(bean) : bean
    return bean;
    }
    /**
    * myBatis分頁查詢符合條件的記錄
    * <p>
    * 注意:調用該方法,必須在myBatis的mapper文件中存在statementId_COUNT片段</br>
    * <b>特別說明:由于該命令采用的分頁技術不是數據庫本身的分頁技術,而是采用ResultSet的absolute定位技術,<br>
    * 需要把查詢結果全部裝入ResultSet再定位。如果查詢結果較大(1萬條記錄以上),效率會很低。<br>
    * 建議使用Hibernate的query方法或在mapper的XML中使用數據庫內部分頁技術<br>
    * (即把pageId,pageSize作為參數傳入SQL語句的類似limit n,m中)來查詢。
    * </b>
    * </p>
    * @param statementId 調用myBatis的mapper文件的聲明段名,規則名:mapper的namespace+"." + 該sqlMap文件某片段的id
    * @param parameter   查詢條件對象
    * @param offset       返回查詢結果的起始行,從0開始
    * @param pageSize        返回查詢結果的最大行數
    * @throws com.fbd.crm.exception.BaseUncheckedException
    * @return com.fbd.crm.common.QueryResult
    */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public QueryResult selectListForPagingBySql(String statementId, Object parameter) throws Exception{
    Assert.hasText(statementId, "傳入的SQL配置ID不能為空.");
    //計算總頁數
    Integer count = null;
    try {
    count = (Integer)sqlSession.selectOne(statementId + "_COUNT", parameter);
    } catch (Exception e) {
    String msg = "配置文件中不存在COUNT語句或發生其它例外,無法執行總數統計. SqlMap id:" + statementId;
    log.error(msg, e);
    throw new Exception(e);
    }
    if ((count == null) || (count.intValue() <= 0)) {
    log.info("執行COUNT后,返回的結果數為0,表示該SQL執行無結果數據返回.因此提前終止其數據查詢并立即返回空集.");
    return new QueryResult(new ArrayList(), 0);
    }
    List resultList = sqlSession.selectList(statementId, parameter);
    QueryResult result = new QueryResult(resultList, count);
    return result;
    }
    /**
    * myBatis帶匯總查詢
    * </b>
    * </p>
    * @param statementId 調用myBatis的mapper文件的聲明段名,規則名:mapper的namespace+"." + 該sqlMap文件某片段的id
    * @param parameter   查詢條件對象
    * @throws com.fbd.crm.exception.BaseUncheckedException
    * @return com.fbd.crm.common.QueryResult
    */
    public QueryResult selectListWithTotal(String statementId, Object parameter) throws Exception{
    Assert.hasText(statementId, "傳入的SQL配置ID不能為空.");
    //計算總頁數
    Integer count = null;
    try {
    count = (Integer)sqlSession.selectOne(statementId + "_COUNT", parameter);
    } catch (Exception e) {
    String msg = "配置文件中不存在COUNT語句或發生其它例外,無法執行總數統計. SqlMap id:" + statementId;
    log.error(msg, e);
    throw new Exception(e);
    }
    if ((count == null) || (count.intValue() <= 0)) {
    log.info("執行COUNT后,返回的結果數為0,表示該SQL執行無結果數據返回.因此提前終止其數據查詢并立即返回空集.");
    return new QueryResult(new ArrayList(),new ArrayList(), 0);
    }
    List resultList = sqlSession.selectList(statementId, parameter);
    List totalList = sqlSession.selectList(statementId + "_TOTAL", parameter);
    QueryResult result = new QueryResult(resultList, totalList, count);
    return result;
    }
    /**
    * 執行sql
    * @category 該事務不在spring事務管理機制內
    * @param sql
    * @return List
    * @throws SQLException
    */
    public List executeQueryBySql(String sql) throws SQLException{
    Connection con = null;
    Statement stmt = null;
    List list = new ArrayList();
    try {
    con = this.getConnection();//sqlSession.getConnection();
    con.setAutoCommit(false);
    stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
    ResultSet rSet = stmt.executeQuery(sql);
    ResultSetMetaData md = rSet.getMetaData();
    int num = md.getColumnCount();
    while (rSet.next()) {
    Map mapOfColValues = new HashMap(num);
    for (int i = 1; i <= num; i++) {
    mapOfColValues.put(md.getColumnName(i), rSet.getObject(i));
    }
    list.add(mapOfColValues);
    }
    con.commit();
    } catch (Exception e) {
    e.printStackTrace();
    throw new SQLException("sql執行失敗");
    }finally {
    if (stmt != null) {
    stmt.close();
    }
    // 此處不關閉,連接關閉由org.springframework.jdbc.datasource.DataSourceTransactionManager自動完成
    if (con != null) {
    con.close();
    }
    }
    return list;
    }
    }
    采用的myBatis版本和Maven配置:
    <!-- mybatis -->
    <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.2.7</version>
    </dependency>
    <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.2.2</version>
    </dependency>
      3.怎么調用
      在service中將上面的BaseDao類注入就可以調用了它的方法了。比如:
    @Autowired
    private BaseDao baseDao;

    posted @ 2014-06-20 13:22 順其自然EVO 閱讀(799) | 評論 (0)編輯 收藏

    數據庫同步和數據庫復制技術分析

    如何針對不同的應用選擇不同的產品,需要我們必須清楚數據庫同步和數據庫復制的具體含義。
      無論概念如何定義,我們都必須清楚,這兩種操作的基礎是數據庫中的數據,但是包含的數據內容卻有所不同
      數據庫同步,顧名思義,就是把數據中的交易數據(包含更改的數據以及相關的DDL數據描述語句)實時的同步到另外一個數據庫;而數據庫復制則針對的是數據庫中的數據,同時他對實時性要求不高,重點關注的是某一時間點的數據一致性,同時也不需要同步DDL語句,只要把數據同步過去就可以了。
      綜上所述,數據庫復制只是對某一時刻的數據的同步,而不是數據庫同步那樣的實時同步,并且不包含DDL語句同步。
      現在市場上有很多復制產品,比如VARITAS、LEGATO、SYMANTEC、EMC 、IBM等陣列廠商都有各自的備份復制產品,都滿足定期備份數據;而數據庫同步產品則獨立于硬件廠商,都是通過數據庫日志的技術來達到數據同步,比如ORACLE、QUEST、WOXINTECH等廠家,他們都是通過軟件來捕捉數據庫改變來達到數據同步的目的。
      但是這些產品如何能滿足我們的系統需求呢?我們要從應用的目標來進行分析就很容易選擇了。
      目前的應用主要分為5類:
      1、  雙機熱備:由于數據庫只有一份,數據做冗余,那么硬件廠家的產品更滿足
      2、  異地容災:對于大企業來說,采用硬件廠家的復制產品+軟件同步產品是個最佳選擇;對于中小企業來說,軟件同步產品應該更加合適,帶寬和硬件等成本最低
      3、  負載分擔:這類應用需要讓備庫能夠實時查詢,分擔主數據庫查詢的壓力,所以對實時要求最高,當主庫產生一筆交易,必須實時能在備庫進行查詢,所以必須選擇實時同步軟件
      4、  數據分發/集中:這類應用一般是總/分公司模式,有些計劃數據必須能從總部傳到分公司,同時分公司也會將生產情況傳回總部,這類需求一般對實時性要求不是很高,一般每天傳一次,所以目前很多系統通過自己的應用系統產生文件之后進行定時FTP發送,當然通過數據庫同步軟件也可以達到此目的,不過企業需要考慮成本問題
      5、  重要數據實時安全查詢系統VIDRSQS(Very Important Data Realtime Safety Query System):目前這類應用已經大范圍開展,主要集中在政府政務公開、醫療衛生系統、社保、商保系統,這類系統的最大特點是,需要實時同步交易系統的某 些數據,并且這些數據能夠對敏感信息進行過濾,從而達到實時公開的效果,這也是目前同步復制軟件正在經歷的新功能,不但要求對OWNER、TABLE甚至對字段都能夠進行過濾和操作
      通過以上的分析,大家可以看到,數據庫同步和數據庫復制還是有很多區別的,同時對應的產品也很多,只有仔細分辨,認真把握,才能在眾多的產品中找到最合適的,保證性假比最高,同時又能夠最大限度滿足需求。

    posted @ 2014-06-20 13:22 順其自然EVO 閱讀(163) | 評論 (0)編輯 收藏

    一個簡單的Java服務器

    /**
    *
    */
    package iotest.serversocket;
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.io.UnsupportedEncodingException;
    import java.net.ServerSocket;
    import java.net.Socket;
    /**
    * @author Brandon B. Lin
    *
    */
    public class SingleFileHttpServer extends Thread {
    private byte[] content;
    private byte[] header;
    private int port = 80;
    public SingleFileHttpServer(String data, String encoding, String MIMEType,
    int port) throws UnsupportedEncodingException {
    this(data.getBytes(encoding), encoding, MIMEType, port);
    }
    public SingleFileHttpServer(byte[] data, String encoding, String MIMEType,
    int port) throws UnsupportedEncodingException {
    this.content = data;
    this.port = port;
    createHeader(encoding, MIMEType);
    }
    private void createHeader(String encoding, String MIMEType)
    throws UnsupportedEncodingException {
    String header = "HTTP/1.0 200 OK\r\n" + "Server: OneFIle 1.0\r\n"
    + "Content-length: " + content.length + "\r\n"
    + "Content-type: " + MIMEType + "\r\n\r\n";
    this.header = header.getBytes(encoding);
    }
    @Override
    public void run() {
    try {
    ServerSocket server = new ServerSocket(port);
    log(server.getLocalPort());
    while (true) {
    respond(server);
    }
    } catch (IOException exception) {
    System.err.println("Could not start server. Port Occupied!");
    }
    }
    private void log(int port) {
    System.out.println("Accepting connection on port " + port);
    System.out.println("Data to be sent: ");
    try {
    System.out.write(content);
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    private void respond(ServerSocket server) {
    Socket connection = null;
    try {
    connection = server.accept();
    String request = readRequestFromSocket(connection);
    boolean writeHeader = (request.toString().indexOf("HTTP/") != -1);
    writeToSocket(connection, writeHeader);
    } catch (IOException e) {
    e.printStackTrace();
    } finally {
    if (connection != null)
    try {
    connection.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
    private String readRequestFromSocket(Socket connection) throws IOException {
    InputStream in = new BufferedInputStream(connection.getInputStream());
    StringBuffer request = new StringBuffer(80);
    while (true) {
    int readByte = in.read();
    if (readByte == '\r' || readByte == '\n' || readByte == -1)
    break;
    request.append((char) readByte);
    }
    return request.toString();
    }
    private void writeToSocket(Socket connection, boolean writeHeader)
    throws IOException {
    OutputStream out = new BufferedOutputStream(
    connection.getOutputStream());
    if (writeHeader) {
    out.write(header);
    }
    out.write(content);
    out.flush();
    }
    public static void main(String[] args) {
    String fileName = "index.html";
    String contentType = "text/html";
    String encoding = "ASCII";
    int port = 80;
    byte[] data = readFileToByteArray(fileName);
    try {
    new SingleFileHttpServer(data, encoding, contentType, port).start();
    } catch (UnsupportedEncodingException e) {
    e.printStackTrace();
    }
    }
    private static byte[] readFileToByteArray(String fileName) {
    byte[] data = null;
    try {
    InputStream in = new FileInputStream(fileName);
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    int readByte;
    while ((readByte = in.read()) != -1)
    out.write(readByte);
    data = out.toByteArray();
    in.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    return data;
    }
    }

    posted @ 2014-06-20 13:21 順其自然EVO 閱讀(263) | 評論 (0)編輯 收藏

    LoadRunner如何開展性能測試

     最近一直想理清思路,特別是碰到一些業務復雜的項目,要求做性能測試,結果一時就不知道怎么下手了。因為之前面試的時候,也碰到很多面試官對性能測試知識方面的提問,面試多了,就有經驗,現在寫下來,腦子不會亂,按思路,照說照做吧!!
      使用工具如何開展性能測試工作呢?不多說,自己的思路如下步驟:
      1、接到項目時,首先了解系統架構,是B/S,還是C/S,使用什么應用服務器(即中間件),什么數據庫
      2、熟悉系統的功能、業務流程,明確項目的性能需求是什么?有哪些性能指標?
      3、編寫性能測試計劃。(有些公司不要求寫)
      4、設計性能測試用例。(按場景設計思路來寫比較清晰)
      5、準備測試數據,如一些業務需要大數據量的,就要先造好數據。
      6、選擇錄制協議,錄制兩份業務需求功能一樣的腳本。(好處是:一是可以做腳本備份,二是腳本優化查找需要關聯的地方)
      7、優化腳本,包括設置參數化,檢查點,關聯,集合點,事務以及自行編寫的函數,日志輸出函數等。
      8、創建場景。(創建兩份一樣的場景,以20/80并發用戶原則遞增來設計,如并發要100個用戶,第一份創建80個用戶并發,第二份創建100個用戶并發,這樣在結果分析中容易發現)
      9、場景設計,添加集合點策略,負載均衡器,對“運行時設置”,如lr_think_time,迭代設置,日志輸出控制等。
      10、場景運行,添加監控圖表,服務器系統資源監控計數器,數據庫系統資源監控等。
      11、性能結果分析,通過監控圖表的數據(事務響應時間、點擊率、吞吐量)、系統資源分析、web頁面診斷分析等。
      12、收集測試結果,編寫性能測試報告。
      以上是自己總結的一點經驗,有不對的思路請大家多多指教。活到老,學到老!

    posted @ 2014-06-20 13:03 順其自然EVO 閱讀(625) | 評論 (0)編輯 收藏

    Python Selenium自動化(二)自動化注冊流程

    需求:使用python selenium來自動測試一個網站注冊的流程。
      假設這個網站的注冊流程分為三步,需要提供比較多的信息:
      在這個流程里面,需要用戶填入信息、在下拉菜單中選擇、選擇單選的radio button、多選框等等比較復雜的頁面元素操作。
      在開始做的時候,我會按照一個用戶真正的填寫習慣,參照頁面,一步步的定義每個步驟應該進行怎樣的操作,我需要查看每一個頁面元素的屬性,決定選擇一個合理的選項,然后再用代碼自動化這樣的操作。但是反思一下,其實這樣的實現方法并不符合一個計算機自動測試的習慣,也浪費了許多時間在手動的尋找頁面元素的相關屬性和合理操作值上。
      所謂的自動化,就是要機器在最大程度上的去代替人進行某些操作。而只有在程序自己沒有辦法判斷如何操作的時候,我們才為這類操作定義更詳細的規定。例如:從計算機的角度,并不需要關心radio button要選擇的是male或是female,也不關心下拉菜單中我們要選擇的具體的值是什么;它只需要確保radio button和下拉菜單中可以順利的選擇到一個元素。因此,在這個注冊流程中,我可以簡單的定義找到頁面上所有的radio button,然后挨個把它們都點擊一遍,這樣既能保證每個radio button都被點擊事件測試過,也能保證在注冊流程中必定有一個選項被選中了。同樣的,我可以定義對每個下拉菜單都選擇第一個字值。這樣的general的方法,可以把我從對每個元素都要找到它的屬性然后對其指定詳細的操作的辛苦中一次性的解放出來:
    def click_radio_btn(driver):
    radioBtns = driver.find_elements_by_xpath("http://input[@type='radio']")
    for radioBtn in radioBtns:
    if radioBtn.is_displayed():
    radioBtn.click()
      這里我多進行了一次radioBtn.is_displayed()的判斷,原因是在我的流程中有的radio button是在選擇了某些特定選項之后才會顯示在頁面上的,因此對于沒有顯示的元素就不必進行點擊操作,否則會拋出異常:
      selenium.common.exceptions.ElementNotVisibleException: Message: u'element not visible
      而對于需要填入具體信息的input field來說,我們就必須要給出具體每個輸入框的輸入值來告訴程序如何處理這些元素了。

      在這個這個注冊流程中,address的下拉菜單中市的選項是基于省份的選值的。這里有一個ajax的異步調用,當用戶選擇了一個省份之后才會對市下拉單中的值進行加載。于是問題出現了:由于網絡的原因,加載市的值的速度是不一定的,有時也許在執行對市的下拉菜單進行選值的時候其中的值其實還沒有加載完成。這時,雖然我們選擇的是第一個選項,還是會拋出異常:
      selenium.common.exceptions.NoSuchElementException: Message: 'Could not locate element with index 1'
      這種情況的解決辦法是:使用selenium.webdriver.support.ui.WebDriverWait提供的until來使webdriver等待到指定的條件滿足或是規定的timeout time到期:
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    driver = webdriver.Firefox()
    driver.get("http://www.zhuce.com")
    city = WebDriverWait(driver,10).until(EC.element_to_be_clickable((By.NAME,"city")))
    Select(city).select_by_index(1)
      這樣,webdriver將會等待name為“city”的元素enable了之后才算找到該元素,并繼續進行接下來的點擊操作。如果在10秒內該元素都沒有加載成功的話,才拋出異常:
      raise TimeoutException(message)
      selenium.common.exceptions.TimeoutException: Message: ''
      python selenium中提供了多種元素等待的條件:
    title_is title_contains
    presence_of_element_located
    visibility_of_element_located
    visibility_of
    presence_of_all_elements_located
    text_to_be_present_in_element
    text_to_be_present_in_element_value frame_to_be_available_and_switch_to_it
    invisibility_of_element_located
    element_to_be_clickable #it is Displayed and Enabled.
    staleness_of
    element_to_be_selected
    element_located_to_be_selected
    element_selection_state_to_be
    element_located_selection_state_to_be alert_is_present
    相關文章:
    Python Selenium自動化(一)點擊頁面鏈接測試

    posted @ 2014-06-20 11:52 順其自然EVO 閱讀(3156) | 評論 (0)編輯 收藏

    Python Selenium自動化(三)Chrome Webdriver的兼容

    當一個自動化測試被實現在一個瀏覽器之后,我們會希望我們的測試能夠覆蓋到盡量多的別的瀏覽器。通過跨平臺的測試來保證我們的程序在多個瀏覽器下都能正常工作
      在安裝了selenium之后,firefox webdriver和IE webdriver就已經是ready to use的了,但是如果想要在Chrome下進行測試的話需要再安裝Chrome webdriver。Chrome webdriver是由Chromium項目自己維護的,因此在實現上也和Firefox driver以及IE driver有一些不同之處。這就導致了在將在Firefox上可以正常運行的測試代碼運用到chrome時會產生一些兼容性的問題。
      1. WebDriverWait的可靠性
      上一篇博客提到過,當頁面上有使用ajax異步加載的元素的時候,為了確保對元素進行操作的時候該元素已經處在一個可用的狀態下了,我們可以使用WebDriverWait來使driver等待至目標元素滿足給出的條件時才繼續下面的操作。但是在Chrome中,這個方法似乎不是總是適用。
      2.一些奇怪的異常
      總的來說,測試代碼在Chrome下的運行是非常快的,至少人眼就可以感覺出來比Firefox下的運行速度要快一點,當然這是在不報異常的情況下。而實際情況是,在Firefox下可以正常運行的代碼,在Chrome下卻會出現問題,并且拋出一些奇怪的異常:
    selenium.common.exceptions.WebDriverException: Message: u'unknown error: Element is not clickable at point
    selenium.common.exceptions.InvalidElementStateException: Message: u'invalid element state
    selenium.common.exceptions.ElementNotVisibleException: Message: u'element not visible
      第一個異常的拋出一般會發生在radio button的點擊事件上,但是一般異常拋出的同時,系統會告訴你當前的element不可以被點擊,但是另外的某個元素可以接受這個點擊事件。這時我們可以查看頁面上系統給出的備選元素是否也可以滿足我們的點擊需求,如果可以,我們可以簡單的把radio button換成這個備選元素來回避這個異常。
      另外,這三個異常都與Chrome的運行速度以及WebDriverWait的可靠性有一定關系。因此,我們可以稍顯“粗暴”的讓webdriver休眠一段時間來slow down測試代碼的執行速度,強制driver等待一個固定的時間來讓元素加載完成。
      import time
      #some test code
      time.sleep(2)
      #continue test code
      time.sleep(2)會使driver休眠兩秒,然后再繼續執行后面的測試代碼
      3.不在當前視圖范圍內的元素的操作
      當我們使用Firefox webdriver來測試某個頁面的時候,如果我們選取了某個頁面元素來對其進行操作,但是這個元素不在當前瀏覽器顯示的視圖范圍內,Firefox webdriver的做法是自動的將視圖調整到該元素顯示的區域,然后對這個元素進行操作。也就是說driver自己完成了頁面的scroll down or up的操作。
      但是在Chrome webdriver中,如果待操作元素不在視圖顯示范圍內,則會拋出Element is not clickable at point異常。或是如果設置了WebDriverWait并且它正常工作的話會拋出Timeout異常。
      因此,在使用Chrome wbedriver的時候,我們要更加小心,對于需要滾動頁面才能顯示在視圖中的元素,我們需要添加代碼使頁面滾動至元素顯示的范圍,然后再對該元素進行操作。使頁面滾動的方法是:
      driver.execute_script("window.scrollBy(0,200)","")  #向下滾動200px
      driver.execute_script("window.scrollBy(0,document.body.scrollHeight)","")  #向下滾動到頁面底部
    相關文章
    Python Selenium自動化(一)點擊頁面鏈接測試
    Python Selenium自動化(二)自動化注冊流程

    posted @ 2014-06-20 11:52 順其自然EVO 閱讀(1530) | 評論 (0)編輯 收藏

    基于XAMPP的Testlink安裝方法

      下載一個最新的安裝包(也有免安裝的ZIP包,不過那個要配置,上面說安裝包是最簡單的了,咱就奔著簡單去吧)
      雙擊安裝包,安裝,記得選擇安裝 Apache和MySQL Service
      安裝完后直接運行XAMPP Control Pannel,但是打開的時候看到MySQL的狀態是Running了,但是Apache卻沒有。點擊Start,卻提示 Error: Apache service not started [-1]
      原因可能是Apache被防火墻阻止了導致它沒有啟動。
      解決:關掉 XAMPP Control Pannel,到 \xampp\apache目錄下,運行腳本apache_uninstallservice.bat, 然后再運行apache_installservice.bat,這個時候什么360啊防火墻啊都跳出來要你確認是否要解除阻止,那是必須的!這下Apache可以運行了。再打開XAMPP Control Pannel,卻提示: Busy.....Apache service started。那是因為80端口被占用了,apache啟動要占用80端口。
      解決:在cmd中運行 netstat -ano | findstr 80,查看80端口有沒有被占用,如果有,那么修改apache的端口。
      修改方法:
      到xampp\apache\conf目錄下,打開httpd.conf文件,將Listen 80 改成 Listen 8081, ServerName localhost:80 改成 ServerName localhost:8081, 保存文件,然后重啟XAMPP Control Pannel,世界又恢復和平了...
      然而,這樣做的好處是端口不會與其他沖突,但壞處也是顯而易見的,就是訪問的時候在瀏覽器中輸入地址后面也得加上端口號8081,這對客戶端用戶來說的確是個麻煩事。如果麻煩不可避免的話,那就越少越好吧,在服務器端操作一下,就是:把占用80端口的這個進程干掉(如上圖的話,關掉5428這個進程),然后再apache_uninstallservice.bat 一下,apache_installservice.bat一下即可(這些操作的時候不要關掉XAMPP Control Panel)
      這樣,我們的服務器就啟動起來了。那么讓我們登錄進去看看吧......
      打開瀏覽器,輸入 http://localhost/xampp/,選擇中文(當然,如果你喜歡其他語言的話也無妨,只要看的懂即可,呵哈)
      點擊 “安全”, 先來設置一下MySQL密碼
      設置完MySQL密碼之后,接下來就到我們最終的目標了,配置TestLink
      將TestLink壓縮包解壓到 xampp\htdocs目錄下,比如就testlink,然后在瀏覽器中輸入 http://localhost/test, 正式登陸testlink了
      一路設置基本默認即可,其實就那兩個地方要設置而已:


     這樣一步步到最后,就基本完成了testlink的配置了,so easy,哈哈~~
      配置好TestLink后,我們就可以登錄它,然后創建我們的project, test suite, test case了
      漢化:
      將strings.txt放到D:\xampp\htdocs\TestLink\locale\zh_CN 目錄下,替換原來的文件
      解決圖表里中文亂碼的問題:
      這個問題,曾經測試過多次都未能解決,這次能解決掉,估計原因是部分字體庫的問題,現在測試幼圓字體(SIMYOU.TTF)可以完美支持中文圖表顯示。此字體也可以直接在Windows系統字體庫中找到,方便的很。
      方法:
      步驟一:
      將下載或從Windows中提取到的字體SIMYOU.TTF拷貝到Testlink中的Pchat的字體目錄,路徑為:
      <testlink>\third_party\pchart\Fonts
      步驟二:
      修改config.inc.php:將$tlCfg->charts_font_path = TL_ABS_PATH . "third_**/pchart/Fonts/tahoma.ttf";中的字體重新設置:$tlCfg->charts_font_path = TL_ABS_PATH . "third_party/pchart/Fonts/SIMYOU.TTF";
      注:本次測試用的字體為從系統中提取的,測試過系統中提取的宋體、黑體和微軟雅黑都不行,其它字體希望有心的童鞋繼續測試。

    posted @ 2014-06-20 11:33 順其自然EVO 閱讀(775) | 評論 (0)編輯 收藏

    線程上下文切換的性能損耗測試

    線程上下文切換的性能損耗到底有多少,一直沒有直觀的理解,今天寫個程序測試一下。先看看下面的程序(點擊下載):
      
      ThreadTester是所有Tester的基類。所有的Tester都干的是同樣一件事情,把counter增加到100000000,每次只能加1。
    1: public abstract class ThreadTester
    2:     {
    3:         public const long MAX_COUNTER_NUMBER = 100000000;
    4:
    5:         private long _counter = 0;
    6:
    7:         //獲得計數
    8:         public virtual long GetCounter()
    9:         {
    10:             return this._counter;
    11:         }
    12:
    13:         //增加計數器
    14:         protected virtual void IncreaseCounter()
    15:         {
    16:             this._counter += 1;
    17:         }
    18:
    19:         //啟動測試
    20:         public abstract void Start();
    21:
    22:         //獲得Counter從開始增加到現在的數字所耗的時間
    23:         public abstract long GetElapsedMillisecondsOfIncreaseCounter();
    24:
    25:         //測試是否正在運行
    26:         public abstract bool IsTesterRunning();
    27:     }
    SingleThreadTester是單線程計數。
    1: class SingleThreadTester : ThreadTester
    2:     {
    3:         private Stopwatch _aStopWatch = new Stopwatch();
    4:
    5:         public override void Start()
    6:         {
    7:             _aStopWatch.Start();
    8:
    9:             Thread aThread = new Thread(() => WorkInThread());
    10:             aThread.Start();
    11:         }
    12:
    13:         public override long GetElapsedMillisecondsOfIncreaseCounter()
    14:         {
    15:             return this._aStopWatch.ElapsedMilliseconds;
    16:         }
    17:
    18:         public override bool IsTesterRunning()
    19:         {
    20:             return _aStopWatch.IsRunning;
    21:         }
    22:
    23:         private void WorkInThread()
    24:         {
    25:             while (true)
    26:             {
    27:                 if (this.GetCounter() > ThreadTester.MAX_COUNTER_NUMBER)
    28:                 {
    29:                     _aStopWatch.Stop();
    30:                     break;
    31:                 }
    32:
    33:                 this.IncreaseCounter();
    34:             }
    35:         }
    36:     }
      TwoThreadSwitchTester是兩個線程交替計數。
    1: class TwoThreadSwitchTester : ThreadTester
    2:     {
    3:         private Stopwatch _aStopWatch = new Stopwatch();
    4:         private AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
    5:
    6:         public override void Start()
    7:         {
    8:             _aStopWatch.Start();
    9:
    10:             Thread aThread1 = new Thread(() => Work1InThread());
    11:             aThread1.Start();
    12:
    13:             Thread aThread2 = new Thread(() => Work2InThread());
    14:             aThread2.Start();
    15:         }
    16:
    17:         public override long GetElapsedMillisecondsOfIncreaseCounter()
    18:         {
    19:             return this._aStopWatch.ElapsedMilliseconds;
    20:         }
    21:
    22:         public override bool IsTesterRunning()
    23:         {
    24:             return _aStopWatch.IsRunning;
    25:         }
    26:
    27:         private void Work1InThread()
    28:         {
    29:             while (true)
    30:             {
    31:                 _autoResetEvent.WaitOne();
    32:
    33:                 this.IncreaseCounter();
    34:
    35:                 if (this.GetCounter() > ThreadTester.MAX_COUNTER_NUMBER)
    36:                 {
    37:                     _aStopWatch.Stop();
    38:                     break;
    39:                 }
    40:
    41:                 _autoResetEvent.Set();
    42:             }
    43:         }
    44:
    45:         private void Work2InThread()
    46:         {
    47:             while (true)
    48:             {
    49:                 _autoResetEvent.Set();
    50:                 _autoResetEvent.WaitOne();
    51:                 this.IncreaseCounter();
    52:
    53:                 if (this.GetCounter() > ThreadTester.MAX_COUNTER_NUMBER)
    54:                 {
    55:                     _aStopWatch.Stop();
    56:                     break;
    57:                 }
    58:             }
    59:         }
    60:     }
     MultiThreadTester可以指定線程數,多個線程爭搶計數。
    1: class MultiThreadTester : ThreadTester
    2:     {
    3:         private Stopwatch _aStopWatch = new Stopwatch();
    4:         private readonly int _threadCount = 0;
    5:         private readonly object _counterLock = new object();
    6:
    7:         public MultiThreadTester(int threadCount)
    8:         {
    9:             this._threadCount = threadCount;
    10:         }
    11:
    12:         public override void Start()
    13:         {
    14:             _aStopWatch.Start();
    15:
    16:             for (int i = 0; i < _threadCount; i++)
    17:             {
    18:                 Thread aThread = new Thread(() => WorkInThread());
    19:                 aThread.Start();
    20:             }
    21:         }
    22:
    23:         public override long GetElapsedMillisecondsOfIncreaseCounter()
    24:         {
    25:             return this._aStopWatch.ElapsedMilliseconds;
    26:         }
    27:
    28:         public override bool IsTesterRunning()
    29:         {
    30:             return _aStopWatch.IsRunning;
    31:         }
    32:
    33:         private void WorkInThread()
    34:         {
    35:             while (true)
    36:             {
    37:                 lock (_counterLock)
    38:                 {
    39:                     if (this.GetCounter() > ThreadTester.MAX_COUNTER_NUMBER)
    40:                     {
    41:                         _aStopWatch.Stop();
    42:                         break;
    43:                     }
    44:
    45:                     this.IncreaseCounter();
    46:                 }
    47:             }
    48:         }
    49:     }
      Program的Main函數中,根據用戶的選擇來決定執行哪個測試類。
    1: class Program
    2:     {
    3:         static void Main(string[] args)
    4:         {
    5:
    6:             string inputText = GetUserChoice();
    7:
    8:             while (!"4".Equals(inputText))
    9:             {
    10:                 ThreadTester tester = GreateThreadTesterByInputText(inputText);
    11:                 tester.Start();
    12:
    13:                 while (true)
    14:                 {
    15:                     Console.WriteLine(GetStatusOfThreadTester(tester));
    16:                     if (!tester.IsTesterRunning())
    17:                     {
    18:                         break;
    19:                     }
    20:                     Thread.Sleep(100);
    21:                 }
    22:
    23:                 inputText = GetUserChoice();
    24:             }
    25:
    26:             Console.Write("Click enter to exit...");
    27:         }
    28:
    29:         private static string GetStatusOfThreadTester(ThreadTester tester)
    30:         {
    31:             return string.Format("[耗時{0}ms] counter = {1}, {2}",
    32:                     tester.GetElapsedMillisecondsOfIncreaseCounter(), tester.GetCounter(),
    33:                     tester.IsTesterRunning() ? "running" : "stopped");
    34:         }
    35:
    36:         private static ThreadTester GreateThreadTesterByInputText(string inputText)
    37:         {
    38:             switch (inputText)
    39:             {
    40:                 case "1":
    41:                     return new SingleThreadTester();
    42:                 case "2":
    43:                     return new TwoThreadSwitchTester();
    44:                 default:
    45:                     return new MultiThreadTester(100);
    46:             }
    47:         }
    48:
    49:         private static string GetUserChoice()
    50:         {
    51:             Console.WriteLine(@"==Please select the option in the following list:==
    52: 1. SingleThreadTester
    53: 2. TwoThreadSwitchTester
    54: 3. MultiThreadTester
    55: 4. Exit");
    56:
    57:             string inputText = Console.ReadLine();
    58:
    59:             return inputText;
    60:         }
    61:     }
    三個測試類,運行結果如下:
    Single Thread:
    [耗時407ms] counter = 100000001, stopped
    [耗時453ms] counter = 100000001, stopped
    [耗時412ms] counter = 100000001, stopped
    Two Thread Switch:
    [耗時161503ms] counter = 100000001, stopped
    [耗時164508ms] counter = 100000001, stopped
    [耗時164201ms] counter = 100000001, stopped
    Multi Threads - 100 Threads:
    [耗時3659ms] counter = 100000001, stopped
    [耗時3950ms] counter = 100000001, stopped
    [耗時3720ms] counter = 100000001, stopped
    Multi Threads - 2 Threads:
    [耗時3078ms] counter = 100000001, stopped
    [耗時3160ms] counter = 100000001, stopped
    [耗時3106ms] counter = 100000001, stopped
      什么是線程上下文切換
      上下文切換的精確定義可以參考: http://www.linfo.org/context_switch.html。多任務系統往往需要同時執行多道作業。作業數往往大于機器的CPU數,然而一顆CPU同時只能執行一項任務,為了讓用戶感覺這些任務正在同時進行,操作系統的設計者巧妙地利用了時間片輪轉的方式,CPU給每個任務都服務一定的時間,然后把當前任務的狀態保存下來,在加載下一任務的狀態后,繼續服務下一任務。任務的狀態保存及再加載,這段過程就叫做上下文切換。時間片輪轉的方式使多個任務在同一顆CPU上執行變成了可能,但同時也帶來了保存現場和加載現場的直接消耗。(Note. 更精確地說, 上下文切換會帶來直接和間接兩種因素影響程序性能的消耗. 直接消耗包括: CPU寄存器需要保存和加載, 系統調度器的代碼需要執行, TLB實例需要重新加載, CPU 的pipeline需要刷掉; 間接消耗指的是多核的cache之間得共享數據, 間接消耗對于程序的影響要看線程工作區操作數據的大小).
      
      根據上面上下文切換的定義,我們做出下面的假設:
      之所以TwoThreadSwitchTester執行速度最慢,因為線程上下文切換的次數最多,時間主要消耗在上下文切換了,兩個線程交替計數,每計數一次就要做一次線程切換。
      “Multi Threads - 100 Threads”比“Multi Threads - 2 Threads”開的線程數量要多,導致線程切換次數也比后者多,執行時間也比后者長。
      由于Windows下沒有像Linux下的vmstat這樣的工具,這里我們使用Process Explorer看看程序執行的時候線程上線文切換的次數。
      Single Thread:
      
      計數期間,線程總共切換了580-548=32次。(548是啟動程序后,初始的數值)
      Two Thread Switch:
     
      計數期間,線程總共切換了33673295-124=33673171次。(124是啟動程序后,初始的數值)
      Multi Threads - 100 Threads:
      
      計數期間,線程總共切換了846-329=517次。(329是啟動程序后,初始的數值)
      Multi Threads - 2 Threads:
      
      計數期間,線程總共切換了295-201=94次。(201是啟動程序后,初始的數值)
      從上面收集的數據來看,和我們的判斷基本相符。
      干活的其實是CPU,而不是線程
      再想想原來學過的知識,之前一直以為線程多干活就快,簡直是把學過的計算機原理都還給老師了。真正干活的不是線程,而是CPU。線程越多,干活不一定越快。
      那么高并發的情況下什么時候適合單線程,什么時候適合多線程呢?
      適合單線程的場景:單個線程的工作邏輯簡單,而且速度非常快,比如從內存中讀取某個值,或者從Hash表根據key獲得某個value。Redis和Node.js這類程序都是單線程,適合單個線程簡單快速的場景。
      適合多線程的場景:單個線程的工作邏輯復雜,等待時間較長或者需要消耗大量系統運算資源,比如需要從多個遠程服務獲得數據并計算,或者圖像處理。
      例子程序:http://pan.baidu.com/s/1ntNUPWP

    posted @ 2014-06-20 11:30 順其自然EVO 閱讀(440) | 評論 (0)編輯 收藏

    僅列出標題
    共394頁: First 上一頁 97 98 99 100 101 102 103 104 105 下一頁 Last 
    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    導航

    統計

    常用鏈接

    留言簿(55)

    隨筆分類

    隨筆檔案

    文章分類

    文章檔案

    搜索

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 国产精品免费视频网站| 性做久久久久久久免费看| 亚洲欧洲高清有无| xvideos亚洲永久网址| 久久这里只精品国产免费10| 亚洲av永久无码| 亚洲精品午夜在线观看| 亚洲人成电影在线天堂 | 亚洲AV无码AV吞精久久| 亚洲中文字幕精品久久| 亚洲久本草在线中文字幕| 久久久久无码精品亚洲日韩| 亚洲国产免费综合| 亚洲精品视频在线免费| 两性色午夜视频免费网| 大桥未久亚洲无av码在线 | 国产一区二区视频免费| 国产91色综合久久免费| 日本xxxx色视频在线观看免费| 日韩在线一区二区三区免费视频 | 亚洲综合熟女久久久30p| 国产伦一区二区三区免费| 免费国产一级特黄久久| 大香人蕉免费视频75| 日本亚洲免费无线码| 18禁成人网站免费观看| 国产免费AV片在线播放唯爱网| 成人黄动漫画免费网站视频| 国产卡二卡三卡四卡免费网址| 成人免费无码大片a毛片软件| 国产免费怕怕免费视频观看| MM131亚洲国产美女久久| 免费大黄网站在线看| 亚洲午夜福利AV一区二区无码| 一本色道久久88亚洲综合| 亚洲色成人网站WWW永久| 亚洲精品国产情侣av在线| 亚洲AV无码一区二区乱子仑 | 亚洲一级片在线播放| 亚洲午夜在线一区| 亚洲大香人伊一本线|