English » | | | | | | | | |
Text-to-speech function is limited to 100 characters
1,簡介
mpstat是Multiprocessor Statistics的縮寫,是實時系統監控工具。其報告是CPU的一些統計信息,這些信息存放在/proc/stat文件中。在多CPUs系統里,其不但能查看所有CPU的平均狀況信息,而且能夠查看特定CPU的信息。mpstat最大的特點是:可以查看多核心cpu中每個計算核心的統計數據;而類似工具vmstat只能查看系統整體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,實例
用法
mpstat -V 顯示mpstat版本信息
mpstat -P ALL 顯示所有CPU信息
mpstat -P n 顯示第n個cup信息,n為數字,計數從0開始
mpstat n m 每個n秒顯示一次cpu信息,連續顯示m次,最后顯示一個平均值
mpstat n 每個n秒顯示一次cpu信息,連續顯示下去
查看每個cpu核心的詳細當前運行狀況信息,輸出如下:
[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核心的當前運行狀況信息, 每2秒更新一次
[root@ora10g ~]# mpstat -P ALL 2
查看某個cpu的使用情況,數值在[0,cpu個數-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核心的當前運行狀況信息, 每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.
參數解釋 從/proc/stat獲得數據
CPU 處理器 ID
user 在internal時間段里,用戶態的CPU時間(%),不包含 nice值為負 進程 (usr/total)*100
nice 在internal時間段里,nice值為負進程的CPU時間(%) (nice/total)*100
system 在internal時間段里,核心時間(%) (system/total)*100
iowait 在internal時間段里,硬盤IO等待時間(%) (iowait/total)*100
irq 在internal時間段里,硬中斷時間(%) (irq/total)*100
soft 在internal時間段里,軟中斷時間(%) (softirq/total)*100
idle 在internal時間段里,CPU除去等待磁盤IO操作外的因為任何原因而空閑的時間閑置時間(%)(idle/total)*100
intr/s 在internal時間段里,每秒CPU接收的中斷的次數intr/total)*100
CPU總的工作時間=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 表示當前值,_pre表示interval時間前的值。上表中的所有值可取到兩位小數點。
English » | | | | | | | | |
Text-to-speech function is limited to 100 characters
原理: 就是原理很分頁原理一樣! 選取一定數量的數據然后變成數組,接著直接寫入文件。接下來繼續選取后面沒選定數據在變成數組,接著在寫入文件!這個解決了內存溢出。但是多CPU還是有個考驗! 由于本人剛剛學PHP(PHP培訓 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) // 重點 { $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);//清空內存中的數據 } } |
頁面
PHPcode include_once"./include/class.excel.PHP";//調用EXCEL類 require_once'./include/class.zipfile.PHP';//調用大包類 $xls=newExcel;//實例化 $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++)//循環寫出 { $s=6000*$i; $ip=$_SG['db']->fetch_all("select*frommain_info".$w[0]."limit".$s.",".$p);//調用自己寫的數據庫(數據庫培訓數據庫認證)方法,寫出數組 $xls->addArray($ip);//調用EXCEL類中addArray方法 xml1=$xls->write("./".$i.".xls");//調用EXCEL類中write方法 unset($ip); unset($xml1); sleep(1); } |
English » | | | | | | | | |
Text-to-speech function is limited to 100 characters
高高興興迎接新的產品新需求,滿心歡喜的開始
工作,結果研究了一下午才發現,是自己想的太簡單了,是我太單純呀。
需求是這樣的類似下雪的效果,隨機產生一些小雪花,然后每個雪花可以點擊到下個頁面。
接到需求之后我的首先想法就是用button實現不久可以了,多簡單點事情,結果實踐之后就知道自己多么的無知了,在
移動中的button根本沒有辦法接收點擊事件。
然后同事給出了一種解決辦法,通過手勢獲取點擊的位置,然后遍歷頁面上的控件,如果在這個范圍內就點擊成功。通過這個想法我嘗試用frame來實現需求,然后發現自己又白癡了,頁面上所有的“雪花”的frame,都是動畫結束的位置,并不是實時的位置。
但是我感到用手勢,然后通過位置,遍歷,這個思路應該是對的,于是繼續
百度,發現了一個好東西
- (CALayer *)hitTest:(CGPoint)p;
通過這個可以得到這個頁面時候覆蓋了這個點,這樣就可以解決我的問題了,把手勢加在“雪花”的父頁面上,然后點擊事件里進行處理(當然 用這個方法就沒有必要一定用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; } } } } |
這樣就可以解決問題,當然應該還有其他的方法,歡迎補充。
English » | | | | | | | | |
Text-to-speech function is limited to 100 characters
Java接口和Java抽象類代表的就是抽象類型,就是我們需要提出的抽象層的具體表現。OOP面向對象的編程,如果要提高程序的復用率,增加程序的可維護性,可擴展性,就必須是面向接口的編程,面向抽象的編程,正確地使用接口、抽象類這些太有用的抽象類型做為你結構層次上的頂層。
1、Java接口和Java抽象類最大的一個區別,就在于Java抽象類可以提供某些方法的部分實現,而Java接口不可以,這大概就是Java抽象類唯一的優點吧,但這個優點非常有用。 如果向一個抽象類里加入一個新的具體方法時,那么它所有的子類都一下子都得到了這個新方法,而Java接口做不到這一點,如果向一個Java接口里加入一個新方法,所有實現這個接口的類就無法成功通過編譯了,因為你必須讓每一個類都再實現這個方法才行.
2、一個抽象類的實現只能由這個抽象類的子類給出,也就是說,這個實現處在抽象類所定義出的繼承的等級結構中,而由于Java語言的單繼承性,所以抽象類作為類型定義工具的效能大打折扣。 在這一點上,Java接口的優勢就出來了,任何一個實現了一個Java接口所規定的方法的類都可以具有這個接口的類型,而一個類可以實現任意多個Java接口,從而這個類就有了多種類型。
3、從第2點不難看出,Java接口是定義混合類型的理想工具,混合類表明一個類不僅僅具有某個主類型的行為,而且具有其他的次要行為。
4、結合1、2點中抽象類和Java接口的各自優勢,具精典的設計模式就出來了:聲明類型的
工作仍然由Java接口承擔,但是同時給出一個Java抽象類,且實現了這個接口,而其他同屬于這個抽象類型的具體類可以選擇實現這個Java接口,也可以選擇繼承這個抽象類,也就是說在層次結構中,Java接口在最上面,然后緊跟著抽象類,哈,這下兩個的最大優點都能發揮到極至了。這個模式就是“缺省適配模式”。 在Java語言API中用了這種模式,而且全都遵循一定的命名規范:Abstract +接口名。
Java接口和Java抽象類的存在就是為了用于具體類的實現和繼承的,如果你準備寫一個具體類去繼承另一個具體類的話,那你的設計就有很大問題了。Java抽象類就是為了繼承而存在的,它的抽象方法就是為了強制子類必須去實現的。
使用Java接口和抽象Java類進行變量的類型聲明、參數是類型聲明、方法的返還類型說明,以及數據類型的轉換等。而不要用具體Java類進行變量的類型聲明、參數是類型聲明、方法的返還類型說明,以及數據類型的轉換等。
我想,如果你編的代碼里面連一個接口和抽象類都沒有的話,也許我可以說你根本沒有用到任何設計模式,任何一個設計模式都是和抽象分不開的,而抽象與Java接口和抽象Java類又是分不開的。
接口的作用,一言以蔽之,就是標志類的類別。把不同類型的類歸于不同的接口,可以更好的管理他們。把一組看如不相關的類歸為一個接口去調用.可以用一個接口型的變量來引用一個對象,這是接口我認為最大的作用.
自己的感想
在平時的JAVA編程中,用JDBC連接
數據庫是非常常用的.而這里面涉及到的有DriverManager,Connection,Statement,其中第一個是類,后兩者是接口.Connection用于獲取一個指定了數據庫的連接.而這個數據庫的指定是在程序的開頭或者配置文件中指定.那么通過DriverManager.getConnection就可以獲得根據指定數據庫的具體數據庫連接對象.
那么,問題的關鍵就在這里,在以后的程序中,我門所使用的這個Connection,都是這個接口引用的一個對象.它即可以是oracle數據庫連接對象, 也可以是sql
server連接對象.但光看內部程序,我們并不知道它具體是那種類型的.因為通過接口.它展現給我們的都是Connection類型的.不管我們換了什么數據庫,程序中總是Connection conn=...
但是假如我們不用Connection接口.而換用具體的類,那么如果我們只用一種數據庫比如sql server,那我們就用這個SqlserverConnection類來實例一個對象然后在程序中調用.但是假設有天我們要換成mysql數據庫呢?那么,程序用所有的SqlServerConnection是不是都要換成MysqlConnection呢,并且,方法可能都會失效.
這就是接口的優勢體現,如果用接口,我們不用去管程序中具體是在調用哪個類,我只要知道是調用具有某種共同屬性的類.而這個類的指定都交給工廠類去完成.在程序內部,我們完全只能看見的是對接口的調用.這個接口就代表著具體的實現類了.
現在學習MVC模式。使得WEB開發以多層的方式。而再這些層中關系比較密切的就是模型層,持久化層,然后是底層數據庫。模型層中需要BO,DTO,VO。而持久化層就是DAO類啦。不過按照大型項目架構。每層之間都應該通過接口。
這點比較重要。接口的作用是為了降低層之間的耦合度。這樣,下層只對上層公開接口。而封閉了內部實現。這是好處1。第二呢就是當接口的實現改變時。上層的調用代碼是不用改變的。最后一點呢。就是接口本身的好處了,那就是一個接口,多種實現。具體要用到那種實現由工廠指定.那么萬一實現改變了,也只用改工廠,不用改程序.
English » | | | | | | | | |
Text-to-speech function is limited to 100 characters
如何使用Java讀寫系統屬性?
讀:
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文件的結構和基本用法
結構:擴展名為properties的文件,內容為key、value的映射,例如”a=2″
用法:
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(); } } } |
English » | | | | | | | | |
Text-to-speech function is limited to 100 characters
項目又延期了,老板恨恨的批評了整個項目組,投入了那么多,產出在哪里?查原因,發現是由于項目的需求不斷變更導致,這恐怕是很多項目經理、程序員都經歷過的事。
我這里就談談項目延期的一個重要因素:需求問題
這張圖大家再熟悉不過了,我再炒一下冷飯,列一下主要可能的情況:
客戶為何提不了真正的需求?
1、業務部門:業務人員基本是站在自身的角度看問題,從自身負責的業務出發,沒有從本部門或更高層次來分析問題,導致需求的著眼點比較低。在此基礎上形成的最終需求也就是把各部門的需求進行匯總,簡單處理罷了。而且,業務部門對技術知識的匱乏,也導致其提出需求時是沒有考慮技術上方面的。
2、技術人員:客戶方面的技術人員由于業務知識有限,無法挖掘更深層次的需求,只能是基于已有需求,或者輕度發掘部分需求,無法從根本上解決需求的問題。
按照以上提出的需求,可想而知,項目的結局如何。也有部分項目,在需求分析階段,生成了完整的需求規格說明書,并且用戶簽字畫押,最終的結果是如果不能真正解決客戶業務的問題,即使系統投產了,也必將引來用戶的各種抱怨,勢必對公司形象、后續項目產生各種不利影響。
我們在整天抱怨需求不斷變化的同時,能否換個角度來看待需求的變化,假設需求就是變化的,事實情況也是如此。從企業及業務自身的發展來看,企業是不斷發展的,而業務也是不斷發展的,為了滿足企業經營需要及業務發展需要,需求本身就是應該是不斷變化和發展的。
那么,真正的需求在哪里?
從企業運營角度看,為什么要做系統?其目的都是滿足企業運營的需要,只有站在企業運營的高度來審視需求,才能真正幫助需求發起人,形成完整的需求。這就需要我們:
1、真正掌握做該系統的目的
2、程序員要深入了解業務,多溝通,最好有領域專家協助,從上而下梳理業務需求,糾正不合理的需求,挖掘潛在的需求
3、以技術的手段來解決需求變更的問題,做到以不變應萬變,從而在最大程度上減少需求變更帶來程序的變化。這方面對程序員、項目設計者的要求比較高。
需求變化不可怕、需求變更也不可怕,可怕的是我們不知道變化及變更的本質,而是停留在表象;可怕的是我們不知道去擁抱這種變化,而是一味的排斥;可怕的是我們不知道用自己的長項(技術手段)最大化的去解決這種變化,而是把自己的弱項(業務)暴露在客戶面前。
English » | | | | | | | | |
Text-to-speech function is limited to 100 characters
每個程序員都有些不畏死亡決戰猛獸的英雄事跡。以下這些是我的。
內存沖突
畢業不到半年,拿著剛到手的文憑,我在Lexmark公司的一個嵌入式
Linux固件開發團隊中負責追蹤一個內存沖突的問題。因為內存沖突的原因和問題表象總是相差非常大,所以這類問題很難調。有可能是因為緩存溢出,也有可能是指針未初始化,或是指針被多次free,亦或是某處的DMA錯誤,但是你所見的卻是一堆神秘的問題:掛起、指令未定義、打印錯誤,以及未處理的內核錯誤。這些都非常頻繁,內存沖突看上去似乎是隨機出現又很難重現。
要調試這種問題,第一步是可重現問題。在我們奇跡般地找到這樣一個場景之后,故事開始變得好玩起來。
當時,我們發現在運行時因內存沖突而產生的程序崩潰每幾百小時就會出現一次。之后有一天有人發現一個特別的打印任務會產生內存沖突從而在幾分鐘之內就使程序崩潰。我從來不知道為什么這個打印任務會產生這個問題。現在,我們就可以進一步做些什么了。
調試
這個問題可重現之后,我就開始尋找崩潰中出現的模式。最引人注意的是未定義指令和內核錯誤,它們差不多三分之一的時間就會發生一次。未定義指令的地址是一個合理的內核代碼地址,但是CPU讀到的這個指令卻不是我們期望出現的。這就很簡單了,可能是有人不小心寫了這些指令。把這些未定義指令的句柄打印出來之后,我可以看到這些錯誤的指令所在位置的周邊內存的狀態。
在做了大量失敗的將更多的代碼排除出崩潰的嘗試之后,一個特殊的崩潰漸漸顯現。
崩潰之王
這個崩潰解開了所有秘密。當時我們用了一個雙核CPU。在這個特殊的崩潰里,首先CPU1在有效的模塊地址范圍內收到了一個未處理的內核錯誤,而此時它正在嘗試執行模塊代碼,這段代碼可能是一個沖突的頁表或是一個無效TLB。而正在處理這個錯誤時,CPU0在內核地址空間內收到了一個非法的指令陷阱。
以下是從修改后的未定義指令句柄中打印出來的數據(已轉為物理地址,括號中是出錯地址)
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
以下是內存域應該顯示的數據:
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)是有沖突的。一個同事指出沖突行中0x49fa30e7這個字是一個魔術cookie,它標記了系統中一個特殊環形緩沖區的入口。入口值的最后一個字永遠是一個時間戳,所以0x5439541b是上一個入口的時間戳。我決定去讀取這個環形緩沖的內容,但它現在掛在一個不可執行的KGDB提示那了。機器現在跟死了一樣。
冷啟動攻擊
為獲取環形緩沖區的數據,我進行了一次冷啟動攻擊。我為正在使用的主板搞到了一份概要拷貝,然后發現CPU的復位線上連了一塊不受歡迎的板子。我把它短路了,重置CPU而不妨礙DRAM的完整性。然后,我把Boot掛載在引導程序上。
在引導程序里,我dump到了問題中環形緩沖區的內容。謝天謝地,這個緩存總是在一個固定的物理地址上被定位到,所以找到它不是問題了。
通過分析錯誤時間戳周邊的環形緩沖區,我們發現了兩個老的cache line。這兩個cache line里有有效數據,但是在這兩個cache line里的時間戳卻是環形緩沖區里之前的時間。
導致CPU0上未定義指令的cache line與環形緩沖區里那兩個老cache line之一相當契合,但是這并不說明其他可能的地方也是這樣。我發現一個決定性的證據。假設,另一個消失的cache line是導致CPU1上未處理內核錯誤的元兇。
錯置的cache line
cache line應該被寫入0x0ebd2bc0(環形緩沖區里的cache line),但是事實上卻寫入了0x0018abc0(沖突的內核碼)。這些地址在我們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
一個地址的低5位是cache line(32字節cache line)里的索引。后10位,即位[14:5],表示緩存集。剩下的17位,即位[31:15],用來表示緩存里當前存的是哪個cache line.
我向我們的CPU供應商提交了一個bug報告,之后他們制定了一個解決方案,并在下一版本CPU里修復了這個bug。
我期望聽到更多牛掰的此類故事,也期望我自己可以再攢點這樣的。
English » | | | | | | | | |
Text-to-speech function is limited to 100 characters
加速之必要
不考慮技術,有一件事是肯定的——人們似乎總是希望可以更快。根據各種各樣的研究,現在用戶只愿意等待一個
web應用程序加載三秒或更短的時間,超過的話,他們就會變得越來越不耐煩或者干脆換一個應用程序。這些高期待不斷被壓到移動web之上;現在還壓到移動
App上。就像Web,現今的
移動移動app都有它們自己的性能問題并需要做出一些微調。最新研究表明,過去,在
手機上獲取app時,47%的移動用戶主要是抱怨速度慢且反應遲鈍。App在
蘋果的app商店上被譴責“慢得可怕”。對于Facebook的iPhone應用程序,38,000條評論中有超過21,000的用戶只給app一星的評價。用戶多數表示app慢,死機,“一直在加載”。
“移動app根據它們在app商店的排名而生存或死亡……排名不佳,用戶采用率就降低”佛里斯特研究公司的MargoVisitacion這么說道。這或許就是為什么80%的品牌iPhone,
Android和Blackberry應用程序無法達到1,000的下載量的原因。拙劣的移動app性能直接影響用戶獲取和用戶維系。那么該做些什么以保證你的移動app性能盡可能的強大呢?
通過捕捉現實中移動app性能“獲得真實信息”
移動app性能首先,最重要的是:為了真正理解移動app性能,你必須衡量你的真正用戶正在體驗的性能。在數據中心的模擬機上進行
測試可以有所幫助但是它基本和你的真實終端用戶的真正體驗無關。你的數據中心和你的終端用戶間有許多影響性能的變量因素,包括云,第三方服務/集成,CDNs,移動瀏覽器和設備。衡量真實用戶是在巨大的復雜物上精準評估性能并確定一個性能提升的基準線的唯一方法。衡量你的真實用戶體驗的性能可以讓你就移動app(關鍵參數方面的,如你客戶使用的所有的地域,設備和網絡)性能做出報告。
現在,移動app測試和使用SDKs監控以提交本地app可以讓你快速輕松地鳥瞰你所有客戶的移動app性能。
負載測試從終端用戶角度看也很重要,尤其是在開始一個app前,綜合測試網絡可以讓你在不同的條件下評估性能水平。
理解拙劣性能的商業影響
確定移動app性能問題以及它們對轉化的影響很重要:比如,你會注意到移動app的響應時間增加與轉化的減少息息相關。這樣你就可以進行分類,基于一些考慮(如:我的哪些客戶,多少客戶受到影響了)按輕重緩急解決問題。如果一個地區的流量份額很高但有問題,而另一個地區的份額較少,那你就知道該優先解決哪個性能問題了。
確保第三方集成有效
就像web應用程序,許多移動app為了給終端用戶提供更豐富更滿意的體驗吸收了大量第三方服務的內容。一個實例便是社交媒體集成,如Twitter就被集成到奧林匹克移動app中了。很不幸,如果你依賴第三方服務的話,你就會完全受限于他們的性能特點。在使用一個第三方集成的app前,你需要確保集成無縫順利且可以提供你期待的性能。此外,你還要確保一直監控著第三方性能且你的app被設計得可以完好地降級以防第三方的問題。
讓你的移動APP快起來
在這個飛速運轉的移動app世界有一句格言比任何時候都真——快比慢好。你可以使用一些特定工具和技術讓你的移動app變得更快,包括以下:
優化緩存–讓你的app數據完全脫離網絡。對于內容多的app,設備上的緩存內容可以通過避免移動網絡和你的內容基礎設施上的過多障礙以提升性能。
將往返時間最小化–考慮使用一個可以提供無數能夠加快你的app服務的CDN,包括減少網絡延遲的邊緣緩存,網絡路由優化,內容預取,以及更多。
將有效荷載規模最小化–專注壓縮,通過使用任意可用的壓縮技術減少你的數據的規模。確保圖像規模適合你最要的設備段。同樣,確保你利用壓縮。如果你有要花很長時間加載的內容,那么你可以一點一點兒的加載。你的app可以在加載時使用該內容而不是等整個加載完成后才使用它。零售app經常使用該技術。
優化你的本機代碼–寫得不好或全是bug的代碼也會導致性能問題。在你的代碼上運行軟件或檢查代碼以找出潛在問題。
優化你的后端服務性能–如果對你的app進行了
性能測試后你發現后端服務是性能削弱的罪魁禍首,那么你就不得不進行評估并決定該如何加快這些服務。
總結
智能手機用戶當然也是“快比慢好”,他們期待他們的app可以飛快。幾乎每隔一段時間,移動運營商和智能手機制造商都要宣布更快的網和設備,但不幸的是,移動app本身的速度卻跟不上。
最主要的原因是一組截然相反的目標使得實現飛速性能變得很困難。移動app開發者總希望提升速度的同時可以提供更豐富的體驗。需要更多內容和特點能夠快速地覆蓋寬帶,內存和計算機能力。
本文給出了一個簡短的本地移動app的性能最佳實踐的例子。性能調整的空間很大,但錯誤的空間同樣也很大。因此,早點測試你的app,絕不要藥聽天由命。記住——快總比慢好。
English » | | | | | | | | |
Text-to-speech function is limited to 100 characters
寫過
Junit單元測試的同學應該會有感覺,Junit本身是不支持普通的多線程測試的,這是因為Junit的底層實現上,是用System.exit退出用例執行的。JVM都終止了,在測試線程啟動的其他線程自然也無法執行。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測試用例,就必須讓主線程等待所有子線程執行完成后再退出。想到的辦法自然是Thread中的join方法。話又說回來,這樣一個簡單而又典型的需求,難道會沒有第三方的包支持么?通過google,很快就找到了GroboUtils這個Junit多線程測試的開源的第三方的工具包。 GroboUtils官網下載解壓后 使用 GroboUtils-5\lib\core\GroboTestingJUnit-1.2.1-core.jar 這個即可 GroboUtils是一個工具集合,里面包含各種測試工具,這里使用的是該工具集中的jUnit擴展. 依賴好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數組,想當于并發多少個。 TestRunnable[] trs = new TestRunnable [10]; for(int i=0;i<10;i++){ trs[i]=new ThreadA(); } // 用于執行多線程測試用例的Runner,將前面定義的單個Runner組成的數組傳入 MultiThreadedTestRunner mttr = new MultiThreadedTestRunner(trs); // 開發并發執行數組里定義的內容 mttr.runTestRunnables(); } private class ThreadA extends TestRunnable { @Override public void runTest() throws Throwable { // 測試內容 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"); } } |
運行時需依賴log4j的jar文件,GroboUtils的jar包。
主要關注3個類:TestRunnable,TestMonitorRunnable,MultiThreadedTestRunner,全部來自包:net.sourceforge.groboutils.junit.v1.MultiThreadedTestRunner.
(1) TestRunnable 抽象類,表示一個測試線程,實例需要實現該類的runTest()方法,在該方法中寫自己用的測試代碼.該類繼承了jUnit的junit.framework.Assert類,所以可以在TestRunnable中使用各種Assert方法
(可惜因為GroboUtils使用的jUnit版本較老,且久未更新,新版本的jUnit中已經不推薦使用這個類的方法了).該類實現了Runnable,在run方法中調用抽象方法runTest().
(2) MultiThreadedTestRunner
這個類相當與一個ExecuteService,可以用來執行 TestRunnable,構造函數需要傳入TestRunnable數組,表示需要測試的線程.調用MultiThreadedTestRunner.runTestRunnables() 方法啟動測試線程,開始執行測試.
這個方法默認讓測試線程TestRunnable的run方法最多運行1天,也可以調用MultiThreadedTestRunner.runTestRunnables(long maxTime) 這個方法,然測試線程TestRunnable最多執行 maxTime 毫秒.如果超過maxTime毫秒之后,TestRunnable還沒有執行完畢,則TestRunnable會被中斷,并且MultiThreadedTestRunner 會拋出異常,導致測試失敗fail("Threads did not finish within " + maxTime + " milliseconds.").每個TestRunnable中runTest需要能夠控制自己在什么時間自己結束自己,精確控制測試時間,不要利用上面的maxTime.
(3) TestMonitorRunnable
表示監控線程,可以讓每一個TestRunnable對應一個TestMonitorRunnable,在TestMonitorRunnable中監控TestRunnable.TestMonitorRunnable是TestRunnable的子類,提供了一些方便使用的方法.
English » | | | | | | | | |
Text-to-speech function is limited to 100 characters