Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all 11804 articles
Browse latest View live

广电行业 | 大数据在用户流量分析中的应用 2015年上半年电视互动业务分析

$
0
0

31910874

199IT数据中心微信账户:i199IT

大数据是什么?如何出现和应用在人们的生活中?我们可以先来设想一个生活场景:当你早上醒来打开电视,遥控器已经自动更换到你以往关注的财经频道;当你准备开车去上班,导航仪已经按照目前的路况规划了你的上班路线,及时避开了拥堵路段;在你上班的时候,手机提醒你快到结婚纪念日了,同时用你的消费喜好数据为你推荐了几款礼物以作备选;当你结束一天的工作回到家中,电视已经根据你的内容喜好为你推荐好了频道和节目,这就是大数据应用。

最近一项研究表明,采用大数据的公司比不采用大数据的公司利润平均高6个百分点。6个百分点也许不那么起眼,但在激烈的市场竞争中,这是可以让企业生存下来、脱颖而出的资本。过去几年,无论是医疗、健康、交通、公共安全,还是生活、购物、旅游、娱乐,都已经逐渐建立起大数据分析系统。大数据的应用也从早期的数据密集型行业(例如电信、金融、能源、科研、互联网),逐步向非数据密集型行业扩张。

近年来,广电行业也开始注重大数据的应用。在广电行业领域内,并不缺乏大数据,关键在于如何应用大数据。观众除了喜欢黄金时段看电视,还喜欢在哪些时段打开电视?他们收看电视都有哪些习惯?解决这一切问题的基础还是数据。

1 大数据的核心是数据

小样本时代正在逐步被大数据时代所取代,但如何应用大数据?如何得到多维度的数据做到领域间的融合呢?在某研掉机构看来,大数据的实现基础在于采集的数据源覆盖量广、维度多。

在数据种类和终端方面,某研掉机构已与多家大型网络运营商达成独家合作模式,获取到多源终端(包括机顶盒、IPTV、PC端、智能电视、平板电脑、手机等)的用户行为数据,同时联合新浪微博,增加互联网维度,实现数据跨屏采集,目前数据范围已涉及娱乐、互联网、消费生活等多个领域。

在广电数据采集范围方面,某研掉机构已在全国范围内覆盖9500万户家庭,其中包括北京、天津、上海、广东、河南、陕西、四川、贵州、海南、吉林、福建等10多个省市的全数据。凭借自主研发的Data Plus大数据运营决策系统及中国传媒大学的资深学术背景,某研掉机构对全网数据实现了高效准确的采集和运算。在这样的数据基础上,大数据的核心价值在一点点被构建出来。

某研掉机构通过瑞智云2.0平台进行数据分析与挖掘,将这些数据结果结合互联网等数据运用到广电行业的运营决策中,为电视台、频道、栏目影视剧制作者以及广告商提供参考。

466318938

2 大数据在电视节目分析中的应用:电视剧《怒放》的收视分析

为电广传媒文化发展有限公司制作的电视剧《怒放》进行数据分析,某研掉机构从剧本入手分析,通过对剧本各个维度的解析,同时结合历史数据,从而对该剧播出后的收视效果做出预测,而预测结果与实际播出效果非常吻合。同时对该剧在播出期间的各个市场的表现情况、该剧的用户行为特征和流入流出的情况,某研掉机构也做了详细的分析。

北京市场更爱言情戏。电视剧《怒放》播出期间,各市场用户接触最多的电视剧题材均为爱情言情、战争、都市生活,这三类题材的电视剧整体为河南市场收视效果最好,北京市场爱情言情题材电视剧的到达率最高,其他市场均为战争题材电视剧的到达率最高(如图1)。

473821586

战争题材在北方更受欢迎。战争题材电视剧在陕西市场所占的收视份额最高,河南市场紧随其后,北京和广东市场战争题材电视剧的收视份额相当,为18%上下,可见,战争题材的电视剧在西北、华中地区很受欢迎。

CCTV-1播出的电视剧《怒放》在河南市场的收视份额最高,陕西市场次之,广东市场收视份额与其他市场相差较大,说明CCTV-1《怒放》在西北、华中地区深受用户喜爱(如图2)。

479827368

各市场CCTV-1《怒放》忠诚用户占比,河南市场最高,潜在用户占比为陕西市场最高,北京市场忠诚用户和潜在用户占比最低。可见,CCTV-1《怒放》在河南市场的用户群最好(如图3)。

782813146

CCTV-1《怒放》在各市场全天时段1小时忠诚和潜在用户的收视率和到达率趋势基本一致,且忠诚用户的收视率和到达率高出潜在用户1倍以上。

CCTV-1《怒放》在河南市场全天时段1小时的收视率明显高于其他市场,广东市场全天时段1小时的收视率低于其他市场;河南市场全天时段1小时忠诚和潜在用户收视率峰值出现在23:00—24:00,其他市场忠诚和潜在用户的收视率高峰出现在20:00—22:00(如图4)。

730314693

各市场全天时段的收视走势为战争题材新剧的首播时段提供收视数据支持。

北京市场和陕西市场用户群下午时段的平均收视要高于上午时段,广东市场上下午时段收视相差不大,河南市场下午时段的平均收视高于上午时段,此外,河南市场战争题材整体收视环境最好,为战争等题材电视剧的二三轮推广提供了参考。

北京市场CCTV-1《怒放》各集收视表现中,第34集收视率最高,为3.103%;该集收视率进点较高,随着剧情的展开,收视率逐步上升,在21:16—21:17到达峰值3.742%,此时,流出用户最多,流入用户也处于较高水平,之后收视率有所下降,在21:33—21:34下降到2.874%后,剧情出现大的转折,收视率继续上升,片尾处收视率直线下降。

3 大数据在电视互动业务分析中的应用:2015年上半年电视互动业务分析

电视节目的直播业务具有线性特征,观众在选择收看节目时受到的限制较多,而互动业务的开设在很大程度上弥补了电视平台的不足,也给了观众更多选择。同时,越来越多的观众也开始在某些场景中选择时移业务和回看业务作为辅助的观看手段。

观众通过手中的遥控器对互动业务进行自主操作,互动业务的数据能充分反映出忠诚观众群及其节目内容偏好,因此,对互动业务及其用户数据进行综合、多维分析具有非常重要的意义。本例将集中梳理分析2015上半年互动业务的使用情况。

2015年电视互动业务中电视剧类的日均到达率、播出量、收视时长均有波动和回落,通过2015年上半年互动业务的排行来具体分析电视剧、综艺节目、频道的综合实力和观众的忠诚度,可以挖掘更多现象背后的社会原因。

2015年1—5月电视时移业务总体分析。以电视剧为例,2015年上半年时移业务的日均到达率、播出量、收视时长呈高度正相关关系。2015年上半年,电视剧类在时移业务的日均到达率和收视时长随电视剧播出量的增加呈下降趋势。在4、5月份下降到低位,其中爱情言情、栏目剧、战争题材的收视时长缩水最大(如图5)。造成电视剧时移业务数据下滑的原因除播出量减少外,还与上半年电视平台播出电视剧的质量不高有很大关系。

730344937

2015年上半年电视剧及综艺节目时移收视排行。在上半年电视剧时移业务榜单中,CCTV-1上榜率最高,可以看出CCTV-1作为资深频道品牌,受众忠诚而且广泛,在这一平台播出的剧集普遍有着不错的收视效果。而从播出题材来看,观众利用时移业务收看爱情言情和社会伦理两类题材电视剧的比重最高。在卫视频道组中,湖南、江苏、浙江、东方四大卫视包揽前十。从节目题材来看,卫视播出的电视剧题材更加丰富和均衡,其中武侠、戏说演绎、都市生活也都是观众喜欢收看的类型(如图6、图7)。

166491829

730346340

据2015年上半年综艺节目时移业务榜单分析,在央视组中,CCTV-1所占比重最大。由于上半年年初有中国最重要的传统节日——春节,榜单中的时移节目与春节相关的最多,共占5成,节目类型以综艺晚会最多,占6成,这也可以从侧面看出春节在传统文化和民族凝聚力中的重要性。而从卫视榜单中能看出,老牌综艺节目仍然具有号召力,并且忠实观众多,而热度高的新节目也令观众瞩目(如图8、图9)。

31914865

730351650

2015年上半年时移业务频道收视排名央视频道组中,CCTV-1高居榜首,高度体现了央视综合频道的综合实力,其凭借权威的新闻、热门电视剧和综艺节目等,始终保持高度的观众黏性。另外,通过榜单可以看出,观众对体育、音乐等节目的忠诚度也比较高。

在卫视频道组中,湖南卫视、江苏卫视、浙江卫视、东方卫视依然是第一梯队中实力相当的频道,通过轮番的电视剧战和综艺战,不仅快速提高了整体竞争力,也拉开了和其他卫视的距离(如图10)。

473852525

2015年1—5月电视回看业务总体分析。与时移业务相比,回看业务更能显示观众对频道和节目的忠诚度。由2015年上半年电视剧回看业务的日均到达率可知,1月到2月有较大幅度下降,之后是小幅波动。电视剧回看业务收视时长数据与日均到达率和播出量相比差别较大,呈逐月下降趋势,其中爱情言情和都市生活均呈现出先上升后下降的趋势。

2015年电视剧回看业务中,央视组榜单被CCTV-1和CCTV-8包揽,节目类型充分反映了CCTV的频道定位,以反特/谍战和社会伦理占比最大。

在卫视组中,由于受回看内容和忠诚用户影响的原因,电视剧回看业务与时移排名略有不同,北京卫视、安徽卫视进入榜单,其中湖南卫视及其高清频道占到了4成。

剧集题材以爱情言情比重最高,说明爱情依然是观众最喜欢也是最主流的题材类型,其次为戏说演绎、社会伦理和时代变迁类题材。

在综艺回看业务榜单中,CCTV-1占据大半壁江山,CCTV-6和CCTV-3均分榜单其余席位。受春节影响,节目类型依然是以综艺晚会为主,显示出时段特点。

在综艺节目回看业务卫视组中,整体格局相对时移业务有所改变,湖南卫视、江苏卫视、浙江卫视、东方卫视所占比重有所下降,北京卫视、辽宁卫视、湖北卫视上榜,但湖南卫视依然领跑,浙江卫视其次。在节目类型中,综艺晚会占6成,都与春节主题有关;真人秀占3成,《奔跑吧,兄弟》回看最高;择偶占1成,这也从侧面说明年轻人的婚姻问题仍是社会关注的主要问题之一。

在2015年上半年回看业务央视组频道排名中,CCTV-1稳居第一,另外,CCTV-8、CCTV-6、CCTV-5、CCTV-少儿排入前五,说明关注电视剧、电影、体育、动画片等节目的观众忠诚度非常高。在卫视组中,湖南卫视、江苏卫视、浙江卫视继续保持前三甲,东方卫视排在第六,安徽卫视凭借播出的《月亮拥抱太阳》排入前五。

随着互联网及移动互联网的发展,一度造成电视节目观众流失。自2014年以来,月均电视节目到达率超七成,高质量、大制作电视节目的出现,使节目内容成为热门话题在互联网上广泛传播。

随着时移、回看等技术的发展和普及,打破了电视节目播出收看受时间限制的特点,即使观众错过了喜爱的节目,通过时移等功能,依然可以在电视机前收看。这些技术手段有效地把观众吸引回了电视荧屏,未来电视互动业务会有更长远的发展。

4 大数据在用户流量分析中的应用:看穿用户收视行为

用户流动分析是针对某一时段或在某一节目播出过程中,通过对用户收视流向的监测,呈现出该时段内用户收视行为及偏好,并对同时段内播出节目的竞争力及竞争对手进行具体的分析。

对节目或者频道进行用户流动分析不仅可以把握观众对于节目内容的收视行为反馈,同时还对节目内容改进和编排提供了有效的数据参考。下面就通过几个例子了解一下用户流动分析是如何应用到实际的节目分析中去的。

图11、图12是针对某频道18:10—23:10这一时段,频道内及频道间用户流动情况的分析,两幅图分别展示了周间和周末的用户在线曲线和观众流动数据。

715663945

数据显示,在频道内的不同节目内容区间中,用户在线率持续较高的是在电视剧时段,而22点之后在线率持续走低;周末的在线观众明显高于平时。

而从频道间流向可以看出,从该频道流向其他频道的用户数量,南方影视和南方卫视分列一二位,其中重叠时段的内容以电视剧、综艺节目和新闻类别为主;从用户行为上可以推断,观众很有可能在不同的剧集之间选择,同时也在浏览选择其他内容的节目。

从用户流动数据中透露出的细节,不仅可以看到观众对不同的剧情、演员的收视反应,还可以看到同时段竞争节目的用户流向。如果增加更多维度,相信可挖掘的信息还会有很多。

结束语

大数据的应用还有更多种可能,在未来综合更多领域的数据也会有更加全面的应用案例,我们也有理由相信,未来大数据应用将会有更多的融合与跨界。

作者,李林杰,北京中传瑞智市场调查有限公司副总经理

《传媒》

http://media.people.com.cn/n/2015/1202/c400317-27881797.html

您可能也喜欢的文章:

广电总局:2013年全国广播电视行业总收入达到3734.88亿元

D-Matrix:2014年上半年度中国平板电视线上零售市场监测

电视遥控应用Peel:截止2013年8月用户达到2500万

乐视:2015年智能电视应用行业报告

预计2013年全球超高清电视面板年出货量或达350到400万片
无觅

漫谈大型网站架构

$
0
0

---应CSDN编辑钱曙光先生之邀请,发表了一些对于网站架构设计的看法,也是最近的一些感悟,欢迎留言交流

 

大型网站架构从来都不是一个预先定义的架构,而是一个演进式的架构。很少有一个网站从建站开始,就能够因具备大型网站的所有属性而一成不变的,从最简单的LAMP架构,再到基于IOE的大型集中式应用架构,再演变成时下的分布式应用架构,随着网站用户规模的扩大,架构也在不断演进。从实体机到虚拟机再到当前流行的Docker技术,从单机房到同城多机房再到异地多活,从LAMP到J2EE再到各种分布式中间件如服务框架、分布式消息队列、配置管理中间件、分布式数据访问层,由简至繁的艰难蜕变,也正是一个网站从小变大由弱变强的成长历程,哪里有挑战,哪里才会有变革,这正是作为技术人建功立业的时刻。

规模不断扩大,但成本不可能随之线性增长,因此,如何利用规模效应降低资源成本,抽取公共部分,避免重复造轮子,提高开发效率和响应速度,成了必须思考的问题。技术存在的核心价值就是为了生产力的提高,当技术架构制约了生产力发展,就需要进行技术变革。当前支撑大型网站的几大核心技术,分布式、服务化、虚拟化,其中分布式解决的是规模化带来的问题,所谓的规模化即包括数据规模越来越大,访问量越来越高,也包括开发团队规模越来越大,工程代码规模越来越大。单机的存储能力以及负载能力必然有限,从PC到小型机再到中型机、大型机,成本将成指数级升高,而成百上千人开发同一个工程,则导致系统臃肿,开发、发布效率极低,互联网将丧失了赖以生存的灵活性,回到以前传统软件的开发模式。通过应用垂直拆分,集群分布式水平扩展,不仅使系统容量得到提升,存储和负载将分配到大规模的廉价集群上,以降低成本,开发效率和开发模式也得到改变。通过公共业务抽取,将诞生一批处于系统底层的基础服务,避免相同的内容重复造轮子,提高开发效率。作为大型网站架构中最重要的中间件,服务化框架简化了服务调用所涉及的对象序列化与反序列化,通信协议,服务路由等操作,以及到后来诞生的一个新名词—服务治理,去梳理服务的依赖关系、调用链路、强弱依赖等等更复杂的问题。除此之外,在架构师的武器库中,还有众多不同应用场景下使用的中间件,如消息中间件、 分布式数据访问层、配置管理中心、数据迁移工具、分布式文件系统等等,这些都是日常系统架构中的粘合剂。大型网站的另外一个核心技术就是资源的虚拟化,从实体机到Xen、KVM再到基于LXC的轻量级虚拟化方案,再到Docker,技术的更新换代使得资源的利用率越来越高,集群的运维、部署和管理越来越方便。另外不同的场景下如何选择存储也十分重要,高并发和大数据往往都不会单独出现,到底是采用磁盘、SSD还是采用内存,到底是采用分布式文件系统,关系数据库,还是NOSQL,还是采用内存分布式缓存,不同的场景下方案会大相径庭,分布式文件系统存储容量几乎可以理解为无限,但是吞吐低,关系型数据库有严谨的schema以及功能强大的SQL语句,可以满足各种复杂的查询条件,但无奈扩展太麻烦,为了应对高并发读写访问,master-slave、读写分离、分库分表一折腾,不仅工作量大增,且查询维度受限,还需要引入垂直化搜索引擎来扩展查询维度,NOSQL虽然能自动分区扩容,但无奈不支持SQL,而缓存虽快,内存条又太贵,架构就是要不断的权衡取舍。

大公司之所以不如小公司响应速度快,原因在于大公司有太多积累,有时候积累多了也会成为包袱,现有的模型会使得新业务难以快速融入。当遇到问题和挫折的时候,就是思考改进和系统变革的时候,从来没有哪个系统在设计好之后就封存代码永不改变的,技术永远是不断发展,需求和市场也是不断变化的,因此不要指望用一种架构满足所有的需求,系统设计需要满足一段时间内的可扩展性,但千万不要过度设计,因为过了半年之后你回过头来重新review,你会发现需求早已改变,这就是互联网的快节奏。对于系统的架构来说,一段时间之内架构的演变,常常会经历从清晰,再到模糊混乱,再重构,再清晰,然后又变得模糊的过程,市场环境总是瞬息万变的,因此,系统的设计要遵循对扩展开放,对修改封闭的原则,做到这点即可方便及时的接入新流程,又能够不影响既有的流程。从宏观来看,各个系统间的关系一定不是烟囱与烟囱的关系,而是犹如城市里的高楼大厦,通过公路连接起来,因此,要提高建房子的速度,就要充分利用已有的基础设施,已有的中间件,来降低系统构建的成本和风险。架构设计的几个层次,没有架构也是架构,专注于解决现有问题也能称为架构,而好的架构应该是即能够约束开发者又能够解放开发者使其专注于功能的设计。尽量将复杂的事情变的简单,而不要将简单的事情变的复杂,技术从来都不是用来炫的,而是用来解决实际问题的,因此我们不需要花拳绣腿,洛克希德·马丁公司的著名飞机设计师凯利·约翰逊所提出的KISS原则,就是最好的诠释。风险驱动的架构理念告诉我们,避免失败是所有工程技术的核心,架构也是技术,运用架构技术去缓解风险,避免走极端,是架构师的最根本职责。



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



JVM的一些概念

$
0
0

数据类型

Java虚拟机中,数据类型可以分为两类:基本类型和引用类型。基本类型的变量保存原始值,即:他代表的值就是数值本身;而引用类型的变量保存引用值。“引用值”代表了某个对象的引用,而不是对象本身,对象本身存放在这个引用值所表示的地址的位置。
基本类型包括:byte,short,int,long,char,float,double,Boolean,returnAddress
引用类型包括:类类型,接口类型和数组。

堆与栈

堆和栈是程序运行的关键,很有必要把他们的关系说清楚。

heap&stack

栈是运行时的单位,而堆是存储的单位。

栈解决程序的运行问题,即程序如何执行,或者说如何处理数据;堆解决的是数据存储的问题,即数据怎么放、放在哪儿。
在Java中一个线程就会相应有一个线程栈与之对应,这点很容易理解,因为不同的线程执行逻辑有所不同,因此需要一个独立的线程栈。而堆则是所有线程共享的。栈因为是运行单位,因此里面存储的信息都是跟当前线程(或程序)相关信息的。包括局部变量、程序运行状态、方法返回值等等;而堆只负责存储对象信息。

为什么要把堆和栈区分出来呢?栈中不是也可以存储数据吗?

第一,从软件设计的角度看,栈代表了处理逻辑,而堆代表了数据。这样分开,使得处理逻辑更为清晰。分而治之的思想。这种隔离、模块化的思想在软件设计的方方面面都有体现。
第二,堆与栈的分离,使得堆中的内容可以被多个栈共享(也可以理解为多个线程访问同一个对象)。这种共享的收益是很多的。一方面这种共享提供了一种有效的数据交互方式(如:共享内存),另一方面,堆中的共享常量和缓存可以被所有栈访问,节省了空间。
第三,栈因为运行时的需要,比如保存系统运行的上下文,需要进行地址段的划分。由于栈只能向上增长,因此就会限制住栈存储内容的能力。而堆不同,堆中的对象是可以根据需要动态增长的,因此栈和堆的拆分,使得动态增长成为可能,相应栈中只需记录堆中的一个地址即可。
第四,面向对象就是堆和栈的完美结合。其实,面向对象方式的程序与以前结构化的程序在执行上没有任何区别。但是,面向对象的引入,使得对待问题的思考方式发生了改变,而更接近于自然方式的思考。当我们把对象拆开,你会发现,对象的属性其实就是数据,存放在堆中;而对象的行为(方法),就是运行逻辑,放在栈中。我们在编写对象的时候,其实即编写了数据结构,也编写的处理数据的逻辑。不得不承认,面向对象的设计,确实很美。

在Java中,Main函数就是栈的起始点,也是程序的起始点。

程序要运行总是有一个起点的。同C语言一样,java中的Main就是那个起点。无论什么java程序,找到main就找到了程序执行的入口:)

堆中存什么?栈中存什么?

堆中存的是对象。栈中存的是基本数据类型和堆中对象的引用。一个对象的大小是不可估计的,或者说是可以动态变化的,但是在栈中,一个对象只对应了一个4btye的引用(堆栈分离的好处:))。
为什么不把基本类型放堆中呢?因为其占用的空间一般是1~8个字节——需要空间比较少,而且因为是基本类型,所以不会出现动态增长的情况——长度固定,因此栈中存储就够了,如果把他存在堆中是没有什么意义的(还会浪费空间,后面说明)。可以这么说,基本类型和对象的引用都是存放在栈中,而且都是几个字节的一个数,因此在程序运行时,他们的处理方式是统一的。但是基本类型、对象引用和对象本身就有所区别了,因为一个是栈中的数据一个是堆中的数据。最常见的一个问题就是,Java中参数传递时的问题。

Java中的参数传递时传值呢?还是传引用?

要说明这个问题,先要明确两点:
1. 不要试图与C进行类比,Java中没有指针的概念
2. 程序运行永远都是在栈中进行的,因而参数传递时,只存在传递基本类型和对象引用的问题。不会直接传对象本身。
明确以上两点后。Java在方法调用传递参数时,因为没有指针,所以它都是进行传值调用(这点可以参考C的传值调用)。因此,很多书里面都说Java是进行传值调用,这点没有问题,而且也简化的C中复杂性。
但是传引用的错觉是如何造成的呢?在运行栈中,基本类型和引用的处理是一样的,都是传值,所以,如果是传引用的方法调用,也同时可以理解为“传引用值”的传值调用,即引用的处理跟基本类型是完全一样的。但是当进入被调用方法时,被传递的这个引用的值,被程序解释(或者查找)到堆中的对象,这个时候才对应到真正的对象。如果此时进行修改,修改的是引用对应的对象,而不是引用本身,即:修改的是堆中的数据。所以这个修改是可以保持的了。
对象,从某种意义上说,是由基本类型组成的。可以把一个对象看作为一棵树,对象的属性如果还是对象,则还是一颗树(即非叶子节点),基本类型则为树的叶子节点。程序参数传递时,被传递的值本身都是不能进行修改的,但是,如果这个值是一个非叶子节点(即一个对象引用),则可以修改这个节点下面的所有内容。
堆和栈中,栈是程序运行最根本的东西。程序运行可以没有堆,但是不能没有栈。而堆是为栈进行数据存储服务,说白了堆就是一块共享的内存。不过,正是因为堆和栈的分离的思想,才使得Java的垃圾回收成为可能。
Java中,栈的大小通过-Xss来设置,当栈中存储数据比较多时,需要适当调大这个值,否则会出现java.lang.StackOverflowError异常。常见的出现这个异常的是无法返回的递归,因为此时栈中保存的信息都是方法返回的记录点。

Java对象的大小

基本数据的类型的大小是固定的,这里就不多说了。对于非基本类型的Java对象,其大小就值得商榷。
在Java中,一个空Object对象的大小是8byte,这个大小只是保存堆中一个没有任何属性的对象的大小。看下面语句:
Object ob = new Object();
这样在程序中完成了一个Java对象的生命,但是它所占的空间为:4byte+8byte。4byte是上面部分所说的Java栈中保存引用的所需要的空间。而那8byte则是Java堆中对象的信息。因为所有的Java非基本类型的对象都需要默认继承Object对象,因此不论什么样的Java对象,其大小都必须是大于8byte。
有了Object对象的大小,我们就可以计算其他对象的大小了。
Class NewObject {
int count;
boolean flag;
Object ob;
}
其大小为:空对象大小(8byte)+int大小(4byte)+Boolean大小(1byte)+空Object引用的大小(4byte)=17byte。但是因为Java在对对象内存分配时都是以8的整数倍来分,因此大于17byte的最接近8的整数倍的是24,因此此对象的大小为24byte。
这里需要注意一下基本类型的包装类型的大小。因为这种包装类型已经成为对象了,因此需要把他们作为对象来看待。包装类型的大小至少是12byte(声明一个空Object至少需要的空间),而且12byte没有包含任何有效信息,同时,因为Java对象大小是8的整数倍,因此一个基本类型包装类的大小至少是16byte。这个内存占用是很恐怖的,它是使用基本类型的N倍(N>2),有些类型的内存占用更是夸张(随便想下就知道了)。因此,可能的话应尽量少使用包装类。在JDK5.0以后,因为加入了自动类型装换,因此,Java虚拟机会在存储方面进行相应的优化。

引用类型

对象引用类型分为强引用、软引用、弱引用和虚引用。
强引用:就是我们一般声明对象是时虚拟机生成的引用,强引用环境下,垃圾回收时需要严格判断当前对象是否被强引用,如果被强引用,则不会被垃圾回收
软引用:软引用一般被做为缓存来使用。与强引用的区别是,软引用在垃圾回收时,虚拟机会根据当前系统的剩余内存来决定是否对软引用进行回收。如果剩余内存比较紧张,则虚拟机会回收软引用所引用的空间;如果剩余内存相对富裕,则不会进行回收。换句话说,虚拟机在发生OutOfMemory时,肯定是没有软引用存在的。
弱引用:弱引用与软引用类似,都是作为缓存来使用。但与软引用不同,弱引用在进行垃圾回收时,是一定会被回收掉的,因此其生命周期只存在于一个垃圾回收周期内。
强引用不用说,我们系统一般在使用时都是用的强引用。而“软引用”和“弱引用”比较少见。他们一般被作为缓存使用,而且一般是在内存大小比较受限的情况下做为缓存。因为如果内存足够大的话,可以直接使用强引用作为缓存即可,同时可控性更高。因而,他们常见的是被使用在桌面应用系统的缓存。

WebPack常用功能介绍

$
0
0

WebPack常用功能介绍

概述

Webpack是一款用户打包前端模块的工具。主要是用来打包在浏览器端使用的javascript的。同时也能转换、捆绑、打包其他的静态资源,包括css、image、font file、template等。个人认为它的优点就是易用,而且常用功能基本都有,另外可以通过自己开发loader和plugin来满足自己的需求。这里就尽量详细的来介绍下一些基本功能的使用。

安装

npm install webpack 

运行webpack

webpack需要编写一个config文件,然后根据这个文件来执行需要的打包功能。我们现在来编写一个最简单的config。新建一个文件,命名为webpack-config.js。config文件实际上就是一个Commonjs的模块。内容如下:

var webpack = require('webpack');
var path = require('path');
var buildPath = path.resolve(__dirname,"build");
var nodemodulesPath = path.resolve(__dirname,'node_modules');

var config = {
    //入口文件配置
    entry:path.resolve(__dirname,'src/main.js'),
    resolve:{
        extentions:["","js"]//当requrie的模块找不到时,添加这些后缀
    },
    //文件导出的配置
    output:{
        path:buildPath,
        filename:"app.js"
    }
}

module.exports = config;

我的目录结构是这样的:


webpack
    |---index.html
    |---webpack-config.js
    |---src
         |---main.js
         |---js
              |---a.js

main.js文件内容如下:


var a = require('./js/a');
a();
console.log('hello world');
document.getElementById("container").innerHTML = "<p>hello world</p>";

a.js文件内容如下:

module.exports = function(){
    console.log('it is a ');
}

然后我们执行如下的命令:


webpack --config webpack-config.js --colors

这样我们就能在目录里面看到一个新生成的目录build,目录结构如下:


webpack
    |---index.html
    |---webpack-config.js
    |---build
         |---app.js

然后引用app.js就Ok啦。main.js和模块a.js的内容就都打包到app.js中了。这就演示了一个最简单的把模块的js打包到一个文件的过程了。

介绍webpack config文件

webpack是根据config里面描述的内容对一个项目进行打包的。接着我们来解释下config文件中的节点分别代表什么意思。一个config文件,基本都是由以下几个配置项组成的。

  • entry

配置要打包的文件的入口;可以配置多个入口文件,下面会有介绍。

  • resolve

    配置文件后缀名,除了js,还有jsx、coffee等等。除了这个功能还可以配置其他有用的功能,由于我还不完全了解,有知道的朋友欢迎指教。

  • output

    配置输出文件的路径,文件名等。

  • module(loaders)

配置要使用的loader。对文件进行一些相应的处理。比如babel-loader可以把es6的文件转换成es5。
大部分的对文件的处理的功能都是通过loader实现的。loader就相当于gulp里的task。loader可以用来处理在入口文件中require的和其他方式引用进来的文件。loader一般是一个独立的node模块,要单独安装。

loader配置项:

    test: /\.(js|jsx)$/,//注意是正则表达式,不要加引号,匹配要处理的文件
    loader: 'eslint-loader',//要使用的loader,"-loader"可以省略
    include: [path.resolve(__dirname, "src/app")],//把要处理的目录包括进来
    exclude: [nodeModulesPath]//排除不处理的目录

目前已有的loader列表:
https://webpack.github.io/docs/list-of-loaders.html

一个module的例子:

module: {
    preLoaders: [
      {
        test: /\.(js|jsx)$/,
        loader: 'eslint-loader',
        include: [path.resolve(__dirname, "src/app")],
        exclude: [nodeModulesPath]
      },
    ],
    loaders: [
      {
        test: /\.(js|jsx)$/, //正则表达式匹配 .js 和 .jsx 文件
        loader: 'babel-loader?optional=runtime&stage=0',//对匹配的文件进行处理的loader 
        exclude: [nodeModulesPath]//排除node module中的文件
      }
    ]
}
  • plugins

    顾名思义,就是配置要使用的插件。不过plugin和loader有什么差别还有待研究。

来看一个使用plugin的例子:

plugins: [
    //压缩打包的文件
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        //supresses warnings, usually from module minification
        warnings: false
      }
    }),
    //允许错误不打断程序
    new webpack.NoErrorsPlugin(),
    //把指定文件夹xia的文件复制到指定的目录
    new TransferWebpackPlugin([
      {from: 'www'}
    ], path.resolve(__dirname,"src"))
  ]

目前已有的plugins列表:
http://webpack.github.io/docs/list-of-plugins.html

如何压缩输出的文件

plugins: [
    //压缩打包的文件
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        //supresses warnings, usually from module minification
        warnings: false
      }
    })]

如何copy目录下的文件到输出目录

copy文件需要通过插件"transfer-webpack-plugin"来完成。

安装:

npm install transfer-webpack-plugin  -save

配置:

var TransferWebpackPlugin = require('transfer-webpack-plugin');
//其他节点省略    
plugins: [
    //把指定文件夹下的文件复制到指定的目录
    new TransferWebpackPlugin([
      {from: 'www'}
    ], path.resolve(__dirname,"src"))
  ]

打包javascript模块

支持的js模块化方案包括:

  • ES6 模块

    import MyModule from './MyModule.js';

  • CommonJS

    var MyModule = require('./MyModule.js');

  • AMD

    define(['./MyModule.js'], function (MyModule) {
    });

上面已经演示了打包js模块,这里不再重复。ES6的模块需要配置babel-loader来先把处理一下js文件。
下面展示下打包ES模块的配置文件:


var webpack = require('webpack');
var path = require('path');
var buildPath = path.resolve(__dirname, 'build');
var nodeModulesPath = path.resolve(__dirname, 'node_modules');
var TransferWebpackPlugin = require('transfer-webpack-plugin');

var config = {
  entry: [path.join(__dirname, 'src/main.js')],
  resolve: {
    extensions: ["", ".js", ".jsx"]
    //node_modules: ["web_modules", "node_modules"]  (Default Settings)
  },
  output: {
    path: buildPath,    
    filename: 'app.js'  
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      }
    }),
    new webpack.NoErrorsPlugin(),
    new TransferWebpackPlugin([
      {from: 'www'}
    ], path.resolve(__dirname,"src"))
  ],
  module: {
    preLoaders: [
      {
        test: /\.(js|jsx)$/,
        loader: 'eslint-loader',
        include: [path.resolve(__dirname, "src/app")],
        exclude: [nodeModulesPath]
      },
    ],
    loaders: [
      {
        test: /\.js$/, //注意是正则表达式,不要加引号
        loader: 'babel-loader?optional=runtime&stage=0',//babel模块相关的功能请自查,这里不做介绍
        exclude: [nodeModulesPath]
      }
    ]
  },
  //Eslint config
  eslint: {
    configFile: '.eslintrc' //Rules for eslint
  },
};

module.exports = config;

打包静态资源

  • css/sass/less

安装css-loader和style-loader


npm install css-loader --save -dev
npm install style-loader --save -dev

config配置:


var config = {
    entry:path.resolve(__dirname,'src/main.js'),
    resolve:{
        extentions:["","js"]
    },
    output:{
        path:buildPath,
        filename:"app.js"
    },
    module:{
        loaders:[{
            test:/\.css$/,
            loader:'style!css',
            exclude:nodemodulesPath
        }]
    }
}

style-loader会把css文件嵌入到html的style标签里,css-loader会把css按字符串导出,这两个基本都是组合使用的。打包完成的文件,引用执行后,会发现css的内容都插入到了head里的一个style标签里。
如果是sass或less配置方式与上面类似。

  • images

可以通过url-loader把较小的图片转换成base64的字符串内嵌在生成的文件里。
安装:


npm install url-loader --save -dev

config配置:

var config = {
    entry:path.resolve(__dirname,'src/main.js'),
    resolve:{
        extentions:["","js"]
    },
    output:{
        path:buildPath,
        filename:"app.js"
    },
    module:{
        loaders:[{
            test:/\.css$/,
            loader:'style!css',//
            exclude:nodemodulesPath
        },
        { test:/\.png$/,loader:'url-loader?limit=10000'}//限制大小小于10k的
        ]
    }
}

css文件内容:


#container{
    color: #f00;
    background:url(images/logo-201305.png);
    /*生成完图片会被处理成base64的字符串 注意:不要写'/images/logo-201305.png',否则图片不被处理*/
}
  • iconfont

内嵌iconfont的使用方法其实和上述处理png图片的方法一致。通过url-loader来处理。

config配置:


var config = {
    entry:path.resolve(__dirname,'src/main.js'),
    resolve:{
        extentions:["","js"]
    },
    output:{
        path:buildPath,
        filename:"app.js"
    },
    module:{
        loaders:[{
            test:/\.css$/,
            loader:'style!css',//
            exclude:nodemodulesPath
        },
        { test:/\.(png|woff|svg|ttf|eot)$/,loader:'url-loader?limit=10000'}//限制大小小于10k的
        ]
    }
}

css文件内容:


@font-face {font-family: 'iconfont';
src: url('fonts/iconfont.eot'); /* IE9*/
src: url('fonts/iconfont.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('fonts/iconfont.woff') format('woff'), /* chrome、firefox */
url('fonts/iconfont.ttf') format('truetype'), /* chrome、firefox、opera、Safari, Android, iOS 4.2+*/
url('fonts/iconfont.svg#iconfont') format('svg'); /* iOS 4.1- */
}

执行打包后会把字体文件都转换成base64字符串内容到文件里.
这里有个头疼的问题,就是每个浏览器支持的字体格式不一样,由于把全部格式的字体打包进去,造成不必要的资源浪费。

打包template

我一大包handlebars的模块为例,来演示下打包模块的过程。有的模板对应的loader,有可能没有现车的,恐怕要自己实现loader。

先安装必须的node模块

npm install handlebars-loader --save -dev
npm install handlebars -save//是必须的

config配置:


var config = {
    entry:path.resolve(__dirname,'src/main.js'),
    resolve:{
        extentions:["","js"]
    },
    output:{
        path:buildPath,
        filename:"app.js"
    },
    module:{
        loaders:[
        { test: /\.html$/, loader: "handlebars-loader" }
        ]
    }
}

新建一个模板文件tb.html,目录结构:

webpack
    |---index.html
    |---webpack-config.js
    |---src
         |---template
         |         |---tb.html
         |---main.js

main.js中调用模块的代码如下:


var template = require("./template/tp.html");
var data={say_hello:"it is handlebars"};
var html = template(data);
document.getElementById('tmpl_container').innerHTML = html;         

公用的模块分开打包

这需要通过插件“CommonsChunkPlugin”来实现。这个插件不需要安装,因为webpack已经把他包含进去了。
接着我们来看配置文件:

var config = {
    entry:{app:path.resolve(__dirname,'src/main.js'),
            vendor: ["./src/js/common"]},//【1】注意这里
    resolve:{
        extentions:["","js"]
    },
    output:{
        path:buildPath,
        filename:"app.js"
    },
    module:{
        loaders:[{
            test:/\.css$/,
            loader:'style!css',
            exclude:nodemodulesPath
        }
        ]
    },
    plugins:[
        new webpack.optimize.UglifyJsPlugin({
             compress: {
                warnings: false
             }
        }),
        //【2】注意这里  这两个地方市用来配置common.js模块单独打包的
        new webpack.optimize.CommonsChunkPlugin({
            name: "vendor",//和上面配置的入口对应
            filename: "vendor.js"//导出的文件的名称
        })
    ]
}

目录结构现在是这样的:


webpack
  |---index.html
  |---webpack-config.js
  |---src
     |---main.js
     |---js
          |---a.js    //a里面require了common
          |---common.js

执行webpack会生成app.js和common.js两个文件.

多个入口

config配置:

var config = {    
    entry:{
        m1:path.resolve(__dirname,'src/main.js'),
         m2:path.resolve(__dirname,'src/main1.js')
    },//注意在这里添加文件的入口
    resolve:{
        extentions:["","js"]
    },
    output:{
        path:buildPath,
        filename:"[name].js"//注意这里使用了name变量
    }    
}

webpack-dev-server

在开发的过程中个,我们肯定不希望,每次修改完都手动执行webpack命令来调试程序。所以我们可以用webpack-dev-server这个模块来取代烦人的执行命令。它会监听文件,在文件修改后,自动编译、刷新浏览器的页面。另外,编译的结果是保存在内存中的,而不是实体的文件,所以是看不到的,因为这样会编译的更快。它就想到与一个轻量的express服务器。
安装:


npm install webpack-dev-server --save -dev

config配置:


var config = {
    entry:path.resolve(__dirname,'src/main.js'),
    resolve:{
        extentions:["","js"]
    },
    //Server Configuration options
    devServer:{
        contentBase: '',  //静态资源的目录 相对路径,相对于当前路径 默认为当前config所在的目录
        devtool: 'eval',
        hot: true,        //自动刷新
        inline: true,    
        port: 3005        
    },
    devtool: 'eval',
    output:{
        path:buildPath,
        filename:"app.js"
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin(),//这个好像也是必须的,虽然我还没搞懂它的作用
        new webpack.NoErrorsPlugin()
    ]
}

我的目录结构:


webpack
  |---index.html
  |---webpack-config.js//我把静态资源目录配置在了这里
  |---src
     |---main.js
     |---js
          |---a.js
          |---common.js

执行命令:


webpack-dev-server --config webpack-dev-config.js  --inline --colors

默认访问地址: http://localhost:3000/index.html(根据配置会不一样)

有一点需要声明,在index.html(引用导出结果的html文件)里直接引用“app.js”,不要加父级目录,因为此时app.js在内存里与output配置的目录无关:

<script type="text/javascript" src="app.js"></script>

详细文档在这里查看:
http://webpack.github.io/docs/webpack-dev-server.html

Java中httpClient中三种超时设置

$
0
0
本文章给大家介绍一下关于Java中httpClient中的三种超时设置小结
 
在Apache的HttpClient包中,有三个设置超时的地方:

/* 从连接池中取连接的超时时间*/ 
ConnManagerParams.setTimeout(params, 1000); 
/*连接超时*/ 
HttpConnectionParams.setConnectionTimeout(params, 2000); 
/*请求超时*/
HttpConnectionParams.setSoTimeout(params, 4000);


第一,ConnectionPoolTimeout:
定义了从ConnectionManager管理的连接池中取出连接的超时时间。
出错会抛出ConnectionPoolTimeoutException


第二,ConnectionTimeout:  
定义了通过网络与服务器建立连接的超时时间,Httpclient包中通过一个异步线程去创建与服务器的socket连接,这就是该socket连接的超时时间。
当连接HTTP服务器或者等待HttpConnectionManager管理的一个有效连接超时出错会抛出ConnectionTimeoutException


第三,SocketTimeout:    
这定义了Socket读数据的超时时间,即从服务器获取响应数据需要等待的时间。
当读取或者接收Socket超时会抛出SocketTimeoutException
作者:undoner 发表于2015/12/21 15:16:26 原文链接
阅读:20 评论:0 查看评论

Spark的性能调优

$
0
0

Spark的性能调优

下面这些关于Spark的性能调优项,有的是来自官方的,有的是来自别的的工程师,有的则是我自己总结的。

Data Serialization,默认使用的是Java Serialization,这个程序员最熟悉,但是性能、空间表现都比较差。还有一个选项是Kryo Serialization,更快,压缩率也更高,但是并非支持任意类的序列化。

Memory Tuning,Java对象会占用原始数据2~5倍甚至更多的空间。最好的检测对象内存消耗的办法就是创建RDD,然后放到cache里面去,然后在UI上面看storage的变化;当然也可以使用SizeEstimator来估算。使用-XX:+UseCompressedOops选项可以压缩指针(8字节变成4字节)。在调用collect等等API的时候也要小心——大块数据往内存拷贝的时候心里要清楚。

GC调优。打印GC信息:-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps。默认60%的executor内存可以被用来作为RDD的缓存,因此只有40%的内存可以被用来作为对象创建的空间,这一点可以通过设置spark.storage.memoryFraction改变。如果有很多小对象创建,但是这些对象在不完全GC的过程中就可以回收,那么增大Eden区会有一定帮助。如果有任务从HDFS拷贝数据,内存消耗有一个简单的估算公式——比如HDFS的block size是64MB,工作区内有4个task拷贝数据,而解压缩一个block要增大3倍大小,那么内存消耗就是:4*3*64MB。另外,工作中遇到过这样的一个问题:GC默认情况下有一个限制,默认是GC时间不能超过2%的CPU时间,但是如果大量对象创建(在Spark里很容易出现,代码模式就是一个RDD转下一个RDD),就会导致大量的GC时间,从而出现OutOfMemoryError: GC overhead limit exceeded,可以通过设置-XX:-UseGCOverheadLimit关掉它。

Level of Parallelism。Spark根据要处理的文件大小设置map task的数量(也可以通过SparkContext.textFile显式指定),并且使用最大的parent RDD的分区数量来执行reduce操作。设置level of parallelism或者属性spark.default.parallelism来改变并行级别,通常来说,每一个CPU核可以分配2~3个task。

Reduce Task的内存使用。在某些情况下reduce task特别消耗内存,比如当shuffle出现的时候,比如sortByKey、groupByKey、reduceByKey和join等,要在内存里面建立一个巨大的hash table。其中一个解决办法是增大level of parallelism,这样每个task的输入规模就相应减小。

Broadcasting Large Variables。在task使用静态大对象的时候,可以把它broadcast出去。Spark会打印序列化后的大小,通常来说如果它超过20KB就值得这么做。有一种常见情形是,一个大表join一个小表,把小表broadcast后,大表的数据就不需要在各个node之间疯跑,安安静静地呆在本地等小表broadcast过来就好了。

Data Locality。数据和代码要放到一起才能处理,通常代码总比数据要小一些,因此把代码送到各处会更快。Data Locality是数据和处理的代码在屋里空间上接近的程度:PROCESS_LOCAL(同一个JVM)、NODE_LOCAL(同一个node,比如数据在HDFS上,但是和代码在同一个node)、NO_PREF、RACK_LOCAL(不在同一个server,但在同一个机架)、ANY。当然优先级从高到低,但是如果在空闲的executor上面没有未处理数据了,那么就有两个选择:(1)要么等如今繁忙的CPU闲下来处理尽可能“本地”的数据,(1)要么就不等直接启动task去处理相对远程的数据。默认当这种情况发生Spark会等一会儿(spark.locality),即策略(1),如果繁忙的CPU停不下来,就会执行策略(2)。

文件存储和读取的优化。比如对于一些case而言,如果只需要某几列,使用rcfile和parquet这样的格式会大大减少文件读取成本。再有就是存储文件到S3上或者HDFS上,可以根据情况选择更合适的格式,比如压缩率更高的格式。

文件分片。比如在S3上面就支持文件以分片形式存放,后缀是partXX。使用coalesce方法来设置分成多少片,这个调整成并行级别或者其整数倍可以提高读写性能。但是太高太低都不好,太低了没法充分利用S3并行读写的能力,太高了则是小文件太多,预处理、合并、连接建立等等都是时间开销啊,读写还容易超过throttle。

Spark的Speculation。通过设置spark.speculation等几个相关选项,可以让Spark在发现某些task执行特别慢的时候,可以在不等待完成的情况下被重新执行,最后相同的task只要有一个执行完了,那么最快执行完的那个结果就会被采纳。

减少Shuffle。其实Spark的计算往往很快,但是大量开销都花在网络和IO上面,而shuffle就是一个典型。举个例子,如果(k, v1) join (k, v2) => (k, v3),那么,这种情况其实Spark是优化得非常好的,因为需要join的都在一个node的一个partition里面,join很快完成,结果也是在同一个node(这一系列操作可以被放在同一个stage里面)。但是如果数据结构被设计为(obj1) join (obj2) => (obj3),而其中的join条件为obj1.column1 == obj2.column1,这个时候往往就被迫shuffle了,因为不再有同一个key使得数据在同一个node上的强保证。在一定要shuffle的情况下,尽可能减少shuffle前的数据规模,比如 这个避免groupByKey的例子

合理的partition。运算过程中数据量时大时小,选择合适的partition数量关系重大,如果太多partition就导致有很多小任务和空任务产生;如果太少则导致运算资源没法充分利用,必要时候可以使用repartition来调整,不过它也不是没有代价的,其中一个最主要代价就是shuffle。再有一个常见问题是数据大小差异太大,这种情况主要是数据的partition的key其实取值并不均匀造成的(默认使用HashPartitioner),需要改进这一点,比如重写hash算法。测试的时候想知道partition的数量可以调用rdd.partitions().size()获知。

其它一些内容。同事发现Spark1.0.1的速度居然比Spark1.1和1.2快很多,而Spark1.2则比前几个版本要吃掉多得多的内存。

可供参考的文档:官方调优文档 Tuning Spark,Spark配置的 官方文档,Spark  Programming Guide,JVM GC调优文档,JVM 性能调优文档,How-to: Tune Your Apache Spark Jobs  part-1 &  part-2

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

分享到:

软件开发的核心

$
0
0

  「我们一直这样做开发,时间做久了,便忘了当初的本意。」

  有关软件系统开发,我们谈些什么?
  我们谈过程,编码规范、开发流程、同行评审、结对编程、持续集成,从瀑布到敏捷再到极限编程。
  我们谈架构,企业级、J2EE、容器化、SOA(面向服务架构)、Microservices(微服务化)。
  我们谈规模,大容量、高并发、大数据。
  我们还谈可靠性、可用率、n个9、响应时间等等。。。
  这一切的核心是什么?

  先讲个电力行业的一个故事,电力的项目我没做过,对电厂的原理虽有所了解,但看见那些大规模的电站还是感觉挺复杂的。 故事是这样开始的:

  记得有个给我们上培训课的主讲老师是个须发皆白的老先生,进门后掏出一堆零件放在讲台上, 一盏酒精灯、一个小水壶、一个叶片、一个铜光闪闪的小电机、一盏小灯泡。 老先生往壶里倒了些水,点燃酒精灯,不一会儿水开了,从壶嘴里喷出了蒸汽,带动叶片旋转,然后小灯泡就亮了。

  他说:这就是电厂。
  他还说:如果烧的是煤炭,这就是燃煤电厂;如果烧的天然气,这就是燃气电厂;
  如果获得热能的方式是核裂变,这就是核电厂;如果带动叶片的能量来自水从高处流向低处,这就是水电厂。
  老先生说:你们或许会问 “那我们看到的电厂怎么这么复杂”,答案其实很简单, 电力项目需要复杂系统的目的,一是为了确保安全(Safety),二是为了提高效率(Efficiency)。

  安全和效率的平衡,是所有工程技术的核心。

  看完这个故事,我就感觉到所谓 “大道至简” 大概就是这样的。

  开发软件系统的根本在于满足需求,不能满足需求的系统本身是没有意义的。 就像一个再安全、有效率的电厂不能发电又有什么意义呢。 所以软件系统开发也就是围绕根本的基础上确保安全与提高效率。

  需求作为软件的根本差异很大,需求是多样,需求也是复杂的。 一个大型 ERP 系统,一个大型仓储系统,一个大型网站系统,到底谁更复杂,没有一个定量标准,甚至都不好定性分析。 所以前面我们谈软件系统开发那么多内容都是关于 “安全” 和 “效率” 这两个围绕根本的核心。

  所有软件开发的方法论,像瀑布、敏捷到极限编程围绕的是开发活动的效率问题,而编码规范、流程制定、同行评审等等则是有关开发的安全问题。 那么 SOA 化或进一步微服务化其实同时考虑到了安全与效率,服务化拆分有利于大规模开发团队的并行开发,提升了开发效率, 但上线部署复杂了降低了运维效率,但运维效率可以通过自动化来得到弥补,而开发则不可能自动化。

  同理,可靠性、可用性和容灾设计这些活动都是围绕 “安全” 这个核心,而性能优化,提升响应性则是围绕 “效率”。 有些关键的软件系统必须同时兼顾 “安全” 和 “效率”,例如用在飞机、汽车内用于控制起落、刹车、油门的软件系统, 不安全或无效率造成事故是会死人的,而另外一大部分软件系统因为不安全或无效率造成的事故则死的是钱。

  没有人去争论建设电厂到底是不是一门艺术,但肯定有人在争论软件开发(程序设计)到底是不是一门艺术, 但终究大部分的软件系统开发还是更偏向于工程技术。

阿里巴巴集团宣布正式加入Apache基金会

$
0
0

2015年11月19日,阿里巴巴集团宣布正式加入Apache基金会,并向Apache基金会捐赠开源项目JStorm。JStorm正式成为Apache Storm里的子项目。JStorm将在 Apache Storm里孵化,孵化成功后会成为Apache Storm主干。 Apache基金会官方表示,非常高兴JStorm能够成为Apache Storm社区的一员。

JStorm是由阿里巴巴开源的实时计算系统,它使用Java语言代替Clojure语言重写了Apache Storm,并在原来的基础上做了诸多性能和功能的优化。

JStorm团队在开源领域屡获殊荣

经过4年发展,阿里巴巴JStorm集群已经成为世界上最大的集群之一,基于JStorm的应用数量超过1000个。数据显示,JStorm集群每天处理的消息数量达到1.5PB。

阿里巴巴共享事业部高级技术专家封仲淹介绍,得益于阿里巴巴内部业务应用的磨练,JStorm平台发展得越来越快。最近两年,JStorm增加了Backpressure,Dynamic HighLevel Batch,Stable Nimbus HA,CGroup Module,Classloader,TopologyMaster等大量新的功能;在性能上,任何场景下JStorm运行速度都比Storm快平均20%。

任何场景下JStorm运行速度都比Storm快平均20%

封仲淹表示,阿里巴巴捐献JStorm给Apache,意味着Apache Storm下一个重大版本会基于JStorm的Java内核进行改造。阿里巴巴的这一举措也将带动更多的开发者致力于Storm的开发,加速Storm革新。

阿里巴巴是目前国内开源贡献最多的企业,公司自身也在开源技术方面受益很多。阿里巴巴表示,公司期待着与Apache基金会更深层次的互动和协作,参与国际通行的协作方式,并不断加入新的协作组织,成为开源社区的积极贡献者。

目前,阿里巴巴是Linux基金会成员和Xen基金会成员。阿里巴巴的开源技术,已直接被国内相关电商企业所采用。

业内评论表示,JStorm被Apache基金会接受意味着中国企业参与开源建设达到一个新高度,此举将进一步提高中国技术人的国际影响力。

Github链接: https://github.com/alibaba/jstorm


通过客户端软件访问Gmail的方法

$
0
0

  Gmail对于很多中国用户来说是很难舍弃的服务,现在虽然Gmail所有客户端通讯协议(IMAP、POP3、SMTP等)端口 均被屏蔽,导致用户无法正常访问Gmail邮箱,不过,通过一些设置,我们依旧可以通过客户端软件来访问Gmail服务。

  首先,需要在Gmail里设置其支持IMAP,这样才能在客户端里使用IMAP来访问Gmail。设置方法是:登录 Gmail,点击右上方的齿轮图标 ,然后选择设置,点击转发和 POP/IMAP,选择启用 IMAP,点击保存更改。

   电脑客户端软件

  电脑客户端我推荐使用Microsoft Outlook,其他客户端建议优先用国外的软件如Windows Mail、Thunderbird等。

  首先修改一下电脑的Hosts文件,在Windows系统里,打开C:\WINDOWS\system32\drivers\etc\hosts文件,在末尾添加如下内容,然后重启电脑即可,腾讯管家、360卫士、金山卫士可能会提示病毒或非法修改系统,请不必理会,并将Hosts文件加入白名单。

173.194.192.109  imap.gmail.com
173.194.192.109  pop.gmail.com
173.194.193.108 smtp.gmail.com

  接下来需要在Outlook里配置客户端。打开 Outlook,在“账户信息”界面里点“添加账户”,然后选择“电子邮件帐户”,然后选择“手动配置服务器设置或其他服务器类型”-“Internet电子邮件”,之后添加IMAP服务器,填写的内容如下:

  接收邮件 (IMAP) 服务器 - 要求 SSL

  imap.gmail.com

  端口:993

  要求 SSL:是

  发送邮件 (SMTP) 服务器 - 要求 TLS

  smtp.gmail.com

  端口:465 或 587

  要求 SSL:是

  要求身份验证:是

  使用相同的设置作为接收邮件服务器

  完整姓名或显示姓名:[用户的姓名]

  帐户名称或用户名称:用户的完整 Gmail 地址 (username@gmail.com)。

  电子邮件地址:用户的完整 Gmail 地址 (username@gmail.com)。

  密码:用户的 Gmail 密码,如果 Gmail 设置了两步验证,则为新增应用的密码。

通过客户端软件访问Gmail的方法

通过客户端软件访问Gmail的方法

  设置好了以后,用户即可通过Outllook来访问Gmail服务。

   iPhone/Android手机端设置

  手机端访问Gmail的方法,和使用电脑客户端的方法基本一样,也是使用IMAP协议来访问,不过不同的是,未越狱的iPhone是无法修改Hosts文件,因此填写IMAP服务器和SMTP服务器的时候,不能填写上面的域名地址,而需要直接填写IP地址才行。

  在iPhone里的设置方法是,新建一个邮件帐号,邮件类型选择“其他”,设置里选用IMAP的方式,在输入IMAP地址的时候写IP地址,不要写域名,IP地址就是上面Hosts里的IP地址,用户名为Gmail地址,密码为Gmail密码,SMTP设置如出现提示信息,点详细信息,信任该IP,之后即可在手机上继续使用Gmail。

通过客户端软件访问Gmail的方法

通过客户端软件访问Gmail的方法

  此外,在iPhone客户端里,还可以设置一下Gmail邮件的归档,邮件设置“账户”高级里,存档邮件修改为Gmail所有邮件,将丢弃邮件转移到归档邮件,即可在手机上方便使用Gmail的邮件归档功能。

通过客户端软件访问Gmail的方法

  最后,如果上述Google的IP地址未来无法访问了,最新的IP地址获取方法如下:添加微信公众号 williamlonginfo ,然后发送关键字google,即可获得最新的IP和设置方法。发送关键字gmail,可获得Gmail的最新IP和设置方法。

评论《通过客户端软件访问Gmail的方法》的内容...

相关文章:


微博: 新浪微博 - 微信公众号:williamlonginfo
月光博客投稿信箱:williamlong.info(at)gmail.com
Created by William Long www.williamlong.info
月光博客

你有必要知道的 25 个 JavaScript 面试题

$
0
0

你有必要知道的 25 个 JavaScript 面试题

1、使用 typeof bar === "object"判断 bar是不是一个对象有神马潜在的弊端?如何避免这种弊端?

使用 typeof的弊端是显而易见的(这种弊端同使用 instanceof):

let obj = {};
let arr = [];

console.log(typeof obj === 'object');  //true
console.log(typeof arr === 'object');  //true
console.log(typeof null === 'object');  //true

从上面的输出结果可知, typeof bar === "object"并不能准确判断 bar就是一个 Object。可以通过 Object.prototype.toString.call(bar) === "[object Object]"来避免这种弊端:

let obj = {};
let arr = [];

console.log(Object.prototype.toString.call(obj));  //[object Object]
console.log(Object.prototype.toString.call(arr));  //[object Array]
console.log(Object.prototype.toString.call(null));  //[object Null]

另外,为了珍爱生命,请远离 ==

珍爱生命

[] === false是返回 false的。

2、下面的代码会在 console 输出神马?为什么?

(function(){
  var a = b = 3;
})();

console.log("a defined? " + (typeof a !== 'undefined'));   
console.log("b defined? " + (typeof b !== 'undefined'));

这跟变量作用域有关,输出换成下面的:

console.log(b); //3
console,log(typeof a); //undefined

拆解一下自执行函数中的变量赋值:

b = 3;
var a = b;

所以 b成了全局变量,而 a是自执行函数的一个局部变量。

3、下面的代码会在 console 输出神马?为什么?

var myObject = {
    foo: "bar",
    func: function() {
        var self = this;
        console.log("outer func:  this.foo = " + this.foo);
        console.log("outer func:  self.foo = " + self.foo);
        (function() {
            console.log("inner func:  this.foo = " + this.foo);
            console.log("inner func:  self.foo = " + self.foo);
        }());
    }
};
myObject.func();

第一个和第二个的输出不难判断,在 ES6 之前,JavaScript 只有函数作用域,所以 func中的 IIFE 有自己的独立作用域,并且它能访问到外部作用域中的 self,所以第三个输出会报错,因为 this在可访问到的作用域内是 undefined,第四个输出是 bar。如果你知道闭包,也很容易解决的:

(function(test) {
            console.log("inner func:  this.foo = " + test.foo);  //'bar'
            console.log("inner func:  self.foo = " + self.foo);
}(self));

如果对闭包不熟悉,可以戳此: 从作用域链谈闭包

4、将 JavaScript 代码包含在一个函数块中有神马意思呢?为什么要这么做?

换句话说,为什么要用立即执行函数表达式(Immediately-Invoked Function Expression)。

IIFE 有两个比较经典的使用场景,一是类似于在循环中定时输出数据项,二是类似于 JQuery/Node 的插件和模块开发。

for(var i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(i);  
    }, 1000);
}

上面的输出并不是你以为的0,1,2,3,4,而输出的全部是5,这时 IIFE 就能有用了:

for(var i = 0; i < 5; i++) {
    (function(i) {
      setTimeout(function() {
        console.log(i);  
      }, 1000);
    })(i)
}

而在 JQuery/Node 的插件和模块开发中,为避免变量污染,也是一个大大的 IIFE:

(function($) { 
        //代码
 } )(jQuery);

5、在严格模式('use strict')下进行 JavaScript 开发有神马好处?

  • 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
  • 消除代码运行的一些不安全之处,保证代码运行的安全;
  • 提高编译器效率,增加运行速度;
  • 为未来新版本的Javascript做好铺垫。

6、下面两个函数的返回值是一样的吗?为什么?

function foo1()
{
  return {
      bar: "hello"
  };
}

function foo2()
{
  return
  {
      bar: "hello"
  };
}

在编程语言中,基本都是使用分号(;)将语句分隔开,这可以增加代码的可读性和整洁性。而在JS中,如若语句各占独立一行,通常可以省略语句间的分号(;),JS 解析器会根据能否正常编译来决定是否自动填充分号:

var test = 1 + 
2
console.log(test);  //3

在上述情况下,为了正确解析代码,就不会自动填充分号了,但是对于 returnbreakcontinue等语句,如果后面紧跟换行,解析器一定会自动在后面填充分号(;),所以上面的第二个函数就变成了这样:

function foo2()
{
  return;
  {
      bar: "hello"
  };
}

所以第二个函数是返回 undefined

7、神马是 NaN,它的类型是神马?怎么测试一个值是否等于 NaN?

NaN是 Not a Number 的缩写,JavaScript 的一种特殊数值,其类型是 Number,可以通过 isNaN(param)来判断一个值是否是 NaN

console.log(isNaN(NaN)); //true
console.log(isNaN(23)); //false
console.log(isNaN('ds')); //true
console.log(isNaN('32131sdasd')); //true
console.log(NaN === NaN); //false
console.log(NaN === undefined); //false
console.log(undefined === undefined); //false
console.log(typeof NaN); //number
console.log(Object.prototype.toString.call(NaN)); //[object Number]

ES6 中, isNaN()成为了 Number 的静态方法: Number.isNaN().

8、解释一下下面代码的输出

console.log(0.1 + 0.2);   //0.30000000000000004
console.log(0.1 + 0.2 == 0.3);  //false

JavaScript 中的 number 类型就是浮点型,JavaScript 中的浮点数采用IEEE-754 格式的规定,这是一种二进制表示法,可以精确地表示分数,比如1/2,1/8,1/1024,每个浮点数占64位。但是,二进制浮点数表示法并不能精确的表示类似0.1这样 的简单的数字,会有舍入误差。

由于采用二进制,JavaScript 也不能有限表示 1/10、1/2 等这样的分数。在二进制中,1/10(0.1)被表示为 0.00110011001100110011……注意 0011是无限重复的,这是舍入误差造成的,所以对于 0.1 + 0.2 这样的运算,操作数会先被转成二进制,然后再计算:

0.1 => 0.0001 1001 1001 1001…(无限循环)
0.2 => 0.0011 0011 0011 0011…(无限循环)

双精度浮点数的小数部分最多支持 52 位,所以两者相加之后得到这么一串 0.0100110011001100110011001100110011001100…因浮点数小数位的限制而截断的二进制数字,这时候,再把它转换为十进制,就成了 0.30000000000000004。

对于保证浮点数计算的正确性,有两种常见方式。

一是先升幂再降幂:

function add(num1, num2){
  let r1, r2, m;
  r1 = (''+num1).split('.')[1].length;
  r2 = (''+num2).split('.')[1].length;

  m = Math.pow(10,Math.max(r1,r2));
  return (num1 * m + num2 * m) / m;
}
console.log(add(0.1,0.2));   //0.3
console.log(add(0.15,0.2256)); //0.3756

二是是使用内置的 toPrecision()toFixed()方法, 注意,方法的返回值字符串。

function add(x, y) {
    return x.toPrecision() + y.toPrecision()
}
console.log(add(0.1,0.2));  //"0.10.2"

9、实现函数 isInteger(x)来判断 x 是否是整数

可以将 x转换成10进制,判断和本身是不是相等即可:

function isInteger(x) { 
    return parseInt(x, 10) === x; 
}

ES6 对数值进行了扩展,提供了静态方法 isInteger()来判断参数是否是整数:

Number.isInteger(25) // true
Number.isInteger(25.0) // true
Number.isInteger(25.1) // false
Number.isInteger("15") // false
Number.isInteger(true) // false

JavaScript能够准确表示的整数范围在 -2^532^53之间(不含两个端点),超过这个范围,无法精确表示这个值。ES6 引入了 Number.MAX_SAFE_INTEGERNumber.MIN_SAFE_INTEGER这两个常量,用来表示这个范围的上下限,并提供了 Number.isSafeInteger()来判断整数是否是安全型整数。

10、在下面的代码中,数字 1-4 会以什么顺序输出?为什么会这样输出?

(function() {
    console.log(1); 
    setTimeout(function(){console.log(2)}, 1000); 
    setTimeout(function(){console.log(3)}, 0); 
    console.log(4);
})();

这个就不多解释了,主要是 JavaScript 的定时机制和时间循环,不要忘了,JavaScript 是单线程的。详解可以参考 从setTimeout谈JavaScript运行机制

11、写一个少于 80 字符的函数,判断一个字符串是不是回文字符串

function isPalindrome(str) {
    str = str.replace(/\W/g, '').toLowerCase();
    return (str == str.split('').reverse().join(''));
}

这个题我在 codewars上碰到过,并收录了一些不错的解决方式,可以戳这里: Palindrome For Your Dome

12、写一个按照下面方式调用都能正常工作的 sum 方法

console.log(sum(2,3));   // Outputs 5
console.log(sum(2)(3));  // Outputs 5

针对这个题,可以判断参数个数来实现:

function sum() {
  var fir = arguments[0];
  if(arguments.length === 2) {
    return arguments[0] + arguments[1]
  } else {
    return function(sec) {
       return fir + sec;
    }
  }
}

13、根据下面的代码片段回答后面的问题

for (var i = 0; i < 5; i++) {
  var btn = document.createElement('button');
  btn.appendChild(document.createTextNode('Button ' + i));
  btn.addEventListener('click', function(){ console.log(i); });
  document.body.appendChild(btn);
}

1、点击 Button 4,会在控制台输出什么?

2、给出一种符合预期的实现方式

1、点击5个按钮中的任意一个,都是输出5

2、参考 IIFE。

14、下面的代码会输出什么?为什么?

var arr1 = "john".split(''); j o h n
var arr2 = arr1.reverse(); n h o j
var arr3 = "jones".split(''); j o n e s
arr2.push(arr3);
console.log("array 1: length=" + arr1.length + " last=" + arr1.slice(-1));
console.log("array 2: length=" + arr2.length + " last=" + arr2.slice(-1));

会输出什么呢?你运行下就知道了,可能会在你的意料之外。

MDN 上对于 reverse()的描述是酱紫的:

Description

The reverse method transposes the elements of the calling array object in place, mutating the array, and returning a reference to the array.

reverse()会改变数组本身,并返回原数组的引用。

slice的用法请参考: slice

15、下面的代码会输出什么?为什么?

console.log(1 +  "2" + "2");
console.log(1 +  +"2" + "2");
console.log(1 +  -"1" + "2");
console.log(+"1" +  "1" + "2");
console.log( "A" - "B" + "2");
console.log( "A" - "B" + 2);

输出什么,自己去运行吧,需要注意三个点:

  • 多个数字和数字字符串混合运算时,跟操作数的位置有关
console.log(2 + 1 + '3'); / /‘33’
console.log('3' + 2 + 1); //'321'
  • 数字字符串之前存在数字中的正负号(+/-)时,会被转换成数字
console.log(typeof '3');   // string
console.log(typeof +'3');  //number

同样,可以在数字前添加 '',将数字转为字符串

console.log(typeof 3);   // number
console.log(typeof (''+3));  //string
  • 对于运算结果不能转换成数字的,将返回 NaN
console.log('a' * 'sd');   //NaN
console.log('A' - 'B');  // NaN

这张图是运算转换的规则

运算符转换

16、如果 list 很大,下面的这段递归代码会造成堆栈溢出。如果在不改变递归模式的前提下修善这段代码?

var list = readHugeList();

var nextListItem = function() {
    var item = list.pop();

    if (item) {
        // process the list item...
        nextListItem();
    }
};

原文上的解决方式是加个定时器:

var list = readHugeList();

var nextListItem = function() {
    var item = list.pop();

    if (item) {
        // process the list item...
        setTimeout( nextListItem, 0);
    }
};

解决方式的原理请参考第10题。

17、什么是闭包?举例说明

可以参考此篇: 从作用域链谈闭包

18、下面的代码会输出什么?为啥?

for (var i = 0; i < 5; i++) {
  setTimeout(function() { console.log(i); }, i * 1000 );
}

请往前面翻,参考第4题,解决方式已经在上面了

19、解释下列代码的输出

console.log("0 || 1 = "+(0 || 1));
console.log("1 || 2 = "+(1 || 2));
console.log("0 && 1 = "+(0 && 1));
console.log("1 && 2 = "+(1 && 2));

逻辑与和逻辑或运算符会返回一个值,并且二者都是短路运算符:

  • 逻辑与返回第一个是 false的操作数 或者 最后一个是 true的操作数
console.log(1 && 2 && 0);  //0
console.log(1 && 0 && 1);  //0
console.log(1 && 2 && 3);  //3

如果某个操作数为 false,则该操作数之后的操作数都不会被计算

  • 逻辑或返回第一个是 true的操作数 或者 最后一个是 false的操作数
console.log(1 || 2 || 0); //1
console.log(0 || 2 || 1); //2
console.log(0 || 0 || false); //false

如果某个操作数为 true,则该操作数之后的操作数都不会被计算

如果逻辑与和逻辑或作混合运算,则逻辑与的优先级高:

console.log(1 && 2 || 0); //2
console.log(0 || 2 && 1); //1
console.log(0 && 2 || 1); //1

在 JavaScript,常见的 false值:

0, '0', +0, -0, false, '',null,undefined,null,NaN

要注意 空数组([])空对象({}):

console.log([] == false) //true
console.log({} == false) //false
console.log(Boolean([])) //true
console.log(Boolean({})) //true

所以在 if中, []{}都表现为 true

if

20、解释下面代码的输出

console.log(false == '0')
console.log(false === '0')

请参考前面第14题运算符转换规则的图。

21、解释下面代码的输出

var a={},
    b={key:'b'},
    c={key:'c'};

a[b]=123;
a[c]=456;

console.log(a[b]);

输出是 456,参考原文的解释:

The reason for this is as follows: When setting an object property, JavaScript will implicitly stringify the parameter value. In this case, since b and c are both objects, they will both be converted to [object Object]. As a result, a[b] anda[c] are both equivalent to a[[object Object]] and can be used interchangeably. Therefore, setting or referencing a[c] is precisely the same as setting or referencing a[b].

22、解释下面代码的输出

console.log((function f(n){return ((n > 1) ? n * f(n-1) : n)})(10));

结果是10的阶乘。这是一个递归调用,为了简化,我初始化 n=5,则调用链和返回链如下:

递归

23、解释下面代码的输出

(function(x) {
    return (function(y) {
        console.log(x);
    })(2)
})(1);

输出1,闭包能够访问外部作用域的变量或参数。

24、解释下面代码的输出,并修复存在的问题

var hero = {
    _name: 'John Doe',
    getSecretIdentity: function (){
        return this._name;
    }
};

var stoleSecretIdentity = hero.getSecretIdentity;

console.log(stoleSecretIdentity());
console.log(hero.getSecretIdentity());

getSecretIdentity赋给 stoleSecretIdentity,等价于定义了 stoleSecretIdentity函数:

var stoleSecretIdentity =  function (){
        return this._name;
}

stoleSecretIdentity的上下文是全局环境,所以第一个输出 undefined。若要输出 John Doe,则要通过 callapplybind等方式改变 stoleSecretIdentitythis指向(hero)。

第二个是调用对象的方法,输出 John Doe

25、给你一个 DOM 元素,创建一个能访问该元素所有子元素的函数,并且要将每个子元素传递给指定的回调函数。

函数接受两个参数:

  • DOM
  • 指定的回调函数

原文利用 深度优先搜索(Depth-First-Search) 给了一个实现:

function Traverse(p_element,p_callback) {
   p_callback(p_element);
   var list = p_element.children;
   for (var i = 0; i < list.length; i++) {
       Traverse(list[i],p_callback);  // recursive call
   }
}

文章参考:

25 Essential JavaScript Interview Questions

JavaScript中的立即执行函数表达式

Javascript 严格模式详解

声明:文中对问题的回答仅代表博主个人观点

转载请注明: 淡忘~浅思» 你有必要知道的 25 个 JavaScript 面试题

预示应用性能问题的十大征兆

$
0
0
一年一度的双11、双12全民网购节已经过去,淘宝、天猫及其他电商都迎来了大量用户,但是,你的基础架构能否承载突如其来的流量?面对预期而至的大量用户,容量规划是否到位?线上商城以及后端系统是否经受住了性能的考验?

对于任何互联网电子商务的成功,有两件事至关重要:创新与性能。创新是打入市场的通行证,而性能则决定了能否在市场中长久立足,如果网站性能差强人意,那么就没有用户会愿意再次访问。今天小编专门为大家总结了十个验证应用性能的标志,如果你也遇到了下面列出的反映性能问题的标志,那么,你应该及时采取措施验证网站程序的性能。

1、月访问量骤降
对比月访问量的变化图表。如果出现下降趋势,原因大致有两个:第一是搜索引擎优化(SEO)不到位,第二是网页响应过于缓慢。这个时候你或许要和基础设施团队沟通以确定过去6到12个月的网站性能,从而找出问题根源。

2、关键页面的响应过慢
如果用户跳出率很高,你需要检查那些高跳出率的页面。尤其是支付页面、添加到购物车页面、比较不同产品页面等。你需要检查这些关键页面的响应时间与吞吐量,每分钟网站处理了多少订单?如果吞吐量下滑,你需要找出性能瓶颈。

3、资源消耗大
你需要持续监控资源使用量,及时找出资源消耗过多的服务器。关键的指标包括 CPU 使用量、内存使用量、磁盘占用量、垃圾回收量以及网络负载等。如果资源使用量出现明显峰值,你需要找出问题根源,并执行纵向或横向的扩容检查(如果必要)。

4、数据库查询
你的基础架构应该配备不同的数据库,分别用于查询、插入或更新操作。数据库应该合理配置以优化查询功能,如果查询过于复杂,会影响数据库操作,进而影响网站性能。



5、重定向次数增加
重定向数量虽然不是性能指标,但仍可能影响应用性能。如果网页重定向至多个页面,就可能影响网站性能。过多的重定向会使用户感到心烦,导致糟糕的用户体验,促使用户离开网站。

6、DNS 查询时间
你服务器的 DNS(域名系统)查询时间是多少?几毫秒是正常值。如果 DNS 查询占用的时间太久,你就需要想办法缩短它,从而减少对总查询时间的影响。

7、浏览器兼容性
你的应用应该支持多种浏览器与设备。且浏览器或设备种类不应对应用响应时间造成偏差。如果某种浏览器或设备的响应时间过长,你需要找出根源并解决之。

8、竞争对手的应用性能
你需要定期地将本网站的性能与竞争对手网站进行比较。如果自己的网站性能较弱,你可能需要对网站性能进行调优或进行架构调整。



9、网站资源优化
利用内容分发网络(CDN),可以缓存 CSS、图片、JS 等文件资源。由于图片对于任何电商网站都至关重要,优化图片加载速度简直是势在必行。

10、缓存
你应该为 Web 应用实施适当的缓存机制。如果缓存不起作用或配置不合理,就会影响性能。因此,你必须启用浏览器缓存,最小化 CSS 与 JS 文件以提高网站速度。

原文链接: https://dzone.com/articles/10-signs-you-should-validate-the-performance

感谢 mengyidan1988投递这篇资讯

资讯来源: OneAPM

已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



service层异常的处理

$
0
0

1、在service方法里面如果对异常进行了捕获的话,该事务是不会进行回滚的
       默认spring事务只在发生未被捕获的 runtimeexcetpion时才回滚。  
       spring aop  异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获runtimeexception的异常,但可以通过配置来捕获特定的异常并回滚,换句话说在service的方法中不使用try catch 或者在catch中最后加上throw new runtimeexcetpion(),这样程序异常时才能被aop捕获进而回滚
  解决方案: 
  方案1.例如service层处理事务,那么service中的方法中不做异常捕获,或者在catch语句中最后增加throw new RuntimeException()语句,以便让aop捕获异常再去回滚,并且在controller层要继续捕获这个异常并处理
  方案2.在service层方法的catch语句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句,手动回滚,这样上层就无需去处理异常(现在项目的做法)



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



深度揭秘马斯克的Open AI:绝不止是要拯救世界

$
0
0

硅谷名人马斯克创建了人工智能公司Open AI,并表示将开源其研究成果分享给研究人工智能的每一个人。国外知名科技媒体《连线》杂志发表评论文章,称开源的Open AI的成立将人工智能研究推向高潮,同时也转变了目前由谷歌、Facebook等巨头引领的人工智能领域竞争格局。未来,Open AI有望成为这一领域的监管者,将其引向对人类更为安全的发展轨迹上来。

特斯拉CEO、Open AI联合创始人马斯克

以下为文章全文:

特斯拉CEO埃隆·马斯克(Elon Musk)和创业孵化器Y Combinator总裁山姆·奥特曼(Sam Altman)担心,人工智能将会接管世界。因此,两位企业家计划建立一家投资额为10亿美元,不以追求利润为目标的公司,希望发挥人工智能的最大潜力,然后将它分享给想要使用人工智能技术的每一个人。

更安全的人工智能技术

至少这是马斯克和奥特曼在宣布他们新举措时想要传达的信息,两人建立了一家史上前所未有的新公司OpenAI。奥特曼在OpenAI成立时接受了科技网站Backchannel创始人史蒂芬·列维(Steven Levy)的采访,他表示,这一长达数十年的项目预计将超过人类智力。但是他们相信,任何风险都会降低,因为这项技术将会供所有人使用,而不是只有谷歌等公司使用。

列维很自然地问道,他们的免费分享策略是否会让人工智能技术实际上被坏人所利用,最终将尖端技术提供给现实世界的“邪恶博士”(Dr. Evils)。不过奥特曼认为,这一风险不会太大。他们认为,大众的力量将超过少数邪恶人的力量。“正像人类与邪恶博士斗争一样,大多数人是好的,人类的集体力量中可以包含一些不安定因素,”奥特曼称,“我们认为,更有可能的是,许多人工智能技术将阻止少数坏人为非作歹。”

这一看似有悖常理的观点能否站得住脚,还需要多年时间的验证。超人类的人工智能就算能够实现,也需要漫长的时间。“这一想法在直觉上有很大吸引力,”亚利桑那州立大学博士生米尔斯·博鲁达(Miles Brundage)在谈到OpenAI时表示,他专注于科学和技术领域的人类和社交层面研究,“但它还不是一个很清楚的论点。就目前我们所处的环境而言,还没有人工智能系统能够接管世界,在可预见的未来也不会有。”

但是随着OpenAI的建立,将有更多人开始研究如何开发出能够主导世界的超人类智力,而不是只让它停留在理论基础上。从短期来看,OpenAI会让马斯克、奥特曼以及他们的公司直接受益。Y Combinator投资的公司包括美国短租网站Airbnb、云存储服务Dropbox以及支付公司Stripe等“独角兽”。在从谷歌等公司挖来了顶尖人工智能研究人员后,两名企业家就可以获取之前无法得到的开发理念。他们承诺,将从各自的公司服务中提取在线数据,这样,他们就拥有了实现这些理念的途径。当前,一个推动人工智能发展的关键因素是工程人才,另外一个就是数据。

如果OpenAI能够坚守他们的使命,让所有人都能接触到新技术理念,那么它至少将是对谷歌、Facebook等巨头的一次考验。有了马斯克、奥特曼等企业家的10多亿美元注资,OpenAI正在向外界展示近几年竞争格局的改变。公司、企业家和投资者正愈加希望,通过开放他们的技术来与对手竞争。他们开始讨论看似有悖常理的理念。

开源的优势

Open AI的两位联合创始人

过去一个月人工智能领域发生了一系列令人钦佩的大事,而OpenAI的成立将其推向了高潮。在11月初,谷歌宣布其人工智能服务的软件引擎将部分开源。该人工智能服务是一种深度学习技术,能够识别图像和语音,翻译并理解人类语言等。Facebook在OpenAI成立不久前也宣布,将开源其深度学习计算机服务器的设计。Facebook的深度学习服务具备与谷歌人工智能服务相同的能力。如今OpenAI更进一步,誓言将分享其全部的研究成果,其中主要就关于深度学习技术。

是的,这三家公司的开源举措实际上是一种竞争的方式。像谷歌和Facebook这样的公司公开分享软件和硬件设计,将能够加速人工智能行业的整体发展进度,并最终使自己受益。比如说,随着外界逐步改进这些开源人工智能技术,谷歌和Facebook将能够反过来把这些技术拿为己用。同时,开源也是招聘和留住人工智能人才的一种手段。尤其是在深度学习领域,相关的研究人员许多来自于学术界,他们乐于分享自己的研究成果,以使尽可能多的人受益。“在招聘研究人员时这当然是一种竞争优势,” 奥特曼对《连线》杂志说。“我们招聘的这些人乐于将OpenAI进行开源,因为他们能够分享自己的研发成果。”

这些竞争可能比看起来的更加直接、激烈。我们不禁要想,谷歌之所以开源其TensorFlow人工智能引擎可能是因为,它知道OpenAI即将成立。而Facebook开源其Big Sur服务器设计可能也是对谷歌和OpenAI的一种回应。对于这种猜测,Facebook回应称其意图并非如此;而谷歌尚未回应置评请求;奥特曼则拒绝评论,但表示谷歌知道OpenAI即将成立。实际上谷歌怎么可能不知道呢,OpenAI在成立之前就把其顶级人工智能专家伊利亚·苏特斯科娃(Ilya Sutskever)挖走了。

但不管怎样,这些都抹杀不了谷歌开源项目的价值。不管谷歌的动机如何,其代码终究会开源,每个人只要觉得合适都可以使用。但有一点值得各位记住,在当今科技圈,分享自己的科技成果并不只是因为高尚这么简单。如今深度学习行业规模尚小,所有这些公司都在争夺能够帮助其掌握这项强大技术的人才。这些公司想要分享,但它们更想赢。因此,它们必须拿出一些自己的秘密成果,但不是全部。开源将会加速人工智能的进程,但防止单个公司和单项技术一家独大也非常重要。而这就是为何OpenAI的成立如此具有意义。

“阿波罗”计划再启

人工智能统治人类

从某些角度来看,马斯克将分享看作某种胜利之道。“众所周知,我一直对人工智能心存芥蒂,”他在一次采访中说道。话虽如此,特斯拉依然在从人工智能的发展中不断汲取养料。像谷歌一样,特斯拉也在搞无人驾驶汽车,而这类产品仰仗的就是人工智能中的深度学习技术。

深度学习依靠的是我们所说的类神经网络,它用强大的软硬件来模拟人类大脑中的神经元。例如,对其展示大量猫咪的图片后,神经元就能学会如何识别猫咪。而听过许多人类的对话后,它也能粗通对话的门道。同理,在获取大量的驾驶数据后,它也能成为合格的司机。

当然,马斯克可以直接为特斯拉挖来AI研究人员,不过创立OpenAI后,研究人员的质量必然会进一步提高(毕竟这是一个开放的机构,不受任何商业模式和短期利益的约束),甚至有可能从老对手谷歌那里吸引到人才。此外,他还可以创建一个更为强大的数据池,从而协助研究人员开展工作。奥特曼表示,旗下的创业孵化器公司Y Combinator将会与OpenAI共享数据,如果再加上特斯拉多年以来的数据积累,OpenAI现在绝对有实力与谷歌一战。

“在某些方面,OpenAI甚至会更有优势。”深度学习初创公司Skymind的CEO克里斯说道,该公司最近刚刚加入Y Combinator 的孵化项目。“毕竟术业有专攻,谷歌在房源信息数据上就肯定没有Airbnb做得好。”

马斯克很久以前就投资了一家名为DeepMind的英国公司,该公司将自己描述为“人工智能界的阿波罗计划”。这次投资也让马斯克认识到人工智能技术的巨大潜力,但最终谷歌收购了这家公司,断了他的念想。不过对于马斯克来说,没有什么不可能,这不,它直接创立了OpenAI,开启了自己的阿波罗计划,重新回到人工智能发展的快车道上来。此外,亚马逊也是OpenAI的合作伙伴之一,想必未来这场人工智能大战会更加异彩纷呈。

悲观与乐观

但是,这并没有抹杀马斯克开源项目的价值。他可能存有私心,同时又有无私的一面,但最终的结果却是广阔的AI世界获得极大的好处。通过与世界分享自己的技术,OpenAI将推动谷歌、Facebook及其它企业的研发工作。对于特斯拉和所有Y Combinator支持的企业来说这也是好事一件,对于所有迷恋AI的人来说也是好事一件。

当然,通过分享技术OpenAI也将为谷歌和Facebook提供新的元素。那些心存邪恶的人不管隐藏在何处,都将吞食OpenAI技术以强大自己的系统。我们不必为此太过担心,邪恶者会将技术向世界散播,技术本身也会向世界散播。在无人驾驶汽车领域,在自然语言理解领域,深度的学习永不会停止,随着数据和算法的正确组合,它的理解将极大拓展并达到人类“常识”的地步,最终甚至有可能发展出超人般的智力。

深度学习初创企业Skymind.io的联合创始人克里斯·尼科尔森(Chris Nicholson)表示:“最可怕的地方在于超级智力自己提升,飞速发展,变得比人类聪明无数倍。”

这正是马斯克和奥特曼试图对抗的。奥特曼说:“开发、授权并丰富技术可以保护人类,只有这样做才是保护我们的最好办法。”但在另一方面来看,他们又在缩短超级智力出现的时间。虽然奥特曼和马斯克相信让每一个人都接入超级智力可以防御一些邪恶人工智能,但事情仍可能向另一面发展。正如布伦代奇(Brundage)指出的:如果企业知道每个人都在朝着最新人工智能技术飞速奔跑,它们可能就会在安全方面有所懈怠。

比较讽刺的是预防有多必要取决于你对人类加速技术进步的能力有多乐观。从他们过去的成功经历来看,马斯克和奥特曼有充分的理由相信进步会越来越快,但其它人却不能肯定人工智能给人类带来的威胁正如马斯克和奥特曼相信的一样。尼科尔森称:“想像一下人工智能是技术专家的毒品,它将让我们兴奋,这点不必怀疑。”

谷歌和Facebook正在将人工智能推向新的时代,OpenAI至少还可以监督它们一下,当然还会监督一下其它人。尼科尔森称:“马斯克和OpenAI已经看到了人工智能的势不可挡,他们唯一希望的是改变其发展轨迹。”


© 推荐 for 互联网的那点事. | 猛击下载: iPhone客户端猛击下载: Android客户端

图片处理 javax.imageio.IIOException: Unsupported Image Type

$
0
0

用ImageIO处理图片:

 

Java代码   收藏代码
  1. ImagetIO img = ImageIO.read(new File(InputDir + InputFileName));  

JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(new FileInputStream( new File("e://1.jpg") ) );
BufferedImage sourceImg = decoder.decodeAsBufferedImage();

 报异常:

 

Caused by: javax.imageio.IIOException: Unsupported Image Type

产生原因:

ps或其他软件处理过的图片保存为jpg格式时,默认的模式是CMYK模式(这是给印刷机用的)。在图像-->模式中改为RGB模式才是显示器用的。

 

http://zhangmingji.iteye.com/blog/1969693

解决办法:方法一:通知用户修改图片格式为RGB。

方法二:用代码在后台转换。

http://iaiai.iteye.com/blog/1461370

 

参考: http://blog.sina.com.cn/s/blog_600ff075010153wn.html

          http://iaiai.iteye.com/blog/1461370

 

附件为错误测试图片。

 

http://www.cnblogs.com/yjhrem/articles/3503060.html

http://zhangmingji.iteye.com/blog/1969693



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



quartz 任务的增删改

$
0
0

从网上找了好多例子,要么太老,要么用不了。无奈,硬着头皮自己来一个。希望对路过在有帮助。。。

QuartzJob

import java.text.SimpleDateFormat;
import java.util.Date;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

/**
 * @Description: 任务执行类
 *
 * @author 那是一条神奇的天路
 * @date 2015-12-22
 * @version V1.0
 */
public class QuartzJob implements Job {

  public void execute(JobExecutionContext arg0) throws JobExecutionException {
    System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+ "★★★★★★★★★★★");  
  }
}

 QuartzManager

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.StdSchedulerFactory;

/**
 * @Description: 任务执行类
 *
 * @author 那是一条神奇的天路
 * @date 2015-12-22
 * @version V1.0
 */
public class QuartzManager {
  private static SchedulerFactory schedulerFactory = new StdSchedulerFactory();
  private static String JOB_GROUP_NAME = "EXTJWEB_JOBGROUP_NAME";
  private static String TRIGGER_GROUP_NAME = "EXTJWEB_TRIGGERGROUP_NAME";

  /**
   * 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名
   * 
   * @param jobName 任务名
   * @param cls 任务
   * @param time 时间设置,参考quartz说明文档
   */
  public static void addJob(String jobName, Class cls, String time) {
    try {
        Scheduler scheduler = schedulerFactory.getScheduler();//创建一个触发器表
        JobDetail jobDetail = JobBuilder.newJob()//创建一个jobbuilder用来定义一个任务明细。
                .ofType(cls)//设置类,将被实例化
                .withIdentity(new JobKey(jobName, JOB_GROUP_NAME))//使用给定的名称和默认组jobkey识别任务明细
                .build();//产品定义的JobDetail实例这jobbuilder。
 
        Trigger trigger = TriggerBuilder.newTrigger()
                .withSchedule(CronScheduleBuilder.cronSchedule(time))//设置schedulebuilder将用于定义触发器的表。
                .withIdentity(new TriggerKey(jobName, TRIGGER_GROUP_NAME))
                .build();//创建Trigger
 
        scheduler.scheduleJob(jobDetail,trigger);//绑定
 
      // 启动
      if (!scheduler.isShutdown()) {
    	 // scheduler.start();
      }
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * 修改一个任务的触发时间(使用默认的任务组名,触发器名,触发器组名)
   * @param jobName
   * @param time
   */
  @SuppressWarnings("unchecked")
  public static void modifyJobTime(String jobName, String time ) {
    try {
      Scheduler sched = schedulerFactory.getScheduler();
      Trigger trigger = TriggerBuilder.newTrigger()
              .withSchedule(CronScheduleBuilder.cronSchedule(time))//设置schedulebuilder将用于定义触发器的表。
              .withIdentity(new TriggerKey(jobName, TRIGGER_GROUP_NAME))
              .build();//创建Trigger
      
      if (trigger == null) {
        return;
      }
      String oldTime = ((CronTrigger) trigger).getCronExpression();
      if (!oldTime.equalsIgnoreCase(time)) {
        JobDetail jobDetail = sched.getJobDetail(new JobKey(jobName, JOB_GROUP_NAME)) ;
        Class objJobClass = jobDetail.getJobClass();
        removeJob(new TriggerKey(jobName, TRIGGER_GROUP_NAME),new JobKey(jobName, JOB_GROUP_NAME));
        addJob(jobName, objJobClass, time);
      }
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * 移除一个任务(使用默认的任务组名,触发器名,触发器组名)
   * @param triggerKey
   * @param jobKey
   */
  public static void removeJob(TriggerKey triggerKey,JobKey jobKey) {
    try {
      Scheduler sched = schedulerFactory.getScheduler();
      sched.pauseTrigger(triggerKey);// 停止触发器
      sched.unscheduleJob(triggerKey);// 移除触发器
      sched.deleteJob(jobKey) ;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * 启动所有定时任务
   */
  public static void startJobs() {
    try {
      Scheduler sched = schedulerFactory.getScheduler();
      sched.start();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * 关闭所有定时任务
   */
  public static void shutdownJobs() {
    try {
      Scheduler sched = schedulerFactory.getScheduler();
      if (!sched.isShutdown()) {
        sched.shutdown();
      }
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
}

 测试

/**
 * 测试类
 * 
 * @author chao.zhao
 *
 */
public class QuartzTest {
  public static void main(String[] args) {
    try {
      String job_name = "动态任务调度";
      System.out.println("【系统启动】开始(每1秒输出一次)...");  
      QuartzManager.addJob(job_name, QuartzJob.class, "0/1 * * * * ?");  
//      
      Thread.sleep(5000);  
      System.out.println("【修改时间】开始(每2秒输出一次)...");  
      QuartzManager.modifyJobTime(job_name, "10/3 * * * * ?");  
//      Thread.sleep(6000);  
      System.out.println("【移除定时】开始...");  
//      QuartzManager.removeJob(new TriggerKey(jobName, TRIGGER_GROUP_NAME),new JobKey(jobName, JOB_GROUP_NAME));  
//      System.out.println("【移除定时】成功");  
      QuartzManager.startJobs();
      Thread.sleep(6000);  
      QuartzManager.shutdownJobs();
//      System.out.println("【移除定时】成功");
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

 pom

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>quartz</groupId><artifactId>quartz</artifactId><version>0.0.1-SNAPSHOT</version><dependencies><dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>2.2.2</version></dependency><dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz-jobs</artifactId><version>2.2.2</version></dependency></dependencies></project>

 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐




话题监测与发现之热点新闻发现技术

$
0
0

 

最近在帮朋友做一个关于“热点新闻发现”的需求。先解释下什么是热点新闻发现:即在海量的新闻文本中,找到内容相似的那一类新闻,如果这类新闻的数量达到一定阈值,便认为该类新闻属于热点新闻。

 

其实这一类型的课题早在几年前就已经开始研究,属于TDT(Topic Detecting and Tracking话题检测与发现)分支之一,而且方法也较为成熟,多半是在文本分析的基础上利用数据挖掘中聚类知识进行处理。

 

但是,笔者遇到的这个需求较为特殊:

第一,这里的文本类型是互联网搜索引擎爬下来的“新闻”;

第二,数据量大约是10W-100W之间;

第三,需要程序单机处理,并且硬件配置和普通PC无异(双核CPU,内存4G左右);

第四,程序要求在30分钟内处理完毕;

最后,程序得到的热点新闻必须在新浪、网易、腾讯等大型门户网站上有相关新闻。

(哎,这就是小公司的悲哀,老板们总希望用最小成本获得最大收益,“又想马儿跑又想马儿不吃草”,天下哪有这等便宜事,但是谁叫他是老板,作为“码农”,还是得想尽办法去满足老板的需求。)

 

理想情况,假设硬件资源充足的情况下,大家会怎么处理?

上图为简易的传统处理流程,如果硬件资源充足,即认为内存无上限,为了达到较快的运行时间,开发者理所当然将存储在硬盘上的文本文件全部Load进内存,然后:

1)对每篇新闻进行分词:文本分析中对文本进行分词处理是每篇文本特征化的第一步;

2)文本特征化:使用这种<分词,词频>来特征化每篇文本,当然你也可以使用较为常用的TF-IDF(即词3频-逆文档率)来进行特征化;

3)聚类:当获得每篇文本特征化表示方式后,开发者可以使用层次聚类、基于密度的聚类或者比较常用的划分式聚类(比如:k-means)来进行聚类分析;

 4)过滤处理:对聚类后的所有类簇进行匹配,如果类簇中的新闻数量未达到阈值则将其过滤,否则认为该类属于热点新闻。

 

理想很美好,现实很骨感。老板给的硬件环境只是普通PC,为了不发生OOM(out of memory)的事件,这就使得开发者不可能将所有数据load至内存。是否有其他解决办法呢?也许有人想到,是不是可以采用类似外排序那种处理方式,处理一部分数据后将其结果保存至外存储,然后再处理下一步部分数据,如此循环,最后对所有结果进行归并。

 

是滴,这确实是一个好办法,即传说中的用时间换空间。但是万恶的老板要求,所有数据必须在30分钟内处理完毕,时间条件上不允许,并且传统的聚类算法处理时间随着数据量的增长往往呈非线性增长趋势,在时间上也较难满足笔者需求。

 

正像其他的数据挖掘问题一样,熟悉业务可能比会使用“牛B”的算法更为重要。为了解决上述问题,笔者对业务本身以及数据源特征进行了梳理,并总结出“某些经验”(这是基于对业务理解所产生的,可能并不一定具有普遍性,但是如果读者的业务也和笔者相似,可以举一反三试用下)形成了下述流程。


 

如图所示,与理想情况相比,新流程多了无效新闻过滤环节和二次聚类环节,并且在第一次聚类过程中特征化方式不一样,这些都是基于业务经验总结出来的,详述如下:

1) 无效新闻过滤:由于互联网上的新闻质量参差不齐以及爬虫本身的问题,所以往往会导致无效新闻的产生,笔者将其分为四类——a)新闻标题为空;b)新闻正文为空;c)新闻正文内容过短;c)新闻标题与正文内容不符。

其中,前三类过滤非常简单,只需判断是否为空或者检查字符串个数是否达标即可,而新闻标题与正文内容不符笔者是这么处理的:

       首先,将标题进行分词(分词过程中需过滤无效词);

       第二,定义同义词库,查找出分词后标题词元的同义词;

       第三,在正文中查找是否出现标题词元,如果出现的词元个数大于阈值,即认为标题与正文相符(比如,标题词元有4个,在正文中出现了3个,阈值为2,则相符);否则,再将标题词元的同义词进行匹配,如果正文中匹配的个数大于阈值,也认为标题与正文相符;否则,判定为新闻内容与标题不相符。

(Tip:其实这个匹配过程属于多字符串匹配过程,笔者选用AC算法进行处理。)

最后,即可获得有效新闻,通过此步骤,可以过滤掉大量无效新闻,优化后续处理步骤的空间和时间。

 

2) 初次聚类:在第一次聚类过程中,笔者的特征提取方式和普通文本特征提取不太一样(传统处理可能选词频或者TF-IDF作为文本特征),笔者选取每篇文本的最长句作为新闻文本特征。这是基于业务特性的经验总结,因为笔者发现互联网上的新闻发表方式多为转载,或者是在各大门户网站所发表新闻的基础上进行局部修改。在此步骤中,如果两篇文章中最长的一句话是相同的,笔者即认为它们是相似新闻。

在此步骤中,笔者将最长句作为文本特征,并采用Hashtable作为存储结构,其中key:是文本最长句,而value是具有该最长句的新闻集合。

 

3) 过滤初次聚类结果:在此步骤中,笔者设定了过滤阈值,如果某个类簇(新闻集合)中新闻数量低于阈值,则将其过滤。

当然,读者会说,此时最好不应该进行过滤,因为通过后续第二次聚类处理,某些小于阈值的类簇有可能形成一个大类簇。对,这确有可能发生,笔者之所以这么做,是在精确度和时空复杂度之间进行了取舍,如果加入此过滤步骤,将会为第二次聚类优化大量时间和空间。

 

4) 二次聚类:或许读者已发现初次聚类并不是一个十分严谨的聚类,假如两篇文本内容相似但是不拥有相同的最长句,那么它们很可能被拆分成两类新闻,而这步骤便是要解决该问题。

经过对初次聚类结果过滤后,其实此时得到的类簇数量已经远远低于最开始的新闻文本数量(笔者针对自己的数据实验,最开始时新闻文本数量有30W,经过初次聚类并过滤后将只剩下2k个类簇),此时选取每个类簇的新闻标题作为原始文本,采用理想硬件条件下的普通的聚类方式进行聚类处理,即完成了二次聚类。

(虽然普通的聚类方式会随着数据量增加时间会曾非线性增长,但是经过多次过滤处理,第二次聚类过程所处理的数据量已骤减(只有2K左右),因此可以选用该算法。)

 

5) 最后,再将二次聚类合并后的类簇进行阈值过滤,即得到热点新闻。

(TIP:在该步过滤环节中,笔者还引入了指定网站过滤——即如果某个类簇中不包含五大网站(sina/sohu/qq/ifeng/163)的新闻便将其过滤,原因是考虑到五大网站的影响力和权威性,如果连它们都没有录入新闻,我们有理由认为这类新闻很难成为热点新闻。)

 

总结一下,笔者的解题思路其实是遵循这么一个模式:为了解决时间和空间问题,需要一步一步的过滤或合并数据,但是每次在过滤或合并数据时选择的处理方式的效率一定要高,即要考虑收获与成本之间的差异,即多引入一个流程所带来的时空开销一定要远远小于后续流程所节省的时空量;其次是关于算法准确率问题,为了获得时空上的优化可能是需要牺牲算法准确率(比如初次聚类的过滤问题),笔者建议读者最好多准备几组实验数据以确定阈值,从而达到准确率和时空复杂度之间的平衡。

 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



问题排查之OOM (非原创,来自于同事的邮件分享)

$
0
0

非原创,来自于同事的邮件分享。

前段时间在测试过程中发现了mina 框架的问题:当mina 一次传输的文件超过一定值(如55m )或者连续传输文件的次数过于频繁,就会内存溢出:

org.apache.mina.filter.codec.ProtocolEncoderException: java.lang.OutOfMemoryError: Java heap space

at org.apache.mina.filter.codec.ProtocolCodecFilter.filterWrite(ProtocolCodecFilter.java:217)

at org.apache.mina.common.support.AbstractIoFilterChain.callPreviousFilterWrite(AbstractIoFilterChain.java:361)

at org.apache.mina.common.support.AbstractIoFilterChain.access$1300(AbstractIoFilterChain.java:53)

at org.apache.mina.common.support.AbstractIoFilterChain$EntryImpl$1.filterWrite(AbstractIoFilterChain.java:659)

at org.apache.mina.common.support.AbstractIoFilterChain$TailFilter.filterWrite(AbstractIoFilterChain.java:587)

at org.apache.mina.common.support.AbstractIoFilterChain.callPreviousFilterWrite(AbstractIoFilterChain.java:361)

at org.apache.mina.common.support.AbstractIoFilterChain.fireFilterWrite(AbstractIoFilterChain.java:355)

at org.apache.mina.transport.socket.nio.SocketSessionImpl.write0(SocketSessionImpl.java:166)

at org.apache.mina.common.support.BaseIoSession.write(BaseIoSession.java:177)

at org.apache.mina.common.support.BaseIoSession.write(BaseIoSession.java:168)

at com.taobao.forest.server.DefaultPushTimeTask.pushcachetothesession(DefaultPushTimeTask.java:441)

1 ) 开始是尝试用常规方法试图分析mina 在内存溢出时什么东东占了那么多内存还无法释放,于是在jboss 启动参数那加了两个参数 -XX:HeapDumpPath=\tmp  -XX:+HeapDumpOnOutOfMemoryError , 作用是在发生OutOfMemoryError 时将当时的内存映像dump 到/tmp 下,然后将dump 出来的内存映像文件下到本地用mat 分析,不过分析结果未发现有内存溢出问题,甚是奇怪。

2 )之后,又上网查了些资料,才发现mina 不是用的堆内存(Heap ),而是使用的本机直接内存(Direct Memory )

所谓本地直接 内存 并不是虚拟机运行时数据区的一部分,它根本就是本机 内存 而不是VM 直接管理的区域。

在JDK1.4 中新加入了 NIO 类,引入一种基于渠道与缓冲区的I/O 方式,它可以通过本机Native 函数库直接分配本机 内存 ,然后通过一个存储在Java 堆里面的DirectByteBuffer 对象作为这块 内存 的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java 对和本机堆中来回复制数据。显然本机直接 内存 的分配不会受到Java 堆大小的限制,但是即然是 内存 那肯定还是要受到本机物理 内存 (包括SWAP 区或者Windows 虚拟 内存 )的限制的,一般服务器管理员配置 JVM 参数时,会根据实际 内存 设置-Xmx 等参数信息,但经常忽略掉直接 内存 ,使得各个 内存 区域总和大于物理 内存 限制(包括物理的和操作系统级的限制),而导致动态扩展时出现OutOfMemoryError 异常

此外,按照jvm 规范,本地直接内存的最大值按以下顺序设定:
(1 )通过-XX:MaxDirectMemorySize=<size> 指定值
(2 )若(1 )未满足,则就取maxMemory ,也就是通过-Xmx 设定的值;
(3 )若(1 )、(2 )都未满足,则取默认值:64M ;

根据以上知识,结合此次测试情况,问题基本水落石出:

在我们测试日常机中,系统启动的时候设定-Xmx 3072m ,没有通过-XX:MaxDirectMemorySize 设定本地直接内存最大值,因此本地直接内存最大值就是-Xmx 设定的值3072m ,整个系统的物理内存为4G ,除掉系统进程占用的内存,剩下的物理内存加上swap 空间也就接近3G 。设想JVM 的 heap size 占用了1.5G ,direct memory 使用了1.5G ,这时候程序申请一100M 的direct 内存,在这种情况下无论是JVM heap size 还是direct memory 不满足触发gc 的条件,于是jvm 向os 申请分配内存,但是OS 却无可分配的内存了,于是就会抛出OutOfMemoryError 错误。

因此,在使用NIO 框架时的时候一定要注意:
如果该NIO 框架使用的直存,需谨慎设定JVM 运行参数,最好用-XX:MaxDirectMemorySize 进行设定,否则你就得清楚你设定的-Xmx 不单单制定了heap size 的最大值,它同时也是direct memory 的最大值;

再大概补充一下NIO 和OOM 知识:

 

一、 首先对于可用内存这一概念的理解

在32 位机器上,CPU 可寻址的物理内存空间最大是4G ,超出4G 将不再可见。【此处忽略PAE 支持,如果进程中使用了AWE(windows) 或者mmap(linux) 一类的方案,这里暂时不管了】

这4G 的物理内存空间又分为用户空间和内核空间。默认情况下,windows 按照50:50 的比例划分,linux 默认下用户空间3G ,内核空间1G 。

所以一个进程可用的物理内存空间,在linux32 位机器下,就是3G 。而在64 位机器下,基本上可以认为是没有任何限制,原理很简单了。。。

不管是linux 还是windows ,可用内存空间由:物理内存+swap/ 虚拟内存组成。Linux 上称作swap 【交换空间】,windows 上称作虚拟内存,本质上都是拿磁盘的一块地方当作物理内存使用。程序是不用关心使用的是物理内存,还是swap ;程序操作的是虚拟地址空间, OS 再将虚拟地址空间映射到物理内存、文件或者其他。不管是操作物理内存,还是swap ,对于程序来说完全是透明的。

Swap/ 虚拟内存啥时候会使用,这个我也没完全搞清楚,不过有一点应该没错的,就是进程新申请的内存,不会在swap/ 虚拟内存中分配,而是直接在物理内存中分配。当内存紧张时,OS 会将活动进程中占用的内存,从物理内存中交换出来,放到Swap/ 虚拟内存上(有时甚至内存不紧张也会这么干)。当进程恢复活动时,OS 再将数据从swap/ 虚拟内存空间中读出来放到物理内存中

所以当需要分析和计算进程需要占用的内存空间时,可以简单地忽略swap/ 虚拟内存的概念 【这一点需要深入再论证一下!】

二、 JVM 对内存的管理

画一张图,很容易就可以理解了,下面这个圆表示jvm 进程所占用的所有的内存空间,分成三部分:

 

1.   堆空间

包括年轻化、年老代、持久域【以SUN HOTSPOT 虚拟机实现为例,其他虚拟机会有区别,比如IBM 的虚拟机,所谓的“ 持久域” 不是在堆分配,而是在本地内存】

如果这个空间不够了,会抛出java.lang.OutOfMemoryError

2.   栈空间

每个线程都会有一个单独的stack 空间,JDK5.0 以前默认好象是256K ,JDK5.0 默认是1M ,很大的一个数值,可以通过-Xss 设置。如果这个空间不够了,会抛出java.lang.StackOverflowError

3.   本地内存

Jvm 进程可使用的内存, 除去堆、栈空间之后,剩下来的就是本地内存

以上三个空间加起来的内存,就是最终jvm 进程所使用的所有内存。如果是在32 位机器下,不能超过用户空间大小,即3G ;在64 位机器下,就要看物理内存的大小了

另再提醒一下大家,在发生了内存不足时,一味地增加-Xms 和-Xmx ,很有可能会适得其反,道理应该很明显了。需要看OOM 的类型,是堆不足,还是栈(StackOverFlow) 不足,还是本地内存不足native memory 。jvm 一般都会有足够的信息提示的。

三、 Nio 的direct memory allocate

我理解的,NIO 的直接内存分配【DMA 】,应该是从本地内存区域中分配内存。像前面讲的,如果不使用-XX:MaxDirectMemorySize 设置,那它就会使用-Xms 的设置,以日常测试环境为例, 这种情况下DMA 需要3G ,堆也需要3G, 很明显实际上这两个空间得到的内存都不可能这么大,所以要么是堆空间被挤压,拿不到3G ,要么是DMA 拿不到足够的空间

看jvm 抛出来的错误,应该是堆空间被挤压导致的。如果是本地内存不足,抛出的应该是OutOfMemoryError :Direct buffer memory ,可以看一下java.nio. DirectByteBuffer 这个类的源码,98 行

四、 NIO2.0 的改进

NIO 的DMA ,性能肯定比在堆中分配要好得多,因为是直接操作本地内存,避免了数据在JVM Heap 和本地内存之间的拷贝操作,尤其是数据量较大时应该更加明显。



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



用十条命令在一分钟内检查Linux服务器性能

$
0
0

如果你的Linux服务器突然负载暴增,告警短信快发爆你的手机,如何在最短时间内找出Linux性能问题所在?来看Netflix性能工程团队的这篇 博文,看它们通过十条命令在一分钟内对机器性能问题进行诊断。

概述

通过执行以下命令,可以在1分钟内对系统资源使用情况有个大致的了解。

  • uptime
  • dmesg | tail
  • vmstat 1
  • mpstat -P ALL 1
  • pidstat 1
  • iostat -xz 1
  • free -m
  • sar -n DEV 1
  • sar -n TCP,ETCP 1
  • top

其中一些命令需要安装sysstat包,有一些由procps包提供。这些命令的输出,有助于快速定位性能瓶颈,检查出所有资源(CPU、内存、磁盘IO等)的利用率(utilization)、饱和度(saturation)和错误(error)度量,也就是所谓的 USE方法

下面我们来逐一介绍下这些命令,有关这些命令更多的参数和说明,请参照命令的手册。

uptime

$ uptime
23:51:26 up 21:31,  1 user,  load average: 30.02, 26.43, 19.02

这个命令可以快速查看机器的负载情况。在Linux系统中,这些数据表示等待CPU资源的进程和阻塞在不可中断IO进程(进程状态为D)的数量。这些数据可以让我们对系统资源使用有一个宏观的了解。

命令的输出分别表示1分钟、5分钟、15分钟的平均负载情况。通过这三个数据,可以了解服务器负载是在趋于紧张还是区域缓解。如果1分钟平均负载很高,而15分钟平均负载很低,说明服务器正在命令高负载情况,需要进一步排查CPU资源都消耗在了哪里。反之,如果15分钟平均负载很高,1分钟平均负载较低,则有可能是CPU资源紧张时刻已经过去。

上面例子中的输出,可以看见最近1分钟的平均负载非常高,且远高于最近15分钟负载,因此我们需要继续排查当前系统中有什么进程消耗了大量的资源。可以通过下文将会介绍的vmstat、mpstat等命令进一步排查。

dmesg | tail

$ dmesg | tail
[1880957.563150] perl invoked oom-killer: gfp_mask=0x280da, order=0, oom_score_adj=0
[...]
[1880957.563400] Out of memory: Kill process 18694 (perl) score 246 or sacrifice child
[1880957.563408] Killed process 18694 (perl) total-vm:1972392kB, anon-rss:1953348kB, file-rss:0kB
[2320864.954447] TCP: Possible SYN flooding on port 7001. Dropping request.  Check SNMP counters.

该命令会输出系统日志的最后10行。示例中的输出,可以看见一次内核的oom kill和一次TCP丢包。这些日志可以帮助排查性能问题。千万不要忘了这一步。

vmstat 1

$ vmstat 1
procs ---------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
34  0    0 200889792  73708 591828    0    0     0     5    6   10 96  1  3  0  0
32  0    0 200889920  73708 591860    0    0     0   592 13284 4282 98  1  1  0  0
32  0    0 200890112  73708 591860    0    0     0     0 9501 2154 99  1  0  0  0
32  0    0 200889568  73712 591856    0    0     0    48 11900 2459 99  0  0  0  0
32  0    0 200890208  73712 591860    0    0     0     0 15898 4840 98  1  1  0  0
^C

vmstat(8) 命令,每行会输出一些系统核心指标,这些指标可以让我们更详细的了解系统状态。后面跟的参数1,表示每秒输出一次统计信息,表头提示了每一列的含义,这几介绍一些和性能调优相关的列:

  • r:等待在CPU资源的进程数。这个数据比平均负载更加能够体现CPU负载情况,数据中不包含等待IO的进程。如果这个数值大于机器CPU核数,那么机器的CPU资源已经饱和。
  • free:系统可用内存数(以千字节为单位),如果剩余内存不足,也会导致系统性能问题。下文介绍到的free命令,可以更详细的了解系统内存的使用情况。
  • si, so:交换区写入和读取的数量。如果这个数据不为0,说明系统已经在使用交换区(swap),机器物理内存已经不足。
  • us, sy, id, wa, st:这些都代表了CPU时间的消耗,它们分别表示用户时间(user)、系统(内核)时间(sys)、空闲时间(idle)、IO等待时间(wait)和被偷走的时间(stolen,一般被其他虚拟机消耗)。

上述这些CPU时间,可以让我们很快了解CPU是否出于繁忙状态。一般情况下,如果用户时间和系统时间相加非常大,CPU出于忙于执行指令。如果IO等待时间很长,那么系统的瓶颈可能在磁盘IO。

示例命令的输出可以看见,大量CPU时间消耗在用户态,也就是用户应用程序消耗了CPU时间。这不一定是性能问题,需要结合r队列,一起分析。

mpstat -P ALL 1

$ mpstat -P ALL 1
Linux 3.13.0-49-generic (titanclusters-xxxxx)  07/14/2015  _x86_64_ (32 CPU)
07:38:49 PM  CPU   %usr  %nice   %sys %iowait   %irq  %soft  %steal  %guest  %gnice  %idle
07:38:50 PM  all  98.47   0.00   0.75    0.00   0.00   0.00    0.00    0.00    0.00   0.78
07:38:50 PM    0  96.04   0.00   2.97    0.00   0.00   0.00    0.00    0.00    0.00   0.99
07:38:50 PM    1  97.00   0.00   1.00    0.00   0.00   0.00    0.00    0.00    0.00   2.00
07:38:50 PM    2  98.00   0.00   1.00    0.00   0.00   0.00    0.00    0.00    0.00   1.00
07:38:50 PM    3  96.97   0.00   0.00    0.00   0.00   0.00    0.00    0.00    0.00   3.03
[...]

该命令可以显示每个CPU的占用情况,如果有一个CPU占用率特别高,那么有可能是一个单线程应用程序引起的。

pidstat 1

$ pidstat 1
Linux 3.13.0-49-generic (titanclusters-xxxxx)  07/14/2015    _x86_64_    (32 CPU)
07:41:02 PM   UID       PID    %usr %system  %guest    %CPU   CPU  Command
07:41:03 PM     0         9    0.00    0.94    0.00    0.94     1  rcuos/0
07:41:03 PM     0      4214    5.66    5.66    0.00   11.32    15  mesos-slave
07:41:03 PM     0      4354    0.94    0.94    0.00    1.89     8  java
07:41:03 PM     0      6521 1596.23    1.89    0.00 1598.11    27  java
07:41:03 PM     0      6564 1571.70    7.55    0.00 1579.25    28  java
07:41:03 PM 60004     60154    0.94    4.72    0.00    5.66     9  pidstat
07:41:03 PM   UID       PID    %usr %system  %guest    %CPU   CPU  Command
07:41:04 PM     0      4214    6.00    2.00    0.00    8.00    15  mesos-slave
07:41:04 PM     0      6521 1590.00    1.00    0.00 1591.00    27  java
07:41:04 PM     0      6564 1573.00   10.00    0.00 1583.00    28  java
07:41:04 PM   108      6718    1.00    0.00    0.00    1.00     0  snmp-pass
07:41:04 PM 60004     60154    1.00    4.00    0.00    5.00     9  pidstat
^C

pidstat命令输出进程的CPU占用率,该命令会持续输出,并且不会覆盖之前的数据,可以方便观察系统动态。如上的输出,可以看见两个JAVA进程占用了将近1600%的CPU时间,既消耗了大约16个CPU核心的运算资源。

iostat -xz 1

$ iostat -xz 1
Linux 3.13.0-49-generic (titanclusters-xxxxx)  07/14/2015  _x86_64_ (32 CPU)
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          73.96    0.00    3.73    0.03    0.06   22.21
Device:   rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
xvda        0.00     0.23    0.21    0.18     4.52     2.08    34.37     0.00    9.98   13.80    5.42   2.44   0.09
xvdb        0.01     0.00    1.02    8.94   127.97   598.53   145.79     0.00    0.43    1.78    0.28   0.25   0.25
xvdc        0.01     0.00    1.02    8.86   127.79   595.94   146.50     0.00    0.45    1.82    0.30   0.27   0.26
dm-0        0.00     0.00    0.69    2.32    10.47    31.69    28.01     0.01    3.23    0.71    3.98   0.13   0.04
dm-1        0.00     0.00    0.00    0.94     0.01     3.78     8.00     0.33  345.84    0.04  346.81   0.01   0.00
dm-2        0.00     0.00    0.09    0.07     1.35     0.36    22.50     0.00    2.55    0.23    5.62   1.78   0.03
[...]
^C

iostat命令主要用于查看机器磁盘IO情况。该命令输出的列,主要含义是:

  • r/s, w/s, rkB/s, wkB/s:分别表示每秒读写次数和每秒读写数据量(千字节)。读写量过大,可能会引起性能问题。
  • await:IO操作的平均等待时间,单位是毫秒。这是应用程序在和磁盘交互时,需要消耗的时间,包括IO等待和实际操作的耗时。如果这个数值过大,可能是硬件设备遇到了瓶颈或者出现故障。
  • avgqu-sz:向设备发出的请求平均数量。如果这个数值大于1,可能是硬件设备已经饱和(部分前端硬件设备支持并行写入)。
  • %util:设备利用率。这个数值表示设备的繁忙程度,经验值是如果超过60,可能会影响IO性能(可以参照IO操作平均等待时间)。如果到达100%,说明硬件设备已经饱和。

如果显示的是逻辑设备的数据,那么设备利用率不代表后端实际的硬件设备已经饱和。值得注意的是,即使IO性能不理想,也不一定意味这应用程序性能会不好,可以利用诸如预读取、写缓存等策略提升应用性能。

free –m

$ free -m
             total       used       free     shared    buffers     cached
Mem:        245998      24545     221453         83         59        541
-/+ buffers/cache:      23944     222053
Swap:            0          0          0

free命令可以查看系统内存的使用情况,-m参数表示按照兆字节展示。最后两列分别表示用于IO缓存的内存数,和用于文件系统页缓存的内存数。需要注意的是,第二行-/+ buffers/cache,看上去缓存占用了大量内存空间。这是Linux系统的内存使用策略,尽可能的利用内存,如果应用程序需要内存,这部分内存会立即被回收并分配给应用程序。因此,这部分内存一般也被当成是可用内存。

如果可用内存非常少,系统可能会动用交换区(如果配置了的话),这样会增加IO开销(可以在iostat命令中提现),降低系统性能。

sar -n DEV 1

$ sar -n DEV 1
Linux 3.13.0-49-generic (titanclusters-xxxxx)  07/14/2015     _x86_64_    (32 CPU)
12:16:48 AM     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil
12:16:49 AM      eth0  18763.00   5032.00  20686.42    478.30      0.00      0.00      0.00      0.00
12:16:49 AM        lo     14.00     14.00      1.36      1.36      0.00      0.00      0.00      0.00
12:16:49 AM   docker0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
12:16:49 AM     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil
12:16:50 AM      eth0  19763.00   5101.00  21999.10    482.56      0.00      0.00      0.00      0.00
12:16:50 AM        lo     20.00     20.00      3.25      3.25      0.00      0.00      0.00      0.00
12:16:50 AM   docker0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
^C

sar命令在这里可以查看网络设备的吞吐率。在排查性能问题时,可以通过网络设备的吞吐量,判断网络设备是否已经饱和。如示例输出中,eth0网卡设备,吞吐率大概在22 Mbytes/s,既176 Mbits/sec,没有达到1Gbit/sec的硬件上限。

sar -n TCP,ETCP 1

$ sar -n TCP,ETCP 1
Linux 3.13.0-49-generic (titanclusters-xxxxx)  07/14/2015    _x86_64_    (32 CPU)
12:17:19 AM  active/s passive/s    iseg/s    oseg/s
12:17:20 AM      1.00      0.00  10233.00  18846.00
12:17:19 AM  atmptf/s  estres/s retrans/s isegerr/s   orsts/s
12:17:20 AM      0.00      0.00      0.00      0.00      0.00
12:17:20 AM  active/s passive/s    iseg/s    oseg/s
12:17:21 AM      1.00      0.00   8359.00   6039.00
12:17:20 AM  atmptf/s  estres/s retrans/s isegerr/s   orsts/s
12:17:21 AM      0.00      0.00      0.00      0.00      0.00
^C

sar命令在这里用于查看TCP连接状态,其中包括:

  • active/s:每秒本地发起的TCP连接数,既通过connect调用创建的TCP连接;
  • passive/s:每秒远程发起的TCP连接数,即通过accept调用创建的TCP连接;
  • retrans/s:每秒TCP重传数量;

TCP连接数可以用来判断性能问题是否由于建立了过多的连接,进一步可以判断是主动发起的连接,还是被动接受的连接。TCP重传可能是因为网络环境恶劣,或者服务器压力过大导致丢包。

top

$ top
top - 00:15:40 up 21:56,  1 user,  load average: 31.09, 29.87, 29.92
Tasks: 871 total,   1 running, 868 sleeping,   0 stopped,   2 zombie
%Cpu(s): 96.8 us,  0.4 sy,  0.0 ni,  2.7 id,  0.1 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:  25190241+total, 24921688 used, 22698073+free,    60448 buffers
KiB Swap:        0 total,        0 used,        0 free.   554208 cached Mem
   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 20248 root      20   0  0.227t 0.012t  18748 S  3090  5.2  29812:58 java
  4213 root      20   0 2722544  64640  44232 S  23.5  0.0 233:35.37 mesos-slave
 66128 titancl+  20   0   24344   2332   1172 R   1.0  0.0   0:00.07 top
  5235 root      20   0 38.227g 547004  49996 S   0.7  0.2   2:02.74 java
  4299 root      20   0 20.015g 2.682g  16836 S   0.3  1.1  33:14.42 java
     1 root      20   0   33620   2920   1496 S   0.0  0.0   0:03.82 init
     2 root      20   0       0      0      0 S   0.0  0.0   0:00.02 kthreadd
     3 root      20   0       0      0      0 S   0.0  0.0   0:05.35 ksoftirqd/0
     5 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/0:0H
     6 root      20   0       0      0      0 S   0.0  0.0   0:06.94 kworker/u256:0
     8 root      20   0       0      0      0 S   0.0  0.0   2:38.05 rcu_sched

top命令包含了前面好几个命令的检查的内容。比如系统负载情况(uptime)、系统内存使用情况(free)、系统CPU使用情况(vmstat)等。因此通过这个命令,可以相对全面的查看系统负载的来源。同时,top命令支持排序,可以按照不同的列排序,方便查找出诸如内存占用最多的进程、CPU占用率最高的进程等。

但是,top命令相对于前面一些命令,输出是一个瞬间值,如果不持续盯着,可能会错过一些线索。这时可能需要暂停top命令刷新,来记录和比对数据。

总结

排查Linux服务器性能问题还有很多工具,上面介绍的一些命令,可以帮助我们快速的定位问题。例如前面的示例输出,多个证据证明有JAVA进程占用了大量CPU资源,之后的性能调优就可以针对应用程序进行。



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



构建一个完整的Cordova应用

$
0
0

本文承接上篇《创建Cordova插件》,通过实现一个简单的应用作为这个Cordova初级系列的结束。

前边对Cordova编程已经讲了不少了,还没有拿真实应用为例完整的演练一遍构建过程。这里将用一个完整的应用为例从头到尾一步步的演示如何创建和测试应用。

关于示例应用

把所有的API集中在一个例子中展示是一个好办法。下面我们以实现一个指南针表盘为例。

应用在屏幕上显示一个表示指南针转盘的图像。当用户沿着水平坐标轴转动设备时,指南针图像也转动。应用效果如下图:

图片描述

这里会用到jQuery Mobile提升用户UI,还会利用Cordova merges文件夹为Android和iOS提供不同的图像(方便起见下面只在Android平台开发)。

创建应用

在开发文件夹中用CLI创建Cordova项:

cordova create compass
cd compass
cordova platform add android

这时我们就有了一个新的Cordova项目,它支持Android平台。项目中可能会用到一个或多个核心插件。可以访问Cordova CLI使用指南网站,找到包含向项目添加每个Cordova插件的完整命令说明。

我们要在程序中调试应用,需要向控制台写信息,参照指南中添加 console插件的命令,如下:

cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-console.git

应用使用了指南针,还需要使用如下命令添加compass插件。

cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-device-orientation.git

注意如果不在Cordova工作时打开 -d开关,它不会向你输出执行过程信息。

接下来复制以前示例中的index.html到项目的www目录。用如下代码编辑这个页面:

<body onload="onBodyLoad()"><h1>Example 13-2</h1><img src="compass.png" id="compass" /><br /><p id="headingInfo"></p></body>       

为了把应用做的漂亮些,可以使用jQuery Mobile把应用创建的更像移动应用。首先下载jQuery和jQuery Mobile,复制到www目录中,并向index.html页的head部分添加对它们的引用。

<link rel="stylesheet" href="jquery.mobile-1.3.2.min.css /><script type="text/javascript" charset="utf-8" src="jquery-2.0.3.min.js"></script><script type="text/javascript" charset="utf-8" src="jquery.mobile-1.3.2.min.js"></script>

应用旋转图像的能力由一个叫jQuery Rotate的免费插件实现;你可以在 http://code.google.com/p/jqueryrotate找到关于这个插件的信息。要添加插件,把这个插件的js文件放在www文件夹中,然后在index.html中添加如下一行script标签:

<script type="text/javascript" charset="utf-8" src="jQueryRotateCompressed.js"></script> 

当需要的jQuery文件都就位后,把index.html的body部分更新成下面这样:

<body onload="onBodyLoad()"><div data-role="page"><div data-role="header"><h1>指南针</h1></div></div><div data-role="content"><div style="text-algin:center;"><img src="img/compass.png" id="compass" alt=""><br/><p id="headingInfo"><b>Heading:</b> 0 Degreess</p></div></div><div data-role="footer" data-position="fixed"><h3>Created By Af</h3></div></body>

是jQuery Mobile使用元素中的data-role属性来把这些特殊的元素定义成适当的外观。因此在前面的例子中,在页面上创建了一个header div并把它的data-role赋为header,同样把footer div的data-role属性设置为footer。data-position="fixed"把页脚固定在页面底部。

接下来我把页面的内容放在一个属性data-role是content的div中。为了加载指南针图像,并在页面居中显示朝向信息,添加一个新的div并设置成"style="text-algin:center"的样式。

注意为了不显得杂乱,没有把style特性放在data-role为content的div中。也方便说明学习重点。

现在已经升级了用户界面,是时候开始关注应用的js代码了。在应用的index.html的script标签中定义了一些应用用到的函数。

第一个要用到的是响应window.onerror事件的函数。因为我们知道代码不会正确执行,并且多数时候Cordova应用发生错误时都是保持静默的,这样为这个事件赋值并关联处理函数以便于发现发生在运行应用上的错误。下面展示的这个函数,基本上是接受了一个错误,应用文件名URL碰到了这个错误,并且行号引起了这个错误;然后它产生了适当的错误信息并把它写到日志,显示到对话框上。

// 遇到错误时触发
window.oneeror = function(msg, url, line) {
    var resStr;
    var index = url.lastIndexOf('/');
    if (index > -1) {
        url = url.substring(index + 1);
    }
    resStr = 'Error in ' + url + ' on line ' + ': ' + msg;
    console.log(appName + resStr);
    alert(resStr);
    return false;
}

接下来是通篇都用到的onDeviceReady函数。它作为Cordova的deviceready事件的监听器创建,并且在Cordova容器完成初始化后触发。在这个函数中了解到Cordova容器准备好了,这样可做想做的一切了。以下是这个函数:

function OnDeviceReady() {
    console.log('onDeiveReady fired.');
    hi = document.getElementById('headingInfo');
    // 设置监视
    // 每秒(1000毫秒)读指南针
    var watchOptions = {
        frequency : 1000
    };
    console.log(appName + 'Creating watch: ' + JSON.stringify(watchOptions));
    watchID = navigator.compass.watchHeading(onSuccess, onError, watchOptions);
}

函数首先定义了一个hi变量,它指向ID为headingInfo的页面元素。变量稍后用来用展示设备朝向的元素替换内容。

接下来函数定义了一个watchOptions变量,它用来设置一个朝向监视,这个监视可以让应用定期更新朝向。watchOptions对象即可以给它的frequency属性或filter属性赋值。

当使用朝向监视时,它每1000毫秒会让Compass API报告设备朝向。filter属性用来在报告朝向前定义朝向变化的数量(用度)。下面的例子filter属性告诉Compass API每秒发布大于1度的朝向:

var watchOptions = {
    filter: 1
};

这里可以指定这两个属性,但要是指定了filter,frequency属性会被Compass API忽略。

注:此处是坑。Android已经不支持filter。

var watchOptions = {
    frequencey: 1000,
    filter: 1
};

定义watchOptions后,函数调用watchHeadin来创建朝向监视:

watchID = navigator.compass.watchHeading(onSuccess, onError, watchOptions);

就像已经了解的所有其他Cordodva API一样,onSucces函数在Compass API发送一个朝向值时执行,onError函数在Compass API遇到错误时执行。

当执行onSuccess时,Compass API把heading对象传递过来,它包括表示设备朝向的属性,如下:

{"magneticHeading":0,"trueHeading":0,"headingAccuracy":0,"timestamp":137873854661
}

onSuccess函数使用heading对象的magneticHeading属性来确定当前朝向,然后使用这个值按照度数旋转指南针图像,就像下面函数展示的:

function onSuccess(heading) {
    console.log(app.Name + 'Received Heading');
    console.log(appName + JSON.stringify(heading));
    var hv = Math.round(heading.magneticHeading);
    console.log(appName + 'Rotating to ' + hv + ' degrees');
    $("#compass").rotating(-hv);
    hi.innerHTML = '<b>Heading:</b> ' + hv + ' Degrees';
}

注意指南针图像以设备当前朝向相反的方向旋转。这是因为设备被转动,指南针图像必须以相反的方向转动,这样指南针的北方总是指向磁极的北方。

最后应用的onError函数在监视发生错误时执行。它用传递给函数的error对象来识别错误的原因并为用户显示适当的错误信息。注意onError函数还取消了监视,因为在应用不能测量朝向时继续监视朝向也没有太大意义了。

function onError(err) {
    console.error(appName + 'Heading Error');
    console.error(appName + 'Error: ' + JSON.stringify(err));
    // 发生问题移除监视
    navigator.compass.clearWatch(watchID);
    // 在页面上清除朝向值
    hi.innerHTML = '<b>Heading: </b>None';
    // 告之用户
    if (err.code == CompassError.COMPASS_NOT_SUPPORTED) {
        aleert('不支持指南针');
    } else if (compassError.code == CompassError.COMPASS_INTERNAL_ERR) {
        alert('指南针内部错误');
    } else {
        alert('未知的朝向错误');
    }
}

注意比以往其他应用更多次的写控制台。这么做是为了更真实的展示创建Cordova应用。多次使用控制台写出所有碰见的应用对象,这让我们更好的了解从应用中获得了什么,这样能在发生错误时更容易的调试问题。

在应用中定义一个appName对象:

var appName = "Compass - ";

当应用写控制台时,把appName的值追加到每个记录中:

console.error(appName + 'some message');

当在Android上调试时,可以用监视器应用来查看实时的日志记录。就像14.4展示的,你可以用appName过滤传入的值以便只查看这个应用的控制台信息。

测试应用且它是合意的,移除或标注多处应用写控制台的地方。

完整的index.html请参考源码。

使用Merges

现在还没有提过Cordova CLI的merge功能。前面"Cordova开发机制"解释过它怎样工作,这里展示它的使用。

假设项目在compass文件夹中,把Android平台的指南针图像compass.png放在compass/merges/android/img/中,把iOS版本的放在merges/ios/img/中。在构建过程中,Cordova CLI把merges中的android文件夹的图像复制到compass/platforms/android/assets/www/img/中。ios也是同样的做法。

结果是可以维护一套代码并在需要的正确资源中按照平台切换。这对不同平台来说更加易于管理资源。

最后注意在模拟器上一般没有指南针,需要需要在真机上测试这个应用。

转眼就到2015年底了,最近几个月有些事就没有持续写。最近想起来好像提到过这个系列应该有个收尾,就不要让它跨年了。另外最近看了看一些其他技术,像ReactJS、ASP.NET Web API、Kotlin等,还正在读一些软件构建技术方面的经典(惭愧,应该是早就读过的),希望自己能把学习和实践中的一些粗浅认识拿出来晒,期待与您共同进步。

【数读】中国每年过早拆除4.6亿平方米房屋

$
0
0

【数读】中国每年过早拆除4.6亿平方米房屋

中国每年过早拆除4.6亿平方米房屋

中国每年过早拆除4.6亿平方米房屋

中国每年过早拆除4.6亿平方米房屋

近日,原江苏省委常委赵少麟之子赵晋在天津的房产帝国崩塌,其开发的水岸银座项目将被拆除,包括一栋已经完工的200米高公寓楼,引发讨论。

过早拆除房屋已经不是中国新问题,在2010年的第6届国际绿色建筑与建筑节能大会上,有专家指出中国被拆除建筑的平均寿命只有30年。而根据中国建筑学会的研究,欧洲国家的建筑寿命普遍较长,比利时为90年,法国为102.9年,德国为63.8年,西班牙为77.4年,英国高达132.6年。

去年9月,中国建筑科学研究院的《建筑拆除管理政策研究》报告显示,十一五期间共拆除46亿平方米建筑,5年间被拆城镇建筑占到30亿平方米,20亿平方米的被拆建筑寿命小于40年。

根据十一五期间的拆建比估算,十二五期间中国每年过早拆除建筑面积约为4.6亿平方米,按平均每平方米1千元计算,因过早拆除房屋每年浪费4600亿元。

过早的拆除房屋,无论是建筑资源还是社会财富均会造成巨大的损失。拆除的过程中出了要消耗大量的人力、物力、运力,还要产生大量的粉尘和废弃物。

中国建筑科学研究院的报告测算,按每年过早拆除4.6亿平方米建筑,每平米需要水泥200公斤,钢材60公斤计算,会造成每年0.92亿吨水泥浪费,0.28亿吨钢材浪费,0.51亿吨原煤浪费,每年增加建筑垃圾4.6亿吨,增加CO2排放1.33亿吨。

该报告还统计了2001年以来公开报道中过早拆除建筑的主要原因,因为工程质量和运营不善等建筑自身原因拆除的仅占19%,而因为追求商业利益、规划不当、形象工程等原因拆除占到了81%,商业利益和政绩形象是造成建筑拆除的关键因素。

在此项统计中,大量追求商业利益的不合理拆除往往符合城市规划,在合法合规的情况下进行的,暴露出中国城市规划和建筑拆除监管方面存在的诸多问题。

更多有态度内容请下载网易新闻  http://www.163.com/newsapp/

数读栏目招聘实习生:http://data.163.com/15/1215/13/BASLS4SO00014MTN.html

[详细]

Viewing all 11804 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>