qileilove

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

          功能測試用例設(shè)計(jì)(表格)


          posted @ 2014-11-27 11:53 順其自然EVO 閱讀(620) | 評論 (0)編輯 收藏

          Linux CPU實(shí)時(shí)監(jiān)控命令mpstat介紹

          1,簡介
            mpstat是Multiprocessor  Statistics的縮寫,是實(shí)時(shí)系統(tǒng)監(jiān)控工具。其報(bào)告是CPU的一些統(tǒng)計(jì)信息,這些信息存放在/proc/stat文件中。在多CPUs系統(tǒng)里,其不但能查看所有CPU的平均狀況信息,而且能夠查看特定CPU的信息。mpstat最大的特點(diǎn)是:可以查看多核心cpu中每個(gè)計(jì)算核心的統(tǒng)計(jì)數(shù)據(jù);而類似工具vmstat只能查看系統(tǒng)整體cpu情況。
            2,安裝
          [root@ora10g ~]# mpstat
          -bash: mpstat: command not found
          [root@ora10g ~]# mount -o loop -t iso9660 /dev/cdrom /mnt/cdrom
          [root@ora10g ~]# cd /mnt/cdrom/Server/
          [root@ora10g Server]# rpm -ivh sysstat-7.0.2-3.el5.i386.rpm
          warning: sysstat-7.0.2-3.el5.i386.rpm: Header V3 DSA signature: NOKEY, key ID 37017186
          Preparing...                ########################################### [100%]
          1:sysstat                ########################################### [100%]
            3,實(shí)例
            用法
            mpstat -V  顯示mpstat版本信息
            mpstat -P ALL 顯示所有CPU信息
            mpstat -P n 顯示第n個(gè)cup信息,n為數(shù)字,計(jì)數(shù)從0開始
            mpstat n m  每個(gè)n秒顯示一次cpu信息,連續(xù)顯示m次,最后顯示一個(gè)平均值
            mpstat n    每個(gè)n秒顯示一次cpu信息,連續(xù)顯示下去
            查看每個(gè)cpu核心的詳細(xì)當(dāng)前運(yùn)行狀況信息,輸出如下:
          [root@ora10g ~]# mpstat -P ALL
          Linux 2.6.18-194.el5 (ora10g.up.com)    11/05/14
          09:13:02     CPU   %user   %nice    %sys %iowait    %irq   %soft  %steal   %idle    intr/s
          09:13:02     all    0.62    0.01    0.54    3.48    0.00    0.02    0.00   95.32   1039.58
          09:13:02       0    0.92    0.01    1.18    8.77    0.01    0.05    0.00   89.06   1030.23
          09:13:02       1    0.27    0.00    0.31    1.46    0.00    0.01    0.00   97.96      1.00
          ....
          09:13:02      14    1.12    0.02    0.45    2.99    0.00    0.01    0.00   95.39      7.74
          09:13:02      15    0.18    0.00    0.22    0.70    0.00    0.01    0.00   98.90      0.59
            查看多核CPU核心的當(dāng)前運(yùn)行狀況信息, 每2秒更新一次
            [root@ora10g ~]# mpstat -P ALL 2
            查看某個(gè)cpu的使用情況,數(shù)值在[0,cpu個(gè)數(shù)-1]中取值
            [root@ora10g ~]# mpstat -P 2
            Linux 2.6.18-194.el5 (ora10g.up.com)    11/05/14
            10:19:28     CPU   %user   %nice    %sys %iowait    %irq   %soft  %steal   %idle    intr/s
            10:19:28       2    0.08    0.00    0.04    0.22    0.00    0.01    0.00   99.64      0.55
            查看多核CPU核心的當(dāng)前運(yùn)行狀況信息, 每2秒更新一次,顯示5次
            [root@ora10g ~]# mpstat -P ALL 2 5
           4,字段含義如下
            英文解釋:
            CPU:Processor number. The keyword all indicates that statistics are calculated as averages among all processors.
            %user:Show the percentage of CPU utilization that occurred while executing at the user level (application).
            %nice:Show the percentage of CPU utilization that occurred while executing at the user level with nice priority.
            %sys:Show  the  percentage  of CPU utilization that occurred while executing at the system level (kernel). Note that
            this does not include time spent servicing interrupts or softirqs.
            %iowait:Show the percentage of time that the CPU or CPUs were idle during which the system had an outstanding disk  I/O request.
            %irq:Show the percentage of time spent by the CPU or CPUs to service interrupts.
            %soft:Show  the  percentage  of time spent by the CPU or CPUs to service softirqs.  A softirq (software interrupt) is
            one of up to 32 enumerated software interrupts which can run on multiple CPUs at once.
            %steal:Show the percentage of time spent in involuntary wait by the virtual CPU or CPUs while the hypervisor was  ser-vicing another virtual processor.
            %idle:Show  the percentage of time that the CPU or CPUs were idle and the system did not have an outstanding disk I/O request.
            intr/s:Show the total number of interrupts received per second by the CPU or CPUs.
            參數(shù)解釋 從/proc/stat獲得數(shù)據(jù)
            CPU 處理器 ID
            user 在internal時(shí)間段里,用戶態(tài)的CPU時(shí)間(%),不包含 nice值為負(fù) 進(jìn)程 (usr/total)*100
            nice 在internal時(shí)間段里,nice值為負(fù)進(jìn)程的CPU時(shí)間(%)   (nice/total)*100
            system 在internal時(shí)間段里,核心時(shí)間(%)   (system/total)*100
            iowait 在internal時(shí)間段里,硬盤IO等待時(shí)間(%) (iowait/total)*100
            irq 在internal時(shí)間段里,硬中斷時(shí)間(%)      (irq/total)*100
            soft 在internal時(shí)間段里,軟中斷時(shí)間(%)    (softirq/total)*100
            idle 在internal時(shí)間段里,CPU除去等待磁盤IO操作外的因?yàn)槿魏卧蚨臻e的時(shí)間閑置時(shí)間(%)(idle/total)*100
            intr/s 在internal時(shí)間段里,每秒CPU接收的中斷的次數(shù)intr/total)*100
            CPU總的工作時(shí)間=total_cur=user+system+nice+idle+iowait+irq+softirq
            total_pre=pre_user+ pre_system+ pre_nice+ pre_idle+ pre_iowait+ pre_irq+ pre_softirq
            user=user_cur – user_pre
            total=total_cur-total_pre
            其中_cur 表示當(dāng)前值,_pre表示interval時(shí)間前的值。上表中的所有值可取到兩位小數(shù)點(diǎn)。

          posted @ 2014-11-26 15:09 順其自然EVO 閱讀(1082) | 評論 (0)編輯 收藏

          PHP寫的從數(shù)據(jù)庫導(dǎo)入到EXCEL

          原理: 就是原理很分頁原理一樣! 選取一定數(shù)量的數(shù)據(jù)然后變成數(shù)組,接著直接寫入文件。接下來繼續(xù)選取后面沒選定數(shù)據(jù)在變成數(shù)組,接著在寫入文件!這個(gè)解決了內(nèi)存溢出。但是多CPU還是有個(gè)考驗(yàn)! 由于本人剛剛學(xué)PHP(PHP培訓(xùn) php教程 )不久,功力不深厚!只能寫出這樣的東西!
            源碼!
            Excel類
          PHP code
          class Excel{
          var $header = "<?xml version="1.0" encoding="utf-8"?>
          <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
          xmlns:x="urn:schemas-microsoft-com:office:excel"
          xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
          xmlns:html="http://www.w3.org/TR/REC-html40">";
          var $footer = "</Workbook>";
          var $lines = array ();
          var $worksheet_title = "Table1";
          function addRow ($array) {
          $cells = "";
          foreach ($array as $k => $v):
          if(is_numeric($v)) {
          if(substr($v, 0, 1) == 0) {
          $cells .= "<Cell><Data ss:Type="String">" . $v . "</Data></Cell>n";
          } else {
          $cells .= "<Cell><Data ss:Type="Number">" . $v . "</Data></Cell>n";
          }
          } else {
          $cells .= "<Cell><Data ss:Type="String">" . $v . "</Data></Cell>n";
          }
          endforeach;
          $this->lines[] = "<Row>n" . $cells . "</Row>n";
          unset($arry);
          }
          function setWorksheetTitle ($title) {
          $title = preg_replace ("/[\|:|/|?|*|[|]]/", "", $title);
          $title = substr ($title, 0, 31);
          $this->worksheet_title = $title;
          }
          function generateXML ($filename) {
          // deliver header (as recommended in PHP manual)
          header("Content-Type: application/vnd.ms-excel; charset=utf-8");
          header("Content-Disposition: inline; filename="" . $filename . ".xls"");
          // print out document to the browser
          // need to use stripslashes for the damn ">"
          echo stripslashes ($this->header);
          echo "n<Worksheet ss:Name="" . $this->worksheet_title . "">n<Table>n";
          echo "<Column ss:Index="1" ss:AutoFitWidth="0" ss:Width="110"/>n";
          echo implode ("n", $this->lines);
          echo "</Table>n</Worksheet>n";
          echo $this->footer;
          exit;
          }
          function write ($filename) // 重點(diǎn)
          {
          $content= stripslashes ($this->header);
          $content.= "n<Worksheet ss:Name="" . $this->worksheet_title . "">n<Table>n";
          $content.= "<Column ss:Index="1" ss:AutoFitWidth="0" ss:Width="110"/>n";
          $content.= implode ("n", $this->lines);
          $content.= "</Table>n</Worksheet>n";
          $content.= $this->footer;//EXCEL文件
          //error_log($content, 3,$filename);
          if (!file_exists($filename))//判斷有沒有文件
          {
          fopen($filename,'a');
          }
          $fp = fopen($filename,'a');
          fwrite($fp, $content);//寫入文件
          fclose($fp);
          unset($this->lines);//清空內(nèi)存中的數(shù)據(jù)
          }
          }
           頁面
          PHPcode
          include_once"./include/class.excel.PHP";//調(diào)用EXCEL類
          require_once'./include/class.zipfile.PHP';//調(diào)用大包類
          $xls=newExcel;//實(shí)例化
          $w=explode("limit",$where_str);//把WHERE
          $p=6000;//分頁原理
          $a=$ip_list_count/$p;//分頁原理
          if($ip_list_count%$p==0)//分頁原理
          else//分頁原理
          for($i=0;$i<=$a;$i++)//循環(huán)寫出
          {
          $s=6000*$i;
          $ip=$_SG['db']->fetch_all("select*frommain_info".$w[0]."limit".$s.",".$p);//調(diào)用自己寫的數(shù)據(jù)庫(數(shù)據(jù)庫培訓(xùn)數(shù)據(jù)庫認(rèn)證)方法,寫出數(shù)組
          $xls->addArray($ip);//調(diào)用EXCEL類中addArray方法
          xml1=$xls->write("./".$i.".xls");//調(diào)用EXCEL類中write方法
          unset($ip);
          unset($xml1);
          sleep(1);
          }

          posted @ 2014-11-26 15:08 順其自然EVO 閱讀(475) | 評論 (0)編輯 收藏

          iOS為移動中的UIView(UIButton )添加點(diǎn)擊事件

           高高興興迎接新的產(chǎn)品新需求,滿心歡喜的開始工作,結(jié)果研究了一下午才發(fā)現(xiàn),是自己想的太簡單了,是我太單純呀。
            需求是這樣的類似下雪的效果,隨機(jī)產(chǎn)生一些小雪花,然后每個(gè)雪花可以點(diǎn)擊到下個(gè)頁面。
            接到需求之后我的首先想法就是用button實(shí)現(xiàn)不久可以了,多簡單點(diǎn)事情,結(jié)果實(shí)踐之后就知道自己多么的無知了,在移動中的button根本沒有辦法接收點(diǎn)擊事件。
            然后同事給出了一種解決辦法,通過手勢獲取點(diǎn)擊的位置,然后遍歷頁面上的控件,如果在這個(gè)范圍內(nèi)就點(diǎn)擊成功。通過這個(gè)想法我嘗試用frame來實(shí)現(xiàn)需求,然后發(fā)現(xiàn)自己又白癡了,頁面上所有的“雪花”的frame,都是動畫結(jié)束的位置,并不是實(shí)時(shí)的位置。
            但是我感到用手勢,然后通過位置,遍歷,這個(gè)思路應(yīng)該是對的,于是繼續(xù)百度,發(fā)現(xiàn)了一個(gè)好東西
            - (CALayer *)hitTest:(CGPoint)p;
            通過這個(gè)可以得到這個(gè)頁面時(shí)候覆蓋了這個(gè)點(diǎn),這樣就可以解決我的問題了,把手勢加在“雪花”的父頁面上,然后點(diǎn)擊事件里進(jìn)行處理(當(dāng)然 用這個(gè)方法就沒有必要一定用button)
          -(void)tapClick:(UITapGestureRecognizer *)tap
          {
          CGPoint clickPoint =  [tap locationInView:self];
          for (UIImageView *imageView in [self subviews])
          {
          if ([imageView.layer.presentationLayer hitTest:clickPoint])
          {
          // This button was hit whilst moving - do something with it here
          break;
          }
          }
          }
          }
            這樣就可以解決問題,當(dāng)然應(yīng)該還有其他的方法,歡迎補(bǔ)充。

          posted @ 2014-11-26 15:08 順其自然EVO 閱讀(1378) | 評論 (0)編輯 收藏

          Java為什么使用抽象類和接口

           Java接口和Java抽象類代表的就是抽象類型,就是我們需要提出的抽象層的具體表現(xiàn)。OOP面向?qū)ο蟮木幊蹋绻岣叱绦虻膹?fù)用率,增加程序的可維護(hù)性,可擴(kuò)展性,就必須是面向接口的編程,面向抽象的編程,正確地使用接口、抽象類這些太有用的抽象類型做為你結(jié)構(gòu)層次上的頂層。
            1、Java接口和Java抽象類最大的一個(gè)區(qū)別,就在于Java抽象類可以提供某些方法的部分實(shí)現(xiàn),而Java接口不可以,這大概就是Java抽象類唯一的優(yōu)點(diǎn)吧,但這個(gè)優(yōu)點(diǎn)非常有用。 如果向一個(gè)抽象類里加入一個(gè)新的具體方法時(shí),那么它所有的子類都一下子都得到了這個(gè)新方法,而Java接口做不到這一點(diǎn),如果向一個(gè)Java接口里加入一個(gè)新方法,所有實(shí)現(xiàn)這個(gè)接口的類就無法成功通過編譯了,因?yàn)槟惚仨氉屆恳粋€(gè)類都再實(shí)現(xiàn)這個(gè)方法才行.
            2、一個(gè)抽象類的實(shí)現(xiàn)只能由這個(gè)抽象類的子類給出,也就是說,這個(gè)實(shí)現(xiàn)處在抽象類所定義出的繼承的等級結(jié)構(gòu)中,而由于Java語言的單繼承性,所以抽象類作為類型定義工具的效能大打折扣。 在這一點(diǎn)上,Java接口的優(yōu)勢就出來了,任何一個(gè)實(shí)現(xiàn)了一個(gè)Java接口所規(guī)定的方法的類都可以具有這個(gè)接口的類型,而一個(gè)類可以實(shí)現(xiàn)任意多個(gè)Java接口,從而這個(gè)類就有了多種類型。
            3、從第2點(diǎn)不難看出,Java接口是定義混合類型的理想工具,混合類表明一個(gè)類不僅僅具有某個(gè)主類型的行為,而且具有其他的次要行為。
            4、結(jié)合1、2點(diǎn)中抽象類和Java接口的各自優(yōu)勢,具精典的設(shè)計(jì)模式就出來了:聲明類型的工作仍然由Java接口承擔(dān),但是同時(shí)給出一個(gè)Java抽象類,且實(shí)現(xiàn)了這個(gè)接口,而其他同屬于這個(gè)抽象類型的具體類可以選擇實(shí)現(xiàn)這個(gè)Java接口,也可以選擇繼承這個(gè)抽象類,也就是說在層次結(jié)構(gòu)中,Java接口在最上面,然后緊跟著抽象類,哈,這下兩個(gè)的最大優(yōu)點(diǎn)都能發(fā)揮到極至了。這個(gè)模式就是“缺省適配模式”。 在Java語言API中用了這種模式,而且全都遵循一定的命名規(guī)范:Abstract +接口名。
            Java接口和Java抽象類的存在就是為了用于具體類的實(shí)現(xiàn)和繼承的,如果你準(zhǔn)備寫一個(gè)具體類去繼承另一個(gè)具體類的話,那你的設(shè)計(jì)就有很大問題了。Java抽象類就是為了繼承而存在的,它的抽象方法就是為了強(qiáng)制子類必須去實(shí)現(xiàn)的。
            使用Java接口和抽象Java類進(jìn)行變量的類型聲明、參數(shù)是類型聲明、方法的返還類型說明,以及數(shù)據(jù)類型的轉(zhuǎn)換等。而不要用具體Java類進(jìn)行變量的類型聲明、參數(shù)是類型聲明、方法的返還類型說明,以及數(shù)據(jù)類型的轉(zhuǎn)換等。
            我想,如果你編的代碼里面連一個(gè)接口和抽象類都沒有的話,也許我可以說你根本沒有用到任何設(shè)計(jì)模式,任何一個(gè)設(shè)計(jì)模式都是和抽象分不開的,而抽象與Java接口和抽象Java類又是分不開的。
            接口的作用,一言以蔽之,就是標(biāo)志類的類別。把不同類型的類歸于不同的接口,可以更好的管理他們。把一組看如不相關(guān)的類歸為一個(gè)接口去調(diào)用.可以用一個(gè)接口型的變量來引用一個(gè)對象,這是接口我認(rèn)為最大的作用.
            自己的感想
            在平時(shí)的JAVA編程中,用JDBC連接數(shù)據(jù)庫是非常常用的.而這里面涉及到的有DriverManager,Connection,Statement,其中第一個(gè)是類,后兩者是接口.Connection用于獲取一個(gè)指定了數(shù)據(jù)庫的連接.而這個(gè)數(shù)據(jù)庫的指定是在程序的開頭或者配置文件中指定.那么通過DriverManager.getConnection就可以獲得根據(jù)指定數(shù)據(jù)庫的具體數(shù)據(jù)庫連接對象.
            那么,問題的關(guān)鍵就在這里,在以后的程序中,我門所使用的這個(gè)Connection,都是這個(gè)接口引用的一個(gè)對象.它即可以是oracle數(shù)據(jù)庫連接對象, 也可以是sql server連接對象.但光看內(nèi)部程序,我們并不知道它具體是那種類型的.因?yàn)橥ㄟ^接口.它展現(xiàn)給我們的都是Connection類型的.不管我們換了什么數(shù)據(jù)庫,程序中總是Connection conn=...
            但是假如我們不用Connection接口.而換用具體的類,那么如果我們只用一種數(shù)據(jù)庫比如sql server,那我們就用這個(gè)SqlserverConnection類來實(shí)例一個(gè)對象然后在程序中調(diào)用.但是假設(shè)有天我們要換成mysql數(shù)據(jù)庫呢?那么,程序用所有的SqlServerConnection是不是都要換成MysqlConnection呢,并且,方法可能都會失效.
            這就是接口的優(yōu)勢體現(xiàn),如果用接口,我們不用去管程序中具體是在調(diào)用哪個(gè)類,我只要知道是調(diào)用具有某種共同屬性的類.而這個(gè)類的指定都交給工廠類去完成.在程序內(nèi)部,我們完全只能看見的是對接口的調(diào)用.這個(gè)接口就代表著具體的實(shí)現(xiàn)類了.
            現(xiàn)在學(xué)習(xí)MVC模式。使得WEB開發(fā)以多層的方式。而再這些層中關(guān)系比較密切的就是模型層,持久化層,然后是底層數(shù)據(jù)庫。模型層中需要BO,DTO,VO。而持久化層就是DAO類啦。不過按照大型項(xiàng)目架構(gòu)。每層之間都應(yīng)該通過接口。
            這點(diǎn)比較重要。接口的作用是為了降低層之間的耦合度。這樣,下層只對上層公開接口。而封閉了內(nèi)部實(shí)現(xiàn)。這是好處1。第二呢就是當(dāng)接口的實(shí)現(xiàn)改變時(shí)。上層的調(diào)用代碼是不用改變的。最后一點(diǎn)呢。就是接口本身的好處了,那就是一個(gè)接口,多種實(shí)現(xiàn)。具體要用到那種實(shí)現(xiàn)由工廠指定.那么萬一實(shí)現(xiàn)改變了,也只用改工廠,不用改程序.

          posted @ 2014-11-26 14:56 順其自然EVO 閱讀(271) | 評論 (0)編輯 收藏

          如何使用Java讀寫系統(tǒng)屬性?

           如何使用Java讀寫系統(tǒng)屬性?
            讀:
          Properties props = System.getProperties();
          Enumeration prop_names = props.propertyNames();
          while (prop_names.hasMoreElements()) {
          String prop_name = (String) prop_names.nextElement();
          String property = props.getProperty(prop_name);
          System.out.println(“Property ‘” + prop_name + “‘ is ‘”+ property + “‘”);
          }
            寫:
            System.setProperties(props);
            簡述properties文件的結(jié)構(gòu)和基本用法
            結(jié)構(gòu):擴(kuò)展名為properties的文件,內(nèi)容為key、value的映射,例如”a=2″
            用法:
          public class Test {
          public static void main(String args[]) {
          try {
          String name = “test.properties”;
          InputStream in = new BufferedInputStream(new FileInputStream(name));
          Properties p = new Properties();
          p.load(in);
          System.out.println(“a的值==” + p.getProperty(“a”));
          } catch (Exception err) {
          err.printStackTrace();
          }
          }
          }

          posted @ 2014-11-26 14:55 順其自然EVO 閱讀(215) | 評論 (0)編輯 收藏

          項(xiàng)目管理雜談-需求無止境

          項(xiàng)目又延期了,老板恨恨的批評了整個(gè)項(xiàng)目組,投入了那么多,產(chǎn)出在哪里?查原因,發(fā)現(xiàn)是由于項(xiàng)目的需求不斷變更導(dǎo)致,這恐怕是很多項(xiàng)目經(jīng)理、程序員都經(jīng)歷過的事。
            我這里就談?wù)勴?xiàng)目延期的一個(gè)重要因素:需求問題
            這張圖大家再熟悉不過了,我再炒一下冷飯,列一下主要可能的情況:
            客戶為何提不了真正的需求?
            1、業(yè)務(wù)部門:業(yè)務(wù)人員基本是站在自身的角度看問題,從自身負(fù)責(zé)的業(yè)務(wù)出發(fā),沒有從本部門或更高層次來分析問題,導(dǎo)致需求的著眼點(diǎn)比較低。在此基礎(chǔ)上形成的最終需求也就是把各部門的需求進(jìn)行匯總,簡單處理罷了。而且,業(yè)務(wù)部門對技術(shù)知識的匱乏,也導(dǎo)致其提出需求時(shí)是沒有考慮技術(shù)上方面的。
            2、技術(shù)人員:客戶方面的技術(shù)人員由于業(yè)務(wù)知識有限,無法挖掘更深層次的需求,只能是基于已有需求,或者輕度發(fā)掘部分需求,無法從根本上解決需求的問題。
            按照以上提出的需求,可想而知,項(xiàng)目的結(jié)局如何。也有部分項(xiàng)目,在需求分析階段,生成了完整的需求規(guī)格說明書,并且用戶簽字畫押,最終的結(jié)果是如果不能真正解決客戶業(yè)務(wù)的問題,即使系統(tǒng)投產(chǎn)了,也必將引來用戶的各種抱怨,勢必對公司形象、后續(xù)項(xiàng)目產(chǎn)生各種不利影響。
            我們在整天抱怨需求不斷變化的同時(shí),能否換個(gè)角度來看待需求的變化,假設(shè)需求就是變化的,事實(shí)情況也是如此。從企業(yè)及業(yè)務(wù)自身的發(fā)展來看,企業(yè)是不斷發(fā)展的,而業(yè)務(wù)也是不斷發(fā)展的,為了滿足企業(yè)經(jīng)營需要及業(yè)務(wù)發(fā)展需要,需求本身就是應(yīng)該是不斷變化和發(fā)展的。
            那么,真正的需求在哪里?
            從企業(yè)運(yùn)營角度看,為什么要做系統(tǒng)?其目的都是滿足企業(yè)運(yùn)營的需要,只有站在企業(yè)運(yùn)營的高度來審視需求,才能真正幫助需求發(fā)起人,形成完整的需求。這就需要我們:
            1、真正掌握做該系統(tǒng)的目的
            2、程序員要深入了解業(yè)務(wù),多溝通,最好有領(lǐng)域?qū)<覅f(xié)助,從上而下梳理業(yè)務(wù)需求,糾正不合理的需求,挖掘潛在的需求
            3、以技術(shù)的手段來解決需求變更的問題,做到以不變應(yīng)萬變,從而在最大程度上減少需求變更帶來程序的變化。這方面對程序員、項(xiàng)目設(shè)計(jì)者的要求比較高。
            需求變化不可怕、需求變更也不可怕,可怕的是我們不知道變化及變更的本質(zhì),而是停留在表象;可怕的是我們不知道去擁抱這種變化,而是一味的排斥;可怕的是我們不知道用自己的長項(xiàng)(技術(shù)手段)最大化的去解決這種變化,而是把自己的弱項(xiàng)(業(yè)務(wù))暴露在客戶面前。

          posted @ 2014-11-26 14:49 順其自然EVO 閱讀(257) | 評論 (0)編輯 收藏

          我調(diào)過的最難調(diào)的Bug

           每個(gè)程序員都有些不畏死亡決戰(zhàn)猛獸的英雄事跡。以下這些是我的。
            內(nèi)存沖突
            畢業(yè)不到半年,拿著剛到手的文憑,我在Lexmark公司的一個(gè)嵌入式Linux固件開發(fā)團(tuán)隊(duì)中負(fù)責(zé)追蹤一個(gè)內(nèi)存沖突的問題。因?yàn)閮?nèi)存沖突的原因和問題表象總是相差非常大,所以這類問題很難調(diào)。有可能是因?yàn)榫彺嬉绯觯灿锌赡苁侵羔樜闯跏蓟蚴侵羔槺欢啻蝔ree,亦或是某處的DMA錯(cuò)誤,但是你所見的卻是一堆神秘的問題:掛起、指令未定義、打印錯(cuò)誤,以及未處理的內(nèi)核錯(cuò)誤。這些都非常頻繁,內(nèi)存沖突看上去似乎是隨機(jī)出現(xiàn)又很難重現(xiàn)。
            要調(diào)試這種問題,第一步是可重現(xiàn)問題。在我們奇跡般地找到這樣一個(gè)場景之后,故事開始變得好玩起來。
            當(dāng)時(shí),我們發(fā)現(xiàn)在運(yùn)行時(shí)因內(nèi)存沖突而產(chǎn)生的程序崩潰每幾百小時(shí)就會出現(xiàn)一次。之后有一天有人發(fā)現(xiàn)一個(gè)特別的打印任務(wù)會產(chǎn)生內(nèi)存沖突從而在幾分鐘之內(nèi)就使程序崩潰。我從來不知道為什么這個(gè)打印任務(wù)會產(chǎn)生這個(gè)問題。現(xiàn)在,我們就可以進(jìn)一步做些什么了。
            調(diào)試
            這個(gè)問題可重現(xiàn)之后,我就開始尋找崩潰中出現(xiàn)的模式。最引人注意的是未定義指令和內(nèi)核錯(cuò)誤,它們差不多三分之一的時(shí)間就會發(fā)生一次。未定義指令的地址是一個(gè)合理的內(nèi)核代碼地址,但是CPU讀到的這個(gè)指令卻不是我們期望出現(xiàn)的。這就很簡單了,可能是有人不小心寫了這些指令。把這些未定義指令的句柄打印出來之后,我可以看到這些錯(cuò)誤的指令所在位置的周邊內(nèi)存的狀態(tài)。
            在做了大量失敗的將更多的代碼排除出崩潰的嘗試之后,一個(gè)特殊的崩潰漸漸顯現(xiàn)。
            崩潰之王
            這個(gè)崩潰解開了所有秘密。當(dāng)時(shí)我們用了一個(gè)雙核CPU。在這個(gè)特殊的崩潰里,首先CPU1在有效的模塊地址范圍內(nèi)收到了一個(gè)未處理的內(nèi)核錯(cuò)誤,而此時(shí)它正在嘗試執(zhí)行模塊代碼,這段代碼可能是一個(gè)沖突的頁表或是一個(gè)無效TLB。而正在處理這個(gè)錯(cuò)誤時(shí),CPU0在內(nèi)核地址空間內(nèi)收到了一個(gè)非法的指令陷阱。
            以下是從修改后的未定義指令句柄中打印出來的數(shù)據(jù)(已轉(zhuǎn)為物理地址,括號中是出錯(cuò)地址)
            undefined instruction: pc=0018abc4
            0018aba0: e7d031a2 e1b03003 1a00000e e2822008
            0018abb0: e1520001 3afffff9 e1a00001 e1a0f00e
            0018abc0: 0bd841e6 (ceb3401c) 00000004 00000001
            0018abd0: 0d066010 5439541b 49fa30e7 c0049ab8
            0018abe0: e2822001 eafffff1 e2630000 e0033000
            0018abf0: e16f3f13 e263301f e0820003 e1510000
            以下是內(nèi)存域應(yīng)該顯示的數(shù)據(jù):
            0018aba0: e7d031a2 e1b03003 1a00000e e2822008
            0018abb0: e1520001 3afffff9 e1a00001 e1a0f00e
            0018abc0: e3310000 (0afffffb) e212c007 0afffff3
            0018abd0: e7d031a2 e1b03c33 1a000002 e3822007
            0018abe0: e2822001 eafffff1 e2630000 e0033000
            0018abf0: e16f3f13 e263301f e0820003 e1510000
            確切地來說,只有一行緩存(中間那32byte)是有沖突的。一個(gè)同事指出沖突行中0x49fa30e7這個(gè)字是一個(gè)魔術(shù)cookie,它標(biāo)記了系統(tǒng)中一個(gè)特殊環(huán)形緩沖區(qū)的入口。入口值的最后一個(gè)字永遠(yuǎn)是一個(gè)時(shí)間戳,所以0x5439541b是上一個(gè)入口的時(shí)間戳。我決定去讀取這個(gè)環(huán)形緩沖的內(nèi)容,但它現(xiàn)在掛在一個(gè)不可執(zhí)行的KGDB提示那了。機(jī)器現(xiàn)在跟死了一樣。
          冷啟動攻擊
            為獲取環(huán)形緩沖區(qū)的數(shù)據(jù),我進(jìn)行了一次冷啟動攻擊。我為正在使用的主板搞到了一份概要拷貝,然后發(fā)現(xiàn)CPU的復(fù)位線上連了一塊不受歡迎的板子。我把它短路了,重置CPU而不妨礙DRAM的完整性。然后,我把Boot掛載在引導(dǎo)程序上。
            在引導(dǎo)程序里,我dump到了問題中環(huán)形緩沖區(qū)的內(nèi)容。謝天謝地,這個(gè)緩存總是在一個(gè)固定的物理地址上被定位到,所以找到它不是問題了。
            通過分析錯(cuò)誤時(shí)間戳周邊的環(huán)形緩沖區(qū),我們發(fā)現(xiàn)了兩個(gè)老的cache line。這兩個(gè)cache line里有有效數(shù)據(jù),但是在這兩個(gè)cache line里的時(shí)間戳卻是環(huán)形緩沖區(qū)里之前的時(shí)間。
            導(dǎo)致CPU0上未定義指令的cache line與環(huán)形緩沖區(qū)里那兩個(gè)老cache line之一相當(dāng)契合,但是這并不說明其他可能的地方也是這樣。我發(fā)現(xiàn)一個(gè)決定性的證據(jù)。假設(shè),另一個(gè)消失的cache line是導(dǎo)致CPU1上未處理內(nèi)核錯(cuò)誤的元兇。
            錯(cuò)置的cache line
            cache line應(yīng)該被寫入0x0ebd2bc0(環(huán)形緩沖區(qū)里的cache line),但是事實(shí)上卻寫入了0x0018abc0(沖突的內(nèi)核碼)。這些地址在我們CPU上屬于相同的緩存,它們的位[14:5]的值是相同的。不知為何它們有別名。
            bit   28   24   20   16   12    8    4    0
            |    |    |    |    |    |    |    |
            0x0ebd2bc0 in binary is 0000 1110 1011 1101 0010 1011 1100 0000
            0x0018abc0 in binary is 0000 0000 0001 1000 1010 1011 1100 0000
            一個(gè)地址的低5位是cache line(32字節(jié)cache line)里的索引。后10位,即位[14:5],表示緩存集。剩下的17位,即位[31:15],用來表示緩存里當(dāng)前存的是哪個(gè)cache line.
            我向我們的CPU供應(yīng)商提交了一個(gè)bug報(bào)告,之后他們制定了一個(gè)解決方案,并在下一版本CPU里修復(fù)了這個(gè)bug。
            我期望聽到更多牛掰的此類故事,也期望我自己可以再攢點(diǎn)這樣的。

          posted @ 2014-11-26 14:48 順其自然EVO 閱讀(250) | 評論 (0)編輯 收藏

          如何保證高質(zhì)量的—移動APP的性能最佳實(shí)踐

          加速之必要
            不考慮技術(shù),有一件事是肯定的——人們似乎總是希望可以更快。根據(jù)各種各樣的研究,現(xiàn)在用戶只愿意等待一個(gè)web應(yīng)用程序加載三秒或更短的時(shí)間,超過的話,他們就會變得越來越不耐煩或者干脆換一個(gè)應(yīng)用程序。這些高期待不斷被壓到移動web之上;現(xiàn)在還壓到移動App上。就像Web,現(xiàn)今的移動移動app都有它們自己的性能問題并需要做出一些微調(diào)。最新研究表明,過去,在手機(jī)上獲取app時(shí),47%的移動用戶主要是抱怨速度慢且反應(yīng)遲鈍。App在蘋果的app商店上被譴責(zé)“慢得可怕”。對于Facebook的iPhone應(yīng)用程序,38,000條評論中有超過21,000的用戶只給app一星的評價(jià)。用戶多數(shù)表示app慢,死機(jī),“一直在加載”。
            “移動app根據(jù)它們在app商店的排名而生存或死亡……排名不佳,用戶采用率就降低”佛里斯特研究公司的MargoVisitacion這么說道。這或許就是為什么80%的品牌iPhone,Android和Blackberry應(yīng)用程序無法達(dá)到1,000的下載量的原因。拙劣的移動app性能直接影響用戶獲取和用戶維系。那么該做些什么以保證你的移動app性能盡可能的強(qiáng)大呢?
            通過捕捉現(xiàn)實(shí)中移動app性能“獲得真實(shí)信息”
            移動app性能首先,最重要的是:為了真正理解移動app性能,你必須衡量你的真正用戶正在體驗(yàn)的性能。在數(shù)據(jù)中心的模擬機(jī)上進(jìn)行測試可以有所幫助但是它基本和你的真實(shí)終端用戶的真正體驗(yàn)無關(guān)。你的數(shù)據(jù)中心和你的終端用戶間有許多影響性能的變量因素,包括云,第三方服務(wù)/集成,CDNs,移動瀏覽器和設(shè)備。衡量真實(shí)用戶是在巨大的復(fù)雜物上精準(zhǔn)評估性能并確定一個(gè)性能提升的基準(zhǔn)線的唯一方法。衡量你的真實(shí)用戶體驗(yàn)的性能可以讓你就移動app(關(guān)鍵參數(shù)方面的,如你客戶使用的所有的地域,設(shè)備和網(wǎng)絡(luò))性能做出報(bào)告。
            現(xiàn)在,移動app測試和使用SDKs監(jiān)控以提交本地app可以讓你快速輕松地鳥瞰你所有客戶的移動app性能。
            負(fù)載測試從終端用戶角度看也很重要,尤其是在開始一個(gè)app前,綜合測試網(wǎng)絡(luò)可以讓你在不同的條件下評估性能水平。
            理解拙劣性能的商業(yè)影響
            確定移動app性能問題以及它們對轉(zhuǎn)化的影響很重要:比如,你會注意到移動app的響應(yīng)時(shí)間增加與轉(zhuǎn)化的減少息息相關(guān)。這樣你就可以進(jìn)行分類,基于一些考慮(如:我的哪些客戶,多少客戶受到影響了)按輕重緩急解決問題。如果一個(gè)地區(qū)的流量份額很高但有問題,而另一個(gè)地區(qū)的份額較少,那你就知道該優(yōu)先解決哪個(gè)性能問題了。
            確保第三方集成有效
            就像web應(yīng)用程序,許多移動app為了給終端用戶提供更豐富更滿意的體驗(yàn)吸收了大量第三方服務(wù)的內(nèi)容。一個(gè)實(shí)例便是社交媒體集成,如Twitter就被集成到奧林匹克移動app中了。很不幸,如果你依賴第三方服務(wù)的話,你就會完全受限于他們的性能特點(diǎn)。在使用一個(gè)第三方集成的app前,你需要確保集成無縫順利且可以提供你期待的性能。此外,你還要確保一直監(jiān)控著第三方性能且你的app被設(shè)計(jì)得可以完好地降級以防第三方的問題。
            讓你的移動APP快起來
            在這個(gè)飛速運(yùn)轉(zhuǎn)的移動app世界有一句格言比任何時(shí)候都真——快比慢好。你可以使用一些特定工具和技術(shù)讓你的移動app變得更快,包括以下:
            優(yōu)化緩存–讓你的app數(shù)據(jù)完全脫離網(wǎng)絡(luò)。對于內(nèi)容多的app,設(shè)備上的緩存內(nèi)容可以通過避免移動網(wǎng)絡(luò)和你的內(nèi)容基礎(chǔ)設(shè)施上的過多障礙以提升性能。
            將往返時(shí)間最小化–考慮使用一個(gè)可以提供無數(shù)能夠加快你的app服務(wù)的CDN,包括減少網(wǎng)絡(luò)延遲的邊緣緩存,網(wǎng)絡(luò)路由優(yōu)化,內(nèi)容預(yù)取,以及更多。
            將有效荷載規(guī)模最小化–專注壓縮,通過使用任意可用的壓縮技術(shù)減少你的數(shù)據(jù)的規(guī)模。確保圖像規(guī)模適合你最要的設(shè)備段。同樣,確保你利用壓縮。如果你有要花很長時(shí)間加載的內(nèi)容,那么你可以一點(diǎn)一點(diǎn)兒的加載。你的app可以在加載時(shí)使用該內(nèi)容而不是等整個(gè)加載完成后才使用它。零售app經(jīng)常使用該技術(shù)。
            優(yōu)化你的本機(jī)代碼–寫得不好或全是bug的代碼也會導(dǎo)致性能問題。在你的代碼上運(yùn)行軟件或檢查代碼以找出潛在問題。
            優(yōu)化你的后端服務(wù)性能–如果對你的app進(jìn)行了性能測試后你發(fā)現(xiàn)后端服務(wù)是性能削弱的罪魁禍?zhǔn)祝敲茨憔筒坏貌贿M(jìn)行評估并決定該如何加快這些服務(wù)。
            總結(jié)
            智能手機(jī)用戶當(dāng)然也是“快比慢好”,他們期待他們的app可以飛快。幾乎每隔一段時(shí)間,移動運(yùn)營商和智能手機(jī)制造商都要宣布更快的網(wǎng)和設(shè)備,但不幸的是,移動app本身的速度卻跟不上。
            最主要的原因是一組截然相反的目標(biāo)使得實(shí)現(xiàn)飛速性能變得很困難。移動app開發(fā)者總希望提升速度的同時(shí)可以提供更豐富的體驗(yàn)。需要更多內(nèi)容和特點(diǎn)能夠快速地覆蓋寬帶,內(nèi)存和計(jì)算機(jī)能力。
            本文給出了一個(gè)簡短的本地移動app的性能最佳實(shí)踐的例子。性能調(diào)整的空間很大,但錯(cuò)誤的空間同樣也很大。因此,早點(diǎn)測試你的app,絕不要藥聽天由命。記住——快總比慢好。

          posted @ 2014-11-26 14:25 順其自然EVO 閱讀(242) | 評論 (0)編輯 收藏

          Junit使用GroboUtils進(jìn)行多線程測試

           寫過Junit單元測試的同學(xué)應(yīng)該會有感覺,Junit本身是不支持普通的多線程測試的,這是因?yàn)镴unit的底層實(shí)現(xiàn)上,是用System.exit退出用例執(zhí)行的。JVM都終止了,在測試線程啟動的其他線程自然也無法執(zhí)行。JunitCore代碼如下:
          /**
          * Run the tests contained in the classes named in the <code>args</code>.
          * If all tests run successfully, exit with a status of 0. Otherwise exit with a status of 1.
          * Write feedback while tests are running and write
          * stack traces for all failed tests after the tests all complete.
          * @param args names of classes in which to find tests to run
          */
          public static void main(String... args) {
          runMainAndExit(new RealSystem(), args);
          }
          /**
          * Do not use. Testing purposes only.
          * @param system
          */
          public static void runMainAndExit(JUnitSystem system, String... args) {
          Result result= new JUnitCore().runMain(system, args);
          system.exit(result.wasSuccessful() ? 0 : 1);
          }
          RealSystem.java:
          public void exit(int code) {
          System.exit(code);
          }
            所以要想編寫多線程Junit測試用例,就必須讓主線程等待所有子線程執(zhí)行完成后再退出。想到的辦法自然是Thread中的join方法。話又說回來,這樣一個(gè)簡單而又典型的需求,難道會沒有第三方的包支持么?通過google,很快就找到了GroboUtils這個(gè)Junit多線程測試的開源的第三方的工具包。
            GroboUtils官網(wǎng)下載解壓后 使用 GroboUtils-5\lib\core\GroboTestingJUnit-1.2.1-core.jar 這個(gè)即可
            GroboUtils是一個(gè)工具集合,里面包含各種測試工具,這里使用的是該工具集中的jUnit擴(kuò)展.
            依賴好Jar包后就可以編寫多線程測試用例了。上手很簡單:
          package com.junittest.threadtest;
          import java.util.ArrayList;
          import java.util.HashSet;
          import java.util.Hashtable;
          import java.util.List;
          import java.util.Map;
          import java.util.Set;
          import net.sourceforge.groboutils.junit.v1.MultiThreadedTestRunner;
          import net.sourceforge.groboutils.junit.v1.TestRunnable;
          import org.junit.Test;
          public class MutiThreadTest {
          static String[] path = new String[] { "" };
          static Map<String, String> countMap = new Hashtable<String, String>();
          static Map<String, String> countMap2 = new Hashtable<String, String>();
          static Set<String> countSet = new HashSet<String>();
          static List<String> list = new ArrayList<String>();
          @Test
          public void testThreadJunit() throws Throwable {
          //Runner數(shù)組,想當(dāng)于并發(fā)多少個(gè)。
          TestRunnable[] trs = new TestRunnable [10];
          for(int i=0;i<10;i++){
          trs[i]=new ThreadA();
          }
          // 用于執(zhí)行多線程測試用例的Runner,將前面定義的單個(gè)Runner組成的數(shù)組傳入
          MultiThreadedTestRunner mttr = new MultiThreadedTestRunner(trs);
          // 開發(fā)并發(fā)執(zhí)行數(shù)組里定義的內(nèi)容
          mttr.runTestRunnables();
          }
          private class ThreadA extends TestRunnable {
          @Override
          public void runTest() throws Throwable {
          // 測試內(nèi)容
          myCommMethod2();
          }
          }
          public void myCommMethod2() throws Exception {
          System.out.println("===" + Thread.currentThread().getId() + "begin to execute myCommMethod2");
          for (int i = 0; i <10; i++) {
          int a  = i*5;
          System.out.println(a);
          }
          System.out.println("===" + Thread.currentThread().getId() + "end to execute myCommMethod2");
          }
          }
           運(yùn)行時(shí)需依賴log4j的jar文件,GroboUtils的jar包。
            主要關(guān)注3個(gè)類:TestRunnable,TestMonitorRunnable,MultiThreadedTestRunner,全部來自包:net.sourceforge.groboutils.junit.v1.MultiThreadedTestRunner.
            (1) TestRunnable 抽象類,表示一個(gè)測試線程,實(shí)例需要實(shí)現(xiàn)該類的runTest()方法,在該方法中寫自己用的測試代碼.該類繼承了jUnit的junit.framework.Assert類,所以可以在TestRunnable中使用各種Assert方法
            (可惜因?yàn)镚roboUtils使用的jUnit版本較老,且久未更新,新版本的jUnit中已經(jīng)不推薦使用這個(gè)類的方法了).該類實(shí)現(xiàn)了Runnable,在run方法中調(diào)用抽象方法runTest().
            (2)   MultiThreadedTestRunner
            這個(gè)類相當(dāng)與一個(gè)ExecuteService,可以用來執(zhí)行 TestRunnable,構(gòu)造函數(shù)需要傳入TestRunnable數(shù)組,表示需要測試的線程.調(diào)用MultiThreadedTestRunner.runTestRunnables() 方法啟動測試線程,開始執(zhí)行測試.
            這個(gè)方法默認(rèn)讓測試線程TestRunnable的run方法最多運(yùn)行1天,也可以調(diào)用MultiThreadedTestRunner.runTestRunnables(long maxTime) 這個(gè)方法,然測試線程TestRunnable最多執(zhí)行 maxTime 毫秒.如果超過maxTime毫秒之后,TestRunnable還沒有執(zhí)行完畢,則TestRunnable會被中斷,并且MultiThreadedTestRunner 會拋出異常,導(dǎo)致測試失敗fail("Threads did not finish within " + maxTime + " milliseconds.").每個(gè)TestRunnable中runTest需要能夠控制自己在什么時(shí)間自己結(jié)束自己,精確控制測試時(shí)間,不要利用上面的maxTime.
            (3) TestMonitorRunnable
            表示監(jiān)控線程,可以讓每一個(gè)TestRunnable對應(yīng)一個(gè)TestMonitorRunnable,在TestMonitorRunnable中監(jiān)控TestRunnable.TestMonitorRunnable是TestRunnable的子類,提供了一些方便使用的方法.

          posted @ 2014-11-26 14:24 順其自然EVO 閱讀(490) | 評論 (0)編輯 收藏

          僅列出標(biāo)題
          共394頁: First 上一頁 10 11 12 13 14 15 16 17 18 下一頁 Last 
          <2025年7月>
          293012345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          導(dǎo)航

          統(tǒng)計(jì)

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 亳州市| 古浪县| 佛坪县| 玛纳斯县| 那曲县| 苍南县| 彰武县| 荃湾区| 南昌市| 黄石市| 垫江县| 绥德县| 高雄县| 内黄县| 江安县| 汉川市| 通渭县| 西吉县| 九龙城区| 碌曲县| 大竹县| 来安县| 绍兴市| 西乌| 沂南县| 二连浩特市| 四会市| 扎兰屯市| 新和县| 青州市| 独山县| 和龙市| 黔江区| 莫力| 通辽市| 绥化市| 大理市| 英吉沙县| 广宗县| 河津市| 静安区|