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